From 9ec0e2e07158fd7e166744a739da4482e2413314 Mon Sep 17 00:00:00 2001 From: derped Date: Thu, 28 Feb 2019 08:42:46 +0100 Subject: [PATCH] Init --- linux_surface/default.nix | 79 + linux_surface/patches/0001-surface-acpi.patch | 3018 +++ linux_surface/patches/0002-resume-delay.patch | 63 + linux_surface/patches/0003-buttons.patch | 207 + linux_surface/patches/0004-cameras.patch | 2753 +++ linux_surface/patches/0005-ipts.patch | 6123 +++++ linux_surface/patches/0006-hid.patch | 144 + .../patches/0007-sdcard-reader.patch | 26 + linux_surface/patches/0008-wifi.patch | 272 + .../patches/0009-surface3-power.patch | 753 + linux_surface/patches/0010-surface-dock.patch | 25 + linux_surface/patches/0011-mwlwifi.patch | 19755 ++++++++++++++++ linux_surface/patches/bridge-stp-helper.patch | 13 + linux_surface/patches/config | 9569 ++++++++ .../patches/modinst-arg-list-too-long.patch | 14 + mu-git/default.nix | 56 + mu-git/failing_tests.patch | 18 + release.nix | 19 + xdiskusage/default.nix | 13 + 19 files changed, 42920 insertions(+) create mode 100644 linux_surface/default.nix create mode 100644 linux_surface/patches/0001-surface-acpi.patch create mode 100644 linux_surface/patches/0002-resume-delay.patch create mode 100644 linux_surface/patches/0003-buttons.patch create mode 100644 linux_surface/patches/0004-cameras.patch create mode 100644 linux_surface/patches/0005-ipts.patch create mode 100644 linux_surface/patches/0006-hid.patch create mode 100644 linux_surface/patches/0007-sdcard-reader.patch create mode 100644 linux_surface/patches/0008-wifi.patch create mode 100644 linux_surface/patches/0009-surface3-power.patch create mode 100644 linux_surface/patches/0010-surface-dock.patch create mode 100644 linux_surface/patches/0011-mwlwifi.patch create mode 100644 linux_surface/patches/bridge-stp-helper.patch create mode 100644 linux_surface/patches/config create mode 100644 linux_surface/patches/modinst-arg-list-too-long.patch create mode 100644 mu-git/default.nix create mode 100644 mu-git/failing_tests.patch create mode 100644 release.nix create mode 100644 xdiskusage/default.nix diff --git a/linux_surface/default.nix b/linux_surface/default.nix new file mode 100644 index 0000000..866395c --- /dev/null +++ b/linux_surface/default.nix @@ -0,0 +1,79 @@ +{ stdenv, buildPackages, fetchurl, perl, buildLinux, modDirVersionArg ? null, ... } @ args: + +with stdenv.lib; + +buildLinux (args // rec { + version = "4.19.25"; + + # modDirVersion needs to be x.y.z, will automatically add .0 if needed + modDirVersion = if (modDirVersionArg == null) then concatStrings (intersperse "." (take 3 (splitString "." "${version}.0"))) else modDirVersionArg; + + # branchVersion needs to be x.y + extraMeta.branch = concatStrings (intersperse "." (take 2 (splitString "." version))); + + src = fetchurl { + url = "mirror://kernel/linux/kernel/v4.x/linux-${version}.tar.xz"; + sha256 = "0ccpj57pv2rw78a4j5mg9sz7a37k0sn5glbn2rs6yvp9ss81vivy"; + }; + + kernelPatches = [ + { + name = "modinst-arg-list-too-long"; + patch = ./patches/modinst-arg-list-too-long.patch; + } + { + name = "bridge-stp-helper"; + patch = ./patches/bridge-stp-helper.patch; + } + { + name = "surface-acpi"; + patch = ./patches/0001-surface-acpi.patch; + } + { + name = "resume-delay"; + patch = ./patches/0002-resume-delay.patch; + } + { + name = "buttons"; + patch = ./patches/0003-buttons.patch; + } + { + name = "cameras"; + patch = ./patches/0004-cameras.patch; + } + { + name = "ipts"; + patch = ./patches/0005-ipts.patch; + } + { + name = "hid"; + patch = ./patches/0006-hid.patch; + } + { + name = "sdcard-reader"; + patch = ./patches/0007-sdcard-reader.patch; + } + { + name = "wifi"; + patch = ./patches/0008-wifi.patch; + } + { + name = "surface3-power"; + patch = ./patches/0009-surface3-power.patch; + } + { + name = "surface-dock"; + patch = ./patches/0010-surface-dock.patch; + } + { + name = "mwlwifi"; + patch = ./patches/0011-mwlwifi.patch; + } + ]; + + extraConfig = '' + I2C_DESIGNWARE_PLATFORM m + X86_INTEL_LPSS y + '' + + (args.extraConfig or ""); +} // (args.argsOverride or {})) diff --git a/linux_surface/patches/0001-surface-acpi.patch b/linux_surface/patches/0001-surface-acpi.patch new file mode 100644 index 0000000..a7e438d --- /dev/null +++ b/linux_surface/patches/0001-surface-acpi.patch @@ -0,0 +1,3018 @@ +From 6694d6dfb749b6d9149dd88ec9d6a51fc2d71869 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Sun, 27 Jan 2019 10:45:19 -0500 +Subject: [PATCH 01/11] surface-acpi + +--- + drivers/acpi/acpica/dsopcode.c | 2 +- + drivers/acpi/acpica/exfield.c | 26 +- + drivers/platform/x86/Kconfig | 56 + + drivers/platform/x86/Makefile | 1 + + drivers/platform/x86/surface_acpi.c | 2727 +++++++++++++++++++++++++++ + drivers/tty/serdev/core.c | 90 +- + 6 files changed, 2883 insertions(+), 19 deletions(-) + create mode 100644 drivers/platform/x86/surface_acpi.c + +diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c +index 78f9de260d5f..0cd858520f5b 100644 +--- a/drivers/acpi/acpica/dsopcode.c ++++ b/drivers/acpi/acpica/dsopcode.c +@@ -123,7 +123,7 @@ acpi_ds_init_buffer_field(u16 aml_opcode, + + /* Offset is in bits, count is in bits */ + +- field_flags = AML_FIELD_ACCESS_BYTE; ++ field_flags = AML_FIELD_ACCESS_BUFFER; + bit_offset = offset; + bit_count = (u32) length_desc->integer.value; + +diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c +index b272c329d45d..cf547883a993 100644 +--- a/drivers/acpi/acpica/exfield.c ++++ b/drivers/acpi/acpica/exfield.c +@@ -102,6 +102,7 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, + void *buffer; + u32 function; + u16 accessor_type; ++ u8 field_flags; + + ACPI_FUNCTION_TRACE_PTR(ex_read_data_from_field, obj_desc); + +@@ -199,11 +200,16 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, + * Note: Field.length is in bits. + */ + length = +- (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length); ++ (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length); ++ field_flags = obj_desc->common_field.field_flags; + +- if (length > acpi_gbl_integer_byte_width) { ++ if (length > acpi_gbl_integer_byte_width || ++ (field_flags & AML_FIELD_ACCESS_TYPE_MASK) == AML_FIELD_ACCESS_BUFFER) { + +- /* Field is too large for an Integer, create a Buffer instead */ ++ /* ++ * Field is either too large for an Integer, or a actually of type ++ * buffer, so create a Buffer. ++ */ + + buffer_desc = acpi_ut_create_buffer_object(length); + if (!buffer_desc) { +@@ -366,19 +372,7 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, + } else if (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_GSBUS) { + accessor_type = obj_desc->field.attribute; +- length = +- acpi_ex_get_serial_access_length(accessor_type, +- obj_desc->field. +- access_length); +- +- /* +- * Add additional 2 bytes for the generic_serial_bus data buffer: +- * +- * Status; (Byte 0 of the data buffer) +- * Length; (Byte 1 of the data buffer) +- * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer) +- */ +- length += 2; ++ length = source_desc->buffer.length; + function = ACPI_WRITE | (accessor_type << 16); + } else { /* IPMI */ + +diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig +index 0c1aa6c314f5..716a32e1c66f 100644 +--- a/drivers/platform/x86/Kconfig ++++ b/drivers/platform/x86/Kconfig +@@ -573,6 +573,62 @@ config THINKPAD_ACPI_HOTKEY_POLL + If you are not sure, say Y here. The driver enables polling only if + it is strictly necessary to do so. + ++config SURFACE_ACPI ++ depends on ACPI ++ tristate "Microsoft Surface ACPI/Platform Drivers" ++ ---help--- ++ ACPI and platform drivers for Microsoft Surface devices. ++ ++config SURFACE_ACPI_SSH ++ bool "Surface Serial Hub Driver" ++ depends on SURFACE_ACPI ++ depends on X86_INTEL_LPSS ++ depends on SERIAL_8250_DW ++ depends on SERIAL_8250_DMA ++ depends on SERIAL_DEV_CTRL_TTYPORT ++ select CRC_CCITT ++ default y ++ ---help--- ++ Surface Serial Hub driver for 5th generation (or later) Microsoft ++ Surface devices. ++ ++ This is the base driver for the embedded serial controller found on ++ 5th generation (and later) Microsoft Surface devices (e.g. Book 2, ++ Laptop, Laptop 2, Pro 2017, Pro 6, ...). This driver itself only ++ provides access to the embedded controller and subsequent drivers are ++ required for the respective functionalities. ++ ++ If you have a 5th generation (or later) Microsoft Surface device, say ++ Y or M here. ++ ++config SURFACE_ACPI_SAN ++ bool "Surface ACPI Notify Driver" ++ depends on SURFACE_ACPI_SSH ++ default y ++ ---help--- ++ Surface ACPI Notify driver for 5th generation (or later) Microsoft ++ Surface devices. ++ ++ This driver enables basic ACPI events and requests, such as battery ++ status requests/events, thermal events, lid status, and possibly more, ++ which would otherwise not work on these devices. ++ ++ If you are not sure, say Y here. ++ ++config SURFACE_ACPI_VHF ++ bool "Surface Virtual HID Framework Driver" ++ depends on SURFACE_ACPI_SSH ++ depends on HID ++ default y ++ ---help--- ++ Surface Virtual HID Framework driver for 5th generation (or later) ++ Microsoft Surface devices. ++ ++ This driver provides support for the Microsoft Virtual HID framework, ++ which is required for the Surface Laptop (1 and newer) keyboard. ++ ++ If you are not sure, say Y here. ++ + config SENSORS_HDAPS + tristate "Thinkpad Hard Drive Active Protection System (hdaps)" + depends on INPUT +diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile +index e6d1becf81ce..ab8be80b6596 100644 +--- a/drivers/platform/x86/Makefile ++++ b/drivers/platform/x86/Makefile +@@ -35,6 +35,7 @@ obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o + obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o + obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o + obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o ++obj-$(CONFIG_SURFACE_ACPI) += surface_acpi.o + obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o + obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o + obj-$(CONFIG_FUJITSU_TABLET) += fujitsu-tablet.o +diff --git a/drivers/platform/x86/surface_acpi.c b/drivers/platform/x86/surface_acpi.c +new file mode 100644 +index 000000000000..6a3aa6a01493 +--- /dev/null ++++ b/drivers/platform/x86/surface_acpi.c +@@ -0,0 +1,2727 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/************************************************************************* ++ * Surface Serial Hub driver (cross-driver interface) ++ */ ++ ++#ifdef CONFIG_SURFACE_ACPI_SSH ++ ++/* ++ * Maximum request payload size in bytes. ++ * Value based on ACPI (255 bytes minus header/status bytes). ++ */ ++#define SURFACEGEN5_MAX_RQST_PAYLOAD (255 - 10) ++ ++/* ++ * Maximum response payload size in bytes. ++ * Value based on ACPI (255 bytes minus header/status bytes). ++ */ ++#define SURFACEGEN5_MAX_RQST_RESPONSE (255 - 4) ++ ++#define SURFACEGEN5_RQID_EVENT_BITS 5 ++ ++#define SURFACEGEN5_EVENT_IMMEDIATE ((unsigned long) -1) ++ ++ ++struct surfacegen5_buf { ++ u8 cap; ++ u8 len; ++ u8 *data; ++}; ++ ++struct surfacegen5_rqst { ++ u8 tc; ++ u8 iid; ++ u8 cid; ++ u8 snc; ++ u8 cdl; ++ u8 *pld; ++}; ++ ++struct surfacegen5_event { ++ u16 rqid; ++ u8 tc; ++ u8 iid; ++ u8 cid; ++ u8 len; ++ u8 *pld; ++}; ++ ++ ++typedef int (*surfacegen5_ec_event_handler_fn)(struct surfacegen5_event *event, void *data); ++typedef unsigned long (*surfacegen5_ec_event_handler_delay)(struct surfacegen5_event *event, void *data); ++ ++struct device_link *surfacegen5_ec_consumer_add(struct device *consumer, u32 flags); ++int surfacegen5_ec_consumer_remove(struct device_link *link); ++ ++int surfacegen5_ec_rqst(const struct surfacegen5_rqst *rqst, struct surfacegen5_buf *result); ++ ++int surfacegen5_ec_enable_event_source(u8 tc, u8 unknown, u16 rqid); ++int surfacegen5_ec_disable_event_source(u8 tc, u8 unknown, u16 rqid); ++int surfacegen5_ec_remove_event_handler(u16 rqid); ++int surfacegen5_ec_set_event_handler(u16 rqid, surfacegen5_ec_event_handler_fn fn, void *data); ++int surfacegen5_ec_set_delayed_event_handler(u16 rqid, ++ surfacegen5_ec_event_handler_fn fn, ++ surfacegen5_ec_event_handler_delay delay, ++ void *data); ++ ++#endif /* CONFIG_SURFACE_ACPI_SSH */ ++ ++ ++/************************************************************************* ++ * Surface Serial Hub driver (private implementation) ++ */ ++ ++#ifdef CONFIG_SURFACE_ACPI_SSH ++ ++#define SG5_RQST_TAG_FULL "surfacegen5_ec_rqst: " ++#define SG5_RQST_TAG "rqst: " ++#define SG5_EVENT_TAG "event: " ++#define SG5_RECV_TAG "recv: " ++ ++#define SG5_SUPPORTED_FLOW_CONTROL_MASK (~((u8) ACPI_UART_FLOW_CONTROL_HW)) ++ ++#define SG5_BYTELEN_SYNC 2 ++#define SG5_BYTELEN_TERM 2 ++#define SG5_BYTELEN_CRC 2 ++#define SG5_BYTELEN_CTRL 4 // command-header, ACK, or RETRY ++#define SG5_BYTELEN_CMDFRAME 8 // without payload ++ ++#define SG5_MAX_WRITE ( \ ++ SG5_BYTELEN_SYNC \ ++ + SG5_BYTELEN_CTRL \ ++ + SG5_BYTELEN_CRC \ ++ + SG5_BYTELEN_CMDFRAME \ ++ + SURFACEGEN5_MAX_RQST_PAYLOAD \ ++ + SG5_BYTELEN_CRC \ ++) ++ ++#define SG5_MSG_LEN_CTRL ( \ ++ SG5_BYTELEN_SYNC \ ++ + SG5_BYTELEN_CTRL \ ++ + SG5_BYTELEN_CRC \ ++ + SG5_BYTELEN_TERM \ ++) ++ ++#define SG5_MSG_LEN_CMD_BASE ( \ ++ SG5_BYTELEN_SYNC \ ++ + SG5_BYTELEN_CTRL \ ++ + SG5_BYTELEN_CRC \ ++ + SG5_BYTELEN_CRC \ ++) // without payload and command-frame ++ ++#define SG5_WRITE_TIMEOUT msecs_to_jiffies(1000) ++#define SG5_READ_TIMEOUT msecs_to_jiffies(1000) ++#define SG5_NUM_RETRY 3 ++ ++#define SG5_WRITE_BUF_LEN SG5_MAX_WRITE ++#define SG5_READ_BUF_LEN 512 // must be power of 2 ++#define SG5_EVAL_BUF_LEN SG5_MAX_WRITE // also works for reading ++ ++#define SG5_FRAME_TYPE_CMD 0x80 ++#define SG5_FRAME_TYPE_ACK 0x40 ++#define SG5_FRAME_TYPE_RETRY 0x04 ++ ++#define SG5_FRAME_OFFS_CTRL SG5_BYTELEN_SYNC ++#define SG5_FRAME_OFFS_CTRL_CRC (SG5_FRAME_OFFS_CTRL + SG5_BYTELEN_CTRL) ++#define SG5_FRAME_OFFS_TERM (SG5_FRAME_OFFS_CTRL_CRC + SG5_BYTELEN_CRC) ++#define SG5_FRAME_OFFS_CMD SG5_FRAME_OFFS_TERM // either TERM or CMD ++#define SG5_FRAME_OFFS_CMD_PLD (SG5_FRAME_OFFS_CMD + SG5_BYTELEN_CMDFRAME) ++ ++/* ++ * A note on Request IDs (RQIDs): ++ * 0x0000 is not a valid RQID ++ * 0x0001 is valid, but reserved for Surface Laptop keyboard events ++ */ ++#define SG5_NUM_EVENT_TYPES ((1 << SURFACEGEN5_RQID_EVENT_BITS) - 1) ++ ++/* ++ * Sync: aa 55 ++ * Terminate: ff ff ++ * ++ * Request Message: sync cmd-hdr crc(cmd-hdr) cmd-rqst-frame crc(cmd-rqst-frame) ++ * Ack Message: sync ack crc(ack) terminate ++ * Retry Message: sync retry crc(retry) terminate ++ * Response Message: sync cmd-hdr crc(cmd-hdr) cmd-resp-frame crc(cmd-resp-frame) ++ * ++ * Command Header: 80 LEN 00 SEQ ++ * Ack: 40 00 00 SEQ ++ * Retry: 04 00 00 00 ++ * Command Request Frame: 80 RTC 01 00 RIID RQID RCID PLD ++ * Command Response Frame: 80 RTC 00 01 RIID RQID RCID PLD ++ */ ++ ++struct surfacegen5_frame_ctrl { ++ u8 type; ++ u8 len; // without crc ++ u8 pad; ++ u8 seq; ++} __packed; ++ ++struct surfacegen5_frame_cmd { ++ u8 type; ++ u8 tc; ++ u8 unknown1; ++ u8 unknown2; ++ u8 iid; ++ u8 rqid_lo; // id for request/response matching (low byte) ++ u8 rqid_hi; // id for request/response matching (high byte) ++ u8 cid; ++} __packed; ++ ++ ++enum surfacegen5_ec_state { ++ SG5_EC_UNINITIALIZED, ++ SG5_EC_INITIALIZED, ++ SG5_EC_SUSPENDED, ++}; ++ ++struct surfacegen5_ec_counters { ++ u8 seq; // control sequence id ++ u16 rqid; // id for request/response matching ++}; ++ ++struct surfacegen5_ec_writer { ++ u8 *data; ++ u8 *ptr; ++} __packed; ++ ++enum surfacegen5_ec_receiver_state { ++ SG5_RCV_DISCARD, ++ SG5_RCV_CONTROL, ++ SG5_RCV_COMMAND, ++}; ++ ++struct surfacegen5_ec_receiver { ++ spinlock_t lock; ++ enum surfacegen5_ec_receiver_state state; ++ struct completion signal; ++ struct kfifo fifo; ++ struct { ++ bool pld; ++ u8 seq; ++ u16 rqid; ++ } expect; ++ struct { ++ u16 cap; ++ u16 len; ++ u8 *ptr; ++ } eval_buf; ++}; ++ ++struct surfacegen5_ec_event_handler { ++ surfacegen5_ec_event_handler_fn handler; ++ surfacegen5_ec_event_handler_delay delay; ++ void *data; ++}; ++ ++struct surfacegen5_ec_events { ++ spinlock_t lock; ++ struct workqueue_struct *queue_ack; ++ struct workqueue_struct *queue_evt; ++ struct surfacegen5_ec_event_handler handler[SG5_NUM_EVENT_TYPES]; ++}; ++ ++struct surfacegen5_ec { ++ struct mutex lock; ++ enum surfacegen5_ec_state state; ++ struct serdev_device *serdev; ++ struct surfacegen5_ec_counters counter; ++ struct surfacegen5_ec_writer writer; ++ struct surfacegen5_ec_receiver receiver; ++ struct surfacegen5_ec_events events; ++}; ++ ++struct surfacegen5_fifo_packet { ++ u8 type; // packet type (ACK/RETRY/CMD) ++ u8 seq; ++ u8 len; ++}; ++ ++struct surfacegen5_event_work { ++ refcount_t refcount; ++ struct surfacegen5_ec *ec; ++ struct work_struct work_ack; ++ struct delayed_work work_evt; ++ struct surfacegen5_event event; ++ u8 seq; ++}; ++ ++ ++static struct surfacegen5_ec surfacegen5_ec = { ++ .lock = __MUTEX_INITIALIZER(surfacegen5_ec.lock), ++ .state = SG5_EC_UNINITIALIZED, ++ .serdev = NULL, ++ .counter = { ++ .seq = 0, ++ .rqid = 0, ++ }, ++ .writer = { ++ .data = NULL, ++ .ptr = NULL, ++ }, ++ .receiver = { ++ .lock = __SPIN_LOCK_UNLOCKED(), ++ .state = SG5_RCV_DISCARD, ++ .expect = {}, ++ }, ++ .events = { ++ .lock = __SPIN_LOCK_UNLOCKED(), ++ .handler = {}, ++ } ++}; ++ ++ ++static int surfacegen5_ec_rqst_unlocked(struct surfacegen5_ec *ec, ++ const struct surfacegen5_rqst *rqst, ++ struct surfacegen5_buf *result); ++ ++ ++inline static struct surfacegen5_ec *surfacegen5_ec_acquire(void) ++{ ++ struct surfacegen5_ec *ec = &surfacegen5_ec; ++ ++ mutex_lock(&ec->lock); ++ return ec; ++} ++ ++inline static void surfacegen5_ec_release(struct surfacegen5_ec *ec) ++{ ++ mutex_unlock(&ec->lock); ++} ++ ++inline static struct surfacegen5_ec *surfacegen5_ec_acquire_init(void) ++{ ++ struct surfacegen5_ec *ec = surfacegen5_ec_acquire(); ++ ++ if (ec->state == SG5_EC_UNINITIALIZED) { ++ surfacegen5_ec_release(ec); ++ return NULL; ++ } ++ ++ return ec; ++} ++ ++struct device_link *surfacegen5_ec_consumer_add(struct device *consumer, u32 flags) ++{ ++ struct surfacegen5_ec *ec; ++ struct device_link *link; ++ ++ ec = surfacegen5_ec_acquire_init(); ++ if (!ec) { ++ return ERR_PTR(-ENXIO); ++ } ++ ++ link = device_link_add(consumer, &ec->serdev->dev, flags); ++ ++ surfacegen5_ec_release(ec); ++ return link; ++} ++ ++int surfacegen5_ec_consumer_remove(struct device_link *link) ++{ ++ struct surfacegen5_ec *ec = surfacegen5_ec_acquire_init(); ++ if (!ec) { ++ return -ENXIO; ++ } ++ ++ device_link_del(link); ++ ++ surfacegen5_ec_release(ec); ++ return 0; ++} ++ ++ ++inline static u16 surfacegen5_rqid_to_rqst(u16 rqid) { ++ return rqid << SURFACEGEN5_RQID_EVENT_BITS; ++} ++ ++inline static bool surfacegen5_rqid_is_event(u16 rqid) { ++ const u16 mask = (1 << SURFACEGEN5_RQID_EVENT_BITS) - 1; ++ return rqid != 0 && (rqid | mask) == mask; ++} ++ ++int surfacegen5_ec_enable_event_source(u8 tc, u8 unknown, u16 rqid) ++{ ++ struct surfacegen5_ec *ec; ++ ++ u8 pld[4] = { tc, unknown, rqid & 0xff, rqid >> 8 }; ++ u8 buf[1] = { 0x00 }; ++ ++ struct surfacegen5_rqst rqst = { ++ .tc = 0x01, ++ .iid = 0x00, ++ .cid = 0x0b, ++ .snc = 0x01, ++ .cdl = 0x04, ++ .pld = pld, ++ }; ++ ++ struct surfacegen5_buf result = { ++ result.cap = ARRAY_SIZE(buf), ++ result.len = 0, ++ result.data = buf, ++ }; ++ ++ int status; ++ ++ // only allow RQIDs that lie within event spectrum ++ if (!surfacegen5_rqid_is_event(rqid)) { ++ return -EINVAL; ++ } ++ ++ ec = surfacegen5_ec_acquire_init(); ++ if (!ec) { ++ printk(KERN_WARNING SG5_RQST_TAG_FULL "embedded controller is uninitialized\n"); ++ return -ENXIO; ++ } ++ ++ if (ec->state == SG5_EC_SUSPENDED) { ++ dev_warn(&ec->serdev->dev, SG5_RQST_TAG "embedded controller is suspended\n"); ++ ++ surfacegen5_ec_release(ec); ++ return -EPERM; ++ } ++ ++ status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result); ++ ++ if (buf[0] != 0x00) { ++ dev_warn(&ec->serdev->dev, ++ "unexpected result while enabling event source: 0x%02x\n", ++ buf[0]); ++ } ++ ++ surfacegen5_ec_release(ec); ++ return status; ++ ++} ++ ++int surfacegen5_ec_disable_event_source(u8 tc, u8 unknown, u16 rqid) ++{ ++ struct surfacegen5_ec *ec; ++ ++ u8 pld[4] = { tc, unknown, rqid & 0xff, rqid >> 8 }; ++ u8 buf[1] = { 0x00 }; ++ ++ struct surfacegen5_rqst rqst = { ++ .tc = 0x01, ++ .iid = 0x00, ++ .cid = 0x0c, ++ .snc = 0x01, ++ .cdl = 0x04, ++ .pld = pld, ++ }; ++ ++ struct surfacegen5_buf result = { ++ result.cap = ARRAY_SIZE(buf), ++ result.len = 0, ++ result.data = buf, ++ }; ++ ++ int status; ++ ++ // only allow RQIDs that lie within event spectrum ++ if (!surfacegen5_rqid_is_event(rqid)) { ++ return -EINVAL; ++ } ++ ++ ec = surfacegen5_ec_acquire_init(); ++ if (!ec) { ++ printk(KERN_WARNING SG5_RQST_TAG_FULL "embedded controller is uninitialized\n"); ++ return -ENXIO; ++ } ++ ++ if (ec->state == SG5_EC_SUSPENDED) { ++ dev_warn(&ec->serdev->dev, SG5_RQST_TAG "embedded controller is suspended\n"); ++ ++ surfacegen5_ec_release(ec); ++ return -EPERM; ++ } ++ ++ status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result); ++ ++ if (buf[0] != 0x00) { ++ dev_warn(&ec->serdev->dev, ++ "unexpected result while disabling event source: 0x%02x\n", ++ buf[0]); ++ } ++ ++ surfacegen5_ec_release(ec); ++ return status; ++} ++ ++int surfacegen5_ec_set_delayed_event_handler( ++ u16 rqid, surfacegen5_ec_event_handler_fn fn, ++ surfacegen5_ec_event_handler_delay delay, ++ void *data) ++{ ++ struct surfacegen5_ec *ec; ++ unsigned long flags; ++ ++ if (!surfacegen5_rqid_is_event(rqid)) { ++ return -EINVAL; ++ } ++ ++ ec = surfacegen5_ec_acquire_init(); ++ if (!ec) { ++ return -ENXIO; ++ } ++ ++ spin_lock_irqsave(&ec->events.lock, flags); ++ ++ // 0 is not a valid event RQID ++ ec->events.handler[rqid - 1].handler = fn; ++ ec->events.handler[rqid - 1].delay = delay; ++ ec->events.handler[rqid - 1].data = data; ++ ++ spin_unlock_irqrestore(&ec->events.lock, flags); ++ surfacegen5_ec_release(ec); ++ ++ return 0; ++} ++ ++int surfacegen5_ec_set_event_handler( ++ u16 rqid, surfacegen5_ec_event_handler_fn fn, void *data) ++{ ++ return surfacegen5_ec_set_delayed_event_handler(rqid, fn, NULL, data); ++} ++ ++int surfacegen5_ec_remove_event_handler(u16 rqid) ++{ ++ struct surfacegen5_ec *ec; ++ unsigned long flags; ++ ++ if (!surfacegen5_rqid_is_event(rqid)) { ++ return -EINVAL; ++ } ++ ++ ec = surfacegen5_ec_acquire_init(); ++ if (!ec) { ++ return -ENXIO; ++ } ++ ++ spin_lock_irqsave(&ec->events.lock, flags); ++ ++ // 0 is not a valid event RQID ++ ec->events.handler[rqid - 1].handler = NULL; ++ ec->events.handler[rqid - 1].delay = NULL; ++ ec->events.handler[rqid - 1].data = NULL; ++ ++ spin_unlock_irqrestore(&ec->events.lock, flags); ++ surfacegen5_ec_release(ec); ++ ++ /* ++ * Make sure that the handler is not in use any more after we've ++ * removed it. ++ */ ++ flush_workqueue(ec->events.queue_evt); ++ ++ return 0; ++} ++ ++ ++inline static u16 surfacegen5_ssh_crc(const u8 *buf, size_t size) ++{ ++ return crc_ccitt_false(0xffff, buf, size); ++} ++ ++inline static void surfacegen5_ssh_write_u16(struct surfacegen5_ec_writer *writer, u16 in) ++{ ++ put_unaligned_le16(in, writer->ptr); ++ writer->ptr += 2; ++} ++ ++inline static void surfacegen5_ssh_write_crc(struct surfacegen5_ec_writer *writer, ++ const u8 *buf, size_t size) ++{ ++ surfacegen5_ssh_write_u16(writer, surfacegen5_ssh_crc(buf, size)); ++} ++ ++inline static void surfacegen5_ssh_write_syn(struct surfacegen5_ec_writer *writer) ++{ ++ u8 *w = writer->ptr; ++ ++ *w++ = 0xaa; ++ *w++ = 0x55; ++ ++ writer->ptr = w; ++} ++ ++inline static void surfacegen5_ssh_write_ter(struct surfacegen5_ec_writer *writer) ++{ ++ u8 *w = writer->ptr; ++ ++ *w++ = 0xff; ++ *w++ = 0xff; ++ ++ writer->ptr = w; ++} ++ ++inline static void surfacegen5_ssh_write_buf(struct surfacegen5_ec_writer *writer, ++ u8 *in, size_t len) ++{ ++ writer->ptr = memcpy(writer->ptr, in, len) + len; ++} ++ ++inline static void surfacegen5_ssh_write_hdr(struct surfacegen5_ec_writer *writer, ++ const struct surfacegen5_rqst *rqst, ++ struct surfacegen5_ec *ec) ++{ ++ struct surfacegen5_frame_ctrl *hdr = (struct surfacegen5_frame_ctrl *)writer->ptr; ++ u8 *begin = writer->ptr; ++ ++ hdr->type = SG5_FRAME_TYPE_CMD; ++ hdr->len = SG5_BYTELEN_CMDFRAME + rqst->cdl; // without CRC ++ hdr->pad = 0x00; ++ hdr->seq = ec->counter.seq; ++ ++ writer->ptr += sizeof(*hdr); ++ ++ surfacegen5_ssh_write_crc(writer, begin, writer->ptr - begin); ++} ++ ++inline static void surfacegen5_ssh_write_cmd(struct surfacegen5_ec_writer *writer, ++ const struct surfacegen5_rqst *rqst, ++ struct surfacegen5_ec *ec) ++{ ++ struct surfacegen5_frame_cmd *cmd = (struct surfacegen5_frame_cmd *)writer->ptr; ++ u8 *begin = writer->ptr; ++ ++ u16 rqid = surfacegen5_rqid_to_rqst(ec->counter.rqid); ++ u8 rqid_lo = rqid & 0xFF; ++ u8 rqid_hi = rqid >> 8; ++ ++ cmd->type = SG5_FRAME_TYPE_CMD; ++ cmd->tc = rqst->tc; ++ cmd->unknown1 = 0x01; ++ cmd->unknown2 = 0x00; ++ cmd->iid = rqst->iid; ++ cmd->rqid_lo = rqid_lo; ++ cmd->rqid_hi = rqid_hi; ++ cmd->cid = rqst->cid; ++ ++ writer->ptr += sizeof(*cmd); ++ ++ surfacegen5_ssh_write_buf(writer, rqst->pld, rqst->cdl); ++ surfacegen5_ssh_write_crc(writer, begin, writer->ptr - begin); ++} ++ ++inline static void surfacegen5_ssh_write_ack(struct surfacegen5_ec_writer *writer, u8 seq) ++{ ++ struct surfacegen5_frame_ctrl *ack = (struct surfacegen5_frame_ctrl *)writer->ptr; ++ u8 *begin = writer->ptr; ++ ++ ack->type = SG5_FRAME_TYPE_ACK; ++ ack->len = 0x00; ++ ack->pad = 0x00; ++ ack->seq = seq; ++ ++ writer->ptr += sizeof(*ack); ++ ++ surfacegen5_ssh_write_crc(writer, begin, writer->ptr - begin); ++} ++ ++inline static void surfacegen5_ssh_writer_reset(struct surfacegen5_ec_writer *writer) ++{ ++ writer->ptr = writer->data; ++} ++ ++inline static int surfacegen5_ssh_writer_flush(struct surfacegen5_ec *ec) ++{ ++ struct surfacegen5_ec_writer *writer = &ec->writer; ++ struct serdev_device *serdev = ec->serdev; ++ ++ size_t len = writer->ptr - writer->data; ++ ++ dev_dbg(&ec->serdev->dev, "sending message\n"); ++ print_hex_dump_debug("send: ", DUMP_PREFIX_OFFSET, 16, 1, ++ writer->data, writer->ptr - writer->data, false); ++ ++ return serdev_device_write(serdev, writer->data, len, SG5_WRITE_TIMEOUT); ++} ++ ++inline static void surfacegen5_ssh_write_msg_cmd(struct surfacegen5_ec *ec, ++ const struct surfacegen5_rqst *rqst) ++{ ++ surfacegen5_ssh_writer_reset(&ec->writer); ++ surfacegen5_ssh_write_syn(&ec->writer); ++ surfacegen5_ssh_write_hdr(&ec->writer, rqst, ec); ++ surfacegen5_ssh_write_cmd(&ec->writer, rqst, ec); ++} ++ ++inline static void surfacegen5_ssh_write_msg_ack(struct surfacegen5_ec *ec, u8 seq) ++{ ++ surfacegen5_ssh_writer_reset(&ec->writer); ++ surfacegen5_ssh_write_syn(&ec->writer); ++ surfacegen5_ssh_write_ack(&ec->writer, seq); ++ surfacegen5_ssh_write_ter(&ec->writer); ++} ++ ++inline static void surfacegen5_ssh_receiver_restart(struct surfacegen5_ec *ec, ++ const struct surfacegen5_rqst *rqst) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ec->receiver.lock, flags); ++ reinit_completion(&ec->receiver.signal); ++ ec->receiver.state = SG5_RCV_CONTROL; ++ ec->receiver.expect.pld = rqst->snc; ++ ec->receiver.expect.seq = ec->counter.seq; ++ ec->receiver.expect.rqid = surfacegen5_rqid_to_rqst(ec->counter.rqid); ++ ec->receiver.eval_buf.len = 0; ++ spin_unlock_irqrestore(&ec->receiver.lock, flags); ++} ++ ++inline static void surfacegen5_ssh_receiver_discard(struct surfacegen5_ec *ec) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ec->receiver.lock, flags); ++ ec->receiver.state = SG5_RCV_DISCARD; ++ ec->receiver.eval_buf.len = 0; ++ kfifo_reset(&ec->receiver.fifo); ++ spin_unlock_irqrestore(&ec->receiver.lock, flags); ++} ++ ++static int surfacegen5_ec_rqst_unlocked(struct surfacegen5_ec *ec, ++ const struct surfacegen5_rqst *rqst, ++ struct surfacegen5_buf *result) ++{ ++ struct device *dev = &ec->serdev->dev; ++ struct surfacegen5_fifo_packet packet = {}; ++ int status; ++ int try; ++ unsigned int rem; ++ ++ if (rqst->cdl > SURFACEGEN5_MAX_RQST_PAYLOAD) { ++ dev_err(dev, SG5_RQST_TAG "request payload too large\n"); ++ return -EINVAL; ++ } ++ ++ // write command in buffer, we may need it multiple times ++ surfacegen5_ssh_write_msg_cmd(ec, rqst); ++ surfacegen5_ssh_receiver_restart(ec, rqst); ++ ++ // send command, try to get an ack response ++ for (try = 0; try < SG5_NUM_RETRY; try++) { ++ status = surfacegen5_ssh_writer_flush(ec); ++ if (status) { ++ goto ec_rqst_out; ++ } ++ ++ rem = wait_for_completion_timeout(&ec->receiver.signal, SG5_READ_TIMEOUT); ++ if (rem) { ++ // completion assures valid packet, thus ignore returned length ++ (void) !kfifo_out(&ec->receiver.fifo, &packet, sizeof(packet)); ++ ++ if (packet.type == SG5_FRAME_TYPE_ACK) { ++ break; ++ } ++ } ++ } ++ ++ // check if we ran out of tries? ++ if (try >= SG5_NUM_RETRY) { ++ dev_err(dev, SG5_RQST_TAG "communication failed %d times, giving up\n", try); ++ status = -EIO; ++ goto ec_rqst_out; ++ } ++ ++ ec->counter.seq += 1; ++ ec->counter.rqid += 1; ++ ++ // get command response/payload ++ if (rqst->snc && result) { ++ rem = wait_for_completion_timeout(&ec->receiver.signal, SG5_READ_TIMEOUT); ++ if (rem) { ++ // completion assures valid packet, thus ignore returned length ++ (void) !kfifo_out(&ec->receiver.fifo, &packet, sizeof(packet)); ++ ++ if (result->cap < packet.len) { ++ status = -EINVAL; ++ goto ec_rqst_out; ++ } ++ ++ // completion assures valid packet, thus ignore returned length ++ (void) !kfifo_out(&ec->receiver.fifo, result->data, packet.len); ++ result->len = packet.len; ++ } else { ++ dev_err(dev, SG5_RQST_TAG "communication timed out\n"); ++ status = -EIO; ++ goto ec_rqst_out; ++ } ++ ++ // send ACK ++ surfacegen5_ssh_write_msg_ack(ec, packet.seq); ++ status = surfacegen5_ssh_writer_flush(ec); ++ if (status) { ++ goto ec_rqst_out; ++ } ++ } ++ ++ec_rqst_out: ++ surfacegen5_ssh_receiver_discard(ec); ++ return status; ++} ++ ++int surfacegen5_ec_rqst(const struct surfacegen5_rqst *rqst, struct surfacegen5_buf *result) ++{ ++ struct surfacegen5_ec *ec; ++ int status; ++ ++ ec = surfacegen5_ec_acquire_init(); ++ if (!ec) { ++ printk(KERN_WARNING SG5_RQST_TAG_FULL "embedded controller is uninitialized\n"); ++ return -ENXIO; ++ } ++ ++ if (ec->state == SG5_EC_SUSPENDED) { ++ dev_warn(&ec->serdev->dev, SG5_RQST_TAG "embedded controller is suspended\n"); ++ ++ surfacegen5_ec_release(ec); ++ return -EPERM; ++ } ++ ++ status = surfacegen5_ec_rqst_unlocked(ec, rqst, result); ++ ++ surfacegen5_ec_release(ec); ++ return status; ++} ++ ++ ++static int surfacegen5_ssh_ec_resume(struct surfacegen5_ec *ec) ++{ ++ u8 buf[1] = { 0x00 }; ++ ++ struct surfacegen5_rqst rqst = { ++ .tc = 0x01, ++ .iid = 0x00, ++ .cid = 0x16, ++ .snc = 0x01, ++ .cdl = 0x00, ++ .pld = NULL, ++ }; ++ ++ struct surfacegen5_buf result = { ++ result.cap = ARRAY_SIZE(buf), ++ result.len = 0, ++ result.data = buf, ++ }; ++ ++ int status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result); ++ if (status) { ++ return status; ++ } ++ ++ if (buf[0] != 0x00) { ++ dev_warn(&ec->serdev->dev, ++ "unexpected result while trying to resume EC: 0x%02x\n", ++ buf[0]); ++ } ++ ++ return 0; ++} ++ ++static int surfacegen5_ssh_ec_suspend(struct surfacegen5_ec *ec) ++{ ++ u8 buf[1] = { 0x00 }; ++ ++ struct surfacegen5_rqst rqst = { ++ .tc = 0x01, ++ .iid = 0x00, ++ .cid = 0x15, ++ .snc = 0x01, ++ .cdl = 0x00, ++ .pld = NULL, ++ }; ++ ++ struct surfacegen5_buf result = { ++ result.cap = ARRAY_SIZE(buf), ++ result.len = 0, ++ result.data = buf, ++ }; ++ ++ int status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result); ++ if (status) { ++ return status; ++ } ++ ++ if (buf[0] != 0x00) { ++ dev_warn(&ec->serdev->dev, ++ "unexpected result while trying to suspend EC: 0x%02x\n", ++ buf[0]); ++ } ++ ++ return 0; ++} ++ ++ ++inline static bool surfacegen5_ssh_is_valid_syn(const u8 *ptr) ++{ ++ return ptr[0] == 0xaa && ptr[1] == 0x55; ++} ++ ++inline static bool surfacegen5_ssh_is_valid_ter(const u8 *ptr) ++{ ++ return ptr[0] == 0xff && ptr[1] == 0xff; ++} ++ ++inline static bool surfacegen5_ssh_is_valid_crc(const u8 *begin, const u8 *end) ++{ ++ u16 crc = surfacegen5_ssh_crc(begin, end - begin); ++ return (end[0] == (crc & 0xff)) && (end[1] == (crc >> 8)); ++} ++ ++ ++static int surfacegen5_ssh_send_ack(struct surfacegen5_ec *ec, u8 seq) ++{ ++ u8 buf[SG5_MSG_LEN_CTRL]; ++ u16 crc; ++ ++ buf[0] = 0xaa; ++ buf[1] = 0x55; ++ buf[2] = 0x40; ++ buf[3] = 0x00; ++ buf[4] = 0x00; ++ buf[5] = seq; ++ ++ crc = surfacegen5_ssh_crc(buf + SG5_FRAME_OFFS_CTRL, SG5_BYTELEN_CTRL); ++ buf[6] = crc & 0xff; ++ buf[7] = crc >> 8; ++ ++ buf[8] = 0xff; ++ buf[9] = 0xff; ++ ++ dev_dbg(&ec->serdev->dev, "sending message\n"); ++ print_hex_dump_debug("send: ", DUMP_PREFIX_OFFSET, 16, 1, ++ buf, SG5_MSG_LEN_CTRL, false); ++ ++ return serdev_device_write(ec->serdev, buf, SG5_MSG_LEN_CTRL, SG5_WRITE_TIMEOUT); ++} ++ ++static void surfacegen5_event_work_ack_handler(struct work_struct *_work) ++{ ++ struct surfacegen5_event_work *work; ++ struct surfacegen5_event *event; ++ struct surfacegen5_ec *ec; ++ struct device *dev; ++ int status; ++ ++ work = container_of(_work, struct surfacegen5_event_work, work_ack); ++ event = &work->event; ++ ec = work->ec; ++ dev = &ec->serdev->dev; ++ ++ // make sure we load a fresh ec state ++ smp_mb(); ++ ++ if (ec->state == SG5_EC_INITIALIZED) { ++ status = surfacegen5_ssh_send_ack(ec, work->seq); ++ if (status) { ++ dev_err(dev, SG5_EVENT_TAG "failed to send ACK: %d\n", status); ++ } ++ } ++ ++ if (refcount_dec_and_test(&work->refcount)) { ++ kfree(work); ++ } ++} ++ ++static void surfacegen5_event_work_evt_handler(struct work_struct *_work) ++{ ++ struct delayed_work *dwork = (struct delayed_work *)_work; ++ struct surfacegen5_event_work *work; ++ struct surfacegen5_event *event; ++ struct surfacegen5_ec *ec; ++ struct device *dev; ++ unsigned long flags; ++ ++ surfacegen5_ec_event_handler_fn handler; ++ void *handler_data; ++ ++ int status = 0; ++ ++ work = container_of(dwork, struct surfacegen5_event_work, work_evt); ++ event = &work->event; ++ ec = work->ec; ++ dev = &ec->serdev->dev; ++ ++ spin_lock_irqsave(&ec->events.lock, flags); ++ handler = ec->events.handler[event->rqid - 1].handler; ++ handler_data = ec->events.handler[event->rqid - 1].data; ++ spin_unlock_irqrestore(&ec->events.lock, flags); ++ ++ /* ++ * During handler removal or driver release, we ensure every event gets ++ * handled before return of that function. Thus a handler obtained here is ++ * guaranteed to be valid at least until this function returns. ++ */ ++ ++ if (handler) { ++ status = handler(event, handler_data); ++ } else { ++ dev_warn(dev, SG5_EVENT_TAG "unhandled event (rqid: %04x)\n", event->rqid); ++ } ++ ++ if (status) { ++ dev_err(dev, SG5_EVENT_TAG "error handling event: %d\n", status); ++ } ++ ++ if (refcount_dec_and_test(&work->refcount)) { ++ kfree(work); ++ } ++} ++ ++static void surfacegen5_ssh_handle_event(struct surfacegen5_ec *ec, const u8 *buf) ++{ ++ struct device *dev = &ec->serdev->dev; ++ const struct surfacegen5_frame_ctrl *ctrl; ++ const struct surfacegen5_frame_cmd *cmd; ++ struct surfacegen5_event_work *work; ++ unsigned long flags; ++ u16 pld_len; ++ ++ surfacegen5_ec_event_handler_delay delay_fn; ++ void *handler_data; ++ unsigned long delay = 0; ++ ++ ctrl = (const struct surfacegen5_frame_ctrl *)(buf + SG5_FRAME_OFFS_CTRL); ++ cmd = (const struct surfacegen5_frame_cmd *)(buf + SG5_FRAME_OFFS_CMD); ++ ++ pld_len = ctrl->len - SG5_BYTELEN_CMDFRAME; ++ ++ work = kzalloc(sizeof(struct surfacegen5_event_work) + pld_len, GFP_ATOMIC); ++ if (!work) { ++ dev_warn(dev, SG5_EVENT_TAG "failed to allocate memory, dropping event\n"); ++ return; ++ } ++ ++ refcount_set(&work->refcount, 2); ++ work->ec = ec; ++ work->seq = ctrl->seq; ++ work->event.rqid = (cmd->rqid_hi << 8) | cmd->rqid_lo; ++ work->event.tc = cmd->tc; ++ work->event.iid = cmd->iid; ++ work->event.cid = cmd->cid; ++ work->event.len = pld_len; ++ work->event.pld = ((u8*) work) + sizeof(struct surfacegen5_event_work); ++ ++ memcpy(work->event.pld, buf + SG5_FRAME_OFFS_CMD_PLD, pld_len); ++ ++ INIT_WORK(&work->work_ack, surfacegen5_event_work_ack_handler); ++ queue_work(ec->events.queue_ack, &work->work_ack); ++ ++ spin_lock_irqsave(&ec->events.lock, flags); ++ handler_data = ec->events.handler[work->event.rqid - 1].data; ++ delay_fn = ec->events.handler[work->event.rqid - 1].delay; ++ if (delay_fn) { ++ delay = delay_fn(&work->event, handler_data); ++ } ++ spin_unlock_irqrestore(&ec->events.lock, flags); ++ ++ // immediate execution for high priority events (e.g. keyboard) ++ if (delay == SURFACEGEN5_EVENT_IMMEDIATE) { ++ surfacegen5_event_work_evt_handler(&work->work_evt.work); ++ } else { ++ INIT_DELAYED_WORK(&work->work_evt, surfacegen5_event_work_evt_handler); ++ queue_delayed_work(ec->events.queue_evt, &work->work_evt, delay); ++ } ++} ++ ++static int surfacegen5_ssh_receive_msg_ctrl(struct surfacegen5_ec *ec, ++ const u8 *buf, size_t size) ++{ ++ struct device *dev = &ec->serdev->dev; ++ struct surfacegen5_ec_receiver *rcv = &ec->receiver; ++ const struct surfacegen5_frame_ctrl *ctrl; ++ struct surfacegen5_fifo_packet packet; ++ ++ const u8 *ctrl_begin = buf + SG5_FRAME_OFFS_CTRL; ++ const u8 *ctrl_end = buf + SG5_FRAME_OFFS_CTRL_CRC; ++ ++ ctrl = (const struct surfacegen5_frame_ctrl *)(ctrl_begin); ++ ++ // actual length check ++ if (size < SG5_MSG_LEN_CTRL) { ++ return 0; // need more bytes ++ } ++ ++ // validate TERM ++ if (!surfacegen5_ssh_is_valid_ter(buf + SG5_FRAME_OFFS_TERM)) { ++ dev_err(dev, SG5_RECV_TAG "invalid end of message\n"); ++ return size; // discard everything ++ } ++ ++ // validate CRC ++ if (!surfacegen5_ssh_is_valid_crc(ctrl_begin, ctrl_end)) { ++ dev_err(dev, SG5_RECV_TAG "invalid checksum (ctrl)\n"); ++ return SG5_MSG_LEN_CTRL; // only discard message ++ } ++ ++ // check if we expect the message ++ if (rcv->state != SG5_RCV_CONTROL) { ++ dev_err(dev, SG5_RECV_TAG "discarding message: ctrl not expected\n"); ++ return SG5_MSG_LEN_CTRL; // discard message ++ } ++ ++ // check if it is for our request ++ if (ctrl->type == SG5_FRAME_TYPE_ACK && ctrl->seq != rcv->expect.seq) { ++ dev_err(dev, SG5_RECV_TAG "discarding message: ack does not match\n"); ++ return SG5_MSG_LEN_CTRL; // discard message ++ } ++ ++ // we now have a valid & expected ACK/RETRY message ++ dev_dbg(dev, SG5_RECV_TAG "valid control message received (type: 0x%02x)\n", ctrl->type); ++ ++ packet.type = ctrl->type; ++ packet.seq = ctrl->seq; ++ packet.len = 0; ++ ++ if (kfifo_avail(&rcv->fifo) >= sizeof(packet)) { ++ kfifo_in(&rcv->fifo, (u8 *) &packet, sizeof(packet)); ++ ++ } else { ++ dev_warn(dev, SG5_RECV_TAG ++ "dropping frame: not enough space in fifo (type = %d)\n", ++ SG5_FRAME_TYPE_CMD); ++ ++ return SG5_MSG_LEN_CTRL; // discard message ++ } ++ ++ // update decoder state ++ if (ctrl->type == SG5_FRAME_TYPE_ACK) { ++ rcv->state = rcv->expect.pld ++ ? SG5_RCV_COMMAND ++ : SG5_RCV_DISCARD; ++ } ++ ++ complete(&rcv->signal); ++ return SG5_MSG_LEN_CTRL; // handled message ++} ++ ++static int surfacegen5_ssh_receive_msg_cmd(struct surfacegen5_ec *ec, ++ const u8 *buf, size_t size) ++{ ++ struct device *dev = &ec->serdev->dev; ++ struct surfacegen5_ec_receiver *rcv = &ec->receiver; ++ const struct surfacegen5_frame_ctrl *ctrl; ++ const struct surfacegen5_frame_cmd *cmd; ++ struct surfacegen5_fifo_packet packet; ++ ++ const u8 *ctrl_begin = buf + SG5_FRAME_OFFS_CTRL; ++ const u8 *ctrl_end = buf + SG5_FRAME_OFFS_CTRL_CRC; ++ const u8 *cmd_begin = buf + SG5_FRAME_OFFS_CMD; ++ const u8 *cmd_begin_pld = buf + SG5_FRAME_OFFS_CMD_PLD; ++ const u8 *cmd_end; ++ ++ size_t msg_len; ++ ++ ctrl = (const struct surfacegen5_frame_ctrl *)(ctrl_begin); ++ cmd = (const struct surfacegen5_frame_cmd *)(cmd_begin); ++ ++ // we need at least a full control frame ++ if (size < (SG5_BYTELEN_SYNC + SG5_BYTELEN_CTRL + SG5_BYTELEN_CRC)) { ++ return 0; // need more bytes ++ } ++ ++ // validate control-frame CRC ++ if (!surfacegen5_ssh_is_valid_crc(ctrl_begin, ctrl_end)) { ++ dev_err(dev, SG5_RECV_TAG "invalid checksum (cmd-ctrl)\n"); ++ /* ++ * We can't be sure here if length is valid, thus ++ * discard everything. ++ */ ++ return size; ++ } ++ ++ // actual length check (ctrl->len contains command-frame but not crc) ++ msg_len = SG5_MSG_LEN_CMD_BASE + ctrl->len; ++ if (size < msg_len) { ++ return 0; // need more bytes ++ } ++ ++ cmd_end = cmd_begin + ctrl->len; ++ ++ // validate command-frame type ++ if (cmd->type != SG5_FRAME_TYPE_CMD) { ++ dev_err(dev, SG5_RECV_TAG "expected command frame type but got 0x%02x\n", cmd->type); ++ return size; // discard everything ++ } ++ ++ // validate command-frame CRC ++ if (!surfacegen5_ssh_is_valid_crc(cmd_begin, cmd_end)) { ++ dev_err(dev, SG5_RECV_TAG "invalid checksum (cmd-pld)\n"); ++ ++ /* ++ * The message length is provided in the control frame. As we ++ * already validated that, we can be sure here that it's ++ * correct, so we only need to discard the message. ++ */ ++ return msg_len; ++ } ++ ++ // check if we received an event notification ++ if (surfacegen5_rqid_is_event((cmd->rqid_hi << 8) | cmd->rqid_lo)) { ++ surfacegen5_ssh_handle_event(ec, buf); ++ return msg_len; // handled message ++ } ++ ++ // check if we expect the message ++ if (rcv->state != SG5_RCV_COMMAND) { ++ dev_dbg(dev, SG5_RECV_TAG "discarding message: command not expected\n"); ++ return msg_len; // discard message ++ } ++ ++ // check if response is for our request ++ if (rcv->expect.rqid != (cmd->rqid_lo | (cmd->rqid_hi << 8))) { ++ dev_dbg(dev, SG5_RECV_TAG "discarding message: command not a match\n"); ++ return msg_len; // discard message ++ } ++ ++ // we now have a valid & expected command message ++ dev_dbg(dev, SG5_RECV_TAG "valid command message received\n"); ++ ++ packet.type = ctrl->type; ++ packet.seq = ctrl->seq; ++ packet.len = cmd_end - cmd_begin_pld; ++ ++ if (kfifo_avail(&rcv->fifo) >= sizeof(packet) + packet.len) { ++ kfifo_in(&rcv->fifo, &packet, sizeof(packet)); ++ kfifo_in(&rcv->fifo, cmd_begin_pld, packet.len); ++ ++ } else { ++ dev_warn(dev, SG5_RECV_TAG ++ "dropping frame: not enough space in fifo (type = %d)\n", ++ SG5_FRAME_TYPE_CMD); ++ ++ return SG5_MSG_LEN_CTRL; // discard message ++ } ++ ++ rcv->state = SG5_RCV_DISCARD; ++ ++ complete(&rcv->signal); ++ return msg_len; // handled message ++} ++ ++static int surfacegen5_ssh_eval_buf(struct surfacegen5_ec *ec, ++ const u8 *buf, size_t size) ++{ ++ struct device *dev = &ec->serdev->dev; ++ struct surfacegen5_frame_ctrl *ctrl; ++ ++ // we need at least a control frame to check what to do ++ if (size < (SG5_BYTELEN_SYNC + SG5_BYTELEN_CTRL)) { ++ return 0; // need more bytes ++ } ++ ++ // make sure we're actually at the start of a new message ++ if (!surfacegen5_ssh_is_valid_syn(buf)) { ++ dev_err(dev, SG5_RECV_TAG "invalid start of message\n"); ++ return size; // discard everything ++ } ++ ++ // handle individual message types seperately ++ ctrl = (struct surfacegen5_frame_ctrl *)(buf + SG5_FRAME_OFFS_CTRL); ++ ++ switch (ctrl->type) { ++ case SG5_FRAME_TYPE_ACK: ++ case SG5_FRAME_TYPE_RETRY: ++ return surfacegen5_ssh_receive_msg_ctrl(ec, buf, size); ++ ++ case SG5_FRAME_TYPE_CMD: ++ return surfacegen5_ssh_receive_msg_cmd(ec, buf, size); ++ ++ default: ++ dev_err(dev, SG5_RECV_TAG "unknown frame type 0x%02x\n", ctrl->type); ++ return size; // discard everything ++ } ++} ++ ++static int surfacegen5_ssh_receive_buf(struct serdev_device *serdev, ++ const unsigned char *buf, size_t size) ++{ ++ struct surfacegen5_ec *ec = serdev_device_get_drvdata(serdev); ++ struct surfacegen5_ec_receiver *rcv = &ec->receiver; ++ unsigned long flags; ++ int offs = 0; ++ int used, n; ++ ++ dev_dbg(&serdev->dev, SG5_RECV_TAG "received buffer (size: %zu)\n", size); ++ print_hex_dump_debug(SG5_RECV_TAG, DUMP_PREFIX_OFFSET, 16, 1, buf, size, false); ++ ++ /* ++ * The battery _BIX message gets a bit long, thus we have to add some ++ * additional buffering here. ++ */ ++ ++ spin_lock_irqsave(&rcv->lock, flags); ++ ++ // copy to eval-buffer ++ used = min(size, (size_t)(rcv->eval_buf.cap - rcv->eval_buf.len)); ++ memcpy(rcv->eval_buf.ptr + rcv->eval_buf.len, buf, used); ++ rcv->eval_buf.len += used; ++ ++ // evaluate buffer until we need more bytes or eval-buf is empty ++ while (offs < rcv->eval_buf.len) { ++ n = rcv->eval_buf.len - offs; ++ n = surfacegen5_ssh_eval_buf(ec, rcv->eval_buf.ptr + offs, n); ++ if (n <= 0) break; // need more bytes ++ ++ offs += n; ++ } ++ ++ // throw away the evaluated parts ++ rcv->eval_buf.len -= offs; ++ memmove(rcv->eval_buf.ptr, rcv->eval_buf.ptr + offs, rcv->eval_buf.len); ++ ++ spin_unlock_irqrestore(&rcv->lock, flags); ++ ++ return used; ++} ++ ++ ++static acpi_status ++surfacegen5_ssh_setup_from_resource(struct acpi_resource *resource, void *context) ++{ ++ struct serdev_device *serdev = context; ++ struct acpi_resource_common_serialbus *serial; ++ struct acpi_resource_uart_serialbus *uart; ++ int status = 0; ++ ++ if (resource->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) { ++ return AE_OK; ++ } ++ ++ serial = &resource->data.common_serial_bus; ++ if (serial->type != ACPI_RESOURCE_SERIAL_TYPE_UART) { ++ return AE_OK; ++ } ++ ++ uart = &resource->data.uart_serial_bus; ++ ++ // set up serdev device ++ serdev_device_set_baudrate(serdev, uart->default_baud_rate); ++ ++ // serdev currently only supports RTSCTS flow control ++ if (uart->flow_control & SG5_SUPPORTED_FLOW_CONTROL_MASK) { ++ dev_warn(&serdev->dev, "unsupported flow control (value: 0x%02x)\n", uart->flow_control); ++ } ++ ++ // set RTSCTS flow control ++ serdev_device_set_flow_control(serdev, uart->flow_control & ACPI_UART_FLOW_CONTROL_HW); ++ ++ // serdev currently only supports EVEN/ODD parity ++ switch (uart->parity) { ++ case ACPI_UART_PARITY_NONE: ++ status = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); ++ break; ++ case ACPI_UART_PARITY_EVEN: ++ status = serdev_device_set_parity(serdev, SERDEV_PARITY_EVEN); ++ break; ++ case ACPI_UART_PARITY_ODD: ++ status = serdev_device_set_parity(serdev, SERDEV_PARITY_ODD); ++ break; ++ default: ++ dev_warn(&serdev->dev, "unsupported parity (value: 0x%02x)\n", uart->parity); ++ break; ++ } ++ ++ if (status) { ++ dev_err(&serdev->dev, "failed to set parity (value: 0x%02x)\n", uart->parity); ++ return status; ++ } ++ ++ return AE_CTRL_TERMINATE; // we've found the resource and are done ++} ++ ++ ++static bool surfacegen5_idma_filter(struct dma_chan *chan, void *param) ++{ ++ // see dw8250_idma_filter ++ return param == chan->device->dev->parent; ++} ++ ++static int surfacegen5_ssh_check_dma(struct serdev_device *serdev) ++{ ++ struct device *dev = serdev->ctrl->dev.parent; ++ struct dma_chan *rx, *tx; ++ dma_cap_mask_t mask; ++ int status = 0; ++ ++ /* ++ * The EC UART requires DMA for proper communication. If we don't use DMA, ++ * we'll drop bytes when the system has high load, e.g. during boot. This ++ * causes some ugly behaviour, i.e. battery information (_BIX) messages ++ * failing frequently. We're making sure the required DMA channels are ++ * available here so serial8250_do_startup is able to grab them later ++ * instead of silently falling back to a non-DMA approach. ++ */ ++ ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_SLAVE, mask); ++ ++ rx = dma_request_slave_channel_compat(mask, surfacegen5_idma_filter, dev->parent, dev, "rx"); ++ if (IS_ERR_OR_NULL(rx)) { ++ status = rx ? PTR_ERR(rx) : -EPROBE_DEFER; ++ if (status != -EPROBE_DEFER) { ++ dev_err(&serdev->dev, "sg5_dma: error requesting rx channel: %d\n", status); ++ } else { ++ dev_dbg(&serdev->dev, "sg5_dma: rx channel not found, deferring probe\n"); ++ } ++ goto check_dma_out; ++ } ++ ++ tx = dma_request_slave_channel_compat(mask, surfacegen5_idma_filter, dev->parent, dev, "tx"); ++ if (IS_ERR_OR_NULL(tx)) { ++ status = tx ? PTR_ERR(tx) : -EPROBE_DEFER; ++ if (status != -EPROBE_DEFER) { ++ dev_err(&serdev->dev, "sg5_dma: error requesting tx channel: %d\n", status); ++ } else { ++ dev_dbg(&serdev->dev, "sg5_dma: tx channel not found, deferring probe\n"); ++ } ++ goto check_dma_release_rx; ++ } ++ ++ dma_release_channel(tx); ++check_dma_release_rx: ++ dma_release_channel(rx); ++check_dma_out: ++ return status; ++} ++ ++ ++static int surfacegen5_ssh_suspend(struct device *dev) ++{ ++ struct surfacegen5_ec *ec; ++ int status = 0; ++ ++ dev_dbg(dev, "suspending\n"); ++ ++ ec = surfacegen5_ec_acquire_init(); ++ if (ec) { ++ status = surfacegen5_ssh_ec_suspend(ec); ++ if (status) { ++ dev_err(dev, "failed to suspend EC: %d\n", status); ++ } ++ ++ ec->state = SG5_EC_SUSPENDED; ++ surfacegen5_ec_release(ec); ++ } ++ ++ return status; ++} ++ ++static int surfacegen5_ssh_resume(struct device *dev) ++{ ++ struct surfacegen5_ec *ec; ++ int status = 0; ++ ++ dev_dbg(dev, "resuming\n"); ++ ++ ec = surfacegen5_ec_acquire_init(); ++ if (ec) { ++ ec->state = SG5_EC_INITIALIZED; ++ ++ status = surfacegen5_ssh_ec_resume(ec); ++ if (status) { ++ dev_err(dev, "failed to resume EC: %d\n", status); ++ } ++ ++ surfacegen5_ec_release(ec); ++ } ++ ++ return status; ++} ++ ++static SIMPLE_DEV_PM_OPS(surfacegen5_ssh_pm_ops, surfacegen5_ssh_suspend, surfacegen5_ssh_resume); ++ ++ ++static const struct serdev_device_ops surfacegen5_ssh_device_ops = { ++ .receive_buf = surfacegen5_ssh_receive_buf, ++ .write_wakeup = serdev_device_write_wakeup, ++}; ++ ++static int surfacegen5_acpi_ssh_probe(struct serdev_device *serdev) ++{ ++ struct surfacegen5_ec *ec; ++ struct workqueue_struct *event_queue_ack; ++ struct workqueue_struct *event_queue_evt; ++ u8 *write_buf; ++ u8 *read_buf; ++ u8 *eval_buf; ++ acpi_handle *ssh = ACPI_HANDLE(&serdev->dev); ++ acpi_status status; ++ ++ dev_dbg(&serdev->dev, "probing\n"); ++ ++ // ensure DMA is ready before we set up the device ++ status = surfacegen5_ssh_check_dma(serdev); ++ if (status) { ++ return status; ++ } ++ ++ // allocate buffers ++ write_buf = kzalloc(SG5_WRITE_BUF_LEN, GFP_KERNEL); ++ if (!write_buf) { ++ status = -ENOMEM; ++ goto err_ssh_probe_write_buf; ++ } ++ ++ read_buf = kzalloc(SG5_READ_BUF_LEN, GFP_KERNEL); ++ if (!read_buf) { ++ status = -ENOMEM; ++ goto err_ssh_probe_read_buf; ++ } ++ ++ eval_buf = kzalloc(SG5_EVAL_BUF_LEN, GFP_KERNEL); ++ if (!eval_buf) { ++ status = -ENOMEM; ++ goto err_ssh_probe_eval_buf; ++ } ++ ++ event_queue_ack = create_singlethread_workqueue("sg5_ackq"); ++ if (!event_queue_ack) { ++ status = -ENOMEM; ++ goto err_ssh_probe_ackq; ++ } ++ ++ event_queue_evt = create_workqueue("sg5_evtq"); ++ if (!event_queue_evt) { ++ status = -ENOMEM; ++ goto err_ssh_probe_evtq; ++ } ++ ++ // set up EC ++ ec = surfacegen5_ec_acquire(); ++ if (ec->state != SG5_EC_UNINITIALIZED) { ++ dev_err(&serdev->dev, "embedded controller already initialized\n"); ++ surfacegen5_ec_release(ec); ++ ++ status = -EBUSY; ++ goto err_ssh_probe_busy; ++ } ++ ++ ec->serdev = serdev; ++ ec->writer.data = write_buf; ++ ec->writer.ptr = write_buf; ++ ++ // initialize receiver ++ init_completion(&ec->receiver.signal); ++ kfifo_init(&ec->receiver.fifo, read_buf, SG5_READ_BUF_LEN); ++ ec->receiver.eval_buf.ptr = eval_buf; ++ ec->receiver.eval_buf.cap = SG5_EVAL_BUF_LEN; ++ ec->receiver.eval_buf.len = 0; ++ ++ // initialize event handling ++ ec->events.queue_ack = event_queue_ack; ++ ec->events.queue_evt = event_queue_evt; ++ ++ ec->state = SG5_EC_INITIALIZED; ++ ++ serdev_device_set_drvdata(serdev, ec); ++ ++ // ensure everything is properly set-up before we open the device ++ smp_mb(); ++ ++ serdev_device_set_client_ops(serdev, &surfacegen5_ssh_device_ops); ++ status = serdev_device_open(serdev); ++ if (status) { ++ goto err_ssh_probe_open; ++ } ++ ++ status = acpi_walk_resources(ssh, METHOD_NAME__CRS, ++ surfacegen5_ssh_setup_from_resource, serdev); ++ if (ACPI_FAILURE(status)) { ++ goto err_ssh_probe_devinit; ++ } ++ ++ status = surfacegen5_ssh_ec_resume(ec); ++ if (status) { ++ goto err_ssh_probe_devinit; ++ } ++ ++ surfacegen5_ec_release(ec); ++ acpi_walk_dep_device_list(ssh); ++ ++ return 0; ++ ++err_ssh_probe_devinit: ++ serdev_device_close(serdev); ++err_ssh_probe_open: ++ ec->state = SG5_EC_UNINITIALIZED; ++ serdev_device_set_drvdata(serdev, NULL); ++ surfacegen5_ec_release(ec); ++err_ssh_probe_busy: ++ destroy_workqueue(event_queue_evt); ++err_ssh_probe_evtq: ++ destroy_workqueue(event_queue_ack); ++err_ssh_probe_ackq: ++ kfree(eval_buf); ++err_ssh_probe_eval_buf: ++ kfree(read_buf); ++err_ssh_probe_read_buf: ++ kfree(write_buf); ++err_ssh_probe_write_buf: ++ return status; ++} ++ ++static void surfacegen5_acpi_ssh_remove(struct serdev_device *serdev) ++{ ++ struct surfacegen5_ec *ec; ++ unsigned long flags; ++ int status; ++ ++ ec = surfacegen5_ec_acquire_init(); ++ if (!ec) { ++ return; ++ } ++ ++ // suspend EC and disable events ++ status = surfacegen5_ssh_ec_suspend(ec); ++ if (status) { ++ dev_err(&serdev->dev, "failed to suspend EC: %d\n", status); ++ } ++ ++ // make sure all events (received up to now) have been properly handled ++ flush_workqueue(ec->events.queue_ack); ++ flush_workqueue(ec->events.queue_evt); ++ ++ // remove event handlers ++ spin_lock_irqsave(&ec->events.lock, flags); ++ memset(ec->events.handler, 0, ++ sizeof(struct surfacegen5_ec_event_handler) ++ * SG5_NUM_EVENT_TYPES); ++ spin_unlock_irqrestore(&ec->events.lock, flags); ++ ++ // set device to deinitialized state ++ ec->state = SG5_EC_UNINITIALIZED; ++ ec->serdev = NULL; ++ ++ // ensure state and serdev get set before continuing ++ smp_mb(); ++ ++ /* ++ * Flush any event that has not been processed yet to ensure we're not going to ++ * use the serial device any more (e.g. for ACKing). ++ */ ++ flush_workqueue(ec->events.queue_ack); ++ flush_workqueue(ec->events.queue_evt); ++ ++ serdev_device_close(serdev); ++ ++ /* ++ * Only at this point, no new events can be received. Destroying the ++ * workqueue here flushes all remaining events. Those events will be ++ * silently ignored and neither ACKed nor any handler gets called. ++ */ ++ destroy_workqueue(ec->events.queue_ack); ++ destroy_workqueue(ec->events.queue_evt); ++ ++ // free writer ++ kfree(ec->writer.data); ++ ec->writer.data = NULL; ++ ec->writer.ptr = NULL; ++ ++ // free receiver ++ spin_lock_irqsave(&ec->receiver.lock, flags); ++ ec->receiver.state = SG5_RCV_DISCARD; ++ kfifo_free(&ec->receiver.fifo); ++ ++ kfree(ec->receiver.eval_buf.ptr); ++ ec->receiver.eval_buf.ptr = NULL; ++ ec->receiver.eval_buf.cap = 0; ++ ec->receiver.eval_buf.len = 0; ++ spin_unlock_irqrestore(&ec->receiver.lock, flags); ++ ++ serdev_device_set_drvdata(serdev, NULL); ++ surfacegen5_ec_release(ec); ++} ++ ++ ++static const struct acpi_device_id surfacegen5_acpi_ssh_match[] = { ++ { "MSHW0084", 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(acpi, surfacegen5_acpi_ssh_match); ++ ++static struct serdev_device_driver surfacegen5_acpi_ssh = { ++ .probe = surfacegen5_acpi_ssh_probe, ++ .remove = surfacegen5_acpi_ssh_remove, ++ .driver = { ++ .name = "surfacegen5_acpi_ssh", ++ .acpi_match_table = ACPI_PTR(surfacegen5_acpi_ssh_match), ++ .pm = &surfacegen5_ssh_pm_ops, ++ }, ++}; ++ ++#endif /* CONFIG_SURFACE_ACPI_SSH */ ++ ++ ++/************************************************************************* ++ * Surface ACPI Notify driver ++ */ ++ ++#ifdef CONFIG_SURFACE_ACPI_SAN ++ ++#define SG5_RQST_RETRY 5 ++ ++#define SG5_SAN_DSM_REVISION 0 ++#define SG5_SAN_DSM_FN_NOTIFY_SENSOR_TRIP_POINT 0x09 ++ ++static const guid_t SG5_SAN_DSM_UUID = ++ GUID_INIT(0x93b666c5, 0x70c6, 0x469f, 0xa2, 0x15, 0x3d, ++ 0x48, 0x7c, 0x91, 0xab, 0x3c); ++ ++#define SG5_EVENT_DELAY_POWER msecs_to_jiffies(5000) ++ ++#define SG5_EVENT_PWR_TC 0x02 ++#define SG5_EVENT_PWR_RQID 0x0002 ++#define SG5_EVENT_PWR_CID_HWCHANGE 0x15 ++#define SG5_EVENT_PWR_CID_CHARGING 0x16 ++#define SG5_EVENT_PWR_CID_ADAPTER 0x17 ++#define SG5_EVENT_PWR_CID_STATE 0x4f ++ ++#define SG5_EVENT_TEMP_TC 0x03 ++#define SG5_EVENT_TEMP_RQID 0x0003 ++#define SG5_EVENT_TEMP_CID_NOTIFY_SENSOR_TRIP_POINT 0x0b ++ ++#define SG5_SAN_RQST_TAG "san_rqst: " ++ ++#define SG5_QUIRK_BASE_STATE_DELAY 1000 ++ ++ ++struct surfacegen5_san_acpi_consumer { ++ char *path; ++ bool required; ++ u32 flags; ++}; ++ ++struct surfacegen5_san_opreg_context { ++ struct acpi_connection_info connection; ++ struct device *dev; ++}; ++ ++struct surfacegen5_san_consumers { ++ u32 num; ++ struct device_link **links; ++}; ++ ++struct surfacegen5_san_drvdata { ++ struct surfacegen5_san_opreg_context opreg_ctx; ++ struct surfacegen5_san_consumers consumers; ++ struct device_link *ec_link; ++}; ++ ++struct gsb_data_in { ++ u8 cv; ++} __packed; ++ ++struct gsb_data_rqsx { ++ u8 cv; // command value (should be 0x01 or 0x03) ++ u8 tc; // target controller ++ u8 tid; // expected to be 0x01, could be revision ++ u8 iid; // target sub-controller (e.g. primary vs. secondary battery) ++ u8 snc; // expect-response-flag ++ u8 cid; // command ID ++ u8 cdl; // payload length ++ u8 _pad; // padding ++ u8 pld[0]; // payload ++} __packed; ++ ++struct gsb_data_etwl { ++ u8 cv; // command value (should be 0x02) ++ u8 etw3; // ? ++ u8 etw4; // ? ++ u8 msg[0]; // error message (ASCIIZ) ++} __packed; ++ ++struct gsb_data_out { ++ u8 status; // _SSH communication status ++ u8 len; // _SSH payload length ++ u8 pld[0]; // _SSH payload ++} __packed; ++ ++union gsb_buffer_data { ++ struct gsb_data_in in; // common input ++ struct gsb_data_rqsx rqsx; // RQSX input ++ struct gsb_data_etwl etwl; // ETWL input ++ struct gsb_data_out out; // output ++}; ++ ++struct gsb_buffer { ++ u8 status; // GSB AttribRawProcess status ++ u8 len; // GSB AttribRawProcess length ++ union gsb_buffer_data data; ++} __packed; ++ ++ ++enum surfacegen5_pwr_event { ++ SURFACEGEN5_PWR_EVENT_BAT1_STAT = 0x03, ++ SURFACEGEN5_PWR_EVENT_BAT1_INFO = 0x04, ++ SURFACEGEN5_PWR_EVENT_ADP1_STAT = 0x05, ++ SURFACEGEN5_PWR_EVENT_ADP1_INFO = 0x06, ++ SURFACEGEN5_PWR_EVENT_BAT2_STAT = 0x07, ++ SURFACEGEN5_PWR_EVENT_BAT2_INFO = 0x08, ++}; ++ ++ ++static int surfacegen5_acpi_notify_power_event(struct device *dev, enum surfacegen5_pwr_event event) ++{ ++ acpi_handle san = ACPI_HANDLE(dev); ++ union acpi_object *obj; ++ ++ obj = acpi_evaluate_dsm_typed(san, &SG5_SAN_DSM_UUID, SG5_SAN_DSM_REVISION, ++ (u8) event, NULL, ACPI_TYPE_BUFFER); ++ ++ if (IS_ERR_OR_NULL(obj)) { ++ return obj ? PTR_ERR(obj) : -ENXIO; ++ } ++ ++ if (obj->buffer.length != 1 || obj->buffer.pointer[0] != 0) { ++ dev_err(dev, "got unexpected result from _DSM\n"); ++ return -EFAULT; ++ } ++ ++ ACPI_FREE(obj); ++ return 0; ++} ++ ++static int surfacegen5_acpi_notify_sensor_trip_point(struct device *dev, u8 iid) ++{ ++ acpi_handle san = ACPI_HANDLE(dev); ++ union acpi_object *obj; ++ union acpi_object param; ++ ++ param.type = ACPI_TYPE_INTEGER; ++ param.integer.value = iid; ++ ++ obj = acpi_evaluate_dsm_typed(san, &SG5_SAN_DSM_UUID, SG5_SAN_DSM_REVISION, ++ SG5_SAN_DSM_FN_NOTIFY_SENSOR_TRIP_POINT, ++ ¶m, ACPI_TYPE_BUFFER); ++ ++ if (IS_ERR_OR_NULL(obj)) { ++ return obj ? PTR_ERR(obj) : -ENXIO; ++ } ++ ++ if (obj->buffer.length != 1 || obj->buffer.pointer[0] != 0) { ++ dev_err(dev, "got unexpected result from _DSM\n"); ++ return -EFAULT; ++ } ++ ++ ACPI_FREE(obj); ++ return 0; ++} ++ ++ ++inline static int surfacegen5_evt_power_adapter(struct device *dev, struct surfacegen5_event *event) ++{ ++ int status; ++ ++ status = surfacegen5_acpi_notify_power_event(dev, SURFACEGEN5_PWR_EVENT_ADP1_STAT); ++ if (status) { ++ dev_err(dev, "error handling power event (cid = %x)\n", event->cid); ++ return status; ++ } ++ ++ return 0; ++} ++ ++inline static int surfacegen5_evt_power_hwchange(struct device *dev, struct surfacegen5_event *event) ++{ ++ enum surfacegen5_pwr_event evcode; ++ int status; ++ ++ if (event->iid == 0x02) { ++ evcode = SURFACEGEN5_PWR_EVENT_BAT2_INFO; ++ } else { ++ evcode = SURFACEGEN5_PWR_EVENT_BAT1_INFO; ++ } ++ ++ status = surfacegen5_acpi_notify_power_event(dev, evcode); ++ if (status) { ++ dev_err(dev, "error handling power event (cid = %x)\n", event->cid); ++ return status; ++ } ++ ++ return 0; ++} ++ ++inline static int surfacegen5_evt_power_state(struct device *dev, struct surfacegen5_event *event) ++{ ++ int status; ++ ++ status = surfacegen5_acpi_notify_power_event(dev, SURFACEGEN5_PWR_EVENT_BAT1_STAT); ++ if (status) { ++ dev_err(dev, "error handling power event (cid = %x)\n", event->cid); ++ return status; ++ } ++ ++ status = surfacegen5_acpi_notify_power_event(dev, SURFACEGEN5_PWR_EVENT_BAT2_STAT); ++ if (status) { ++ dev_err(dev, "error handling power event (cid = %x)\n", event->cid); ++ return status; ++ } ++ ++ return 0; ++} ++ ++static unsigned long surfacegen5_evt_power_delay(struct surfacegen5_event *event, void *data) ++{ ++ switch (event->cid) { ++ case SG5_EVENT_PWR_CID_CHARGING: ++ case SG5_EVENT_PWR_CID_STATE: ++ return SG5_EVENT_DELAY_POWER; ++ ++ case SG5_EVENT_PWR_CID_ADAPTER: ++ case SG5_EVENT_PWR_CID_HWCHANGE: ++ default: ++ return 0; ++ } ++} ++ ++static int surfacegen5_evt_power(struct surfacegen5_event *event, void *data) ++{ ++ struct device *dev = (struct device *)data; ++ ++ switch (event->cid) { ++ case SG5_EVENT_PWR_CID_HWCHANGE: ++ return surfacegen5_evt_power_hwchange(dev, event); ++ ++ case SG5_EVENT_PWR_CID_ADAPTER: ++ return surfacegen5_evt_power_adapter(dev, event); ++ ++ case SG5_EVENT_PWR_CID_CHARGING: ++ case SG5_EVENT_PWR_CID_STATE: ++ return surfacegen5_evt_power_state(dev, event); ++ ++ default: ++ dev_warn(dev, "unhandled power event (cid = %x)\n", event->cid); ++ } ++ ++ return 0; ++} ++ ++ ++inline static int surfacegen5_evt_thermal_notify(struct device *dev, struct surfacegen5_event *event) ++{ ++ int status; ++ ++ status = surfacegen5_acpi_notify_sensor_trip_point(dev, event->iid); ++ if (status) { ++ dev_err(dev, "error handling thermal event (cid = %x)\n", event->cid); ++ return status; ++ } ++ ++ return 0; ++} ++ ++static int surfacegen5_evt_thermal(struct surfacegen5_event *event, void *data) ++{ ++ struct device *dev = (struct device *)data; ++ ++ switch (event->cid) { ++ case SG5_EVENT_TEMP_CID_NOTIFY_SENSOR_TRIP_POINT: ++ return surfacegen5_evt_thermal_notify(dev, event); ++ ++ default: ++ dev_warn(dev, "unhandled thermal event (cid = %x)\n", event->cid); ++ } ++ ++ return 0; ++} ++ ++ ++static struct gsb_data_rqsx *surfacegen5_san_validate_rqsx( ++ struct device *dev, const char *type, struct gsb_buffer *buffer) ++{ ++ struct gsb_data_rqsx *rqsx = &buffer->data.rqsx; ++ ++ if (buffer->len < 8) { ++ dev_err(dev, "invalid %s package (len = %d)\n", ++ type, buffer->len); ++ return NULL; ++ } ++ ++ if (rqsx->cdl != buffer->len - 8) { ++ dev_err(dev, "bogus %s package (len = %d, cdl = %d)\n", ++ type, buffer->len, rqsx->cdl); ++ return NULL; ++ } ++ ++ if (rqsx->tid != 0x01) { ++ dev_warn(dev, "unsupported %s package (tid = 0x%02x)\n", ++ type, rqsx->tid); ++ return NULL; ++ } ++ ++ return rqsx; ++} ++ ++static acpi_status ++surfacegen5_san_etwl(struct surfacegen5_san_opreg_context *ctx, struct gsb_buffer *buffer) ++{ ++ struct gsb_data_etwl *etwl = &buffer->data.etwl; ++ ++ if (buffer->len < 3) { ++ dev_err(ctx->dev, "invalid ETWL package (len = %d)\n", buffer->len); ++ return AE_OK; ++ } ++ ++ dev_err(ctx->dev, "ETWL(0x%02x, 0x%02x): %.*s\n", ++ etwl->etw3, etwl->etw4, ++ buffer->len - 3, (char *)etwl->msg); ++ ++ // indicate success ++ buffer->status = 0x00; ++ buffer->len = 0x00; ++ ++ return AE_OK; ++} ++ ++static acpi_status ++surfacegen5_san_rqst(struct surfacegen5_san_opreg_context *ctx, struct gsb_buffer *buffer) ++{ ++ struct gsb_data_rqsx *gsb_rqst = surfacegen5_san_validate_rqsx(ctx->dev, "RQST", buffer); ++ struct surfacegen5_rqst rqst = {}; ++ struct surfacegen5_buf result = {}; ++ int status = 0; ++ int try; ++ ++ if (!gsb_rqst) { ++ return AE_OK; ++ } ++ ++ rqst.tc = gsb_rqst->tc; ++ rqst.iid = gsb_rqst->iid; ++ rqst.cid = gsb_rqst->cid; ++ rqst.snc = gsb_rqst->snc; ++ rqst.cdl = gsb_rqst->cdl; ++ rqst.pld = &gsb_rqst->pld[0]; ++ ++ result.cap = SURFACEGEN5_MAX_RQST_RESPONSE; ++ result.len = 0; ++ result.data = kzalloc(result.cap, GFP_KERNEL); ++ ++ if (!result.data) { ++ return AE_NO_MEMORY; ++ } ++ ++ for (try = 0; try < SG5_RQST_RETRY; try++) { ++ if (try) { ++ dev_warn(ctx->dev, SG5_SAN_RQST_TAG "IO error occured, trying again\n"); ++ } ++ ++ status = surfacegen5_ec_rqst(&rqst, &result); ++ if (status != -EIO) break; ++ } ++ ++ if (rqst.tc == 0x11 && rqst.cid == 0x0D && status == -EPERM) { ++ /* Base state quirk: ++ * The base state may be queried from ACPI when the EC is ++ * still suspended. In this case it will return '-EPERM'. ++ * Returning 0xff (unknown base status) here will suppress ++ * error messages and cause an immediate re-query of the ++ * state. Delay return to avoid spinning. ++ */ ++ ++ buffer->status = 0x00; ++ buffer->len = 0x03; ++ buffer->data.out.status = 0x00; ++ buffer->data.out.len = 0x01; ++ buffer->data.out.pld[0] = 0xFF; ++ msleep(SG5_QUIRK_BASE_STATE_DELAY); ++ ++ } else if (!status) { // success ++ buffer->status = 0x00; ++ buffer->len = result.len + 2; ++ buffer->data.out.status = 0x00; ++ buffer->data.out.len = result.len; ++ memcpy(&buffer->data.out.pld[0], result.data, result.len); ++ ++ } else { // failure ++ dev_err(ctx->dev, SG5_SAN_RQST_TAG "failed with error %d\n", status); ++ buffer->status = 0x00; ++ buffer->len = 0x02; ++ buffer->data.out.status = 0x01; // indicate _SSH error ++ buffer->data.out.len = 0x00; ++ } ++ ++ kfree(result.data); ++ ++ return AE_OK; ++} ++ ++static acpi_status ++surfacegen5_san_rqsg(struct surfacegen5_san_opreg_context *ctx, struct gsb_buffer *buffer) ++{ ++ struct gsb_data_rqsx *rqsg = surfacegen5_san_validate_rqsx(ctx->dev, "RQSG", buffer); ++ ++ if (!rqsg) { ++ return AE_OK; ++ } ++ ++ // TODO: RQSG handler ++ ++ dev_warn(ctx->dev, "unsupported request: RQSG(0x%02x, 0x%02x, 0x%02x)\n", ++ rqsg->tc, rqsg->cid, rqsg->iid); ++ ++ return AE_OK; ++} ++ ++ ++static acpi_status ++surfacegen5_san_opreg_handler(u32 function, acpi_physical_address command, ++ u32 bits, u64 *value64, ++ void *opreg_context, void *region_context) ++{ ++ struct surfacegen5_san_opreg_context *context = opreg_context; ++ struct gsb_buffer *buffer = (struct gsb_buffer *)value64; ++ int accessor_type = (0xFFFF0000 & function) >> 16; ++ ++ if (command != 0) { ++ dev_warn(context->dev, "unsupported command: 0x%02llx\n", command); ++ return AE_OK; ++ } ++ ++ if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) { ++ dev_err(context->dev, "invalid access type: 0x%02x\n", accessor_type); ++ return AE_OK; ++ } ++ ++ // buffer must have at least contain the command-value ++ if (buffer->len == 0) { ++ dev_err(context->dev, "request-package too small\n"); ++ return AE_OK; ++ } ++ ++ switch (buffer->data.in.cv) { ++ case 0x01: return surfacegen5_san_rqst(context, buffer); ++ case 0x02: return surfacegen5_san_etwl(context, buffer); ++ case 0x03: return surfacegen5_san_rqsg(context, buffer); ++ } ++ ++ dev_warn(context->dev, "unsupported SAN0 request (cv: 0x%02x)\n", buffer->data.in.cv); ++ return AE_OK; ++} ++ ++static int surfacegen5_san_enable_events(struct device *dev) ++{ ++ int status; ++ ++ status = surfacegen5_ec_set_delayed_event_handler( ++ SG5_EVENT_PWR_RQID, surfacegen5_evt_power, ++ surfacegen5_evt_power_delay, dev); ++ if (status) { ++ goto err_san_event_handler_power; ++ } ++ ++ status = surfacegen5_ec_set_event_handler( ++ SG5_EVENT_TEMP_RQID, surfacegen5_evt_thermal, ++ dev); ++ if (status) { ++ goto err_san_event_handler_thermal; ++ } ++ ++ status = surfacegen5_ec_enable_event_source(SG5_EVENT_PWR_TC, 0x01, SG5_EVENT_PWR_RQID); ++ if (status) { ++ goto err_san_event_source_power; ++ } ++ ++ status = surfacegen5_ec_enable_event_source(SG5_EVENT_TEMP_TC, 0x01, SG5_EVENT_TEMP_RQID); ++ if (status) { ++ goto err_san_event_source_thermal; ++ } ++ ++ return 0; ++ ++err_san_event_source_thermal: ++ surfacegen5_ec_disable_event_source(SG5_EVENT_PWR_TC, 0x01, SG5_EVENT_PWR_RQID); ++err_san_event_source_power: ++ surfacegen5_ec_remove_event_handler(SG5_EVENT_TEMP_RQID); ++err_san_event_handler_thermal: ++ surfacegen5_ec_remove_event_handler(SG5_EVENT_PWR_RQID); ++err_san_event_handler_power: ++ return status; ++} ++ ++static void surfacegen5_san_disable_events(void) ++{ ++ surfacegen5_ec_disable_event_source(SG5_EVENT_TEMP_TC, 0x01, SG5_EVENT_TEMP_RQID); ++ surfacegen5_ec_disable_event_source(SG5_EVENT_PWR_TC, 0x01, SG5_EVENT_PWR_RQID); ++ surfacegen5_ec_remove_event_handler(SG5_EVENT_TEMP_RQID); ++ surfacegen5_ec_remove_event_handler(SG5_EVENT_PWR_RQID); ++} ++ ++ ++static int surfacegen5_san_consumers_link(struct platform_device *pdev, ++ const struct surfacegen5_san_acpi_consumer *cons, ++ struct surfacegen5_san_consumers *out) ++{ ++ const struct surfacegen5_san_acpi_consumer *con; ++ struct device_link **links, **link; ++ struct acpi_device *adev; ++ acpi_handle handle; ++ u32 max_links = 0; ++ int status; ++ ++ if (!cons) { ++ return 0; ++ } ++ ++ // count links ++ for (con = cons; con->path; ++con) { ++ max_links += 1; ++ } ++ ++ // allocate ++ links = kzalloc(max_links * sizeof(struct device_link *), GFP_KERNEL); ++ link = &links[0]; ++ ++ if (!links) { ++ return -ENOMEM; ++ } ++ ++ // create links ++ for (con = cons; con->path; ++con) { ++ status = acpi_get_handle(NULL, con->path, &handle); ++ if (status) { ++ if (con->required || status != AE_NOT_FOUND) { ++ status = -ENXIO; ++ goto san_consumers_link_cleanup; ++ } else { ++ continue; ++ } ++ } ++ ++ status = acpi_bus_get_device(handle, &adev); ++ if (status) { ++ goto san_consumers_link_cleanup; ++ } ++ ++ *link = device_link_add(&adev->dev, &pdev->dev, con->flags); ++ if (!(*link)) { ++ status = -EFAULT; ++ goto san_consumers_link_cleanup; ++ } ++ ++ link += 1; ++ } ++ ++ out->num = link - links; ++ out->links = links; ++ ++ return 0; ++ ++san_consumers_link_cleanup: ++ for (link = link - 1; link >= links; --link) { ++ device_link_del(*link); ++ } ++ ++ return status; ++} ++ ++static void surfacegen5_san_consumers_unlink(struct surfacegen5_san_consumers *consumers) { ++ u32 i; ++ ++ if (!consumers) { ++ return; ++ } ++ ++ for (i = 0; i < consumers->num; ++i) { ++ device_link_del(consumers->links[i]); ++ } ++ ++ kfree(consumers->links); ++ ++ consumers->num = 0; ++ consumers->links = NULL; ++} ++ ++static int surfacegen5_acpi_san_probe(struct platform_device *pdev) ++{ ++ const struct surfacegen5_san_acpi_consumer *cons; ++ struct surfacegen5_san_drvdata *drvdata; ++ struct device_link *ec_link; ++ acpi_handle san = ACPI_HANDLE(&pdev->dev); // _SAN device node ++ int status; ++ ++ drvdata = kzalloc(sizeof(struct surfacegen5_san_drvdata), GFP_KERNEL); ++ if (!drvdata) { ++ return -ENOMEM; ++ } ++ ++ /* ++ * Defer probe if the _SSH driver has not set up the controller yet. This ++ * makes sure we do not fail any initial requests (e.g. _STA request without ++ * which the battery does not get set up correctly). Otherwise register as ++ * consumer to set up a device_link. ++ */ ++ ec_link = surfacegen5_ec_consumer_add(&pdev->dev, DL_FLAG_PM_RUNTIME); ++ if (IS_ERR_OR_NULL(ec_link)) { ++ if (PTR_ERR(ec_link) == -ENXIO) { ++ status = -EPROBE_DEFER; ++ } else { ++ status = -EFAULT; ++ } ++ ++ goto err_san_probe_ec_link; ++ } ++ ++ drvdata->ec_link = ec_link; ++ drvdata->opreg_ctx.dev = &pdev->dev; ++ ++ cons = acpi_device_get_match_data(&pdev->dev); ++ status = surfacegen5_san_consumers_link(pdev, cons, &drvdata->consumers); ++ if (status) { ++ goto err_san_probe_consumers; ++ } ++ ++ platform_set_drvdata(pdev, drvdata); ++ ++ status = acpi_install_address_space_handler(san, ++ ACPI_ADR_SPACE_GSBUS, ++ &surfacegen5_san_opreg_handler, ++ NULL, &drvdata->opreg_ctx); ++ ++ if (ACPI_FAILURE(status)) { ++ status = -ENODEV; ++ goto err_san_probe_install_handler; ++ } ++ ++ status = surfacegen5_san_enable_events(&pdev->dev); ++ if (status) { ++ goto err_san_probe_enable_events; ++ } ++ ++ acpi_walk_dep_device_list(san); ++ return 0; ++ ++err_san_probe_enable_events: ++ acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &surfacegen5_san_opreg_handler); ++err_san_probe_install_handler: ++ platform_set_drvdata(san, NULL); ++ surfacegen5_san_consumers_unlink(&drvdata->consumers); ++err_san_probe_consumers: ++ surfacegen5_ec_consumer_remove(drvdata->ec_link); ++err_san_probe_ec_link: ++ kfree(drvdata); ++ return status; ++} ++ ++static int surfacegen5_acpi_san_remove(struct platform_device *pdev) ++{ ++ struct surfacegen5_san_drvdata *drvdata = platform_get_drvdata(pdev); ++ acpi_handle san = ACPI_HANDLE(&pdev->dev); // _SAN device node ++ acpi_status status = AE_OK; ++ ++ acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &surfacegen5_san_opreg_handler); ++ surfacegen5_san_disable_events(); ++ ++ surfacegen5_san_consumers_unlink(&drvdata->consumers); ++ surfacegen5_ec_consumer_remove(drvdata->ec_link); ++ kfree(drvdata); ++ ++ platform_set_drvdata(pdev, NULL); ++ return status; ++} ++ ++ ++static const struct surfacegen5_san_acpi_consumer surfacegen5_mshw0091_consumers[] = { ++ { "\\_SB.SRTC", true, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS }, ++ { "\\ADP1", true, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS }, ++ { "\\_SB.BAT1", true, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS }, ++ { "\\_SB.BAT2", false, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS }, ++ { }, ++}; ++ ++static const struct acpi_device_id surfacegen5_acpi_san_match[] = { ++ { "MSHW0091", (long unsigned int) surfacegen5_mshw0091_consumers }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(acpi, surfacegen5_acpi_san_match); ++ ++static struct platform_driver surfacegen5_acpi_san = { ++ .probe = surfacegen5_acpi_san_probe, ++ .remove = surfacegen5_acpi_san_remove, ++ .driver = { ++ .name = "surfacegen5_acpi_san", ++ .acpi_match_table = ACPI_PTR(surfacegen5_acpi_san_match), ++ }, ++}; ++ ++#endif /* CONFIG_SURFACE_ACPI_SAN */ ++ ++ ++/************************************************************************* ++ * Virtual HID Framework driver ++ */ ++ ++#ifdef CONFIG_SURFACE_ACPI_VHF ++ ++#define USB_VENDOR_ID_MICROSOFT 0x045e ++#define USB_DEVICE_ID_MS_VHF 0xf001 ++ ++/* ++ * Request ID for VHF events. This value is based on the output of the Surface ++ * EC and should not be changed. ++ */ ++#define SG5_VHF_RQID 0x0001 ++ ++ ++struct surfacegen5_vhf_evtctx { ++ struct device *dev; ++ struct hid_device *hid; ++}; ++ ++struct surfacegen5_vhf_drvdata { ++ struct device_link *ec_link; ++ struct surfacegen5_vhf_evtctx event_ctx; ++}; ++ ++ ++/* ++ * These report descriptors have been extracted from a Surface Book 2. ++ * They seems to be similar enough to be usable on the Surface Laptop. ++ */ ++static const u8 vhf_hid_desc[] = { ++ // keyboard descriptor (event command ID 0x03) ++ 0x05, 0x01, /* Usage Page (Desktop), */ ++ 0x09, 0x06, /* Usage (Keyboard), */ ++ 0xA1, 0x01, /* Collection (Application), */ ++ 0x85, 0x01, /* Report ID (1), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x25, 0x01, /* Logical Maximum (1), */ ++ 0x75, 0x01, /* Report Size (1), */ ++ 0x95, 0x08, /* Report Count (8), */ ++ 0x05, 0x07, /* Usage Page (Keyboard), */ ++ 0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */ ++ 0x29, 0xE7, /* Usage Maximum (KB Right GUI), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x95, 0x0A, /* Report Count (10), */ ++ 0x19, 0x00, /* Usage Minimum (None), */ ++ 0x29, 0x91, /* Usage Maximum (KB LANG2), */ ++ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ ++ 0x81, 0x00, /* Input, */ ++ 0x05, 0x0C, /* Usage Page (Consumer), */ ++ 0x0A, 0xC0, 0x02, /* Usage (02C0h), */ ++ 0xA1, 0x02, /* Collection (Logical), */ ++ 0x1A, 0xC1, 0x02, /* Usage Minimum (02C1h), */ ++ 0x2A, 0xC6, 0x02, /* Usage Maximum (02C6h), */ ++ 0x95, 0x06, /* Report Count (6), */ ++ 0xB1, 0x03, /* Feature (Constant, Variable), */ ++ 0xC0, /* End Collection, */ ++ 0x05, 0x08, /* Usage Page (LED), */ ++ 0x19, 0x01, /* Usage Minimum (01h), */ ++ 0x29, 0x03, /* Usage Maximum (03h), */ ++ 0x75, 0x01, /* Report Size (1), */ ++ 0x95, 0x03, /* Report Count (3), */ ++ 0x25, 0x01, /* Logical Maximum (1), */ ++ 0x91, 0x02, /* Output (Variable), */ ++ 0x95, 0x05, /* Report Count (5), */ ++ 0x91, 0x01, /* Output (Constant), */ ++ 0xC0, /* End Collection, */ ++ ++ // media key descriptor (event command ID 0x04) ++ 0x05, 0x0C, /* Usage Page (Consumer), */ ++ 0x09, 0x01, /* Usage (Consumer Control), */ ++ 0xA1, 0x01, /* Collection (Application), */ ++ 0x85, 0x03, /* Report ID (3), */ ++ 0x75, 0x10, /* Report Size (16), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ ++ 0x19, 0x00, /* Usage Minimum (00h), */ ++ 0x2A, 0xFF, 0x03, /* Usage Maximum (03FFh), */ ++ 0x81, 0x00, /* Input, */ ++ 0xC0, /* End Collection, */ ++}; ++ ++ ++static int vhf_hid_start(struct hid_device *hid) ++{ ++ hid_dbg(hid, "%s\n", __func__); ++ return 0; ++} ++ ++static void vhf_hid_stop(struct hid_device *hid) ++{ ++ hid_dbg(hid, "%s\n", __func__); ++} ++ ++static int vhf_hid_open(struct hid_device *hid) ++{ ++ hid_dbg(hid, "%s\n", __func__); ++ return 0; ++} ++ ++static void vhf_hid_close(struct hid_device *hid) ++{ ++ hid_dbg(hid, "%s\n", __func__); ++} ++ ++static int vhf_hid_parse(struct hid_device *hid) ++{ ++ return hid_parse_report(hid, (u8 *)vhf_hid_desc, ARRAY_SIZE(vhf_hid_desc)); ++} ++ ++static int vhf_hid_raw_request(struct hid_device *hid, unsigned char reportnum, ++ u8 *buf, size_t len, unsigned char rtype, ++ int reqtype) ++{ ++ hid_dbg(hid, "%s\n", __func__); ++ return 0; ++} ++ ++static int vhf_hid_output_report(struct hid_device *hid, u8 *buf, size_t len) ++{ ++ hid_dbg(hid, "%s\n", __func__); ++ print_hex_dump_debug("report:", DUMP_PREFIX_OFFSET, 16, 1, buf, len, false); ++ ++ return len; ++} ++ ++static struct hid_ll_driver vhf_hid_ll_driver = { ++ .start = vhf_hid_start, ++ .stop = vhf_hid_stop, ++ .open = vhf_hid_open, ++ .close = vhf_hid_close, ++ .parse = vhf_hid_parse, ++ .raw_request = vhf_hid_raw_request, ++ .output_report = vhf_hid_output_report, ++}; ++ ++ ++static struct hid_device *surfacegen5_vhf_create_hid_device(struct platform_device *pdev) ++{ ++ struct hid_device *hid; ++ ++ hid = hid_allocate_device(); ++ if (IS_ERR(hid)) { ++ return hid; ++ } ++ ++ hid->dev.parent = &pdev->dev; ++ ++ hid->bus = BUS_VIRTUAL; ++ hid->vendor = USB_VENDOR_ID_MICROSOFT; ++ hid->product = USB_DEVICE_ID_MS_VHF; ++ ++ hid->ll_driver = &vhf_hid_ll_driver; ++ ++ sprintf(hid->name, "%s", "Microsoft Virtual HID Framework Device"); ++ ++ return hid; ++} ++ ++static int surfacegen5_vhf_event_handler(struct surfacegen5_event *event, void *data) ++{ ++ struct surfacegen5_vhf_evtctx *ctx = (struct surfacegen5_vhf_evtctx *)data; ++ ++ if (event->tc == 0x08 && (event->cid == 0x03 || event->cid == 0x04)) { ++ return hid_input_report(ctx->hid, HID_INPUT_REPORT, event->pld, event->len, 1); ++ } ++ ++ dev_warn(ctx->dev, "unsupported event (tc = %d, cid = %d)\n", event->tc, event->cid); ++ return 0; ++} ++ ++static unsigned long surfacegen5_vhf_event_delay(struct surfacegen5_event *event, void *data) ++{ ++ // high priority immediate execution for keyboard events ++ if (event->tc == 0x08 && (event->cid == 0x03 || event->cid == 0x04)) { ++ return SURFACEGEN5_EVENT_IMMEDIATE; ++ } ++ ++ return 0; ++} ++ ++static int surfacegen5_acpi_vhf_probe(struct platform_device *pdev) ++{ ++ struct surfacegen5_vhf_drvdata *drvdata; ++ struct device_link *ec_link; ++ struct hid_device *hid; ++ int status; ++ ++ drvdata = kzalloc(sizeof(struct surfacegen5_vhf_drvdata), GFP_KERNEL); ++ if (!drvdata) { ++ return -ENOMEM; ++ } ++ ++ // add device link to EC ++ ec_link = surfacegen5_ec_consumer_add(&pdev->dev, DL_FLAG_PM_RUNTIME); ++ if (IS_ERR_OR_NULL(ec_link)) { ++ if (PTR_ERR(ec_link) == -ENXIO) { ++ status = -EPROBE_DEFER; ++ } else { ++ status = -EFAULT; ++ } ++ ++ goto err_probe_ec_link; ++ } ++ ++ hid = surfacegen5_vhf_create_hid_device(pdev); ++ if (IS_ERR(hid)) { ++ status = PTR_ERR(hid); ++ goto err_probe_hid; ++ } ++ ++ status = hid_add_device(hid); ++ if (status) { ++ goto err_add_hid; ++ } ++ ++ drvdata->ec_link = ec_link; ++ drvdata->event_ctx.dev = &pdev->dev; ++ drvdata->event_ctx.hid = hid; ++ ++ platform_set_drvdata(pdev, drvdata); ++ ++ /* ++ * Set event hanlder for VHF events. They seem to be enabled by ++ * default, thus there should be no need to explicitly enable them. ++ */ ++ status = surfacegen5_ec_set_delayed_event_handler( ++ SG5_VHF_RQID, ++ surfacegen5_vhf_event_handler, ++ surfacegen5_vhf_event_delay, ++ &drvdata->event_ctx); ++ if (status) { ++ goto err_add_hid; ++ } ++ ++ return 0; ++ ++err_add_hid: ++ hid_destroy_device(hid); ++ platform_set_drvdata(pdev, NULL); ++err_probe_hid: ++ surfacegen5_ec_consumer_remove(drvdata->ec_link); ++err_probe_ec_link: ++ kfree(drvdata); ++ return status; ++} ++ ++static int surfacegen5_acpi_vhf_remove(struct platform_device *pdev) ++{ ++ struct surfacegen5_vhf_drvdata *drvdata = platform_get_drvdata(pdev); ++ ++ surfacegen5_ec_remove_event_handler(SG5_VHF_RQID); ++ ++ hid_destroy_device(drvdata->event_ctx.hid); ++ surfacegen5_ec_consumer_remove(drvdata->ec_link); ++ kfree(drvdata); ++ ++ platform_set_drvdata(pdev, NULL); ++ return 0; ++} ++ ++ ++static const struct acpi_device_id surfacegen5_acpi_vhf_match[] = { ++ { "MSHW0096" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(acpi, surfacegen5_acpi_vhf_match); ++ ++static struct platform_driver surfacegen5_acpi_vhf = { ++ .probe = surfacegen5_acpi_vhf_probe, ++ .remove = surfacegen5_acpi_vhf_remove, ++ .driver = { ++ .name = "surfacegen5_acpi_vhf", ++ .acpi_match_table = ACPI_PTR(surfacegen5_acpi_vhf_match), ++ }, ++}; ++ ++#endif /* CONFIG_SURFACE_ACPI_VHF */ ++ ++ ++/************************************************************************* ++ * Module initialization ++ */ ++ ++int __init surface_acpi_init(void) ++{ ++ int status; ++ ++#ifdef CONFIG_SURFACE_ACPI_SSH ++ status = serdev_device_driver_register(&surfacegen5_acpi_ssh); ++ if (status) { ++ goto err_modinit_ssh; ++ } ++#endif /* CONFIG_SURFACE_ACPI_SSH */ ++ ++#ifdef CONFIG_SURFACE_ACPI_SAN ++ status = platform_driver_register(&surfacegen5_acpi_san); ++ if (status) { ++ goto err_modinit_san; ++ } ++#endif /* CONFIG_SURFACE_ACPI_SAN */ ++ ++#ifdef CONFIG_SURFACE_ACPI_VHF ++ status = platform_driver_register(&surfacegen5_acpi_vhf); ++ if (status) { ++ goto err_modinit_vhf; ++ } ++#endif /* CONFIG_SURFACE_ACPI_VHF */ ++ ++ return 0; ++ ++#ifdef CONFIG_SURFACE_ACPI_VHF ++ platform_driver_unregister(&surfacegen5_acpi_vhf); ++err_modinit_vhf: ++#endif /* CONFIG_SURFACE_ACPI_VHF */ ++ ++#ifdef CONFIG_SURFACE_ACPI_SAN ++ platform_driver_unregister(&surfacegen5_acpi_san); ++err_modinit_san: ++#endif /* CONFIG_SURFACE_ACPI_SAN */ ++ ++#ifdef CONFIG_SURFACE_ACPI_SSH ++ serdev_device_driver_unregister(&surfacegen5_acpi_ssh); ++err_modinit_ssh: ++#endif /* CONFIG_SURFACE_ACPI_SSH */ ++ ++ return status; ++} ++ ++void __exit surface_acpi_exit(void) ++{ ++#ifdef CONFIG_SURFACE_ACPI_VHF ++ platform_driver_unregister(&surfacegen5_acpi_vhf); ++#endif /* CONFIG_SURFACE_ACPI_VHF */ ++ ++#ifdef CONFIG_SURFACE_ACPI_SAN ++ platform_driver_unregister(&surfacegen5_acpi_san); ++#endif /* CONFIG_SURFACE_ACPI_SAN */ ++ ++#ifdef CONFIG_SURFACE_ACPI_SSH ++ serdev_device_driver_unregister(&surfacegen5_acpi_ssh); ++#endif /* CONFIG_SURFACE_ACPI_SSH */ ++} ++ ++ ++module_init(surface_acpi_init) ++module_exit(surface_acpi_exit) ++ ++MODULE_AUTHOR("Maximilian Luz "); ++MODULE_DESCRIPTION("ACPI/Platform Drivers for Microsoft Surface Devices"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c +index 9db93f500b4e..2290d4f86ca4 100644 +--- a/drivers/tty/serdev/core.c ++++ b/drivers/tty/serdev/core.c +@@ -502,8 +502,7 @@ static acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl, + struct serdev_device *serdev = NULL; + int err; + +- if (acpi_bus_get_status(adev) || !adev->status.present || +- acpi_device_enumerated(adev)) ++ if (acpi_bus_get_status(adev) || !adev->status.present) + return AE_OK; + + serdev = serdev_device_alloc(ctrl); +@@ -526,6 +525,81 @@ static acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl, + return AE_OK; + } + ++struct acpi_serdev_add_device_from_resource_ctx { ++ acpi_handle ctrl_handle; ++ acpi_handle device_handle; ++ struct serdev_controller *ctrl; ++ struct acpi_device *device; ++}; ++ ++static acpi_status ++acpi_serdev_add_device_from_resource(struct acpi_resource *resource, void *data) ++{ ++ struct acpi_serdev_add_device_from_resource_ctx* ctx = data; ++ struct acpi_resource_source *ctrl_name; ++ acpi_handle ctrl_handle; ++ acpi_status status; ++ ++ if (resource->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) ++ return AE_OK; ++ ++ if (resource->data.common_serial_bus.type != ACPI_RESOURCE_SERIAL_TYPE_UART) ++ return AE_OK; ++ ++ ctrl_name = &resource->data.common_serial_bus.resource_source; ++ if (ctrl_name->string_length == 0 || !ctrl_name->string_ptr) { ++ return AE_OK; ++ } ++ ++ status = acpi_get_handle(ctx->device_handle, ctrl_name->string_ptr, ++ &ctrl_handle); ++ if (ACPI_FAILURE(status)) { ++ return AE_OK; ++ } ++ ++ if (ctrl_handle == ctx->ctrl_handle) { ++ return acpi_serdev_register_device(ctx->ctrl, ctx->device); ++ } ++ ++ return AE_OK; ++} ++ ++static acpi_status ++acpi_serdev_add_devices_from_resources(acpi_handle handle, u32 level, ++ void *data, void **return_value) ++{ ++ struct acpi_serdev_add_device_from_resource_ctx *ctx = data; ++ acpi_status status; ++ ++ ctx->device_handle = handle; ++ ++ status = acpi_bus_get_device(handle, &ctx->device); ++ if (status) return AE_OK; ++ ++ status = acpi_walk_resources(handle, METHOD_NAME__CRS, ++ acpi_serdev_add_device_from_resource, ++ ctx); ++ ++ if (status == AE_NOT_FOUND) ++ return AE_OK; // not finding _CRS is not an error ++ else ++ return status; ++} ++ ++static int ++acpi_serdev_register_devices_from_resources(acpi_handle handle, ++ struct serdev_controller *ctrl) ++{ ++ struct acpi_serdev_add_device_from_resource_ctx ctx = { ++ .ctrl = ctrl, ++ .ctrl_handle = handle, ++ }; ++ ++ return acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 128, ++ acpi_serdev_add_devices_from_resources, ++ NULL, &ctx, NULL); ++} ++ + static acpi_status acpi_serdev_add_device(acpi_handle handle, u32 level, + void *data, void **return_value) + { +@@ -535,6 +609,9 @@ static acpi_status acpi_serdev_add_device(acpi_handle handle, u32 level, + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + ++ if (acpi_device_enumerated(adev)) ++ return AE_OK; ++ + return acpi_serdev_register_device(ctrl, adev); + } + +@@ -552,6 +629,15 @@ static int acpi_serdev_register_devices(struct serdev_controller *ctrl) + if (ACPI_FAILURE(status)) + dev_dbg(&ctrl->dev, "failed to enumerate serdev slaves\n"); + ++ if (!ctrl->serdev) { ++ status = acpi_serdev_register_devices_from_resources(handle, ctrl); ++ if (ACPI_FAILURE(status)) { ++ dev_dbg(&ctrl->dev, ++ "failed to register serdev slaves from resources: %x\n", ++ status); ++ } ++ } ++ + if (!ctrl->serdev) + return -ENODEV; + +-- +2.17.1 + diff --git a/linux_surface/patches/0002-resume-delay.patch b/linux_surface/patches/0002-resume-delay.patch new file mode 100644 index 0000000..3527625 --- /dev/null +++ b/linux_surface/patches/0002-resume-delay.patch @@ -0,0 +1,63 @@ +From 2c96c2eabfe092f7b1ada3827737009ee84cb60f Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Sun, 27 Jan 2019 10:45:42 -0500 +Subject: [PATCH 02/11] resume-delay + +--- + kernel/power/suspend.c | 11 +++++++++++ + kernel/sysctl.c | 9 +++++++++ + 2 files changed, 20 insertions(+) + +diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c +index 0bd595a0b610..a8385e8894a5 100644 +--- a/kernel/power/suspend.c ++++ b/kernel/power/suspend.c +@@ -526,6 +526,8 @@ int suspend_devices_and_enter(suspend_state_t state) + goto Resume_devices; + } + ++unsigned int resume_delay = 3000; ++ + /** + * suspend_finish - Clean up before finishing the suspend sequence. + * +@@ -534,6 +536,15 @@ int suspend_devices_and_enter(suspend_state_t state) + */ + static void suspend_finish(void) + { ++ if (resume_delay) { ++ /* Give kernel threads a head start, such that usb-storage ++ * can detect devices before syslog attempts to write log ++ * messages from the suspend code. ++ */ ++ thaw_kernel_threads(); ++ pr_debug("PM: Sleeping for %d milliseconds.\n", resume_delay); ++ msleep(resume_delay); ++ } + suspend_thaw_processes(); + pm_notifier_call_chain(PM_POST_SUSPEND); + pm_restore_console(); +diff --git a/kernel/sysctl.c b/kernel/sysctl.c +index cc02050fd0c4..11802bda1a5d 100644 +--- a/kernel/sysctl.c ++++ b/kernel/sysctl.c +@@ -311,7 +311,16 @@ static int min_extfrag_threshold; + static int max_extfrag_threshold = 1000; + #endif + ++extern unsigned int resume_delay; ++ + static struct ctl_table kern_table[] = { ++ { ++ .procname = "resume_delay", ++ .data = &resume_delay, ++ .maxlen = sizeof(unsigned int), ++ .mode = 0644, ++ .proc_handler = proc_dointvec, ++ }, + { + .procname = "sched_child_runs_first", + .data = &sysctl_sched_child_runs_first, +-- +2.17.1 + diff --git a/linux_surface/patches/0003-buttons.patch b/linux_surface/patches/0003-buttons.patch new file mode 100644 index 0000000..c59cad2 --- /dev/null +++ b/linux_surface/patches/0003-buttons.patch @@ -0,0 +1,207 @@ +From 8a6ddaf22fc30f97b8d47d40d610671b58158fc7 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Sun, 27 Jan 2019 10:51:25 -0500 +Subject: [PATCH 03/11] buttons + +--- + drivers/input/misc/soc_button_array.c | 84 +++++++++++++++++++++-- + drivers/platform/x86/surfacepro3_button.c | 36 ++++++++++ + 2 files changed, 114 insertions(+), 6 deletions(-) + +diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c +index 23520df7650f..1ea239ff426d 100644 +--- a/drivers/input/misc/soc_button_array.c ++++ b/drivers/input/misc/soc_button_array.c +@@ -29,12 +29,24 @@ struct soc_button_info { + bool wakeup; + }; + ++struct soc_device_data { ++ /* Button info, may be NULL. */ ++ struct soc_button_info *button_info; ++ /* Special device check function, may be NULL. */ ++ int (*check)(struct device *); ++}; ++ + /* + * Some of the buttons like volume up/down are auto repeat, while others + * are not. To support both, we register two platform devices, and put + * buttons into them based on whether the key should be auto repeat. + */ +-#define BUTTON_TYPES 2 ++#define BUTTON_TYPES 2 ++ ++#define MSHW0040_DSM_REVISION 0x01 ++#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision ++static const guid_t MSHW0040_DSM_UUID = ++ GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, 0x49, 0x80, 0x35); + + struct soc_button_data { + struct platform_device *children[BUTTON_TYPES]; +@@ -310,6 +322,7 @@ static int soc_button_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + const struct acpi_device_id *id; ++ struct soc_device_data *device_data; + struct soc_button_info *button_info; + struct soc_button_data *priv; + struct platform_device *pd; +@@ -320,12 +333,20 @@ static int soc_button_probe(struct platform_device *pdev) + if (!id) + return -ENODEV; + +- if (!id->driver_data) { ++ device_data = (struct soc_device_data *)id->driver_data; ++ if (device_data && device_data->check) { ++ // device dependent check, required for MSHW0040 ++ error = device_data->check(dev); ++ if (error != 0) ++ return error; ++ } ++ ++ if (device_data && device_data->button_info) { ++ button_info = (struct soc_button_info *)device_data->button_info; ++ } else { + button_info = soc_button_get_button_info(dev); + if (IS_ERR(button_info)) + return PTR_ERR(button_info); +- } else { +- button_info = (struct soc_button_info *)id->driver_data; + } + + error = gpiod_count(dev, NULL); +@@ -357,7 +378,7 @@ static int soc_button_probe(struct platform_device *pdev) + if (!priv->children[0] && !priv->children[1]) + return -ENODEV; + +- if (!id->driver_data) ++ if (!device_data || !device_data->button_info) + devm_kfree(dev, button_info); + + return 0; +@@ -377,9 +398,60 @@ static struct soc_button_info soc_button_PNP0C40[] = { + { } + }; + ++static struct soc_device_data soc_device_PNP0C40 = { ++ .button_info = soc_button_PNP0C40, ++ .check = NULL, ++}; ++ ++/* ++ * Special device check for Surface Book 2 and Surface Pro (2017). ++ * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned ++ * devices use MSHW0040 for power and volume buttons, however the way they ++ * have to be addressed differs. Make sure that we only load this drivers ++ * for the correct devices by checking the OEM Platform Revision provided by ++ * the _DSM method. ++ */ ++static int soc_device_check_MSHW0040(struct device *dev) ++{ ++ acpi_handle handle = ACPI_HANDLE(dev); ++ union acpi_object *result; ++ u64 oem_platform_rev = 0; ++ ++ // get OEM board revision ++ result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, MSHW0040_DSM_REVISION, ++ MSHW0040_DSM_GET_OMPR, NULL, ACPI_TYPE_INTEGER); ++ ++ if (result) { ++ oem_platform_rev = result->integer.value; ++ ACPI_FREE(result); ++ } ++ ++ dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); ++ ++ return oem_platform_rev > 0 ? 0 : -ENODEV; ++} ++ ++/* ++ * Button infos for Microsoft Surface Book 2 and Surface Pro (2017). ++ * Extracted from DSDT. ++ */ ++static struct soc_button_info soc_button_MSHW0040[] = { ++ { "power", 0, EV_KEY, KEY_POWER, false, true }, ++ { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, ++ { "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false }, ++ { } ++}; ++ ++static struct soc_device_data soc_device_MSHW0040 = { ++ .button_info = soc_button_MSHW0040, ++ .check = soc_device_check_MSHW0040, ++}; ++ + static const struct acpi_device_id soc_button_acpi_match[] = { +- { "PNP0C40", (unsigned long)soc_button_PNP0C40 }, ++ { "PNP0C40", (unsigned long)&soc_device_PNP0C40 }, + { "ACPI0011", 0 }, ++ /* Microsoft Surface Book 2 and Surface Pro (2017) */ ++ { "MSHW0040", (unsigned long)&soc_device_MSHW0040 }, + { } + }; + +diff --git a/drivers/platform/x86/surfacepro3_button.c b/drivers/platform/x86/surfacepro3_button.c +index 1b491690ce07..b67f559ee209 100644 +--- a/drivers/platform/x86/surfacepro3_button.c ++++ b/drivers/platform/x86/surfacepro3_button.c +@@ -24,6 +24,12 @@ + #define SURFACE_BUTTON_OBJ_NAME "VGBI" + #define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3/4 Buttons" + ++#define MSHW0040_DSM_REVISION 0x01 ++#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision ++static const guid_t MSHW0040_DSM_UUID = ++ GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, 0x49, 0x80, 0x35); ++ ++ + #define SURFACE_BUTTON_NOTIFY_TABLET_MODE 0xc8 + + #define SURFACE_BUTTON_NOTIFY_PRESS_POWER 0xc6 +@@ -146,6 +152,32 @@ static int surface_button_resume(struct device *dev) + } + #endif + ++/* ++ * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device ++ * ID (MSHW0040) for the power/volume buttons. Make sure this is the right ++ * device by checking for the _DSM method and OEM Platform Revision. ++ */ ++static int surface_button_check_MSHW0040(struct acpi_device *dev) ++{ ++ acpi_handle handle = dev->handle; ++ union acpi_object *result; ++ u64 oem_platform_rev = 0; ++ ++ // get OEM board revision ++ result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, MSHW0040_DSM_REVISION, ++ MSHW0040_DSM_GET_OMPR, NULL, ACPI_TYPE_INTEGER); ++ ++ if (result) { ++ oem_platform_rev = result->integer.value; ++ ACPI_FREE(result); ++ } ++ ++ dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev); ++ ++ return oem_platform_rev == 0 ? 0 : -ENODEV; ++} ++ ++ + static int surface_button_add(struct acpi_device *device) + { + struct surface_button *button; +@@ -158,6 +190,10 @@ static int surface_button_add(struct acpi_device *device) + strlen(SURFACE_BUTTON_OBJ_NAME))) + return -ENODEV; + ++ error = surface_button_check_MSHW0040(device); ++ if (error) ++ return error; ++ + button = kzalloc(sizeof(struct surface_button), GFP_KERNEL); + if (!button) + return -ENOMEM; +-- +2.17.1 + diff --git a/linux_surface/patches/0004-cameras.patch b/linux_surface/patches/0004-cameras.patch new file mode 100644 index 0000000..07bc9ad --- /dev/null +++ b/linux_surface/patches/0004-cameras.patch @@ -0,0 +1,2753 @@ +From 0c7d455e598ee386ffa5ee877d77943a3259f5d6 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Sun, 27 Jan 2019 10:51:49 -0500 +Subject: [PATCH 04/11] cameras + +--- + drivers/media/usb/uvc/uvc_driver.c | 40 + + drivers/staging/Makefile | 1 + + drivers/staging/ov5693/Kconfig | 10 + + drivers/staging/ov5693/Makefile | 5 + + drivers/staging/ov5693/ad5823.c | 218 +++++ + drivers/staging/ov5693/ad5823.h | 90 ++ + drivers/staging/ov5693/ov5693.c | 1461 ++++++++++++++++++++++++++++ + drivers/staging/ov5693/ov5693.h | 848 ++++++++++++++++ + 8 files changed, 2673 insertions(+) + create mode 100644 drivers/staging/ov5693/Kconfig + create mode 100644 drivers/staging/ov5693/Makefile + create mode 100644 drivers/staging/ov5693/ad5823.c + create mode 100644 drivers/staging/ov5693/ad5823.h + create mode 100644 drivers/staging/ov5693/ov5693.c + create mode 100644 drivers/staging/ov5693/ov5693.h + +diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c +index d46dc432456c..2552fb6fcfbe 100644 +--- a/drivers/media/usb/uvc/uvc_driver.c ++++ b/drivers/media/usb/uvc/uvc_driver.c +@@ -2352,6 +2352,46 @@ static const struct uvc_device_info uvc_quirk_force_y8 = { + * though they are compliant. + */ + static const struct usb_device_id uvc_ids[] = { ++ /* Microsoft Surface Pro 3 Front */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x045e, ++ .idProduct = 0x07be, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1 }, ++ /* Microsoft Surface Pro 3 Rear */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x045e, ++ .idProduct = 0x07bf, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1 }, ++ /* Microsoft Surface Pro 4 Cam */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x045e, ++ .idProduct = 0x090c, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1 }, ++ /* Microsoft Surface Book Cam 1 */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x045e, ++ .idProduct = 0x090b, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1 }, ++ /* Microsoft Surface Book Cam 2 */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x045e, ++ .idProduct = 0x091a, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1 }, + /* LogiLink Wireless Webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, +diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile +index ab0cbe8815b1..96c4c669a93f 100644 +--- a/drivers/staging/Makefile ++++ b/drivers/staging/Makefile +@@ -53,3 +53,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-dts/ + obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/ + obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ + obj-$(CONFIG_EROFS_FS) += erofs/ ++obj-$(CONFIG_VIDEO_OV5693) += ov5693/ +diff --git a/drivers/staging/ov5693/Kconfig b/drivers/staging/ov5693/Kconfig +new file mode 100644 +index 000000000000..96000f112c4d +--- /dev/null ++++ b/drivers/staging/ov5693/Kconfig +@@ -0,0 +1,10 @@ ++config VIDEO_OV5693 ++ tristate "Omnivision ov5693 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ ---help--- ++ This is a Video4Linux2 sensor-level driver for the Micron ++ ov5693 5 Mpixel camera. ++ ++ ov5693 is video camera sensor. ++ ++ It currently only works with the atomisp driver. +diff --git a/drivers/staging/ov5693/Makefile b/drivers/staging/ov5693/Makefile +new file mode 100644 +index 000000000000..d8a63faa591f +--- /dev/null ++++ b/drivers/staging/ov5693/Makefile +@@ -0,0 +1,5 @@ ++obj-$(CONFIG_VIDEO_OV5693) += ov569x.o ++ ++ov569x-objs := ov5693.o ad5823.o ++ ++ccflags-y += -Werror +diff --git a/drivers/staging/ov5693/ad5823.c b/drivers/staging/ov5693/ad5823.c +new file mode 100644 +index 000000000000..7c34c36e77e5 +--- /dev/null ++++ b/drivers/staging/ov5693/ad5823.c +@@ -0,0 +1,218 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ad5823.h" ++ ++static struct ad5823_device ad5823_dev; ++static int ad5823_i2c_write(struct i2c_client *client, u8 reg, u8 val) ++{ ++ struct i2c_msg msg; ++ u8 buf[2]; ++ buf[0] = reg; ++ buf[1] = val; ++ msg.addr = AD5823_VCM_ADDR; ++ msg.flags = 0; ++ msg.len = AD5823_8BIT; ++ msg.buf = &buf[0]; ++ ++ if (i2c_transfer(client->adapter, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static int ad5823_i2c_read(struct i2c_client *client, u8 reg, u8 *val) ++{ ++ struct i2c_msg msg[2]; ++ u8 buf[2]; ++ buf[0] = reg; ++ buf[1] = 0; ++ ++ msg[0].addr = AD5823_VCM_ADDR; ++ msg[0].flags = 0; ++ msg[0].len = AD5823_8BIT; ++ msg[0].buf = &buf[0]; ++ ++ msg[1].addr = AD5823_VCM_ADDR; ++ msg[1].flags = I2C_M_RD; ++ msg[1].len = AD5823_8BIT; ++ msg[1].buf = &buf[1]; ++ *val = 0; ++ if (i2c_transfer(client->adapter, msg, 2) != 2) ++ return -EIO; ++ *val = buf[1]; ++ return 0; ++} ++ ++int ad5823_vcm_power_up(struct v4l2_subdev *sd) ++{ ++ int ret = -ENODEV; ++ ++ /* Enable power */ ++ if (ad5823_dev.platform_data) ++ ret = ad5823_dev.platform_data->power_ctrl(sd, 1); ++ /* ++ * waiting time requested by AD5823(vcm) ++ */ ++ usleep_range(1000, 2000); ++ return ret; ++} ++ ++int ad5823_vcm_power_down(struct v4l2_subdev *sd) ++{ ++ int ret = -ENODEV; ++ ++ if (ad5823_dev.platform_data) ++ ret = ad5823_dev.platform_data->power_ctrl(sd, 0); ++ ++ return ret; ++} ++ ++ ++int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = -EINVAL; ++ u8 vcm_code; ++ u8 vcm_mode_reg_val[4] = { ++ AD5823_ARC_RES0, ++ AD5823_ARC_RES1, ++ AD5823_ARC_RES2, ++ AD5823_ESRC ++ }; ++ ++ if (ad5823_dev.vcm_mode != AD5823_DIRECT) { ++ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, ++ AD5823_RING_CTRL_ENABLE); ++ if (ret) ++ return ret; ++ ++ ret = ad5823_i2c_write(client, AD5823_REG_MODE, ++ vcm_mode_reg_val[ad5823_dev.vcm_mode]); ++ if (ret) ++ return ret; ++ } else { ++ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, ++ AD5823_RING_CTRL_DISABLE); ++ if (ret) ++ return ret; ++ } ++ ++ ret = ad5823_i2c_read(client, AD5823_REG_VCM_CODE_MSB, &vcm_code); ++ if (ret) ++ return ret; ++ ++ /* set reg VCM_CODE_MSB Bit[1:0] */ ++ vcm_code = (vcm_code & VCM_CODE_MSB_MASK) | ((val >> 8) & ~VCM_CODE_MSB_MASK); ++ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, vcm_code); ++ if (ret) ++ return ret; ++ ++ /* set reg VCM_CODE_LSB Bit[7:0] */ ++ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_LSB, ++ (val & 0x0f)); ++ if (ret) ++ return ret; ++ ++ /* set required vcm move time */ ++ vcm_code = AD5823_RESONANCE_PERIOD / AD5823_RESONANCE_COEF ++ - AD5823_HIGH_FREQ_RANGE; ++ ret = ad5823_i2c_write(client, AD5823_REG_VCM_MOVE_TIME, vcm_code); ++ ++ return ret; ++} ++ ++int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value) ++{ ++ int ret; ++ ++ value = min(value, AD5823_MAX_FOCUS_POS); ++ ret = ad5823_t_focus_vcm(sd, AD5823_MAX_FOCUS_POS - value); ++ if (ret == 0) { ++ ad5823_dev.number_of_steps = value - ad5823_dev.focus; ++ ad5823_dev.focus = value; ++ ktime_get_ts(&ad5823_dev.timestamp_t_focus_abs); ++ } ++ ++ return ret; ++} ++ ++int ad5823_t_focus_rel(struct v4l2_subdev *sd, s32 value) ++{ ++ return ad5823_t_focus_abs(sd, ad5823_dev.focus + value); ++} ++ ++int ad5823_q_focus_status(struct v4l2_subdev *sd, s32 *value) ++{ ++ u32 status = 0; ++ struct timespec temptime; ++ const struct timespec timedelay = { ++ 0, ++ min_t(u32, abs(ad5823_dev.number_of_steps)*DELAY_PER_STEP_NS, ++ DELAY_MAX_PER_STEP_NS), ++ }; ++ ++ ktime_get_ts(&temptime); ++ ++ temptime = timespec_sub(temptime, (ad5823_dev.timestamp_t_focus_abs)); ++ ++ if (timespec_compare(&temptime, &timedelay) <= 0) ++ status = ATOMISP_FOCUS_STATUS_MOVING ++ | ATOMISP_FOCUS_HP_IN_PROGRESS; ++ else ++ status = ATOMISP_FOCUS_STATUS_ACCEPTS_NEW_MOVE ++ | ATOMISP_FOCUS_HP_COMPLETE; ++ ++ *value = status; ++ ++ return 0; ++} ++ ++int ad5823_q_focus_abs(struct v4l2_subdev *sd, s32 *value) ++{ ++ s32 val; ++ ++ ad5823_q_focus_status(sd, &val); ++ ++ if (val & ATOMISP_FOCUS_STATUS_MOVING) ++ *value = ad5823_dev.focus - ad5823_dev.number_of_steps; ++ else ++ *value = ad5823_dev.focus ; ++ ++ return 0; ++} ++ ++int ad5823_t_vcm_slew(struct v4l2_subdev *sd, s32 value) ++{ ++ return 0; ++} ++ ++int ad5823_t_vcm_timing(struct v4l2_subdev *sd, s32 value) ++{ ++ return 0; ++} ++ ++int ad5823_vcm_init(struct v4l2_subdev *sd) ++{ ++ /* set vcm mode to ARC RES0.5 */ ++ ad5823_dev.vcm_mode = AD5823_ARC_RES1; ++ ad5823_dev.platform_data = camera_get_af_platform_data(); ++ return ad5823_dev.platform_data ? 0 : -ENODEV; ++} +diff --git a/drivers/staging/ov5693/ad5823.h b/drivers/staging/ov5693/ad5823.h +new file mode 100644 +index 000000000000..8b046c31f3af +--- /dev/null ++++ b/drivers/staging/ov5693/ad5823.h +@@ -0,0 +1,90 @@ ++/* ++ * Support for AD5823 VCM. ++ * ++ * Copyright (c) 2013 Intel Corporation. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License version ++ * 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ */ ++ ++#ifndef __AD5823_H__ ++#define __AD5823_H__ ++ ++#include ++#include ++ ++ ++#define AD5823_VCM_ADDR 0x0c ++ ++#define AD5823_REG_RESET 0x01 ++#define AD5823_REG_MODE 0x02 ++#define AD5823_REG_VCM_MOVE_TIME 0x03 ++#define AD5823_REG_VCM_CODE_MSB 0x04 ++#define AD5823_REG_VCM_CODE_LSB 0x05 ++#define AD5823_REG_VCM_THRESHOLD_MSB 0x06 ++#define AD5823_REG_VCM_THRESHOLD_LSB 0x07 ++ ++#define AD5823_RING_CTRL_ENABLE 0x04 ++#define AD5823_RING_CTRL_DISABLE 0x00 ++ ++#define AD5823_RESONANCE_PERIOD 100000 ++#define AD5823_RESONANCE_COEF 512 ++#define AD5823_HIGH_FREQ_RANGE 0x80 ++ ++#define VCM_CODE_MSB_MASK 0xfc ++ ++enum ad5823_tok_type { ++ AD5823_8BIT = 0x0001, ++ AD5823_16BIT = 0x0002, ++}; ++ ++enum ad5823_vcm_mode { ++ AD5823_ARC_RES0 = 0x0, /* Actuator response control RES1 */ ++ AD5823_ARC_RES1 = 0x1, /* Actuator response control RES0.5 */ ++ AD5823_ARC_RES2 = 0x2, /* Actuator response control RES2 */ ++ AD5823_ESRC = 0x3, /* Enhanced slew rate control */ ++ AD5823_DIRECT = 0x4, /* Direct control */ ++}; ++ ++/* ad5823 device structure */ ++struct ad5823_device { ++ struct timespec timestamp_t_focus_abs; ++ enum ad5823_vcm_mode vcm_mode; ++ s16 number_of_steps; ++ bool initialized; /* true if ad5823 is detected */ ++ s32 focus; /* Current focus value */ ++ struct timespec focus_time; /* Time when focus was last time set */ ++ __u8 buffer[4]; /* Used for i2c transactions */ ++ const struct camera_af_platform_data *platform_data; ++}; ++ ++#define AD5823_INVALID_CONFIG 0xffffffff ++#define AD5823_MAX_FOCUS_POS 1023 ++ ++ ++#define DELAY_PER_STEP_NS 1000000 ++#define DELAY_MAX_PER_STEP_NS (1000000 * 1023) ++ ++int ad5823_vcm_power_up(struct v4l2_subdev *sd); ++int ad5823_vcm_power_down(struct v4l2_subdev *sd); ++int ad5823_vcm_init(struct v4l2_subdev *sd); ++ ++int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val); ++int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value); ++int ad5823_t_focus_rel(struct v4l2_subdev *sd, s32 value); ++int ad5823_q_focus_status(struct v4l2_subdev *sd, s32 *value); ++int ad5823_q_focus_abs(struct v4l2_subdev *sd, s32 *value); ++ ++#endif +diff --git a/drivers/staging/ov5693/ov5693.c b/drivers/staging/ov5693/ov5693.c +new file mode 100644 +index 000000000000..51d218da3722 +--- /dev/null ++++ b/drivers/staging/ov5693/ov5693.c +@@ -0,0 +1,1461 @@ ++/* ++ * Support for OmniVision OV5693 5M HD camera sensor. ++ * ++ * Copyright (c) 2013 Intel Corporation. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License version ++ * 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ov5693.h" ++ ++/* i2c read/write stuff */ ++static int ov5693_read_reg(struct i2c_client *client, ++ u16 data_length, u16 reg, u16 *val) ++{ ++ int err; ++ struct i2c_msg msg[2]; ++ unsigned char data[6]; ++ ++ if (!client->adapter) { ++ dev_err(&client->dev, "%s error, no client->adapter\n", ++ __func__); ++ return -ENODEV; ++ } ++ ++ if (data_length != OV5693_8BIT && data_length != OV5693_16BIT ++ && data_length != OV5693_32BIT) { ++ dev_err(&client->dev, "%s error, invalid data length\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ memset(msg, 0 , sizeof(msg)); ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].len = I2C_MSG_LENGTH; ++ msg[0].buf = data; ++ ++ /* high byte goes out first */ ++ data[0] = (u8)(reg >> 8); ++ data[1] = (u8)(reg & 0xff); ++ ++ msg[1].addr = client->addr; ++ msg[1].len = data_length; ++ msg[1].flags = I2C_M_RD; ++ msg[1].buf = data; ++ ++ err = i2c_transfer(client->adapter, msg, 2); ++ if (err != 2) { ++ if (err >= 0) ++ err = -EIO; ++ dev_err(&client->dev, ++ "read from offset 0x%x error %d", reg, err); ++ return err; ++ } ++ ++ *val = 0; ++ /* high byte comes first */ ++ if (data_length == OV5693_8BIT) ++ *val = (u8)data[0]; ++ else if (data_length == OV5693_16BIT) ++ *val = be16_to_cpu(*(u16 *)&data[0]); ++ else ++ *val = be32_to_cpu(*(u32 *)&data[0]); ++ ++ return 0; ++} ++ ++static int ov5693_i2c_write(struct i2c_client *client, u16 len, u8 *data) ++{ ++ struct i2c_msg msg; ++ const int num_msg = 1; ++ int ret; ++ ++ msg.addr = client->addr; ++ msg.flags = 0; ++ msg.len = len; ++ msg.buf = data; ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ ++ return ret == num_msg ? 0 : -EIO; ++} ++ ++static int ov5693_write_reg(struct i2c_client *client, u16 data_length, ++ u16 reg, u16 val) ++{ ++ int ret; ++ unsigned char data[4] = {0}; ++ u16 *wreg = (u16 *)data; ++ const u16 len = data_length + sizeof(u16); /* 16-bit address + data */ ++ ++ if (data_length != OV5693_8BIT && data_length != OV5693_16BIT) { ++ dev_err(&client->dev, ++ "%s error, invalid data_length\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* high byte goes out first */ ++ *wreg = cpu_to_be16(reg); ++ ++ if (data_length == OV5693_8BIT) { ++ data[2] = (u8)(val); ++ } else { ++ /* OV5693_16BIT */ ++ u16 *wdata = (u16 *)&data[2]; ++ *wdata = cpu_to_be16(val); ++ } ++ ++ ret = ov5693_i2c_write(client, len, data); ++ if (ret) ++ dev_err(&client->dev, ++ "write error: wrote 0x%x to offset 0x%x error %d", ++ val, reg, ret); ++ ++ return ret; ++} ++ ++/* ++ * ov5693_write_reg_array - Initializes a list of OV5693 registers ++ * @client: i2c driver client structure ++ * @reglist: list of registers to be written ++ * ++ * This function initializes a list of registers. When consecutive addresses ++ * are found in a row on the list, this function creates a buffer and sends ++ * consecutive data in a single i2c_transfer(). ++ * ++ * __ov5693_flush_reg_array, __ov5693_buf_reg_array() and ++ * __ov5693_write_reg_is_consecutive() are internal functions to ++ * ov5693_write_reg_array_fast() and should be not used anywhere else. ++ * ++ */ ++static int __ov5693_flush_reg_array(struct i2c_client *client, ++ struct ov5693_write_ctrl *ctrl) ++{ ++ u16 size; ++ ++ if (ctrl->index == 0) ++ return 0; ++ ++ size = sizeof(u16) + ctrl->index; /* 16-bit address + data */ ++ ctrl->buffer.addr = cpu_to_be16(ctrl->buffer.addr); ++ ctrl->index = 0; ++ ++ return ov5693_i2c_write(client, size, (u8 *)&ctrl->buffer); ++} ++ ++static int __ov5693_buf_reg_array(struct i2c_client *client, ++ struct ov5693_write_ctrl *ctrl, ++ const struct ov5693_reg *next) ++{ ++ int size; ++ u16 *data16; ++ ++ switch (next->type) { ++ case OV5693_8BIT: ++ size = 1; ++ ctrl->buffer.data[ctrl->index] = (u8)next->val; ++ break; ++ case OV5693_16BIT: ++ size = 2; ++ data16 = (u16 *)&ctrl->buffer.data[ctrl->index]; ++ *data16 = cpu_to_be16((u16)next->val); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* When first item is added, we need to store its starting address */ ++ if (ctrl->index == 0) ++ ctrl->buffer.addr = next->reg; ++ ++ ctrl->index += size; ++ ++ /* ++ * Buffer cannot guarantee free space for u32? Better flush it to avoid ++ * possible lack of memory for next item. ++ */ ++ if (ctrl->index + sizeof(u16) >= OV5693_MAX_WRITE_BUF_SIZE) ++ return __ov5693_flush_reg_array(client, ctrl); ++ ++ return 0; ++} ++ ++static int __ov5693_write_reg_is_consecutive(struct i2c_client *client, ++ struct ov5693_write_ctrl *ctrl, ++ const struct ov5693_reg *next) ++{ ++ if (ctrl->index == 0) ++ return 1; ++ ++ return ctrl->buffer.addr + ctrl->index == next->reg; ++} ++ ++static int ov5693_write_reg_array(struct i2c_client *client, ++ const struct ov5693_reg *reglist) ++{ ++ const struct ov5693_reg *next = reglist; ++ struct ov5693_write_ctrl ctrl; ++ int err; ++ ++ ctrl.index = 0; ++ for (; next->type != OV5693_TOK_TERM; next++) { ++ switch (next->type & OV5693_TOK_MASK) { ++ case OV5693_TOK_DELAY: ++ err = __ov5693_flush_reg_array(client, &ctrl); ++ if (err) ++ return err; ++ usleep_range(next->val * 1000, (next->val + 1) * 1000); ++ break; ++ default: ++ /* ++ * If next address is not consecutive, data needs to be ++ * flushed before proceed. ++ */ ++ if (!__ov5693_write_reg_is_consecutive(client, &ctrl, ++ next)) { ++ err = __ov5693_flush_reg_array(client, &ctrl); ++ if (err) ++ return err; ++ } ++ err = __ov5693_buf_reg_array(client, &ctrl, next); ++ if (err) { ++ dev_err(&client->dev, "%s: write error, aborted\n", ++ __func__); ++ return err; ++ } ++ break; ++ } ++ } ++ ++ return __ov5693_flush_reg_array(client, &ctrl); ++} ++static int ov5693_g_focal(struct v4l2_subdev *sd, s32 *val) ++{ ++ *val = (OV5693_FOCAL_LENGTH_NUM << 16) | OV5693_FOCAL_LENGTH_DEM; ++ return 0; ++} ++ ++static int ov5693_g_fnumber(struct v4l2_subdev *sd, s32 *val) ++{ ++ /*const f number for ov5693*/ ++ *val = (OV5693_F_NUMBER_DEFAULT_NUM << 16) | OV5693_F_NUMBER_DEM; ++ return 0; ++} ++ ++static int ov5693_g_fnumber_range(struct v4l2_subdev *sd, s32 *val) ++{ ++ *val = (OV5693_F_NUMBER_DEFAULT_NUM << 24) | ++ (OV5693_F_NUMBER_DEM << 16) | ++ (OV5693_F_NUMBER_DEFAULT_NUM << 8) | OV5693_F_NUMBER_DEM; ++ return 0; ++} ++ ++ ++static int ov5693_get_intg_factor(struct i2c_client *client, ++ struct camera_mipi_info *info, ++ const struct ov5693_resolution *res) ++{ ++ struct atomisp_sensor_mode_data *buf = &info->data; ++ unsigned int pix_clk_freq_hz; ++ u16 reg_val; ++ int ret; ++ ++ if (info == NULL) ++ return -EINVAL; ++ ++ /* pixel clock calculattion */ ++ pix_clk_freq_hz = res->pix_clk_freq * 1000000; ++ ++ buf->vt_pix_clk_freq_mhz = pix_clk_freq_hz; ++ ++ /* get integration time */ ++ buf->coarse_integration_time_min = OV5693_COARSE_INTG_TIME_MIN; ++ buf->coarse_integration_time_max_margin = ++ OV5693_COARSE_INTG_TIME_MAX_MARGIN; ++ ++ buf->fine_integration_time_min = OV5693_FINE_INTG_TIME_MIN; ++ buf->fine_integration_time_max_margin = ++ OV5693_FINE_INTG_TIME_MAX_MARGIN; ++ ++ buf->fine_integration_time_def = OV5693_FINE_INTG_TIME_MIN; ++ buf->frame_length_lines = res->lines_per_frame; ++ buf->line_length_pck = res->pixels_per_line; ++ buf->read_mode = res->bin_mode; ++ ++ /* get the cropping and output resolution to ISP for this mode. */ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_H_CROP_START_H, ®_val); ++ if (ret) ++ return ret; ++ buf->crop_horizontal_start = reg_val; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_V_CROP_START_H, ®_val); ++ if (ret) ++ return ret; ++ buf->crop_vertical_start = reg_val; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_H_CROP_END_H, ®_val); ++ if (ret) ++ return ret; ++ buf->crop_horizontal_end = reg_val; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_V_CROP_END_H, ®_val); ++ if (ret) ++ return ret; ++ buf->crop_vertical_end = reg_val; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_H_OUTSIZE_H, ®_val); ++ if (ret) ++ return ret; ++ buf->output_width = reg_val; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_V_OUTSIZE_H, ®_val); ++ if (ret) ++ return ret; ++ buf->output_height = reg_val; ++ ++ /* ++ * we can't return 0 for bin_factor, this is because camera ++ * HAL will use them as denominator, bin_factor = 0 will ++ * cause camera HAL crash. So we return bin_factor as this ++ * rules: ++ * [1]. res->bin_factor = 0, return 1 for bin_factor. ++ * [2]. res->bin_factor > 0, return res->bin_factor. ++ */ ++ buf->binning_factor_x = res->bin_factor_x ? ++ res->bin_factor_x : 1; ++ buf->binning_factor_y = res->bin_factor_y ? ++ res->bin_factor_y : 1; ++ return 0; ++} ++ ++static long __ov5693_set_exposure(struct v4l2_subdev *sd, int coarse_itg, ++ int gain, int digitgain) ++ ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u16 vts; ++ int ret; ++ ++ /* ++ * According to spec, the low 4 bits of exposure/gain reg are ++ * fraction bits, so need to take 4 bits left shift to align ++ * reg integer bits. ++ */ ++ coarse_itg <<= 4; ++ gain <<= 4; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_VTS_H, &vts); ++ if (ret) ++ return ret; ++ ++ if (coarse_itg + OV5693_INTEGRATION_TIME_MARGIN >= vts) ++ vts = coarse_itg + OV5693_INTEGRATION_TIME_MARGIN; ++ ++ ret = ov5693_write_reg(client, OV5693_16BIT, OV5693_VTS_H, vts); ++ if (ret) ++ return ret; ++ ++ /* group hold start */ ++ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_GROUP_ACCESS, 0); ++ if (ret) ++ return ret; ++ ++ /* set exposure */ ++ ret = ov5693_write_reg(client, OV5693_8BIT, ++ OV5693_AEC_PK_EXPO_L, ++ coarse_itg & 0xff); ++ if (ret) ++ return ret; ++ ++ ret = ov5693_write_reg(client, OV5693_16BIT, ++ OV5693_AEC_PK_EXPO_H, ++ (coarse_itg >> 8) & 0xfff); ++ if (ret) ++ return ret; ++ ++ /* set analog gain */ ++ ret = ov5693_write_reg(client, OV5693_16BIT, ++ OV5693_AGC_ADJ_H, gain); ++ if (ret) ++ return ret; ++ ++ /* set digital gain */ ++ ret = ov5693_write_reg(client, OV5693_16BIT, ++ OV5693_MWB_GAIN_R_H, digitgain); ++ if (ret) ++ return ret; ++ ++ ret = ov5693_write_reg(client, OV5693_16BIT, ++ OV5693_MWB_GAIN_G_H, digitgain); ++ if (ret) ++ return ret; ++ ++ ret = ov5693_write_reg(client, OV5693_16BIT, ++ OV5693_MWB_GAIN_B_H, digitgain); ++ if (ret) ++ return ret; ++ ++ /* group hold end */ ++ ret = ov5693_write_reg(client, OV5693_8BIT, ++ OV5693_GROUP_ACCESS, 0x10); ++ if (ret) ++ return ret; ++ ++ /* group hold launch */ ++ ret = ov5693_write_reg(client, OV5693_8BIT, ++ OV5693_GROUP_ACCESS, 0xa0); ++ ++ return ret; ++} ++ ++static int ov5693_set_exposure(struct v4l2_subdev *sd, int exposure, ++ int gain, int digitgain) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret; ++ ++ mutex_lock(&dev->input_lock); ++ ret = __ov5693_set_exposure(sd, exposure, gain, digitgain); ++ mutex_unlock(&dev->input_lock); ++ ++ return ret; ++} ++ ++static long ov5693_s_exposure(struct v4l2_subdev *sd, ++ struct atomisp_exposure *exposure) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int exp = exposure->integration_time[0]; ++ int gain = exposure->gain[0]; ++ int digitgain = exposure->gain[1]; ++ ++ /* we should not accept the invalid value below. */ ++ if (gain == 0) { ++ dev_err(&client->dev, "%s: invalid value\n", __func__); ++ return -EINVAL; ++ } ++ ++ return ov5693_set_exposure(sd, exp, gain, digitgain); ++} ++ ++static long ov5693_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ ++ switch (cmd) { ++ case ATOMISP_IOC_S_EXPOSURE: ++ return ov5693_s_exposure(sd, arg); ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* This returns the exposure time being used. This should only be used ++ for filling in EXIF data, not for actual image processing. */ ++static int ov5693_q_exposure(struct v4l2_subdev *sd, s32 *value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u16 reg_v, reg_v2; ++ int ret; ++ ++ /* get exposure */ ++ ret = ov5693_read_reg(client, OV5693_8BIT, ++ OV5693_AEC_PK_EXPO_L, ++ ®_v); ++ if (ret) ++ goto err; ++ ++ ret = ov5693_read_reg(client, OV5693_8BIT, ++ OV5693_AEC_PK_EXPO_M, ++ ®_v2); ++ if (ret) ++ goto err; ++ ++ reg_v += reg_v2 << 8; ++ ret = ov5693_read_reg(client, OV5693_8BIT, ++ OV5693_AEC_PK_EXPO_H, ++ ®_v2); ++ if (ret) ++ goto err; ++ ++ *value = (reg_v + (((u32)reg_v2 << 16))) >> 4; ++err: ++ return ret; ++} ++ ++/* ++ * This below focus func don't need input_lock mutex_lock ++ * since they are just called in v4l2 s_ctrl/g_ctrl framework ++ * where mutex input_lock have been done. ++ */ ++int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret = 0; ++ ++ if (dev->vcm_driver && dev->vcm_driver->t_focus_abs) ++ ret = dev->vcm_driver->t_focus_abs(sd, value); ++ ++ return ret; ++} ++ ++int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret = 0; ++ ++ if (dev->vcm_driver && dev->vcm_driver->t_focus_rel) ++ ret = dev->vcm_driver->t_focus_rel(sd, value); ++ ++ return ret; ++} ++ ++int ov5693_q_focus_status(struct v4l2_subdev *sd, s32 *value) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret = 0; ++ ++ if (dev->vcm_driver && dev->vcm_driver->q_focus_status) ++ ret = dev->vcm_driver->q_focus_status(sd, value); ++ ++ return ret; ++} ++ ++int ov5693_q_focus_abs(struct v4l2_subdev *sd, s32 *value) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret = 0; ++ ++ if (dev->vcm_driver && dev->vcm_driver->q_focus_abs) ++ ret = dev->vcm_driver->q_focus_abs(sd, value); ++ ++ return ret; ++} ++ ++/* ov5693 control set/get */ ++static int ov5693_g_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ov5693_device *dev = container_of( ++ ctrl->handler, struct ov5693_device, ctrl_handler); ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE_ABSOLUTE: ++ ret = ov5693_q_exposure(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_FOCUS_ABSOLUTE: ++ ret = ov5693_q_focus_abs(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_FOCUS_STATUS: ++ ret = ov5693_q_focus_status(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_FOCAL_ABSOLUTE: ++ ret = ov5693_g_focal(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_FNUMBER_ABSOLUTE: ++ ret = ov5693_g_fnumber(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_FNUMBER_RANGE: ++ ret = ov5693_g_fnumber_range(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_BIN_FACTOR_HORZ: ++ ctrl->val = dev->ov5693_res[dev->fmt_idx].bin_factor_x; ++ break; ++ case V4L2_CID_BIN_FACTOR_VERT: ++ ctrl->val = dev->ov5693_res[dev->fmt_idx].bin_factor_y; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ov5693_device *dev = container_of( ++ ctrl->handler, struct ov5693_device, ctrl_handler); ++ int ret = 0; ++ ++ if (!ctrl) ++ return -EINVAL; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_RUN_MODE: ++ switch (ctrl->val) { ++ case ATOMISP_RUN_MODE_VIDEO: ++ dev->ov5693_res = ov5693_res_video; ++ dev->curr_res_num = N_RES_VIDEO; ++ break; ++ case ATOMISP_RUN_MODE_STILL_CAPTURE: ++ dev->ov5693_res = ov5693_res_still; ++ dev->curr_res_num = N_RES_STILL; ++ break; ++ default: ++ dev->ov5693_res = ov5693_res_preview; ++ dev->curr_res_num = N_RES_PREVIEW; ++ } ++ break; ++ case V4L2_CID_FOCUS_ABSOLUTE: ++ ret = ov5693_t_focus_abs(&dev->sd, ctrl->val); ++ break; ++ case V4L2_CID_FOCUS_RELATIVE: ++ ret = ov5693_t_focus_rel(&dev->sd, ctrl->val); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int ov5693_init(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret = 0; ++ ++ /* restore settings */ ++ dev->ov5693_res = ov5693_res_preview; ++ dev->curr_res_num = N_RES_PREVIEW; ++ ++ ret = ov5693_write_reg_array(client, ov5693_init_setting); ++ if (ret) ++ dev_err(&client->dev, "ov5693 write init setting reg err.\n"); ++ ++ return ret; ++} ++ ++ ++static int power_up(struct v4l2_subdev *sd) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ if (NULL == dev->platform_data) { ++ dev_err(&client->dev, ++ "no camera_sensor_platform_data"); ++ return -ENODEV; ++ } ++ ++ /* power control */ ++ ret = dev->platform_data->power_ctrl(sd, 1); ++ if (ret) ++ goto fail_power; ++ ++ /* gpio ctrl */ ++ ret = dev->platform_data->gpio_ctrl(sd, 1); ++ if (ret) ++ goto fail_power; ++ ++ /* flis clock control */ ++ ret = dev->platform_data->flisclk_ctrl(sd, 1); ++ if (ret) ++ goto fail_clk; ++ ++ /* according to DS, 20ms is needed between PWDN and i2c access */ ++ msleep(20); ++ ++ return 0; ++ ++fail_clk: ++ dev->platform_data->gpio_ctrl(sd, 0); ++fail_power: ++ dev->platform_data->power_ctrl(sd, 0); ++ dev_err(&client->dev, "sensor power-up failed\n"); ++ ++ return ret; ++} ++ ++static int power_down(struct v4l2_subdev *sd) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = 0; ++ ++ if (NULL == dev->platform_data) { ++ dev_err(&client->dev, ++ "no camera_sensor_platform_data"); ++ return -ENODEV; ++ } ++ ++ ret = dev->platform_data->flisclk_ctrl(sd, 0); ++ if (ret) ++ dev_err(&client->dev, "flisclk failed\n"); ++ ++ /* gpio ctrl */ ++ ret = dev->platform_data->gpio_ctrl(sd, 0); ++ if (ret) ++ dev_err(&client->dev, "gpio failed.\n"); ++ ++ /* power control */ ++ ret = dev->platform_data->power_ctrl(sd, 0); ++ if (ret) ++ dev_err(&client->dev, "vprog failed.\n"); ++ ++ return ret; ++} ++ ++static int ov5693_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = 0; ++ ++ mutex_lock(&dev->input_lock); ++ if (on == 0) { ++ if (dev->vcm_driver && dev->vcm_driver->power_down) ++ ret = dev->vcm_driver->power_down(sd); ++ if (ret) ++ dev_err(&client->dev, "vcm power-down failed.\n"); ++ ++ ret = power_down(sd); ++ } else { ++ if (dev->vcm_driver && dev->vcm_driver->power_up) ++ ret = dev->vcm_driver->power_up(sd); ++ if (ret) ++ dev_err(&client->dev, "vcm power-up failed.\n"); ++ ++ ret = power_up(sd); ++ if (!ret) ++ ret = ov5693_init(sd); ++ } ++ mutex_unlock(&dev->input_lock); ++ return ret; ++} ++ ++/* ++ * distance - calculate the distance ++ * @res: resolution ++ * @w: width ++ * @h: height ++ * ++ * Get the gap between resolution and w/h. ++ * res->width/height smaller than w/h wouldn't be considered. ++ * Returns the value of gap or -1 if fail. ++ */ ++static int distance(struct ov5693_resolution *res, u32 w, u32 h) ++{ ++ unsigned int w_ratio = ((res->width << RATIO_SHIFT_BITS)/w); ++ unsigned int h_ratio; ++ int match; ++ ++ if (h == 0) ++ return -1; ++ h_ratio = ((res->height << RATIO_SHIFT_BITS) / h); ++ if (h_ratio == 0) ++ return -1; ++ match = abs(((w_ratio << RATIO_SHIFT_BITS) / h_ratio) ++ - ((int)(1 << RATIO_SHIFT_BITS))); ++ ++ if ((w_ratio < (int)(1 << RATIO_SHIFT_BITS)) ++ || (h_ratio < (int)(1 << RATIO_SHIFT_BITS)) || ++ (match > LARGEST_ALLOWED_RATIO_MISMATCH)) ++ return -1; ++ ++ return w_ratio + h_ratio; ++} ++ ++/* Return the nearest higher resolution index */ ++static int nearest_resolution_index(struct v4l2_subdev *sd, ++ int w, int h) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int i; ++ int idx = dev->curr_res_num-1; ++ int dist; ++ int min_dist = INT_MAX; ++ struct ov5693_resolution *tmp_res = NULL; ++ ++ for (i = 0; i < dev->curr_res_num; i++) { ++ tmp_res = &dev->ov5693_res[i]; ++ dist = distance(tmp_res, w, h); ++ if (dist == -1) ++ continue; ++ if (dist < min_dist) { ++ min_dist = dist; ++ idx = i; ++ } ++ } ++ ++ return idx; ++} ++ ++static int get_resolution_index(struct v4l2_subdev *sd, ++ int w, int h) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int i; ++ ++ for (i = 0; i < dev->curr_res_num; i++) { ++ if (w != dev->ov5693_res[i].width) ++ continue; ++ if (h != dev->ov5693_res[i].height) ++ continue; ++ ++ return i; ++ } ++ ++ return -1; ++} ++ ++static int __ov5693_try_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int idx; ++ ++ if (!fmt) ++ return -EINVAL; ++ ++ idx = nearest_resolution_index(sd, fmt->width, fmt->height); ++ fmt->width = dev->ov5693_res[idx].width; ++ fmt->height = dev->ov5693_res[idx].height; ++ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ ++ return 0; ++} ++ ++static int ov5693_try_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret; ++ ++ mutex_lock(&dev->input_lock); ++ ret = __ov5693_try_mbus_fmt(sd, fmt); ++ mutex_unlock(&dev->input_lock); ++ ++ return ret; ++} ++ ++static int ov5693_s_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_mipi_info *ov5693_info = NULL; ++ int ret = 0; ++ ++ ov5693_info = v4l2_get_subdev_hostdata(sd); ++ if (ov5693_info == NULL) ++ return -EINVAL; ++ ++ mutex_lock(&dev->input_lock); ++ ret = __ov5693_try_mbus_fmt(sd, fmt); ++ if (ret == -1) { ++ dev_err(&client->dev, "try fmt fail\n"); ++ goto done; ++ } ++ ++ dev->fmt_idx = get_resolution_index(sd, fmt->width, fmt->height); ++ if (dev->fmt_idx == -1) { ++ dev_err(&client->dev, "get resolution fail\n"); ++ goto done; ++ } ++ ++ ret = ov5693_write_reg_array(client, dev->ov5693_res[dev->fmt_idx].regs); ++ if (ret) { ++ dev_err(&client->dev, "ov5693 write fmt register err.\n"); ++ goto done; ++ } ++ ++ ret = ov5693_get_intg_factor(client, ov5693_info, ++ &dev->ov5693_res[dev->fmt_idx]); ++ if (ret) ++ dev_err(&client->dev, "failed to get integration_factor\n"); ++ ++done: ++ mutex_unlock(&dev->input_lock); ++ return ret; ++} ++static int ov5693_g_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ ++ mutex_lock(&dev->input_lock); ++ fmt->width = dev->ov5693_res[dev->fmt_idx].width; ++ fmt->height = dev->ov5693_res[dev->fmt_idx].height; ++ fmt->code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ mutex_unlock(&dev->input_lock); ++ ++ return 0; ++} ++ ++static int ov5693_detect(struct i2c_client *client) ++{ ++ struct i2c_adapter *adapter = client->adapter; ++ int ret = 0; ++ u16 id; ++ u8 revision; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) ++ return -ENODEV; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_SC_CMMN_CHIP_ID, &id); ++ if (ret) { ++ dev_err(&client->dev, "read sensor_id err.\n"); ++ return -ENODEV; ++ } ++ ++ if (id != OV5693_ID) { ++ dev_err(&client->dev, "sensor ID error\n"); ++ return -ENODEV; ++ } ++ ++ ret = ov5693_read_reg(client, OV5693_8BIT, ++ OV5693_SC_CMMN_SUB_ID, &id); ++ revision = (u8)id & 0x0f; ++ ++ dev_dbg(&client->dev, "sensor_revision = 0x%x\n", revision); ++ dev_dbg(&client->dev, "detect ov5693 success\n"); ++ return ret; ++} ++ ++static int ov5693_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ mutex_lock(&dev->input_lock); ++ ++ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_SW_STREAM, ++ enable ? OV5693_START_STREAMING : ++ OV5693_STOP_STREAMING); ++ ++ mutex_unlock(&dev->input_lock); ++ return ret; ++} ++ ++/* ov5693 enum frame size, frame intervals */ ++static int ov5693_enum_framesizes(struct v4l2_subdev *sd, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ unsigned int index = fsize->index; ++ ++ mutex_lock(&dev->input_lock); ++ ++ if (index >= dev->curr_res_num) { ++ mutex_unlock(&dev->input_lock); ++ return -EINVAL; ++ } ++ ++ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; ++ fsize->discrete.width = dev->ov5693_res[index].width; ++ fsize->discrete.height = dev->ov5693_res[index].height; ++ ++ mutex_unlock(&dev->input_lock); ++ return 0; ++} ++ ++static int ov5693_enum_frameintervals(struct v4l2_subdev *sd, ++ struct v4l2_frmivalenum *fival) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ unsigned int index = fival->index; ++ ++ mutex_lock(&dev->input_lock); ++ ++ if (index >= dev->curr_res_num) { ++ mutex_unlock(&dev->input_lock); ++ return -EINVAL; ++ } ++ ++ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ fival->width = dev->ov5693_res[index].width; ++ fival->height = dev->ov5693_res[index].height; ++ fival->discrete.numerator = 1; ++ fival->discrete.denominator = dev->ov5693_res[index].fps; ++ ++ mutex_unlock(&dev->input_lock); ++ ++ return 0; ++} ++ ++static int ov5693_enum_mbus_fmt(struct v4l2_subdev *sd, ++ unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ *code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ ++ return 0; ++} ++ ++static int ov5693_s_config(struct v4l2_subdev *sd, ++ int irq, void *platform_data) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = 0; ++ ++ if (platform_data == NULL) ++ return -ENODEV; ++ ++ mutex_lock(&dev->input_lock); ++ ++ dev->platform_data = platform_data; ++ ++ ret = power_up(sd); ++ if (ret) { ++ dev_err(&client->dev, "ov5693 power-up err.\n"); ++ goto fail_power_on; ++ } ++ ++ ret = dev->platform_data->csi_cfg(sd, 1); ++ if (ret) ++ goto fail_csi_cfg; ++ ++ /* config & detect sensor */ ++ ret = ov5693_detect(client); ++ if (ret) { ++ dev_err(&client->dev, "ov5693_detect err s_config.\n"); ++ goto fail_csi_cfg; ++ } ++ ++ /* turn off sensor, after probed */ ++ ret = power_down(sd); ++ if (ret) { ++ dev_err(&client->dev, "ov5693 power-off err.\n"); ++ goto fail_csi_cfg; ++ } ++ mutex_unlock(&dev->input_lock); ++ ++ return 0; ++ ++fail_csi_cfg: ++ dev->platform_data->csi_cfg(sd, 0); ++fail_power_on: ++ power_down(sd); ++ dev_err(&client->dev, "sensor power-gating failed\n"); ++ mutex_unlock(&dev->input_lock); ++ return ret; ++} ++ ++static int ov5693_g_parm(struct v4l2_subdev *sd, ++ struct v4l2_streamparm *param) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ dev_err(&client->dev, "unsupported buffer type.\n"); ++ return -EINVAL; ++ } ++ ++ memset(param, 0, sizeof(*param)); ++ param->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ mutex_lock(&dev->input_lock); ++ if (dev->fmt_idx >= 0 && dev->fmt_idx < dev->curr_res_num) { ++ param->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ param->parm.capture.timeperframe.numerator = 1; ++ param->parm.capture.capturemode = dev->run_mode->val; ++ param->parm.capture.timeperframe.denominator = ++ dev->ov5693_res[dev->fmt_idx].fps; ++ } ++ mutex_unlock(&dev->input_lock); ++ return 0; ++} ++ ++static int ov5693_g_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_frame_interval *interval) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ ++ mutex_lock(&dev->input_lock); ++ interval->interval.numerator = 1; ++ interval->interval.denominator = dev->ov5693_res[dev->fmt_idx].fps; ++ mutex_unlock(&dev->input_lock); ++ ++ return 0; ++} ++ ++static int ov5693_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ code->code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ return 0; ++} ++ ++static int ov5693_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int index = fse->index; ++ ++ mutex_lock(&dev->input_lock); ++ ++ if (index >= dev->curr_res_num) { ++ mutex_unlock(&dev->input_lock); ++ return -EINVAL; ++ } ++ ++ fse->min_width = dev->ov5693_res[index].width; ++ fse->min_height = dev->ov5693_res[index].height; ++ fse->max_width = dev->ov5693_res[index].width; ++ fse->max_height = dev->ov5693_res[index].height; ++ ++ mutex_unlock(&dev->input_lock); ++ return 0; ++} ++ ++static int ov5693_get_pad_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ mutex_lock(&dev->input_lock); ++ ++ switch (fmt->which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ format = v4l2_subdev_get_try_format(fh, fmt->pad); ++ break; ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ format = &dev->format; ++ break; ++ default: ++ format = NULL; ++ } ++ ++ mutex_unlock(&dev->input_lock); ++ ++ if (!format) ++ return -EINVAL; ++ ++ fmt->format = *format; ++ return 0; ++} ++ ++static int ov5693_set_pad_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ ++ mutex_lock(&dev->input_lock); ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) ++ dev->format = fmt->format; ++ ++ mutex_unlock(&dev->input_lock); ++ return 0; ++} ++ ++static int ov5693_g_skip_frames(struct v4l2_subdev *sd, u32 *frames) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ ++ mutex_lock(&dev->input_lock); ++ *frames = dev->ov5693_res[dev->fmt_idx].skip_frames; ++ mutex_unlock(&dev->input_lock); ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops ctrl_ops = { ++ .s_ctrl = ov5693_s_ctrl, ++ .g_volatile_ctrl = ov5693_g_ctrl, ++}; ++ ++static const char * const ctrl_run_mode_menu[] = { ++ NULL, ++ "Video", ++ "Still capture", ++ "Continuous capture", ++ "Preview", ++}; ++ ++static const struct v4l2_ctrl_config ctrl_run_mode = { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_RUN_MODE, ++ .name = "run mode", ++ .type = V4L2_CTRL_TYPE_MENU, ++ .min = 1, ++ .def = 4, ++ .max = 4, ++ .qmenu = ctrl_run_mode_menu, ++}; ++ ++static const struct v4l2_ctrl_config ctrls[] = { ++ { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_EXPOSURE_ABSOLUTE, ++ .name = "absolute exposure", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .min = 0x0, ++ .max = 0xffff, ++ .step = 0x01, ++ .def = 0x00, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FOCUS_ABSOLUTE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "focus move absolute", ++ .min = 0, ++ .max = OV5693_MAX_FOCUS_POS, ++ .step = 1, ++ .def = 0, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FOCUS_RELATIVE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "focus move relative", ++ .min = OV5693_MAX_FOCUS_NEG, ++ .max = OV5693_MAX_FOCUS_POS, ++ .step = 1, ++ .def = 0, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FOCUS_STATUS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "focus status", ++ .min = 0, ++ .max = 100, ++ .step = 1, ++ .def = 0, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FOCAL_ABSOLUTE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "focal length", ++ .min = OV5693_FOCAL_LENGTH_DEFAULT, ++ .max = OV5693_FOCAL_LENGTH_DEFAULT, ++ .step = 0x01, ++ .def = OV5693_FOCAL_LENGTH_DEFAULT, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FNUMBER_ABSOLUTE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "f-number", ++ .min = OV5693_F_NUMBER_DEFAULT, ++ .max = OV5693_F_NUMBER_DEFAULT, ++ .step = 0x01, ++ .def = OV5693_F_NUMBER_DEFAULT, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FNUMBER_RANGE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "f-number range", ++ .min = OV5693_F_NUMBER_RANGE, ++ .max = OV5693_F_NUMBER_RANGE, ++ .step = 0x01, ++ .def = OV5693_F_NUMBER_RANGE, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_BIN_FACTOR_HORZ, ++ .name = "horizontal binning factor", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .max = OV5693_BIN_FACTOR_MAX, ++ .step = 2, ++ .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_BIN_FACTOR_VERT, ++ .name = "vertical binning factor", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .max = OV5693_BIN_FACTOR_MAX, ++ .step = 2, ++ .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, ++ } ++}; ++ ++static const struct v4l2_subdev_sensor_ops ov5693_sensor_ops = { ++ .g_skip_frames = ov5693_g_skip_frames, ++}; ++ ++static const struct v4l2_subdev_video_ops ov5693_video_ops = { ++ .s_stream = ov5693_s_stream, ++ .g_parm = ov5693_g_parm, ++ .enum_framesizes = ov5693_enum_framesizes, ++ .enum_frameintervals = ov5693_enum_frameintervals, ++ .enum_mbus_fmt = ov5693_enum_mbus_fmt, ++ .try_mbus_fmt = ov5693_try_mbus_fmt, ++ .g_mbus_fmt = ov5693_g_mbus_fmt, ++ .s_mbus_fmt = ov5693_s_mbus_fmt, ++ .g_frame_interval = ov5693_g_frame_interval, ++}; ++ ++static const struct v4l2_subdev_core_ops ov5693_core_ops = { ++ .s_power = ov5693_s_power, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .ioctl = ov5693_ioctl, ++}; ++ ++static const struct v4l2_subdev_pad_ops ov5693_pad_ops = { ++ .enum_mbus_code = ov5693_enum_mbus_code, ++ .enum_frame_size = ov5693_enum_frame_size, ++ .get_fmt = ov5693_get_pad_format, ++ .set_fmt = ov5693_set_pad_format, ++}; ++ ++static const struct v4l2_subdev_ops ov5693_ops = { ++ .core = &ov5693_core_ops, ++ .video = &ov5693_video_ops, ++ .pad = &ov5693_pad_ops, ++ .sensor = &ov5693_sensor_ops, ++}; ++ ++static int ov5693_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ dev_dbg(&client->dev, "ov5693_remove...\n"); ++ ++ dev->platform_data->csi_cfg(sd, 0); ++ ++ v4l2_device_unregister_subdev(sd); ++ media_entity_cleanup(&dev->sd.entity); ++ devm_kfree(&client->dev, dev); ++ ++ return 0; ++} ++ ++static int ov5693_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct ov5693_device *dev; ++ int i; ++ int ret; ++ ++ dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); ++ if (!dev) { ++ dev_err(&client->dev, "out of memory\n"); ++ return -ENOMEM; ++ } ++ ++ mutex_init(&dev->input_lock); ++ ++ /* ++ * Initialize related res members of dev. ++ */ ++ dev->fmt_idx = 0; ++ dev->ov5693_res = ov5693_res_preview; ++ dev->curr_res_num = N_RES_PREVIEW; ++ ++ v4l2_i2c_subdev_init(&(dev->sd), client, &ov5693_ops); ++ ++ if (client->dev.platform_data) { ++ ret = ov5693_s_config(&dev->sd, client->irq, ++ client->dev.platform_data); ++ if (ret) ++ goto out_free; ++ } ++ ++ dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ dev->pad.flags = MEDIA_PAD_FL_SOURCE; ++ dev->format.code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ dev->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ++ dev->vcm_driver = &ov5693_vcm_ops; ++ ++ ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(ctrls) + 1); ++ if (ret) { ++ ov5693_remove(client); ++ return ret; ++ } ++ ++ dev->run_mode = v4l2_ctrl_new_custom(&dev->ctrl_handler, ++ &ctrl_run_mode, NULL); ++ ++ for (i = 0; i < ARRAY_SIZE(ctrls); i++) ++ v4l2_ctrl_new_custom(&dev->ctrl_handler, &ctrls[i], NULL); ++ ++ if (dev->ctrl_handler.error) { ++ ov5693_remove(client); ++ return dev->ctrl_handler.error; ++ } ++ ++ dev->ctrl_handler.lock = &dev->input_lock; ++ dev->sd.ctrl_handler = &dev->ctrl_handler; ++ v4l2_ctrl_handler_setup(&dev->ctrl_handler); ++ ++ ret = media_entity_init(&dev->sd.entity, 1, &dev->pad, 0); ++ if (ret) ++ ov5693_remove(client); ++ ++ /* vcm initialization */ ++ if (dev->vcm_driver && dev->vcm_driver->init) ++ ret = dev->vcm_driver->init(&dev->sd); ++ if (ret) { ++ dev_err(&client->dev, "vcm init failed.\n"); ++ ov5693_remove(client); ++ } ++ ++ return ret; ++out_free: ++ v4l2_device_unregister_subdev(&dev->sd); ++ devm_kfree(&client->dev, dev); ++ return ret; ++} ++ ++MODULE_DEVICE_TABLE(i2c, ov5693_id); ++static struct i2c_driver ov5693_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = OV5693_NAME, ++ }, ++ .probe = ov5693_probe, ++ .remove = ov5693_remove, ++ .id_table = ov5693_id, ++}; ++ ++module_i2c_driver(ov5693_driver); ++ ++MODULE_DESCRIPTION("A low-level driver for OmniVision 5693 sensors"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/staging/ov5693/ov5693.h b/drivers/staging/ov5693/ov5693.h +new file mode 100644 +index 000000000000..79aef69666e8 +--- /dev/null ++++ b/drivers/staging/ov5693/ov5693.h +@@ -0,0 +1,848 @@ ++/* ++ * Support for OmniVision OV5693 5M HD camera sensor. ++ * ++ * Copyright (c) 2013 Intel Corporation. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License version ++ * 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ */ ++ ++#ifndef __OV5693_H__ ++#define __OV5693_H__ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "ad5823.h" ++ ++#define OV5693_NAME "ov5693" ++ ++/* Defines for register writes and register array processing */ ++#define I2C_MSG_LENGTH 0x2 ++ ++#define OV5693_FOCAL_LENGTH_NUM 278 /*2.78mm*/ ++#define OV5693_FOCAL_LENGTH_DEM 100 ++#define OV5693_F_NUMBER_DEFAULT_NUM 26 ++#define OV5693_F_NUMBER_DEM 10 ++ ++#define OV5693_MAX_FOCUS_POS 1023 ++#define OV5693_MAX_FOCUS_POS 1023 ++#define OV5693_MAX_FOCUS_NEG (-1023) ++ ++#define LARGEST_ALLOWED_RATIO_MISMATCH 800 ++#define RATIO_SHIFT_BITS 13 ++ ++/* ++ * focal length bits definition: ++ * bits 31-16: numerator, bits 15-0: denominator ++ */ ++#define OV5693_FOCAL_LENGTH_DEFAULT 0x1160064 ++ ++/* ++ * current f-number bits definition: ++ * bits 31-16: numerator, bits 15-0: denominator ++ */ ++#define OV5693_F_NUMBER_DEFAULT 0x1a000a ++ ++/* ++ * f-number range bits definition: ++ * bits 31-24: max f-number numerator ++ * bits 23-16: max f-number denominator ++ * bits 15-8: min f-number numerator ++ * bits 7-0: min f-number denominator ++ */ ++#define OV5693_F_NUMBER_RANGE 0x1a0a1a0a ++#define OV5693_ID 0x5690 ++ ++#define OV5693_FINE_INTG_TIME_MIN 0 ++#define OV5693_FINE_INTG_TIME_MAX_MARGIN 0 ++#define OV5693_COARSE_INTG_TIME_MIN 1 ++#define OV5693_COARSE_INTG_TIME_MAX_MARGIN (0xffff - 6) ++#define OV5693_INTEGRATION_TIME_MARGIN 8 ++ ++#define OV5693_BIN_FACTOR_MAX 2 ++ ++/* ++ * OV5693 System control registers ++ */ ++#define OV5693_SW_RESET 0x0103 ++#define OV5693_SW_STREAM 0x0100 ++ ++#define OV5693_SC_CMMN_CHIP_ID 0x300a ++#define OV5693_SC_CMMN_SUB_ID 0x302a /* process, version*/ ++ ++#define OV5693_AEC_PK_EXPO_H 0x3500 ++#define OV5693_AEC_PK_EXPO_M 0x3501 ++#define OV5693_AEC_PK_EXPO_L 0x3502 ++#define OV5693_AGC_ADJ_H 0x350a ++#define OV5693_VTS_H 0x380e ++#define OV5693_GROUP_ACCESS 0x3208 ++ ++#define OV5693_MWB_GAIN_R_H 0x3400 ++#define OV5693_MWB_GAIN_G_H 0x3402 ++#define OV5693_MWB_GAIN_B_H 0x3404 ++ ++#define OV5693_H_CROP_START_H 0x3800 ++#define OV5693_V_CROP_START_H 0x3802 ++#define OV5693_H_CROP_END_H 0x3804 ++#define OV5693_V_CROP_END_H 0x3806 ++#define OV5693_H_OUTSIZE_H 0x3808 ++#define OV5693_V_OUTSIZE_H 0x380a ++ ++#define OV5693_START_STREAMING 0x01 ++#define OV5693_STOP_STREAMING 0x00 ++ ++struct ov5693_vcm { ++ int (*power_up)(struct v4l2_subdev *sd); ++ int (*power_down)(struct v4l2_subdev *sd); ++ int (*init)(struct v4l2_subdev *sd); ++ int (*t_focus_vcm)(struct v4l2_subdev *sd, u16 val); ++ int (*t_focus_abs)(struct v4l2_subdev *sd, s32 value); ++ int (*t_focus_rel)(struct v4l2_subdev *sd, s32 value); ++ int (*q_focus_status)(struct v4l2_subdev *sd, s32 *value); ++ int (*q_focus_abs)(struct v4l2_subdev *sd, s32 *value); ++}; ++ ++struct ov5693_resolution { ++ u8 *desc; ++ const struct ov5693_reg *regs; ++ int res; ++ int width; ++ int height; ++ int fps; ++ int pix_clk_freq; ++ u16 skip_frames; ++ u16 pixels_per_line; ++ u16 lines_per_frame; ++ u8 bin_factor_x; ++ u8 bin_factor_y; ++ u8 bin_mode; ++ bool used; ++}; ++ ++struct ov5693_control { ++ struct v4l2_queryctrl qc; ++ int (*query)(struct v4l2_subdev *sd, s32 *value); ++ int (*tweak)(struct v4l2_subdev *sd, s32 value); ++}; ++ ++/* ++ * ov5693 device structure. ++ */ ++struct ov5693_device { ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ struct v4l2_mbus_framefmt format; ++ struct mutex input_lock; ++ ++ struct camera_sensor_platform_data *platform_data; ++ struct ov5693_vcm *vcm_driver; ++ int fmt_idx; ++ u8 res; ++ u8 type; ++ ++ struct ov5693_resolution *ov5693_res; ++ int curr_res_num; ++ ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_ctrl *run_mode; ++}; ++ ++enum ov5693_tok_type { ++ OV5693_8BIT = 0x0001, ++ OV5693_16BIT = 0x0002, ++ OV5693_32BIT = 0x0004, ++ OV5693_TOK_TERM = 0xf000, /* terminating token for reg list */ ++ OV5693_TOK_DELAY = 0xfe00, /* delay token for reg list */ ++ OV5693_TOK_MASK = 0xfff0 ++}; ++ ++/** ++ * struct ov5693_reg - MI sensor register format ++ * @type: type of the register ++ * @reg: 16-bit offset to register ++ * @val: 8/16/32-bit register value ++ * ++ * Define a structure for sensor register initialization values ++ */ ++struct ov5693_reg { ++ enum ov5693_tok_type type; ++ u16 reg; ++ u32 val; /* @set value for read/mod/write, @mask */ ++}; ++ ++#define to_ov5693_sensor(x) container_of(x, struct ov5693_device, sd) ++ ++#define OV5693_MAX_WRITE_BUF_SIZE 30 ++ ++struct ov5693_write_buffer { ++ u16 addr; ++ u8 data[OV5693_MAX_WRITE_BUF_SIZE]; ++}; ++ ++struct ov5693_write_ctrl { ++ int index; ++ struct ov5693_write_buffer buffer; ++}; ++ ++static const struct i2c_device_id ov5693_id[] = { ++ {OV5693_NAME, 0}, ++ {} ++}; ++ ++/* ov5693 sensor initialization setting */ ++static struct ov5693_reg const ov5693_init_setting[] = { ++ {OV5693_8BIT, 0x0103, 0x01}, ++ {OV5693_8BIT, 0x3001, 0x0a}, ++ {OV5693_8BIT, 0x3002, 0x80}, ++ {OV5693_8BIT, 0x3006, 0x00}, ++ {OV5693_8BIT, 0x3011, 0x21}, ++ {OV5693_8BIT, 0x3012, 0x09}, ++ {OV5693_8BIT, 0x3013, 0x10}, ++ {OV5693_8BIT, 0x3014, 0x00}, ++ {OV5693_8BIT, 0x3015, 0x08}, ++ {OV5693_8BIT, 0x3016, 0xf0}, ++ {OV5693_8BIT, 0x3017, 0xf0}, ++ {OV5693_8BIT, 0x3018, 0xf0}, ++ {OV5693_8BIT, 0x301b, 0xb4}, ++ {OV5693_8BIT, 0x301d, 0x02}, ++ {OV5693_8BIT, 0x3021, 0x00}, ++ {OV5693_8BIT, 0x3022, 0x01}, ++ {OV5693_8BIT, 0x3028, 0x44}, ++ {OV5693_8BIT, 0x3098, 0x02}, ++ {OV5693_8BIT, 0x3099, 0x19}, ++ {OV5693_8BIT, 0x309a, 0x02}, ++ {OV5693_8BIT, 0x309b, 0x01}, ++ {OV5693_8BIT, 0x309c, 0x00}, ++ {OV5693_8BIT, 0x30a0, 0xd2}, ++ {OV5693_8BIT, 0x30a2, 0x01}, ++ {OV5693_8BIT, 0x30b2, 0x00}, ++ {OV5693_8BIT, 0x30b3, 0x7d}, ++ {OV5693_8BIT, 0x30b4, 0x03}, ++ {OV5693_8BIT, 0x30b5, 0x04}, ++ {OV5693_8BIT, 0x30b6, 0x01}, ++ {OV5693_8BIT, 0x3104, 0x21}, ++ {OV5693_8BIT, 0x3106, 0x00}, ++ ++ /* Manual white balance */ ++ {OV5693_8BIT, 0x3400, 0x04}, ++ {OV5693_8BIT, 0x3401, 0x00}, ++ {OV5693_8BIT, 0x3402, 0x04}, ++ {OV5693_8BIT, 0x3403, 0x00}, ++ {OV5693_8BIT, 0x3404, 0x04}, ++ {OV5693_8BIT, 0x3405, 0x00}, ++ {OV5693_8BIT, 0x3406, 0x01}, ++ ++ /* Manual exposure control */ ++ {OV5693_8BIT, 0x3500, 0x00}, ++ {OV5693_8BIT, 0x3503, 0x07}, ++ {OV5693_8BIT, 0x3504, 0x00}, ++ {OV5693_8BIT, 0x3505, 0x00}, ++ {OV5693_8BIT, 0x3506, 0x00}, ++ {OV5693_8BIT, 0x3507, 0x02}, ++ {OV5693_8BIT, 0x3508, 0x00}, ++ ++ /* Manual gain control */ ++ {OV5693_8BIT, 0x3509, 0x10}, ++ {OV5693_8BIT, 0x350a, 0x00}, ++ {OV5693_8BIT, 0x350b, 0x40}, ++ ++ {OV5693_8BIT, 0x3601, 0x0a}, ++ {OV5693_8BIT, 0x3602, 0x38}, ++ {OV5693_8BIT, 0x3612, 0x80}, ++ {OV5693_8BIT, 0x3620, 0x54}, ++ {OV5693_8BIT, 0x3621, 0xc7}, ++ {OV5693_8BIT, 0x3622, 0x0f}, ++ {OV5693_8BIT, 0x3625, 0x10}, ++ {OV5693_8BIT, 0x3630, 0x55}, ++ {OV5693_8BIT, 0x3631, 0xf4}, ++ {OV5693_8BIT, 0x3632, 0x00}, ++ {OV5693_8BIT, 0x3633, 0x34}, ++ {OV5693_8BIT, 0x3634, 0x02}, ++ {OV5693_8BIT, 0x364d, 0x0d}, ++ {OV5693_8BIT, 0x364f, 0xdd}, ++ {OV5693_8BIT, 0x3660, 0x04}, ++ {OV5693_8BIT, 0x3662, 0x10}, ++ {OV5693_8BIT, 0x3663, 0xf1}, ++ {OV5693_8BIT, 0x3665, 0x00}, ++ {OV5693_8BIT, 0x3666, 0x20}, ++ {OV5693_8BIT, 0x3667, 0x00}, ++ {OV5693_8BIT, 0x366a, 0x80}, ++ {OV5693_8BIT, 0x3680, 0xe0}, ++ {OV5693_8BIT, 0x3681, 0x00}, ++ {OV5693_8BIT, 0x3700, 0x42}, ++ {OV5693_8BIT, 0x3701, 0x14}, ++ {OV5693_8BIT, 0x3702, 0xa0}, ++ {OV5693_8BIT, 0x3703, 0xd8}, ++ {OV5693_8BIT, 0x3704, 0x78}, ++ {OV5693_8BIT, 0x3705, 0x02}, ++ {OV5693_8BIT, 0x370a, 0x00}, ++ {OV5693_8BIT, 0x370b, 0x20}, ++ {OV5693_8BIT, 0x370c, 0x0c}, ++ {OV5693_8BIT, 0x370d, 0x11}, ++ {OV5693_8BIT, 0x370e, 0x00}, ++ {OV5693_8BIT, 0x370f, 0x40}, ++ {OV5693_8BIT, 0x3710, 0x00}, ++ {OV5693_8BIT, 0x371a, 0x1c}, ++ {OV5693_8BIT, 0x371b, 0x05}, ++ {OV5693_8BIT, 0x371c, 0x01}, ++ {OV5693_8BIT, 0x371e, 0xa1}, ++ {OV5693_8BIT, 0x371f, 0x0c}, ++ {OV5693_8BIT, 0x3721, 0x00}, ++ {OV5693_8BIT, 0x3724, 0x10}, ++ {OV5693_8BIT, 0x3726, 0x00}, ++ {OV5693_8BIT, 0x372a, 0x01}, ++ {OV5693_8BIT, 0x3730, 0x10}, ++ {OV5693_8BIT, 0x3738, 0x22}, ++ {OV5693_8BIT, 0x3739, 0xe5}, ++ {OV5693_8BIT, 0x373a, 0x50}, ++ {OV5693_8BIT, 0x373b, 0x02}, ++ {OV5693_8BIT, 0x373c, 0x41}, ++ {OV5693_8BIT, 0x373f, 0x02}, ++ {OV5693_8BIT, 0x3740, 0x42}, ++ {OV5693_8BIT, 0x3741, 0x02}, ++ {OV5693_8BIT, 0x3742, 0x18}, ++ {OV5693_8BIT, 0x3743, 0x01}, ++ {OV5693_8BIT, 0x3744, 0x02}, ++ {OV5693_8BIT, 0x3747, 0x10}, ++ {OV5693_8BIT, 0x374c, 0x04}, ++ {OV5693_8BIT, 0x3751, 0xf0}, ++ {OV5693_8BIT, 0x3752, 0x00}, ++ {OV5693_8BIT, 0x3753, 0x00}, ++ {OV5693_8BIT, 0x3754, 0xc0}, ++ {OV5693_8BIT, 0x3755, 0x00}, ++ {OV5693_8BIT, 0x3756, 0x1a}, ++ {OV5693_8BIT, 0x3758, 0x00}, ++ {OV5693_8BIT, 0x3759, 0x0f}, ++ {OV5693_8BIT, 0x376b, 0x44}, ++ {OV5693_8BIT, 0x375c, 0x04}, ++ {OV5693_8BIT, 0x3774, 0x10}, ++ {OV5693_8BIT, 0x3776, 0x00}, ++ {OV5693_8BIT, 0x377f, 0x08}, ++ {OV5693_8BIT, 0x3780, 0x22}, ++ {OV5693_8BIT, 0x3781, 0x0c}, ++ {OV5693_8BIT, 0x3784, 0x2c}, ++ {OV5693_8BIT, 0x3785, 0x1e}, ++ {OV5693_8BIT, 0x378f, 0xf5}, ++ {OV5693_8BIT, 0x3791, 0xb0}, ++ {OV5693_8BIT, 0x3795, 0x00}, ++ {OV5693_8BIT, 0x3796, 0x64}, ++ {OV5693_8BIT, 0x3797, 0x11}, ++ {OV5693_8BIT, 0x3798, 0x30}, ++ {OV5693_8BIT, 0x3799, 0x41}, ++ {OV5693_8BIT, 0x379a, 0x07}, ++ {OV5693_8BIT, 0x379b, 0xb0}, ++ {OV5693_8BIT, 0x379c, 0x0c}, ++ {OV5693_8BIT, 0x37c5, 0x00}, ++ {OV5693_8BIT, 0x37c6, 0x00}, ++ {OV5693_8BIT, 0x37c7, 0x00}, ++ {OV5693_8BIT, 0x37c9, 0x00}, ++ {OV5693_8BIT, 0x37ca, 0x00}, ++ {OV5693_8BIT, 0x37cb, 0x00}, ++ {OV5693_8BIT, 0x37de, 0x00}, ++ {OV5693_8BIT, 0x37df, 0x00}, ++ {OV5693_8BIT, 0x3800, 0x00}, ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, ++ {OV5693_8BIT, 0x3804, 0x0a}, ++ {OV5693_8BIT, 0x3805, 0x3f}, ++ {OV5693_8BIT, 0x3810, 0x00}, ++ {OV5693_8BIT, 0x3812, 0x00}, ++ {OV5693_8BIT, 0x3823, 0x00}, ++ {OV5693_8BIT, 0x3824, 0x00}, ++ {OV5693_8BIT, 0x3825, 0x00}, ++ {OV5693_8BIT, 0x3826, 0x00}, ++ {OV5693_8BIT, 0x3827, 0x00}, ++ {OV5693_8BIT, 0x382a, 0x04}, ++ {OV5693_8BIT, 0x3a04, 0x06}, ++ {OV5693_8BIT, 0x3a05, 0x14}, ++ {OV5693_8BIT, 0x3a06, 0x00}, ++ {OV5693_8BIT, 0x3a07, 0xfe}, ++ {OV5693_8BIT, 0x3b00, 0x00}, ++ {OV5693_8BIT, 0x3b02, 0x00}, ++ {OV5693_8BIT, 0x3b03, 0x00}, ++ {OV5693_8BIT, 0x3b04, 0x00}, ++ {OV5693_8BIT, 0x3b05, 0x00}, ++ {OV5693_8BIT, 0x3e07, 0x20}, ++ {OV5693_8BIT, 0x4000, 0x08}, ++ {OV5693_8BIT, 0x4001, 0x04}, ++ {OV5693_8BIT, 0x4002, 0x45}, ++ {OV5693_8BIT, 0x4004, 0x08}, ++ {OV5693_8BIT, 0x4005, 0x18}, ++ {OV5693_8BIT, 0x4006, 0x20}, ++ {OV5693_8BIT, 0x4008, 0x24}, ++ {OV5693_8BIT, 0x4009, 0x10}, ++ {OV5693_8BIT, 0x400c, 0x00}, ++ {OV5693_8BIT, 0x400d, 0x00}, ++ {OV5693_8BIT, 0x4058, 0x00}, ++ {OV5693_8BIT, 0x404e, 0x37}, ++ {OV5693_8BIT, 0x404f, 0x8f}, ++ {OV5693_8BIT, 0x4058, 0x00}, ++ {OV5693_8BIT, 0x4101, 0xb2}, ++ {OV5693_8BIT, 0x4303, 0x00}, ++ {OV5693_8BIT, 0x4304, 0x08}, ++ {OV5693_8BIT, 0x4307, 0x30}, ++ {OV5693_8BIT, 0x4311, 0x04}, ++ {OV5693_8BIT, 0x4315, 0x01}, ++ {OV5693_8BIT, 0x4511, 0x05}, ++ {OV5693_8BIT, 0x4512, 0x01}, ++ {OV5693_8BIT, 0x4806, 0x00}, ++ {OV5693_8BIT, 0x4816, 0x52}, ++ {OV5693_8BIT, 0x481f, 0x30}, ++ {OV5693_8BIT, 0x4826, 0x2c}, ++ {OV5693_8BIT, 0x4831, 0x64}, ++ {OV5693_8BIT, 0x4d00, 0x04}, ++ {OV5693_8BIT, 0x4d01, 0x71}, ++ {OV5693_8BIT, 0x4d02, 0xfd}, ++ {OV5693_8BIT, 0x4d03, 0xf5}, ++ {OV5693_8BIT, 0x4d04, 0x0c}, ++ {OV5693_8BIT, 0x4d05, 0xcc}, ++ {OV5693_8BIT, 0x4837, 0x0a}, ++ {OV5693_8BIT, 0x5000, 0x06}, ++ {OV5693_8BIT, 0x5001, 0x01}, ++ {OV5693_8BIT, 0x5003, 0x20}, ++ {OV5693_8BIT, 0x5046, 0x0a}, ++ {OV5693_8BIT, 0x5013, 0x00}, ++ {OV5693_8BIT, 0x5046, 0x0a}, ++ {OV5693_8BIT, 0x5780, 0x1c}, ++ {OV5693_8BIT, 0x5786, 0x20}, ++ {OV5693_8BIT, 0x5787, 0x10}, ++ {OV5693_8BIT, 0x5788, 0x18}, ++ {OV5693_8BIT, 0x578a, 0x04}, ++ {OV5693_8BIT, 0x578b, 0x02}, ++ {OV5693_8BIT, 0x578c, 0x02}, ++ {OV5693_8BIT, 0x578e, 0x06}, ++ {OV5693_8BIT, 0x578f, 0x02}, ++ {OV5693_8BIT, 0x5790, 0x02}, ++ {OV5693_8BIT, 0x5791, 0xff}, ++ {OV5693_8BIT, 0x5842, 0x01}, ++ {OV5693_8BIT, 0x5843, 0x2b}, ++ {OV5693_8BIT, 0x5844, 0x01}, ++ {OV5693_8BIT, 0x5845, 0x92}, ++ {OV5693_8BIT, 0x5846, 0x01}, ++ {OV5693_8BIT, 0x5847, 0x8f}, ++ {OV5693_8BIT, 0x5848, 0x01}, ++ {OV5693_8BIT, 0x5849, 0x0c}, ++ {OV5693_8BIT, 0x5e00, 0x00}, ++ {OV5693_8BIT, 0x5e10, 0x0c}, ++ {OV5693_8BIT, 0x0100, 0x00}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++/* ++ * Register settings for various resolution ++ */ ++ ++/* ++------------------------------------ ++@@ FULL QSXGA (2592x1944) 15fps 33.33ms VBlank 2lane 10Bit ++100 99 2592 1944 ++100 98 1 0 ++102 3601 bb8 ;Pather tool use only ++c8 1 f2 ; New FPGA Board ++c8 20 22 ; New FPGA Board ++c8 10 42 ; MIPI DFGA CYCY3 Board use only ++; ++c8 f 32 ; input clock to 19.2MHz ++; ++; OV5690 setting version History ++; ++; ++; V18b ++*/ ++static struct ov5693_reg const ov5693_5M_15fps[] = { ++ {OV5693_8BIT, 0x3501, 0x7b}, ++ {OV5693_8BIT, 0x3502, 0x00}, ++ {OV5693_8BIT, 0x3708, 0xe2}, ++ {OV5693_8BIT, 0x3709, 0xc3}, ++ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ ++ {OV5693_8BIT, 0x3803, 0x00}, ++ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ ++ {OV5693_8BIT, 0x3805, 0x3f}, ++ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ ++ {OV5693_8BIT, 0x3807, 0xa3}, ++ {OV5693_8BIT, 0x3808, 0x0a}, /* x output size: 2592 */ ++ {OV5693_8BIT, 0x3809, 0x20}, ++ {OV5693_8BIT, 0x380a, 0x07}, /* y output size: 1944 */ ++ {OV5693_8BIT, 0x380b, 0x98}, ++ {OV5693_8BIT, 0x380c, 0x0e}, /* total x output size: 3688 */ ++ {OV5693_8BIT, 0x380d, 0x68}, ++ {OV5693_8BIT, 0x380e, 0x0f}, /* total y output size: 3968 */ ++ {OV5693_8BIT, 0x380f, 0x80}, ++ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 16 */ ++ {OV5693_8BIT, 0x3811, 0x10}, ++ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 6 */ ++ {OV5693_8BIT, 0x3813, 0x06}, ++ {OV5693_8BIT, 0x3814, 0x11}, ++ {OV5693_8BIT, 0x3815, 0x11}, ++ {OV5693_8BIT, 0x3820, 0x00}, ++ {OV5693_8BIT, 0x3821, 0x1e}, ++ {OV5693_8BIT, 0x5002, 0x00}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++/* ++@@ OV5693 1940x1096 30fps 8.8ms VBlanking 2lane 10Bit(Scaling) ++100 99 1940 1096 ++100 98 1 0 ++102 3601 bb8 ;Pather tool use only ++102 40 0 ; HDR Mode off ++c8 1 f2 ; New FPGA Board ++c8 20 22 ; New FPGA Board ++c8 10 42 ; MIPI DFGA CYCY3 Board use only ++; ++c8 f 32 ; input clock to 19.2MHz ++; ++; OV5690 setting version History ++; ++; ++; V18b ++*/ ++static struct ov5693_reg const ov5693_1080p_30fps[] = { ++ {OV5693_8BIT, 0x3501, 0x7b}, ++ {OV5693_8BIT, 0x3502, 0x00}, ++ {OV5693_8BIT, 0x3708, 0xe2}, ++ {OV5693_8BIT, 0x3709, 0xc3}, ++ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 240 */ ++ {OV5693_8BIT, 0x3803, 0xf0}, ++ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2591 */ ++ {OV5693_8BIT, 0x3805, 0x1f}, ++ {OV5693_8BIT, 0x3806, 0x06}, /* y_addr_end: 1703 */ ++ {OV5693_8BIT, 0x3807, 0xa7}, ++ {OV5693_8BIT, 0x3808, 0x07}, /* x output size: 1940 */ ++ {OV5693_8BIT, 0x3809, 0x94}, ++ {OV5693_8BIT, 0x380a, 0x04}, /* y output size: 1096 */ ++ {OV5693_8BIT, 0x380b, 0x48}, ++ {OV5693_8BIT, 0x380c, 0x0e}, /* total x output size: 3688 */ ++ {OV5693_8BIT, 0x380d, 0x68}, ++ {OV5693_8BIT, 0x380e, 0x0b}, /* total y output size: 2984 */ ++ {OV5693_8BIT, 0x380f, 0xa8}, ++ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 2 */ ++ {OV5693_8BIT, 0x3811, 0x02}, ++ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ ++ {OV5693_8BIT, 0x3813, 0x02}, ++ {OV5693_8BIT, 0x3814, 0x11}, ++ {OV5693_8BIT, 0x3815, 0x11}, ++ {OV5693_8BIT, 0x3820, 0x00}, ++ {OV5693_8BIT, 0x3821, 0x1e}, ++ {OV5693_8BIT, 0x5002, 0x80}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++/* ++ * 1296x736 30fps 8.8ms VBlanking 2lane 10Bit (Scaling) ++ */ ++static struct ov5693_reg const ov5693_720p_30fps[] = { ++ {OV5693_8BIT, 0x3501, 0x3d}, ++ {OV5693_8BIT, 0x3502, 0x00}, ++ {OV5693_8BIT, 0x3708, 0xe6}, ++ {OV5693_8BIT, 0x3709, 0xc7}, ++ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ ++ {OV5693_8BIT, 0x3803, 0x00}, ++ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ ++ {OV5693_8BIT, 0x3805, 0x3f}, ++ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ ++ {OV5693_8BIT, 0x3807, 0xa3}, ++ {OV5693_8BIT, 0x3808, 0x05}, /* x output size: 1296 */ ++ {OV5693_8BIT, 0x3809, 0x10}, ++ {OV5693_8BIT, 0x380a, 0x02}, /* y output size: 736 */ ++ {OV5693_8BIT, 0x380b, 0xe0}, ++ {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ ++ {OV5693_8BIT, 0x380d, 0x80}, ++ {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ ++ {OV5693_8BIT, 0x380f, 0xc0}, ++ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 8 */ ++ {OV5693_8BIT, 0x3811, 0x08}, ++ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ ++ {OV5693_8BIT, 0x3813, 0x02}, ++ {OV5693_8BIT, 0x3814, 0x31}, ++ {OV5693_8BIT, 0x3815, 0x31}, ++ {OV5693_8BIT, 0x3820, 0x04}, ++ {OV5693_8BIT, 0x3821, 0x1f}, ++ {OV5693_8BIT, 0x5002, 0x80}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++/* ++ * 736x496 30fps 8.8ms VBlanking 2lane 10Bit (Scaling) ++ */ ++static struct ov5693_reg const ov5693_480p_30fps[] = { ++ {OV5693_8BIT, 0x3501, 0x3d}, ++ {OV5693_8BIT, 0x3502, 0x00}, ++ {OV5693_8BIT, 0x3708, 0xe6}, ++ {OV5693_8BIT, 0x3709, 0xc7}, ++ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 7 */ ++ {OV5693_8BIT, 0x3803, 0x07}, ++ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ ++ {OV5693_8BIT, 0x3805, 0x3f}, ++ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ ++ {OV5693_8BIT, 0x3807, 0xa3}, ++ {OV5693_8BIT, 0x3808, 0x02}, /* x output size: 736 */ ++ {OV5693_8BIT, 0x3809, 0xe0}, ++ {OV5693_8BIT, 0x380a, 0x01}, /* y output size: 496 */ ++ {OV5693_8BIT, 0x380b, 0xf0}, ++ {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ ++ {OV5693_8BIT, 0x380d, 0x80}, ++ {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ ++ {OV5693_8BIT, 0x380f, 0xc0}, ++ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 8 */ ++ {OV5693_8BIT, 0x3811, 0x08}, ++ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ ++ {OV5693_8BIT, 0x3813, 0x02}, ++ {OV5693_8BIT, 0x3814, 0x31}, ++ {OV5693_8BIT, 0x3815, 0x31}, ++ {OV5693_8BIT, 0x3820, 0x04}, ++ {OV5693_8BIT, 0x3821, 0x1f}, ++ {OV5693_8BIT, 0x5002, 0x80}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++/* ++@@ OV5693 656x496 30fps 17ms VBlanking 2lane 10Bit(Scaling) ++100 99 656 496 ++100 98 1 0 ++102 3601 BB8 ;Pather tool use only ++c8 1 f2 ; New FPGA Board ++c8 20 22 ; New FPGA Board ++; OV5690 setting version History ++; ++c8 f 32 ; input clock to 19.2MHz ++; ++; V18b ++*/ ++static struct ov5693_reg const ov5693_VGA_30fps[] = { ++ {OV5693_8BIT, 0x3501, 0x3d}, ++ {OV5693_8BIT, 0x3502, 0x00}, ++ {OV5693_8BIT, 0x3708, 0xe6}, ++ {OV5693_8BIT, 0x3709, 0xc7}, ++ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ ++ {OV5693_8BIT, 0x3803, 0x00}, ++ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ ++ {OV5693_8BIT, 0x3805, 0x3f}, ++ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ ++ {OV5693_8BIT, 0x3807, 0xa3}, ++ {OV5693_8BIT, 0x3808, 0x02}, /* x output size: 656 */ ++ {OV5693_8BIT, 0x3809, 0x90}, ++ {OV5693_8BIT, 0x380a, 0x01}, /* y output size: 496 */ ++ {OV5693_8BIT, 0x380b, 0xf0}, ++ {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ ++ {OV5693_8BIT, 0x380d, 0x80}, ++ {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ ++ {OV5693_8BIT, 0x380f, 0xc0}, ++ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 13 */ ++ {OV5693_8BIT, 0x3811, 0x0d}, ++ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 3 */ ++ {OV5693_8BIT, 0x3813, 0x03}, ++ {OV5693_8BIT, 0x3814, 0x31}, ++ {OV5693_8BIT, 0x3815, 0x31}, ++ {OV5693_8BIT, 0x3820, 0x04}, ++ {OV5693_8BIT, 0x3821, 0x1f}, ++ {OV5693_8BIT, 0x5002, 0x80}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++struct ov5693_resolution ov5693_res_preview[] = { ++ { ++ .desc = "ov5693_VGA_30fps", ++ .width = 656, ++ .height = 496, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 2688, ++ .lines_per_frame = 1984, ++ .bin_factor_x = 2, ++ .bin_factor_y = 2, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_VGA_30fps, ++ }, ++ { ++ .desc = "ov5693_1080P_30fps", ++ .width = 1940, ++ .height = 1096, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 3688, ++ .lines_per_frame = 2984, ++ .bin_factor_x = 0, ++ .bin_factor_y = 0, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_1080p_30fps, ++ }, ++ { ++ .desc = "ov5693_5M_15fps", ++ .width = 2592, ++ .height = 1944, ++ .fps = 15, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 3688, ++ .lines_per_frame = 3968, ++ .bin_factor_x = 0, ++ .bin_factor_y = 0, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_5M_15fps, ++ }, ++}; ++#define N_RES_PREVIEW (ARRAY_SIZE(ov5693_res_preview)) ++ ++struct ov5693_resolution ov5693_res_still[] = { ++ { ++ .desc = "ov5693_VGA_30fps", ++ .width = 656, ++ .height = 496, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 2688, ++ .lines_per_frame = 1984, ++ .bin_factor_x = 2, ++ .bin_factor_y = 2, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_VGA_30fps, ++ }, ++ { ++ .desc = "ov5693_1080P_30fps", ++ .width = 1940, ++ .height = 1096, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 3688, ++ .lines_per_frame = 2984, ++ .bin_factor_x = 0, ++ .bin_factor_y = 0, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_1080p_30fps, ++ }, ++ { ++ .desc = "ov5693_5M_15fps", ++ .width = 2592, ++ .height = 1944, ++ .fps = 15, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 3688, ++ .lines_per_frame = 3968, ++ .bin_factor_x = 0, ++ .bin_factor_y = 0, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_5M_15fps, ++ }, ++}; ++#define N_RES_STILL (ARRAY_SIZE(ov5693_res_still)) ++ ++struct ov5693_resolution ov5693_res_video[] = { ++ { ++ .desc = "ov5693_VGA_30fps", ++ .width = 656, ++ .height = 496, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 2688, ++ .lines_per_frame = 1984, ++ .bin_factor_x = 2, ++ .bin_factor_y = 2, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_VGA_30fps, ++ }, ++ { ++ .desc = "ov5693_480P_30fps", ++ .width = 736, ++ .height = 496, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 2688, ++ .lines_per_frame = 1984, ++ .bin_factor_x = 2, ++ .bin_factor_y = 2, ++ .bin_mode = 0, ++ .skip_frames = 1, ++ .regs = ov5693_480p_30fps, ++ }, ++ { ++ .desc = "ov5693_720p_30fps", ++ .width = 1296, ++ .height = 736, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 2688, ++ .lines_per_frame = 1984, ++ .bin_factor_x = 2, ++ .bin_factor_y = 2, ++ .bin_mode = 0, ++ .skip_frames = 1, ++ .regs = ov5693_720p_30fps, ++ }, ++ { ++ .desc = "ov5693_1080P_30fps", ++ .width = 1940, ++ .height = 1096, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 3688, ++ .lines_per_frame = 2984, ++ .bin_factor_x = 0, ++ .bin_factor_y = 0, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_1080p_30fps, ++ }, ++}; ++#define N_RES_VIDEO (ARRAY_SIZE(ov5693_res_video)) ++ ++struct ov5693_vcm ov5693_vcm_ops = { ++ .power_up = ad5823_vcm_power_up, ++ .power_down = ad5823_vcm_power_down, ++ .init = ad5823_vcm_init, ++ .t_focus_vcm = ad5823_t_focus_vcm, ++ .t_focus_abs = ad5823_t_focus_abs, ++ .t_focus_rel = ad5823_t_focus_rel, ++ .q_focus_status = ad5823_q_focus_status, ++ .q_focus_abs = ad5823_q_focus_abs, ++}; ++#endif +-- +2.17.1 + diff --git a/linux_surface/patches/0005-ipts.patch b/linux_surface/patches/0005-ipts.patch new file mode 100644 index 0000000..0a03b54 --- /dev/null +++ b/linux_surface/patches/0005-ipts.patch @@ -0,0 +1,6123 @@ +From bcd4a6318d1e33330eade2b1716ac8d1010c2d25 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Sun, 27 Jan 2019 10:52:28 -0500 +Subject: [PATCH 05/11] ipts + +--- + drivers/gpu/drm/i915/Makefile | 3 + + drivers/gpu/drm/i915/i915_drv.c | 7 + + drivers/gpu/drm/i915/i915_drv.h | 3 + + drivers/gpu/drm/i915/i915_gem_context.c | 12 + + drivers/gpu/drm/i915/i915_irq.c | 7 +- + drivers/gpu/drm/i915/i915_params.c | 5 +- + drivers/gpu/drm/i915/i915_params.h | 5 +- + drivers/gpu/drm/i915/intel_dp.c | 4 +- + drivers/gpu/drm/i915/intel_guc.h | 1 + + drivers/gpu/drm/i915/intel_guc_submission.c | 79 +- + drivers/gpu/drm/i915/intel_guc_submission.h | 4 + + drivers/gpu/drm/i915/intel_ipts.c | 642 ++++++++++++ + drivers/gpu/drm/i915/intel_ipts.h | 34 + + drivers/gpu/drm/i915/intel_lrc.c | 12 +- + drivers/gpu/drm/i915/intel_lrc.h | 8 + + drivers/gpu/drm/i915/intel_panel.c | 7 + + drivers/hid/hid-multitouch.c | 22 +- + drivers/misc/Kconfig | 1 + + drivers/misc/Makefile | 1 + + drivers/misc/ipts/Kconfig | 9 + + drivers/misc/ipts/Makefile | 13 + + drivers/misc/ipts/ipts-binary-spec.h | 118 +++ + drivers/misc/ipts/ipts-dbgfs.c | 152 +++ + drivers/misc/ipts/ipts-gfx.c | 184 ++++ + drivers/misc/ipts/ipts-gfx.h | 24 + + drivers/misc/ipts/ipts-hid.c | 456 ++++++++ + drivers/misc/ipts/ipts-hid.h | 34 + + drivers/misc/ipts/ipts-kernel.c | 1050 +++++++++++++++++++ + drivers/misc/ipts/ipts-kernel.h | 23 + + drivers/misc/ipts/ipts-mei-msgs.h | 585 +++++++++++ + drivers/misc/ipts/ipts-mei.c | 282 +++++ + drivers/misc/ipts/ipts-msg-handler.c | 431 ++++++++ + drivers/misc/ipts/ipts-msg-handler.h | 32 + + drivers/misc/ipts/ipts-resource.c | 277 +++++ + drivers/misc/ipts/ipts-resource.h | 30 + + drivers/misc/ipts/ipts-sensor-regs.h | 700 +++++++++++++ + drivers/misc/ipts/ipts-state.h | 29 + + drivers/misc/ipts/ipts.h | 200 ++++ + drivers/misc/mei/hw-me-regs.h | 1 + + drivers/misc/mei/pci-me.c | 1 + + include/linux/intel_ipts_if.h | 75 ++ + 41 files changed, 5541 insertions(+), 22 deletions(-) + create mode 100644 drivers/gpu/drm/i915/intel_ipts.c + create mode 100644 drivers/gpu/drm/i915/intel_ipts.h + create mode 100644 drivers/misc/ipts/Kconfig + create mode 100644 drivers/misc/ipts/Makefile + create mode 100644 drivers/misc/ipts/ipts-binary-spec.h + create mode 100644 drivers/misc/ipts/ipts-dbgfs.c + create mode 100644 drivers/misc/ipts/ipts-gfx.c + create mode 100644 drivers/misc/ipts/ipts-gfx.h + create mode 100644 drivers/misc/ipts/ipts-hid.c + create mode 100644 drivers/misc/ipts/ipts-hid.h + create mode 100644 drivers/misc/ipts/ipts-kernel.c + create mode 100644 drivers/misc/ipts/ipts-kernel.h + create mode 100644 drivers/misc/ipts/ipts-mei-msgs.h + create mode 100644 drivers/misc/ipts/ipts-mei.c + create mode 100644 drivers/misc/ipts/ipts-msg-handler.c + create mode 100644 drivers/misc/ipts/ipts-msg-handler.h + create mode 100644 drivers/misc/ipts/ipts-resource.c + create mode 100644 drivers/misc/ipts/ipts-resource.h + create mode 100644 drivers/misc/ipts/ipts-sensor-regs.h + create mode 100644 drivers/misc/ipts/ipts-state.h + create mode 100644 drivers/misc/ipts/ipts.h + create mode 100644 include/linux/intel_ipts_if.h + +diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile +index 5794f102f9b8..6ae0e91a213a 100644 +--- a/drivers/gpu/drm/i915/Makefile ++++ b/drivers/gpu/drm/i915/Makefile +@@ -155,6 +155,9 @@ i915-y += dvo_ch7017.o \ + vlv_dsi.o \ + vlv_dsi_pll.o + ++# intel precise touch & stylus ++i915-y += intel_ipts.o ++ + # Post-mortem debug and GPU hang state capture + i915-$(CONFIG_DRM_I915_CAPTURE_ERROR) += i915_gpu_error.o + i915-$(CONFIG_DRM_I915_SELFTEST) += \ +diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c +index f8cfd16be534..f48122746b6e 100644 +--- a/drivers/gpu/drm/i915/i915_drv.c ++++ b/drivers/gpu/drm/i915/i915_drv.c +@@ -53,6 +53,7 @@ + #include "i915_vgpu.h" + #include "intel_drv.h" + #include "intel_uc.h" ++#include "intel_ipts.h" + + static struct drm_driver driver; + +@@ -696,6 +697,9 @@ static int i915_load_modeset_init(struct drm_device *dev) + /* Only enable hotplug handling once the fbdev is fully set up. */ + intel_hpd_init(dev_priv); + ++ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) ++ intel_ipts_init(dev); ++ + return 0; + + cleanup_gem: +@@ -1432,6 +1436,9 @@ void i915_driver_unload(struct drm_device *dev) + struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; + ++ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) ++ intel_ipts_cleanup(dev); ++ + i915_driver_unregister(dev_priv); + + if (i915_gem_suspend(dev_priv)) +diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h +index d6c25bea4382..d6b507fb7626 100644 +--- a/drivers/gpu/drm/i915/i915_drv.h ++++ b/drivers/gpu/drm/i915/i915_drv.h +@@ -3213,6 +3213,9 @@ void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj, + void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj, + struct sg_table *pages); + ++struct i915_gem_context * ++i915_gem_context_create_ipts(struct drm_device *dev); ++ + static inline struct i915_gem_context * + __i915_gem_context_lookup_rcu(struct drm_i915_file_private *file_priv, u32 id) + { +diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c +index b10770cfccd2..0222f0a55262 100644 +--- a/drivers/gpu/drm/i915/i915_gem_context.c ++++ b/drivers/gpu/drm/i915/i915_gem_context.c +@@ -467,6 +467,18 @@ static bool needs_preempt_context(struct drm_i915_private *i915) + return HAS_LOGICAL_RING_PREEMPTION(i915); + } + ++struct i915_gem_context *i915_gem_context_create_ipts(struct drm_device *dev) ++{ ++ struct drm_i915_private *dev_priv = to_i915(dev); ++ struct i915_gem_context *ctx; ++ ++ BUG_ON(!mutex_is_locked(&dev->struct_mutex)); ++ ++ ctx = i915_gem_create_context(dev_priv, NULL); ++ ++ return ctx; ++} ++ + int i915_gem_contexts_init(struct drm_i915_private *dev_priv) + { + struct i915_gem_context *ctx; +diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c +index 29877969310d..e199a6d687b2 100644 +--- a/drivers/gpu/drm/i915/i915_irq.c ++++ b/drivers/gpu/drm/i915/i915_irq.c +@@ -36,6 +36,7 @@ + #include "i915_drv.h" + #include "i915_trace.h" + #include "intel_drv.h" ++#include "intel_ipts.h" + + /** + * DOC: interrupt handling +@@ -1503,6 +1504,9 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir) + tasklet |= USES_GUC_SUBMISSION(engine->i915); + } + ++ if (iir & GT_RENDER_PIPECTL_NOTIFY_INTERRUPT) ++ intel_ipts_notify_complete(); ++ + if (tasklet) + tasklet_hi_schedule(&engine->execlists.tasklet); + } +@@ -4122,7 +4126,8 @@ static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv) + { + /* These are interrupts we'll toggle with the ring mask register */ + uint32_t gt_interrupts[] = { +- GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | ++ GT_RENDER_PIPECTL_NOTIFY_INTERRUPT << GEN8_RCS_IRQ_SHIFT | ++ GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_RCS_IRQ_SHIFT | + GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT | + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT, +diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c +index 295e981e4a39..80cd47fd8c09 100644 +--- a/drivers/gpu/drm/i915/i915_params.c ++++ b/drivers/gpu/drm/i915/i915_params.c +@@ -145,7 +145,10 @@ i915_param_named_unsafe(edp_vswing, int, 0400, + i915_param_named_unsafe(enable_guc, int, 0400, + "Enable GuC load for GuC submission and/or HuC load. " + "Required functionality can be selected using bitmask values. " +- "(-1=auto, 0=disable [default], 1=GuC submission, 2=HuC load)"); ++ "(-1=auto, 0=disable, 1=GuC submission [default], 2=HuC load)"); ++ ++i915_param_named_unsafe(enable_ipts, int, 0400, ++ "Enable IPTS Touchscreen and Pen support (default: 1)"); + + i915_param_named(guc_log_level, int, 0400, + "GuC firmware logging level. Requires GuC to be loaded. " +diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h +index 6c4d4a21474b..b15f81d55d70 100644 +--- a/drivers/gpu/drm/i915/i915_params.h ++++ b/drivers/gpu/drm/i915/i915_params.h +@@ -46,7 +46,7 @@ struct drm_printer; + param(int, disable_power_well, -1) \ + param(int, enable_ips, 1) \ + param(int, invert_brightness, 0) \ +- param(int, enable_guc, 0) \ ++ param(int, enable_guc, 1) \ + param(int, guc_log_level, -1) \ + param(char *, guc_firmware_path, NULL) \ + param(char *, huc_firmware_path, NULL) \ +@@ -68,7 +68,8 @@ struct drm_printer; + param(bool, nuclear_pageflip, false) \ + param(bool, enable_dp_mst, true) \ + param(bool, enable_dpcd_backlight, false) \ +- param(bool, enable_gvt, false) ++ param(bool, enable_gvt, false) \ ++ param(int, enable_ipts, 1) + + #define MEMBER(T, member, ...) T member; + struct i915_params { +diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c +index f92079e19de8..7c2d78044035 100644 +--- a/drivers/gpu/drm/i915/intel_dp.c ++++ b/drivers/gpu/drm/i915/intel_dp.c +@@ -2634,8 +2634,8 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) + return; + + if (mode != DRM_MODE_DPMS_ON) { +- if (downstream_hpd_needs_d0(intel_dp)) +- return; ++ //if (downstream_hpd_needs_d0(intel_dp)) ++ // return; + + ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, + DP_SET_POWER_D3); +diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h +index 4121928a495e..8967376accf3 100644 +--- a/drivers/gpu/drm/i915/intel_guc.h ++++ b/drivers/gpu/drm/i915/intel_guc.h +@@ -69,6 +69,7 @@ struct intel_guc { + + struct intel_guc_client *execbuf_client; + struct intel_guc_client *preempt_client; ++ struct intel_guc_client *ipts_client; + + struct guc_preempt_work preempt_work[I915_NUM_ENGINES]; + struct workqueue_struct *preempt_wq; +diff --git a/drivers/gpu/drm/i915/intel_guc_submission.c b/drivers/gpu/drm/i915/intel_guc_submission.c +index 4aa5e6463e7b..c153ee6af69a 100644 +--- a/drivers/gpu/drm/i915/intel_guc_submission.c ++++ b/drivers/gpu/drm/i915/intel_guc_submission.c +@@ -94,6 +94,7 @@ static inline bool is_high_priority(struct intel_guc_client *client) + + static int reserve_doorbell(struct intel_guc_client *client) + { ++ struct drm_i915_private *dev_priv = guc_to_i915(client->guc); + unsigned long offset; + unsigned long end; + u16 id; +@@ -106,10 +107,15 @@ static int reserve_doorbell(struct intel_guc_client *client) + * priority contexts, the second half for high-priority ones. + */ + offset = 0; +- end = GUC_NUM_DOORBELLS / 2; +- if (is_high_priority(client)) { +- offset = end; +- end += offset; ++ if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { ++ end = GUC_NUM_DOORBELLS; ++ } ++ else { ++ end = GUC_NUM_DOORBELLS/2; ++ if (is_high_priority(client)) { ++ offset = end; ++ end += offset; ++ } + } + + id = find_next_zero_bit(client->guc->doorbell_bitmap, end, offset); +@@ -355,8 +361,14 @@ static void guc_stage_desc_init(struct intel_guc *guc, + desc = __get_stage_desc(client); + memset(desc, 0, sizeof(*desc)); + +- desc->attribute = GUC_STAGE_DESC_ATTR_ACTIVE | +- GUC_STAGE_DESC_ATTR_KERNEL; ++ desc->attribute = GUC_STAGE_DESC_ATTR_ACTIVE; ++ if ((client->priority == GUC_CLIENT_PRIORITY_KMD_NORMAL) || ++ (client->priority == GUC_CLIENT_PRIORITY_KMD_HIGH)) { ++ desc->attribute |= GUC_STAGE_DESC_ATTR_KERNEL; ++ } else { ++ desc->attribute |= GUC_STAGE_DESC_ATTR_PCH; ++ } ++ + if (is_high_priority(client)) + desc->attribute |= GUC_STAGE_DESC_ATTR_PREEMPT; + desc->stage_id = client->stage_id; +@@ -1204,7 +1216,8 @@ static void guc_interrupts_capture(struct drm_i915_private *dev_priv) + I915_WRITE(RING_MODE_GEN7(engine), irqs); + + /* route USER_INTERRUPT to Host, all others are sent to GuC. */ +- irqs = GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | ++ irqs = (GT_RENDER_USER_INTERRUPT | GT_RENDER_PIPECTL_NOTIFY_INTERRUPT) ++ << GEN8_RCS_IRQ_SHIFT | + GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT; + /* These three registers have the same bit definitions */ + I915_WRITE(GUC_BCS_RCS_IER, ~irqs); +@@ -1349,6 +1362,58 @@ void intel_guc_submission_disable(struct intel_guc *guc) + guc_clients_doorbell_fini(guc); + } + ++int i915_guc_ipts_submission_enable(struct drm_i915_private *dev_priv, ++ struct i915_gem_context *ctx) ++{ ++ struct intel_guc *guc = &dev_priv->guc; ++ struct intel_guc_client *client; ++ int err; ++ int ret; ++ ++ /* client for execbuf submission */ ++ client = guc_client_alloc(dev_priv, ++ INTEL_INFO(dev_priv)->ring_mask, ++ GUC_CLIENT_PRIORITY_NORMAL, ++ ctx); ++ if (IS_ERR(client)) { ++ DRM_ERROR("Failed to create normal GuC client!\n"); ++ return -ENOMEM; ++ } ++ ++ guc->ipts_client = client; ++ ++ err = intel_guc_sample_forcewake(guc); ++ if (err) ++ return err; ++ ++ ret = create_doorbell(guc->ipts_client); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++void i915_guc_ipts_submission_disable(struct drm_i915_private *dev_priv) ++{ ++ struct intel_guc *guc = &dev_priv->guc; ++ ++ if (!guc->ipts_client) ++ return; ++ ++ guc_client_free(guc->ipts_client); ++ guc->ipts_client = NULL; ++} ++ ++void i915_guc_ipts_reacquire_doorbell(struct drm_i915_private *dev_priv) ++{ ++ struct intel_guc *guc = &dev_priv->guc; ++ ++ int err = __guc_allocate_doorbell(guc, guc->ipts_client->stage_id); ++ ++ if (err) ++ DRM_ERROR("Not able to reacquire IPTS doorbell\n"); ++} ++ + #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) + #include "selftests/intel_guc.c" + #endif +diff --git a/drivers/gpu/drm/i915/intel_guc_submission.h b/drivers/gpu/drm/i915/intel_guc_submission.h +index fb081cefef93..71fc7986585a 100644 +--- a/drivers/gpu/drm/i915/intel_guc_submission.h ++++ b/drivers/gpu/drm/i915/intel_guc_submission.h +@@ -79,5 +79,9 @@ void intel_guc_submission_disable(struct intel_guc *guc); + void intel_guc_submission_fini(struct intel_guc *guc); + int intel_guc_preempt_work_create(struct intel_guc *guc); + void intel_guc_preempt_work_destroy(struct intel_guc *guc); ++int i915_guc_ipts_submission_enable(struct drm_i915_private *dev_priv, ++ struct i915_gem_context *ctx); ++void i915_guc_ipts_submission_disable(struct drm_i915_private *dev_priv); ++void i915_guc_ipts_reacquire_doorbell(struct drm_i915_private *dev_priv); + + #endif +diff --git a/drivers/gpu/drm/i915/intel_ipts.c b/drivers/gpu/drm/i915/intel_ipts.c +new file mode 100644 +index 000000000000..1888e450b425 +--- /dev/null ++++ b/drivers/gpu/drm/i915/intel_ipts.c +@@ -0,0 +1,642 @@ ++/* ++ * Copyright 2016 Intel Corporation ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "intel_guc_submission.h" ++#include "i915_drv.h" ++ ++#define SUPPORTED_IPTS_INTERFACE_VERSION 1 ++ ++#define REACQUIRE_DB_THRESHOLD 8 ++#define DB_LOST_CHECK_STEP1_INTERVAL 2000 /* ms */ ++#define DB_LOST_CHECK_STEP2_INTERVAL 500 /* ms */ ++ ++/* intel IPTS ctx for ipts support */ ++typedef struct intel_ipts { ++ struct drm_device *dev; ++ struct i915_gem_context *ipts_context; ++ intel_ipts_callback_t ipts_clbks; ++ ++ /* buffers' list */ ++ struct { ++ spinlock_t lock; ++ struct list_head list; ++ } buffers; ++ ++ void *data; ++ ++ struct delayed_work reacquire_db_work; ++ intel_ipts_wq_info_t wq_info; ++ u32 old_tail; ++ u32 old_head; ++ bool need_reacquire_db; ++ ++ bool connected; ++ bool initialized; ++} intel_ipts_t; ++ ++intel_ipts_t intel_ipts; ++ ++typedef struct intel_ipts_object { ++ struct list_head list; ++ struct drm_i915_gem_object *gem_obj; ++ void *cpu_addr; ++} intel_ipts_object_t; ++ ++static intel_ipts_object_t *ipts_object_create(size_t size, u32 flags) ++{ ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ intel_ipts_object_t *obj = NULL; ++ struct drm_i915_gem_object *gem_obj = NULL; ++ int ret = 0; ++ ++ obj = kzalloc(sizeof(*obj), GFP_KERNEL); ++ if (!obj) ++ return NULL; ++ ++ size = roundup(size, PAGE_SIZE); ++ if (size == 0) { ++ ret = -EINVAL; ++ goto err_out; ++ } ++ ++ /* Allocate the new object */ ++ gem_obj = i915_gem_object_create(dev_priv, size); ++ if (gem_obj == NULL) { ++ ret = -ENOMEM; ++ goto err_out; ++ } ++ ++ if (flags & IPTS_BUF_FLAG_CONTIGUOUS) { ++ ret = i915_gem_object_attach_phys(gem_obj, PAGE_SIZE); ++ if (ret) { ++ pr_info(">> ipts no contiguous : %d\n", ret); ++ goto err_out; ++ } ++ } ++ ++ obj->gem_obj = gem_obj; ++ ++ spin_lock(&intel_ipts.buffers.lock); ++ list_add_tail(&obj->list, &intel_ipts.buffers.list); ++ spin_unlock(&intel_ipts.buffers.lock); ++ ++ return obj; ++ ++err_out: ++ if (gem_obj) ++ i915_gem_free_object(&gem_obj->base); ++ ++ if (obj) ++ kfree(obj); ++ ++ return NULL; ++} ++ ++static void ipts_object_free(intel_ipts_object_t* obj) ++{ ++ spin_lock(&intel_ipts.buffers.lock); ++ list_del(&obj->list); ++ spin_unlock(&intel_ipts.buffers.lock); ++ ++ i915_gem_free_object(&obj->gem_obj->base); ++ kfree(obj); ++} ++ ++static int ipts_object_pin(intel_ipts_object_t* obj, ++ struct i915_gem_context *ipts_ctx) ++{ ++ struct i915_address_space *vm = NULL; ++ struct i915_vma *vma = NULL; ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ int ret = 0; ++ ++ if (ipts_ctx->ppgtt) { ++ vm = &ipts_ctx->ppgtt->vm; ++ } else { ++ vm = &dev_priv->ggtt.vm; ++ } ++ ++ vma = i915_vma_instance(obj->gem_obj, vm, NULL); ++ if (IS_ERR(vma)) { ++ DRM_ERROR("cannot find or create vma\n"); ++ return -1; ++ } ++ ++ ret = i915_vma_pin(vma, 0, PAGE_SIZE, PIN_USER); ++ ++ return ret; ++} ++ ++static void ipts_object_unpin(intel_ipts_object_t *obj) ++{ ++ /* TBD: Add support */ ++} ++ ++static void* ipts_object_map(intel_ipts_object_t *obj) ++{ ++ ++ return i915_gem_object_pin_map(obj->gem_obj, I915_MAP_WB); ++} ++ ++static void ipts_object_unmap(intel_ipts_object_t* obj) ++{ ++ i915_gem_object_unpin_map(obj->gem_obj); ++ obj->cpu_addr = NULL; ++} ++ ++static int create_ipts_context(void) ++{ ++ struct i915_gem_context *ipts_ctx = NULL; ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ struct intel_context *ce = NULL; ++ struct intel_context *pin_ret; ++ int ret = 0; ++ ++ /* Initialize the context right away.*/ ++ ret = i915_mutex_lock_interruptible(intel_ipts.dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); ++ return ret; ++ } ++ ++ ipts_ctx = i915_gem_context_create_ipts(intel_ipts.dev); ++ if (IS_ERR(ipts_ctx)) { ++ DRM_ERROR("Failed to create IPTS context (error %ld)\n", ++ PTR_ERR(ipts_ctx)); ++ ret = PTR_ERR(ipts_ctx); ++ goto err_unlock; ++ } ++ ++ ce = to_intel_context(ipts_ctx, dev_priv->engine[RCS]); ++ if (IS_ERR(ce)) { ++ DRM_ERROR("Failed to create intel context (error %ld)\n", ++ PTR_ERR(ce)); ++ ret = PTR_ERR(ce); ++ goto err_unlock; ++ } ++ ++ ret = execlists_context_deferred_alloc(ipts_ctx, dev_priv->engine[RCS], ce); ++ if (ret) { ++ DRM_DEBUG("lr context allocation failed : %d\n", ret); ++ goto err_ctx; ++ } ++ ++ pin_ret = execlists_context_pin(dev_priv->engine[RCS], ipts_ctx); ++ if (IS_ERR(pin_ret)) { ++ DRM_DEBUG("lr context pinning failed : %ld\n", PTR_ERR(pin_ret)); ++ goto err_ctx; ++ } ++ ++ /* Release the mutex */ ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++ ++ spin_lock_init(&intel_ipts.buffers.lock); ++ INIT_LIST_HEAD(&intel_ipts.buffers.list); ++ ++ intel_ipts.ipts_context = ipts_ctx; ++ ++ return 0; ++ ++err_ctx: ++ if (ipts_ctx) ++ i915_gem_context_put(ipts_ctx); ++ ++err_unlock: ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++ ++ return ret; ++} ++ ++static void destroy_ipts_context(void) ++{ ++ struct i915_gem_context *ipts_ctx = NULL; ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ struct intel_context *ce = NULL; ++ int ret = 0; ++ ++ ipts_ctx = intel_ipts.ipts_context; ++ ++ ce = to_intel_context(ipts_ctx, dev_priv->engine[RCS]); ++ ++ /* Initialize the context right away.*/ ++ ret = i915_mutex_lock_interruptible(intel_ipts.dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); ++ return; ++ } ++ ++ execlists_context_unpin(ce); ++ i915_gem_context_put(ipts_ctx); ++ ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++} ++ ++int intel_ipts_notify_complete(void) ++{ ++ if (intel_ipts.ipts_clbks.workload_complete) ++ intel_ipts.ipts_clbks.workload_complete(intel_ipts.data); ++ ++ return 0; ++} ++ ++int intel_ipts_notify_backlight_status(bool backlight_on) ++{ ++ if (intel_ipts.ipts_clbks.notify_gfx_status) { ++ if (backlight_on) { ++ intel_ipts.ipts_clbks.notify_gfx_status( ++ IPTS_NOTIFY_STA_BACKLIGHT_ON, ++ intel_ipts.data); ++ schedule_delayed_work(&intel_ipts.reacquire_db_work, ++ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); ++ } else { ++ intel_ipts.ipts_clbks.notify_gfx_status( ++ IPTS_NOTIFY_STA_BACKLIGHT_OFF, ++ intel_ipts.data); ++ cancel_delayed_work(&intel_ipts.reacquire_db_work); ++ } ++ } ++ ++ return 0; ++} ++ ++static void intel_ipts_reacquire_db(intel_ipts_t *intel_ipts_p) ++{ ++ int ret = 0; ++ ++ ret = i915_mutex_lock_interruptible(intel_ipts_p->dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); ++ return; ++ } ++ ++ /* Reacquire the doorbell */ ++ i915_guc_ipts_reacquire_doorbell(intel_ipts_p->dev->dev_private); ++ ++ mutex_unlock(&intel_ipts_p->dev->struct_mutex); ++ ++ return; ++} ++ ++static int intel_ipts_get_wq_info(uint64_t gfx_handle, ++ intel_ipts_wq_info_t *wq_info) ++{ ++ if (gfx_handle != (uint64_t)&intel_ipts) { ++ DRM_ERROR("invalid gfx handle\n"); ++ return -EINVAL; ++ } ++ ++ *wq_info = intel_ipts.wq_info; ++ ++ intel_ipts_reacquire_db(&intel_ipts); ++ schedule_delayed_work(&intel_ipts.reacquire_db_work, ++ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); ++ ++ return 0; ++} ++ ++static int set_wq_info(void) ++{ ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ struct intel_guc *guc = &dev_priv->guc; ++ struct intel_guc_client *client; ++ struct guc_process_desc *desc; ++ void *base = NULL; ++ intel_ipts_wq_info_t *wq_info; ++ u64 phy_base = 0; ++ ++ wq_info = &intel_ipts.wq_info; ++ ++ client = guc->ipts_client; ++ if (!client) { ++ DRM_ERROR("IPTS GuC client is NOT available\n"); ++ return -EINVAL; ++ } ++ ++ base = client->vaddr; ++ desc = (struct guc_process_desc *)((u64)base + client->proc_desc_offset); ++ ++ desc->wq_base_addr = (u64)base + GUC_DB_SIZE; ++ desc->db_base_addr = (u64)base + client->doorbell_offset; ++ ++ /* IPTS expects physical addresses to pass it to ME */ ++ phy_base = sg_dma_address(client->vma->pages->sgl); ++ ++ wq_info->db_addr = desc->db_base_addr; ++ wq_info->db_phy_addr = phy_base + client->doorbell_offset; ++ wq_info->db_cookie_offset = offsetof(struct guc_doorbell_info, cookie); ++ wq_info->wq_addr = desc->wq_base_addr; ++ wq_info->wq_phy_addr = phy_base + GUC_DB_SIZE; ++ wq_info->wq_head_addr = (u64)&desc->head; ++ wq_info->wq_head_phy_addr = phy_base + client->proc_desc_offset + ++ offsetof(struct guc_process_desc, head); ++ wq_info->wq_tail_addr = (u64)&desc->tail; ++ wq_info->wq_tail_phy_addr = phy_base + client->proc_desc_offset + ++ offsetof(struct guc_process_desc, tail); ++ wq_info->wq_size = desc->wq_size_bytes; ++ ++ return 0; ++} ++ ++static int intel_ipts_init_wq(void) ++{ ++ int ret = 0; ++ ++ ret = i915_mutex_lock_interruptible(intel_ipts.dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed\n"); ++ return ret; ++ } ++ ++ /* disable IPTS submission */ ++ i915_guc_ipts_submission_disable(intel_ipts.dev->dev_private); ++ ++ /* enable IPTS submission */ ++ ret = i915_guc_ipts_submission_enable(intel_ipts.dev->dev_private, ++ intel_ipts.ipts_context); ++ if (ret) { ++ DRM_ERROR("i915_guc_ipts_submission_enable failed : %d\n", ret); ++ goto out; ++ } ++ ++ ret = set_wq_info(); ++ if (ret) { ++ DRM_ERROR("set_wq_info failed\n"); ++ goto out; ++ } ++ ++out: ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++ ++ return ret; ++} ++ ++static void intel_ipts_release_wq(void) ++{ ++ int ret = 0; ++ ++ ret = i915_mutex_lock_interruptible(intel_ipts.dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed\n"); ++ return; ++ } ++ ++ /* disable IPTS submission */ ++ i915_guc_ipts_submission_disable(intel_ipts.dev->dev_private); ++ ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++} ++ ++static int intel_ipts_map_buffer(u64 gfx_handle, intel_ipts_mapbuffer_t *mapbuf) ++{ ++ intel_ipts_object_t* obj; ++ struct i915_gem_context *ipts_ctx = NULL; ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ struct i915_address_space *vm = NULL; ++ struct i915_vma *vma = NULL; ++ int ret = 0; ++ ++ if (gfx_handle != (uint64_t)&intel_ipts) { ++ DRM_ERROR("invalid gfx handle\n"); ++ return -EINVAL; ++ } ++ ++ /* Acquire mutex first */ ++ ret = i915_mutex_lock_interruptible(intel_ipts.dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); ++ return -EINVAL; ++ } ++ ++ obj = ipts_object_create(mapbuf->size, mapbuf->flags); ++ if (!obj) ++ return -ENOMEM; ++ ++ ipts_ctx = intel_ipts.ipts_context; ++ ret = ipts_object_pin(obj, ipts_ctx); ++ if (ret) { ++ DRM_ERROR("Not able to pin iTouch obj\n"); ++ ipts_object_free(obj); ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++ return -ENOMEM; ++ } ++ ++ if (mapbuf->flags & IPTS_BUF_FLAG_CONTIGUOUS) { ++ obj->cpu_addr = obj->gem_obj->phys_handle->vaddr; ++ } else { ++ obj->cpu_addr = ipts_object_map(obj); ++ } ++ ++ if (ipts_ctx->ppgtt) { ++ vm = &ipts_ctx->ppgtt->vm; ++ } else { ++ vm = &dev_priv->ggtt.vm; ++ } ++ ++ vma = i915_vma_instance(obj->gem_obj, vm, NULL); ++ if (IS_ERR(vma)) { ++ DRM_ERROR("cannot find or create vma\n"); ++ return -EINVAL; ++ } ++ ++ mapbuf->gfx_addr = (void*)vma->node.start; ++ mapbuf->cpu_addr = (void*)obj->cpu_addr; ++ mapbuf->buf_handle = (u64)obj; ++ if (mapbuf->flags & IPTS_BUF_FLAG_CONTIGUOUS) { ++ mapbuf->phy_addr = (u64)obj->gem_obj->phys_handle->busaddr; ++ } ++ ++ /* Release the mutex */ ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++ ++ return 0; ++} ++ ++static int intel_ipts_unmap_buffer(uint64_t gfx_handle, uint64_t buf_handle) ++{ ++ intel_ipts_object_t* obj = (intel_ipts_object_t*)buf_handle; ++ ++ if (gfx_handle != (uint64_t)&intel_ipts) { ++ DRM_ERROR("invalid gfx handle\n"); ++ return -EINVAL; ++ } ++ ++ if (!obj->gem_obj->phys_handle) ++ ipts_object_unmap(obj); ++ ipts_object_unpin(obj); ++ ipts_object_free(obj); ++ ++ return 0; ++} ++ ++int intel_ipts_connect(intel_ipts_connect_t *ipts_connect) ++{ ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ int ret = 0; ++ ++ if (!intel_ipts.initialized) ++ return -EIO; ++ ++ if (ipts_connect && ipts_connect->if_version <= ++ SUPPORTED_IPTS_INTERFACE_VERSION) { ++ ++ /* return gpu operations for ipts */ ++ ipts_connect->ipts_ops.get_wq_info = intel_ipts_get_wq_info; ++ ipts_connect->ipts_ops.map_buffer = intel_ipts_map_buffer; ++ ipts_connect->ipts_ops.unmap_buffer = intel_ipts_unmap_buffer; ++ ipts_connect->gfx_version = INTEL_INFO(dev_priv)->gen; ++ ipts_connect->gfx_handle = (uint64_t)&intel_ipts; ++ ++ /* save callback and data */ ++ intel_ipts.data = ipts_connect->data; ++ intel_ipts.ipts_clbks = ipts_connect->ipts_cb; ++ ++ intel_ipts.connected = true; ++ } else { ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(intel_ipts_connect); ++ ++void intel_ipts_disconnect(uint64_t gfx_handle) ++{ ++ if (!intel_ipts.initialized) ++ return; ++ ++ if (gfx_handle != (uint64_t)&intel_ipts || ++ intel_ipts.connected == false) { ++ DRM_ERROR("invalid gfx handle\n"); ++ return; ++ } ++ ++ intel_ipts.data = 0; ++ memset(&intel_ipts.ipts_clbks, 0, sizeof(intel_ipts_callback_t)); ++ ++ intel_ipts.connected = false; ++} ++EXPORT_SYMBOL_GPL(intel_ipts_disconnect); ++ ++static void reacquire_db_work_func(struct work_struct *work) ++{ ++ struct delayed_work *d_work = container_of(work, struct delayed_work, ++ work); ++ intel_ipts_t *intel_ipts_p = container_of(d_work, intel_ipts_t, ++ reacquire_db_work); ++ u32 head; ++ u32 tail; ++ u32 size; ++ u32 load; ++ ++ head = *(u32*)intel_ipts_p->wq_info.wq_head_addr; ++ tail = *(u32*)intel_ipts_p->wq_info.wq_tail_addr; ++ size = intel_ipts_p->wq_info.wq_size; ++ ++ if (head >= tail) ++ load = head - tail; ++ else ++ load = head + size - tail; ++ ++ if (load < REACQUIRE_DB_THRESHOLD) { ++ intel_ipts_p->need_reacquire_db = false; ++ goto reschedule_work; ++ } ++ ++ if (intel_ipts_p->need_reacquire_db) { ++ if (intel_ipts_p->old_head == head && intel_ipts_p->old_tail == tail) ++ intel_ipts_reacquire_db(intel_ipts_p); ++ intel_ipts_p->need_reacquire_db = false; ++ } else { ++ intel_ipts_p->old_head = head; ++ intel_ipts_p->old_tail = tail; ++ intel_ipts_p->need_reacquire_db = true; ++ ++ /* recheck */ ++ schedule_delayed_work(&intel_ipts_p->reacquire_db_work, ++ msecs_to_jiffies(DB_LOST_CHECK_STEP2_INTERVAL)); ++ return; ++ } ++ ++reschedule_work: ++ schedule_delayed_work(&intel_ipts_p->reacquire_db_work, ++ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); ++} ++ ++/** ++ * intel_ipts_init - Initialize ipts support ++ * @dev: drm device ++ * ++ * Setup the required structures for ipts. ++ */ ++int intel_ipts_init(struct drm_device *dev) ++{ ++ int ret = 0; ++ ++ pr_info("ipts: initializing ipts\n"); ++ ++ intel_ipts.dev = dev; ++ INIT_DELAYED_WORK(&intel_ipts.reacquire_db_work, reacquire_db_work_func); ++ ++ pr_info("ipts: creating ipts context\n"); ++ ret = create_ipts_context(); ++ if (ret) ++ return -ENOMEM; ++ ++ pr_info("ipts: starting ipts work queue\n"); ++ ret = intel_ipts_init_wq(); ++ if (ret) ++ return ret; ++ ++ intel_ipts.initialized = true; ++ //DRM_DEBUG_DRIVER("Intel iTouch framework initialized\n"); ++ pr_info("ipts: Intel iTouch framework initialized\n"); ++ ++ return ret; ++} ++ ++void intel_ipts_cleanup(struct drm_device *dev) ++{ ++ intel_ipts_object_t *obj, *n; ++ ++ if (intel_ipts.dev == dev) { ++ list_for_each_entry_safe(obj, n, &intel_ipts.buffers.list, list) { ++ list_del(&obj->list); ++ ++ if (!obj->gem_obj->phys_handle) ++ ipts_object_unmap(obj); ++ ipts_object_unpin(obj); ++ i915_gem_free_object(&obj->gem_obj->base); ++ kfree(obj); ++ } ++ ++ intel_ipts_release_wq(); ++ destroy_ipts_context(); ++ cancel_delayed_work(&intel_ipts.reacquire_db_work); ++ } ++} +diff --git a/drivers/gpu/drm/i915/intel_ipts.h b/drivers/gpu/drm/i915/intel_ipts.h +new file mode 100644 +index 000000000000..a6965d102417 +--- /dev/null ++++ b/drivers/gpu/drm/i915/intel_ipts.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright © 2016 Intel Corporation ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ * ++ */ ++#ifndef _INTEL_IPTS_H_ ++#define _INTEL_IPTS_H_ ++ ++struct drm_device; ++ ++int intel_ipts_init(struct drm_device *dev); ++void intel_ipts_cleanup(struct drm_device *dev); ++int intel_ipts_notify_backlight_status(bool backlight_on); ++int intel_ipts_notify_complete(void); ++ ++#endif //_INTEL_IPTS_H_ +diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c +index 6937ef0b4bfc..f669087d6419 100644 +--- a/drivers/gpu/drm/i915/intel_lrc.c ++++ b/drivers/gpu/drm/i915/intel_lrc.c +@@ -164,9 +164,6 @@ + #define WA_TAIL_DWORDS 2 + #define WA_TAIL_BYTES (sizeof(u32) * WA_TAIL_DWORDS) + +-static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, +- struct intel_engine_cs *engine, +- struct intel_context *ce); + static void execlists_init_reg_state(u32 *reg_state, + struct i915_gem_context *ctx, + struct intel_engine_cs *engine, +@@ -1292,7 +1289,7 @@ static void execlists_context_destroy(struct intel_context *ce) + i915_gem_object_put(ce->state->obj); + } + +-static void execlists_context_unpin(struct intel_context *ce) ++void execlists_context_unpin(struct intel_context *ce) + { + intel_ring_unpin(ce->ring); + +@@ -1379,7 +1376,7 @@ static const struct intel_context_ops execlists_context_ops = { + .destroy = execlists_context_destroy, + }; + +-static struct intel_context * ++struct intel_context * + execlists_context_pin(struct intel_engine_cs *engine, + struct i915_gem_context *ctx) + { +@@ -2470,6 +2467,9 @@ int logical_render_ring_init(struct intel_engine_cs *engine) + + logical_ring_setup(engine); + ++ engine->irq_keep_mask |= GT_RENDER_PIPECTL_NOTIFY_INTERRUPT ++ << GEN8_RCS_IRQ_SHIFT; ++ + if (HAS_L3_DPF(dev_priv)) + engine->irq_keep_mask |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT; + +@@ -2734,7 +2734,7 @@ populate_lr_context(struct i915_gem_context *ctx, + return ret; + } + +-static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, ++int execlists_context_deferred_alloc(struct i915_gem_context *ctx, + struct intel_engine_cs *engine, + struct intel_context *ce) + { +diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h +index 4dfb78e3ec7e..32159231a16e 100644 +--- a/drivers/gpu/drm/i915/intel_lrc.h ++++ b/drivers/gpu/drm/i915/intel_lrc.h +@@ -106,4 +106,12 @@ void intel_lr_context_resume(struct drm_i915_private *dev_priv); + + void intel_execlists_set_default_submission(struct intel_engine_cs *engine); + ++struct intel_context * ++execlists_context_pin(struct intel_engine_cs *engine, ++ struct i915_gem_context *ctx); ++void execlists_context_unpin(struct intel_context *ce); ++int execlists_context_deferred_alloc(struct i915_gem_context *ctx, ++ struct intel_engine_cs *engine, ++ struct intel_context *ce); ++ + #endif /* _INTEL_LRC_H_ */ +diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c +index 4a9f139e7b73..a800b93cf33d 100644 +--- a/drivers/gpu/drm/i915/intel_panel.c ++++ b/drivers/gpu/drm/i915/intel_panel.c +@@ -34,6 +34,7 @@ + #include + #include + #include "intel_drv.h" ++#include "intel_ipts.h" + + #define CRC_PMIC_PWM_PERIOD_NS 21333 + +@@ -659,6 +660,9 @@ static void lpt_disable_backlight(const struct drm_connector_state *old_conn_sta + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + u32 tmp; + ++ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) ++ intel_ipts_notify_backlight_status(false); ++ + intel_panel_actually_set_backlight(old_conn_state, 0); + + /* +@@ -846,6 +850,9 @@ static void lpt_enable_backlight(const struct intel_crtc_state *crtc_state, + + /* This won't stick until the above enable. */ + intel_panel_actually_set_backlight(conn_state, panel->backlight.level); ++ ++ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) ++ intel_ipts_notify_backlight_status(true); + } + + static void pch_enable_backlight(const struct intel_crtc_state *crtc_state, +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index 2faf5421fdd0..831617c386e6 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -173,6 +173,7 @@ struct mt_device { + static void mt_post_parse_default_settings(struct mt_device *td, + struct mt_application *app); + static void mt_post_parse(struct mt_device *td, struct mt_application *app); ++static int cc_seen = 0; + + /* classes of device behavior */ + #define MT_CLS_DEFAULT 0x0001 +@@ -792,8 +793,11 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, + app->scantime_logical_max = field->logical_maximum; + return 1; + case HID_DG_CONTACTCOUNT: +- app->have_contact_count = true; +- app->raw_cc = &field->value[usage->usage_index]; ++ if(cc_seen != 1) { ++ app->have_contact_count = true; ++ app->raw_cc = &field->value[usage->usage_index]; ++ cc_seen++; ++ } + return 1; + case HID_DG_AZIMUTH: + /* +@@ -1283,9 +1287,11 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, + field->application != HID_DG_TOUCHSCREEN && + field->application != HID_DG_PEN && + field->application != HID_DG_TOUCHPAD && ++ field->application != HID_GD_MOUSE && + field->application != HID_GD_KEYBOARD && + field->application != HID_GD_SYSTEM_CONTROL && + field->application != HID_CP_CONSUMER_CONTROL && ++ field->logical != HID_DG_TOUCHSCREEN && + field->application != HID_GD_WIRELESS_RADIO_CTLS && + field->application != HID_GD_SYSTEM_MULTIAXIS && + !(field->application == HID_VD_ASUS_CUSTOM_MEDIA_KEYS && +@@ -1330,6 +1336,14 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct mt_device *td = hid_get_drvdata(hdev); + struct mt_report_data *rdata; + ++ if (field->application == HID_DG_TOUCHSCREEN || ++ field->application == HID_DG_TOUCHPAD) { ++ if (usage->type == EV_KEY || usage->type == EV_ABS) ++ set_bit(usage->type, hi->input->evbit); ++ ++ return -1; ++ } ++ + rdata = mt_find_report_data(td, field->report); + if (rdata && rdata->is_mt_collection) { + /* We own these mappings, tell hid-input to ignore them */ +@@ -1555,12 +1569,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) + /* already handled by hid core */ + break; + case HID_DG_TOUCHSCREEN: +- /* we do not set suffix = "Touchscreen" */ ++ suffix = "Touchscreen"; + hi->input->name = hdev->name; + break; + case HID_DG_STYLUS: + /* force BTN_STYLUS to allow tablet matching in udev */ + __set_bit(BTN_STYLUS, hi->input->keybit); ++ __set_bit(INPUT_PROP_DIRECT, hi->input->propbit); + break; + case HID_VD_ASUS_CUSTOM_MEDIA_KEYS: + suffix = "Custom Media Keys"; +@@ -1677,6 +1692,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) + td->hdev = hdev; + td->mtclass = *mtclass; + td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN; ++ cc_seen = 0; + hid_set_drvdata(hdev, td); + + INIT_LIST_HEAD(&td->applications); +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 3726eacdf65d..77263b5f5915 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -520,6 +520,7 @@ source "drivers/misc/ti-st/Kconfig" + source "drivers/misc/lis3lv02d/Kconfig" + source "drivers/misc/altera-stapl/Kconfig" + source "drivers/misc/mei/Kconfig" ++source "drivers/misc/ipts/Kconfig" + source "drivers/misc/vmw_vmci/Kconfig" + source "drivers/misc/mic/Kconfig" + source "drivers/misc/genwqe/Kconfig" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index af22bbc3d00c..eb1eb0d58c32 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -44,6 +44,7 @@ obj-y += lis3lv02d/ + obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o + obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ + obj-$(CONFIG_INTEL_MEI) += mei/ ++obj-$(CONFIG_INTEL_IPTS) += ipts/ + obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ + obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o + obj-$(CONFIG_SRAM) += sram.o +diff --git a/drivers/misc/ipts/Kconfig b/drivers/misc/ipts/Kconfig +new file mode 100644 +index 000000000000..360ed3861b82 +--- /dev/null ++++ b/drivers/misc/ipts/Kconfig +@@ -0,0 +1,9 @@ ++config INTEL_IPTS ++ tristate "Intel Precise Touch & Stylus" ++ select INTEL_MEI ++ depends on X86 && PCI && HID ++ help ++ Intel Precise Touch & Stylus support ++ Supported SoCs: ++ Intel Skylake ++ Intel Kabylake +diff --git a/drivers/misc/ipts/Makefile b/drivers/misc/ipts/Makefile +new file mode 100644 +index 000000000000..1783e9cf13c9 +--- /dev/null ++++ b/drivers/misc/ipts/Makefile +@@ -0,0 +1,13 @@ ++# ++# Makefile - Intel Precise Touch & Stylus device driver ++# Copyright (c) 2016, Intel Corporation. ++# ++ ++obj-$(CONFIG_INTEL_IPTS)+= intel-ipts.o ++intel-ipts-objs += ipts-mei.o ++intel-ipts-objs += ipts-hid.o ++intel-ipts-objs += ipts-msg-handler.o ++intel-ipts-objs += ipts-kernel.o ++intel-ipts-objs += ipts-resource.o ++intel-ipts-objs += ipts-gfx.o ++intel-ipts-$(CONFIG_DEBUG_FS) += ipts-dbgfs.o +diff --git a/drivers/misc/ipts/ipts-binary-spec.h b/drivers/misc/ipts/ipts-binary-spec.h +new file mode 100644 +index 000000000000..87d4bc4133c4 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-binary-spec.h +@@ -0,0 +1,118 @@ ++/* ++ * ++ * Intel Precise Touch & Stylus binary spec ++ * Copyright (c) 2016 Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#ifndef _IPTS_BINARY_SPEC_H ++#define _IPTS_BINARY_SPEC_H ++ ++#define IPTS_BIN_HEADER_VERSION 2 ++ ++#pragma pack(1) ++ ++/* we support 16 output buffers(1:feedback, 15:HID) */ ++#define MAX_NUM_OUTPUT_BUFFERS 16 ++ ++typedef enum { ++ IPTS_BIN_KERNEL, ++ IPTS_BIN_RO_DATA, ++ IPTS_BIN_RW_DATA, ++ IPTS_BIN_SENSOR_FRAME, ++ IPTS_BIN_OUTPUT, ++ IPTS_BIN_DYNAMIC_STATE_HEAP, ++ IPTS_BIN_PATCH_LOCATION_LIST, ++ IPTS_BIN_ALLOCATION_LIST, ++ IPTS_BIN_COMMAND_BUFFER_PACKET, ++ IPTS_BIN_TAG, ++} ipts_bin_res_type_t; ++ ++typedef struct ipts_bin_header { ++ char str[4]; ++ unsigned int version; ++ ++#if IPTS_BIN_HEADER_VERSION > 1 ++ unsigned int gfxcore; ++ unsigned int revid; ++#endif ++} ipts_bin_header_t; ++ ++typedef struct ipts_bin_alloc { ++ unsigned int handle; ++ unsigned int reserved; ++} ipts_bin_alloc_t; ++ ++typedef struct ipts_bin_alloc_list { ++ unsigned int num; ++ ipts_bin_alloc_t alloc[]; ++} ipts_bin_alloc_list_t; ++ ++typedef struct ipts_bin_cmdbuf { ++ unsigned int size; ++ char data[]; ++} ipts_bin_cmdbuf_t; ++ ++typedef struct ipts_bin_res { ++ unsigned int handle; ++ ipts_bin_res_type_t type; ++ unsigned int initialize; ++ unsigned int aligned_size; ++ unsigned int size; ++ char data[]; ++} ipts_bin_res_t; ++ ++typedef enum { ++ IPTS_INPUT, ++ IPTS_OUTPUT, ++ IPTS_CONFIGURATION, ++ IPTS_CALIBRATION, ++ IPTS_FEATURE, ++} ipts_bin_io_buffer_type_t; ++ ++typedef struct ipts_bin_io_header { ++ char str[10]; ++ unsigned short type; ++} ipts_bin_io_header_t; ++ ++typedef struct ipts_bin_res_list { ++ unsigned int num; ++ ipts_bin_res_t res[]; ++} ipts_bin_res_list_t; ++ ++typedef struct ipts_bin_patch { ++ unsigned int index; ++ unsigned int reserved1[2]; ++ unsigned int alloc_offset; ++ unsigned int patch_offset; ++ unsigned int reserved2; ++} ipts_bin_patch_t; ++ ++typedef struct ipts_bin_patch_list { ++ unsigned int num; ++ ipts_bin_patch_t patch[]; ++} ipts_bin_patch_list_t; ++ ++typedef struct ipts_bin_guc_wq_info { ++ unsigned int batch_offset; ++ unsigned int size; ++ char data[]; ++} ipts_bin_guc_wq_info_t; ++ ++typedef struct ipts_bin_bufid_patch { ++ unsigned int imm_offset; ++ unsigned int mem_offset; ++} ipts_bin_bufid_patch_t; ++ ++#pragma pack() ++ ++#endif /* _IPTS_BINARY_SPEC_H */ +diff --git a/drivers/misc/ipts/ipts-dbgfs.c b/drivers/misc/ipts/ipts-dbgfs.c +new file mode 100644 +index 000000000000..1c5c92f7d4ba +--- /dev/null ++++ b/drivers/misc/ipts/ipts-dbgfs.c +@@ -0,0 +1,152 @@ ++/* ++ * Intel Precise Touch & Stylus device driver ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++#include ++#include ++#include ++ ++#include "ipts.h" ++#include "ipts-sensor-regs.h" ++#include "ipts-msg-handler.h" ++#include "ipts-state.h" ++ ++const char sensor_mode_fmt[] = "sensor mode : %01d\n"; ++const char ipts_status_fmt[] = "sensor mode : %01d\nipts state : %01d\n"; ++ ++static ssize_t ipts_dbgfs_mode_read(struct file *fp, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ ipts_info_t *ipts = fp->private_data; ++ char mode[80]; ++ int len = 0; ++ ++ if (cnt < sizeof(sensor_mode_fmt) - 3) ++ return -EINVAL; ++ ++ len = scnprintf(mode, 80, sensor_mode_fmt, ipts->sensor_mode); ++ if (len < 0) ++ return -EIO; ++ ++ return simple_read_from_buffer(ubuf, cnt, ppos, mode, len); ++} ++ ++static ssize_t ipts_dbgfs_mode_write(struct file *fp, const char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ ipts_info_t *ipts = fp->private_data; ++ ipts_state_t state; ++ int sensor_mode, len; ++ char mode[3]; ++ ++ if (cnt == 0 || cnt > 3) ++ return -EINVAL; ++ ++ state = ipts_get_state(ipts); ++ if (state != IPTS_STA_RAW_DATA_STARTED && state != IPTS_STA_HID_STARTED) { ++ return -EIO; ++ } ++ ++ len = cnt; ++ if (copy_from_user(mode, ubuf, len)) ++ return -EFAULT; ++ ++ while(len > 0 && (isspace(mode[len-1]) || mode[len-1] == '\n')) ++ len--; ++ mode[len] = '\0'; ++ ++ if (sscanf(mode, "%d", &sensor_mode) != 1) ++ return -EINVAL; ++ ++ if (sensor_mode != TOUCH_SENSOR_MODE_RAW_DATA && ++ sensor_mode != TOUCH_SENSOR_MODE_HID) { ++ return -EINVAL; ++ } ++ ++ if (sensor_mode == ipts->sensor_mode) ++ return 0; ++ ++ ipts_switch_sensor_mode(ipts, sensor_mode); ++ ++ return cnt; ++} ++ ++static const struct file_operations ipts_mode_dbgfs_fops = { ++ .open = simple_open, ++ .read = ipts_dbgfs_mode_read, ++ .write = ipts_dbgfs_mode_write, ++ .llseek = generic_file_llseek, ++}; ++ ++static ssize_t ipts_dbgfs_status_read(struct file *fp, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ ipts_info_t *ipts = fp->private_data; ++ char status[256]; ++ int len = 0; ++ ++ if (cnt < sizeof(ipts_status_fmt) - 3) ++ return -EINVAL; ++ ++ len = scnprintf(status, 256, ipts_status_fmt, ipts->sensor_mode, ++ ipts->state); ++ if (len < 0) ++ return -EIO; ++ ++ return simple_read_from_buffer(ubuf, cnt, ppos, status, len); ++} ++ ++static const struct file_operations ipts_status_dbgfs_fops = { ++ .open = simple_open, ++ .read = ipts_dbgfs_status_read, ++ .llseek = generic_file_llseek, ++}; ++ ++void ipts_dbgfs_deregister(ipts_info_t* ipts) ++{ ++ if (!ipts->dbgfs_dir) ++ return; ++ ++ debugfs_remove_recursive(ipts->dbgfs_dir); ++ ipts->dbgfs_dir = NULL; ++} ++ ++int ipts_dbgfs_register(ipts_info_t* ipts, const char *name) ++{ ++ struct dentry *dir, *f; ++ ++ dir = debugfs_create_dir(name, NULL); ++ if (!dir) ++ return -ENOMEM; ++ ++ f = debugfs_create_file("mode", S_IRUSR | S_IWUSR, dir, ++ ipts, &ipts_mode_dbgfs_fops); ++ if (!f) { ++ ipts_err(ipts, "debugfs mode creation failed\n"); ++ goto err; ++ } ++ ++ f = debugfs_create_file("status", S_IRUSR, dir, ++ ipts, &ipts_status_dbgfs_fops); ++ if (!f) { ++ ipts_err(ipts, "debugfs status creation failed\n"); ++ goto err; ++ } ++ ++ ipts->dbgfs_dir = dir; ++ ++ return 0; ++err: ++ ipts_dbgfs_deregister(ipts); ++ return -ENODEV; ++} +diff --git a/drivers/misc/ipts/ipts-gfx.c b/drivers/misc/ipts/ipts-gfx.c +new file mode 100644 +index 000000000000..51727770e75d +--- /dev/null ++++ b/drivers/misc/ipts/ipts-gfx.c +@@ -0,0 +1,184 @@ ++/* ++ * ++ * Intel Integrated Touch Gfx Interface Layer ++ * Copyright (c) 2016 Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++#include ++#include ++#include ++ ++#include "ipts.h" ++#include "ipts-msg-handler.h" ++#include "ipts-state.h" ++ ++static void gfx_processing_complete(void *data) ++{ ++ ipts_info_t *ipts = data; ++ ++ if (ipts_get_state(ipts) == IPTS_STA_RAW_DATA_STARTED) { ++ schedule_work(&ipts->raw_data_work); ++ return; ++ } ++ ++ ipts_dbg(ipts, "not ready to handle gfx event\n"); ++} ++ ++static void notify_gfx_status(u32 status, void *data) ++{ ++ ipts_info_t *ipts = data; ++ ++ ipts->gfx_status = status; ++ schedule_work(&ipts->gfx_status_work); ++} ++ ++static int connect_gfx(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ intel_ipts_connect_t ipts_connect; ++ ++ ipts_connect.if_version = IPTS_INTERFACE_V1; ++ ipts_connect.ipts_cb.workload_complete = gfx_processing_complete; ++ ipts_connect.ipts_cb.notify_gfx_status = notify_gfx_status; ++ ipts_connect.data = (void*)ipts; ++ ++ ret = intel_ipts_connect(&ipts_connect); ++ if (ret) ++ return ret; ++ ++ /* TODO: gfx version check */ ++ ipts->gfx_info.gfx_handle = ipts_connect.gfx_handle; ++ ipts->gfx_info.ipts_ops = ipts_connect.ipts_ops; ++ ++ return ret; ++} ++ ++static void disconnect_gfx(ipts_info_t *ipts) ++{ ++ intel_ipts_disconnect(ipts->gfx_info.gfx_handle); ++} ++ ++#ifdef RUN_DBG_THREAD ++#include "../mei/mei_dev.h" ++ ++static struct task_struct *dbg_thread; ++ ++static void ipts_print_dbg_info(ipts_info_t* ipts) ++{ ++ char fw_sts_str[MEI_FW_STATUS_STR_SZ]; ++ u32 *db, *head, *tail; ++ intel_ipts_wq_info_t* wq_info; ++ ++ wq_info = &ipts->resource.wq_info; ++ ++ mei_fw_status_str(ipts->cldev->bus, fw_sts_str, MEI_FW_STATUS_STR_SZ); ++ pr_info(">> tdt : fw status : %s\n", fw_sts_str); ++ ++ db = (u32*)wq_info->db_addr; ++ head = (u32*)wq_info->wq_head_addr; ++ tail = (u32*)wq_info->wq_tail_addr; ++ pr_info(">> == DB s:%x, c:%x ==\n", *db, *(db+1)); ++ pr_info(">> == WQ h:%u, t:%u ==\n", *head, *tail); ++} ++ ++static int ipts_dbg_thread(void *data) ++{ ++ ipts_info_t *ipts = (ipts_info_t *)data; ++ ++ pr_info(">> start debug thread\n"); ++ ++ while (!kthread_should_stop()) { ++ if (ipts_get_state(ipts) != IPTS_STA_RAW_DATA_STARTED) { ++ pr_info("state is not IPTS_STA_RAW_DATA_STARTED : %d\n", ++ ipts_get_state(ipts)); ++ msleep(5000); ++ continue; ++ } ++ ++ ipts_print_dbg_info(ipts); ++ ++ msleep(3000); ++ } ++ ++ return 0; ++} ++#endif ++ ++int ipts_open_gpu(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ ++ ret = connect_gfx(ipts); ++ if (ret) { ++ ipts_dbg(ipts, "cannot connect GPU\n"); ++ return ret; ++ } ++ ++ ret = ipts->gfx_info.ipts_ops.get_wq_info(ipts->gfx_info.gfx_handle, ++ &ipts->resource.wq_info); ++ if (ret) { ++ ipts_dbg(ipts, "error in get_wq_info\n"); ++ return ret; ++ } ++ ++#ifdef RUN_DBG_THREAD ++ dbg_thread = kthread_run(ipts_dbg_thread, (void *)ipts, "ipts_debug"); ++#endif ++ ++ return 0; ++} ++ ++void ipts_close_gpu(ipts_info_t *ipts) ++{ ++ disconnect_gfx(ipts); ++ ++#ifdef RUN_DBG_THREAD ++ kthread_stop(dbg_thread); ++#endif ++} ++ ++intel_ipts_mapbuffer_t *ipts_map_buffer(ipts_info_t *ipts, u32 size, u32 flags) ++{ ++ intel_ipts_mapbuffer_t *buf; ++ u64 handle; ++ int ret; ++ ++ buf = devm_kzalloc(&ipts->cldev->dev, sizeof(*buf), GFP_KERNEL); ++ if (!buf) ++ return NULL; ++ ++ buf->size = size; ++ buf->flags = flags; ++ ++ handle = ipts->gfx_info.gfx_handle; ++ ret = ipts->gfx_info.ipts_ops.map_buffer(handle, buf); ++ if (ret) { ++ devm_kfree(&ipts->cldev->dev, buf); ++ return NULL; ++ } ++ ++ return buf; ++} ++ ++void ipts_unmap_buffer(ipts_info_t *ipts, intel_ipts_mapbuffer_t *buf) ++{ ++ u64 handle; ++ int ret; ++ ++ if (!buf) ++ return; ++ ++ handle = ipts->gfx_info.gfx_handle; ++ ret = ipts->gfx_info.ipts_ops.unmap_buffer(handle, buf->buf_handle); ++ ++ devm_kfree(&ipts->cldev->dev, buf); ++} +diff --git a/drivers/misc/ipts/ipts-gfx.h b/drivers/misc/ipts/ipts-gfx.h +new file mode 100644 +index 000000000000..03a5f3551ddf +--- /dev/null ++++ b/drivers/misc/ipts/ipts-gfx.h +@@ -0,0 +1,24 @@ ++/* ++ * Intel Precise Touch & Stylus gpu wrapper ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++ ++#ifndef _IPTS_GFX_H_ ++#define _IPTS_GFX_H_ ++ ++int ipts_open_gpu(ipts_info_t *ipts); ++void ipts_close_gpu(ipts_info_t *ipts); ++intel_ipts_mapbuffer_t *ipts_map_buffer(ipts_info_t *ipts, u32 size, u32 flags); ++void ipts_unmap_buffer(ipts_info_t *ipts, intel_ipts_mapbuffer_t *buf); ++ ++#endif // _IPTS_GFX_H_ +diff --git a/drivers/misc/ipts/ipts-hid.c b/drivers/misc/ipts/ipts-hid.c +new file mode 100644 +index 000000000000..e85844dc1158 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-hid.c +@@ -0,0 +1,456 @@ ++/* ++ * Intel Precise Touch & Stylus HID driver ++ * ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "ipts.h" ++#include "ipts-resource.h" ++#include "ipts-sensor-regs.h" ++#include "ipts-msg-handler.h" ++ ++#define BUS_MEI 0x44 ++ ++#define HID_DESC_INTEL "intel/ipts/intel_desc.bin" ++#define HID_DESC_VENDOR "intel/ipts/vendor_desc.bin" ++MODULE_FIRMWARE(HID_DESC_INTEL); ++MODULE_FIRMWARE(HID_DESC_VENDOR); ++ ++typedef enum output_buffer_payload_type { ++ OUTPUT_BUFFER_PAYLOAD_ERROR = 0, ++ OUTPUT_BUFFER_PAYLOAD_HID_INPUT_REPORT, ++ OUTPUT_BUFFER_PAYLOAD_HID_FEATURE_REPORT, ++ OUTPUT_BUFFER_PAYLOAD_KERNEL_LOAD, ++ OUTPUT_BUFFER_PAYLOAD_FEEDBACK_BUFFER ++} output_buffer_payload_type_t; ++ ++typedef struct kernel_output_buffer_header { ++ u16 length; ++ u8 payload_type; ++ u8 reserved1; ++ touch_hid_private_data_t hid_private_data; ++ u8 reserved2[28]; ++ u8 data[0]; ++} kernel_output_buffer_header_t; ++ ++typedef struct kernel_output_payload_error { ++ u16 severity; ++ u16 source; ++ u8 code[4]; ++ char string[128]; ++} kernel_output_payload_error_t; ++ ++static int ipts_hid_get_hid_descriptor(ipts_info_t *ipts, u8 **desc, int *size) ++{ ++ u8 *buf; ++ int hid_size = 0, ret = 0; ++ const struct firmware *intel_desc = NULL; ++ const struct firmware *vendor_desc = NULL; ++ const char *intel_desc_path = HID_DESC_INTEL; ++ const char *vendor_desc_path = HID_DESC_VENDOR; ++ ++ ret = request_firmware(&intel_desc, intel_desc_path, &ipts->cldev->dev); ++ if (ret) { ++ goto no_hid; ++ } ++ hid_size = intel_desc->size; ++ ++ ret = request_firmware(&vendor_desc, vendor_desc_path, &ipts->cldev->dev); ++ if (ret) { ++ ipts_dbg(ipts, "error in reading HID Vendor Descriptor\n"); ++ } else { ++ hid_size += vendor_desc->size; ++ } ++ ++ ipts_dbg(ipts, "hid size = %d\n", hid_size); ++ buf = vmalloc(hid_size); ++ if (buf == NULL) { ++ ret = -ENOMEM; ++ goto no_mem; ++ } ++ ++ memcpy(buf, intel_desc->data, intel_desc->size); ++ if (vendor_desc) { ++ memcpy(&buf[intel_desc->size], vendor_desc->data, ++ vendor_desc->size); ++ release_firmware(vendor_desc); ++ } ++ ++ release_firmware(intel_desc); ++ ++ *desc = buf; ++ *size = hid_size; ++ ++ return 0; ++no_mem : ++ if (vendor_desc) ++ release_firmware(vendor_desc); ++ release_firmware(intel_desc); ++ ++no_hid : ++ return ret; ++} ++ ++static int ipts_hid_parse(struct hid_device *hid) ++{ ++ ipts_info_t *ipts = hid->driver_data; ++ int ret = 0, size; ++ u8 *buf; ++ ++ ipts_dbg(ipts, "ipts_hid_parse() start\n"); ++ ret = ipts_hid_get_hid_descriptor(ipts, &buf, &size); ++ if (ret != 0) { ++ ipts_dbg(ipts, "ipts_hid_ipts_get_hid_descriptor ret %d\n", ret); ++ return -EIO; ++ } ++ ++ ret = hid_parse_report(hid, buf, size); ++ vfree(buf); ++ if (ret) { ++ ipts_err(ipts, "hid_parse_report error : %d\n", ret); ++ goto out; ++ } ++ ++ ipts->hid_desc_ready = true; ++out: ++ return ret; ++} ++ ++static int ipts_hid_start(struct hid_device *hid) ++{ ++ return 0; ++} ++ ++static void ipts_hid_stop(struct hid_device *hid) ++{ ++ return; ++} ++ ++static int ipts_hid_open(struct hid_device *hid) ++{ ++ return 0; ++} ++ ++static void ipts_hid_close(struct hid_device *hid) ++{ ++ ipts_info_t *ipts = hid->driver_data; ++ ++ ipts->hid_desc_ready = false; ++ ++ return; ++} ++ ++static int ipts_hid_send_hid2me_feedback(ipts_info_t *ipts, u32 fb_data_type, ++ __u8 *buf, size_t count) ++{ ++ ipts_buffer_info_t *fb_buf; ++ touch_feedback_hdr_t *feedback; ++ u8 *payload; ++ int header_size; ++ ipts_state_t state; ++ ++ header_size = sizeof(touch_feedback_hdr_t); ++ ++ if (count > ipts->resource.hid2me_buffer_size - header_size) ++ return -EINVAL; ++ ++ state = ipts_get_state(ipts); ++ if (state != IPTS_STA_RAW_DATA_STARTED && state != IPTS_STA_HID_STARTED) ++ return 0; ++ ++ fb_buf = ipts_get_hid2me_buffer(ipts); ++ feedback = (touch_feedback_hdr_t *)fb_buf->addr; ++ payload = fb_buf->addr + header_size; ++ memset(feedback, 0, header_size); ++ ++ feedback->feedback_data_type = fb_data_type; ++ feedback->feedback_cmd_type = TOUCH_FEEDBACK_CMD_TYPE_NONE; ++ feedback->payload_size_bytes = count; ++ feedback->buffer_id = TOUCH_HID_2_ME_BUFFER_ID; ++ feedback->protocol_ver = 0; ++ feedback->reserved[0] = 0xAC; ++ ++ /* copy payload */ ++ memcpy(payload, buf, count); ++ ++ ipts_send_feedback(ipts, TOUCH_HID_2_ME_BUFFER_ID, 0); ++ ++ return 0; ++} ++ ++static int ipts_hid_raw_request(struct hid_device *hid, ++ unsigned char report_number, __u8 *buf, ++ size_t count, unsigned char report_type, ++ int reqtype) ++{ ++ ipts_info_t *ipts = hid->driver_data; ++ u32 fb_data_type; ++ ++ ipts_dbg(ipts, "hid raw request => report %d, request %d\n", ++ (int)report_type, reqtype); ++ ++ if (report_type != HID_FEATURE_REPORT) ++ return 0; ++ ++ switch (reqtype) { ++ case HID_REQ_GET_REPORT: ++ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_GET_FEATURES; ++ break; ++ case HID_REQ_SET_REPORT: ++ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_SET_FEATURES; ++ break; ++ default: ++ ipts_err(ipts, "raw request not supprted: %d\n", reqtype); ++ return -EIO; ++ } ++ ++ return ipts_hid_send_hid2me_feedback(ipts, fb_data_type, buf, count); ++} ++ ++static int ipts_hid_output_report(struct hid_device *hid, ++ __u8 *buf, size_t count) ++{ ++ ipts_info_t *ipts = hid->driver_data; ++ u32 fb_data_type; ++ ++ ipts_dbg(ipts, "hid output report\n"); ++ ++ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_OUTPUT_REPORT; ++ ++ return ipts_hid_send_hid2me_feedback(ipts, fb_data_type, buf, count); ++} ++ ++static struct hid_ll_driver ipts_hid_ll_driver = { ++ .parse = ipts_hid_parse, ++ .start = ipts_hid_start, ++ .stop = ipts_hid_stop, ++ .open = ipts_hid_open, ++ .close = ipts_hid_close, ++ .raw_request = ipts_hid_raw_request, ++ .output_report = ipts_hid_output_report, ++}; ++ ++int ipts_hid_init(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ struct hid_device *hid; ++ ++ hid = hid_allocate_device(); ++ if (IS_ERR(hid)) { ++ ret = PTR_ERR(hid); ++ goto err_dev; ++ } ++ ++ hid->driver_data = ipts; ++ hid->ll_driver = &ipts_hid_ll_driver; ++ hid->dev.parent = &ipts->cldev->dev; ++ hid->bus = BUS_MEI; ++ hid->version = ipts->device_info.fw_rev; ++ hid->vendor = ipts->device_info.vendor_id; ++ hid->product = ipts->device_info.device_id; ++ ++ snprintf(hid->phys, sizeof(hid->phys), "heci3"); ++ snprintf(hid->name, sizeof(hid->name), ++ "%s %04hX:%04hX", "ipts", hid->vendor, hid->product); ++ ++ ret = hid_add_device(hid); ++ if (ret) { ++ if (ret != -ENODEV) ++ ipts_err(ipts, "can't add hid device: %d\n", ret); ++ goto err_mem_free; ++ } ++ ++ ipts->hid = hid; ++ ++ return 0; ++ ++err_mem_free: ++ hid_destroy_device(hid); ++err_dev: ++ return ret; ++} ++ ++void ipts_hid_release(ipts_info_t *ipts) ++{ ++ if (!ipts->hid) ++ return; ++ hid_destroy_device(ipts->hid); ++} ++ ++int ipts_handle_hid_data(ipts_info_t *ipts, ++ touch_sensor_hid_ready_for_data_rsp_data_t *hid_rsp) ++{ ++ touch_raw_data_hdr_t *raw_header; ++ ipts_buffer_info_t *buffer_info; ++ touch_feedback_hdr_t *feedback; ++ u8 *raw_data; ++ int touch_data_buffer_index; ++ int transaction_id; ++ int ret = 0; ++ ++ touch_data_buffer_index = (int)hid_rsp->touch_data_buffer_index; ++ buffer_info = ipts_get_touch_data_buffer_hid(ipts); ++ raw_header = (touch_raw_data_hdr_t *)buffer_info->addr; ++ transaction_id = raw_header->hid_private_data.transaction_id; ++ ++ raw_data = (u8*)raw_header + sizeof(touch_raw_data_hdr_t); ++ if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_HID_REPORT) { ++ memcpy(ipts->hid_input_report, raw_data, ++ raw_header->raw_data_size_bytes); ++ ++ ret = hid_input_report(ipts->hid, HID_INPUT_REPORT, ++ (u8*)ipts->hid_input_report, ++ raw_header->raw_data_size_bytes, 1); ++ if (ret) { ++ ipts_err(ipts, "error in hid_input_report : %d\n", ret); ++ } ++ } else if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_GET_FEATURES) { ++ /* TODO: implement together with "get feature ioctl" */ ++ } else if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_ERROR) { ++ touch_error_t *touch_err = (touch_error_t *)raw_data; ++ ++ ipts_err(ipts, "error type : %d, me fw error : %x, err reg : %x\n", ++ touch_err->touch_error_type, ++ touch_err->touch_me_fw_error.value, ++ touch_err->touch_error_register.reg_value); ++ } ++ ++ /* send feedback data for HID mode */ ++ buffer_info = ipts_get_feedback_buffer(ipts, touch_data_buffer_index); ++ feedback = (touch_feedback_hdr_t *)buffer_info->addr; ++ memset(feedback, 0, sizeof(touch_feedback_hdr_t)); ++ feedback->feedback_cmd_type = TOUCH_FEEDBACK_CMD_TYPE_NONE; ++ feedback->payload_size_bytes = 0; ++ feedback->buffer_id = touch_data_buffer_index; ++ feedback->protocol_ver = 0; ++ feedback->reserved[0] = 0xAC; ++ ++ ret = ipts_send_feedback(ipts, touch_data_buffer_index, transaction_id); ++ ++ return ret; ++} ++ ++static int handle_outputs(ipts_info_t *ipts, int parallel_idx) ++{ ++ kernel_output_buffer_header_t *out_buf_hdr; ++ ipts_buffer_info_t *output_buf, *fb_buf = NULL; ++ u8 *input_report, *payload; ++ u32 transaction_id; ++ int i, payload_size, ret = 0, header_size; ++ ++ header_size = sizeof(kernel_output_buffer_header_t); ++ output_buf = ipts_get_output_buffers_by_parallel_id(ipts, parallel_idx); ++ for (i = 0; i < ipts->resource.num_of_outputs; i++) { ++ out_buf_hdr = (kernel_output_buffer_header_t*)output_buf[i].addr; ++ if (out_buf_hdr->length < header_size) ++ continue; ++ ++ payload_size = out_buf_hdr->length - header_size; ++ payload = out_buf_hdr->data; ++ ++ switch(out_buf_hdr->payload_type) { ++ case OUTPUT_BUFFER_PAYLOAD_HID_INPUT_REPORT: ++ input_report = ipts->hid_input_report; ++ memcpy(input_report, payload, payload_size); ++ hid_input_report(ipts->hid, HID_INPUT_REPORT, ++ input_report, payload_size, 1); ++ break; ++ case OUTPUT_BUFFER_PAYLOAD_HID_FEATURE_REPORT: ++ ipts_dbg(ipts, "output hid feature report\n"); ++ break; ++ case OUTPUT_BUFFER_PAYLOAD_KERNEL_LOAD: ++ ipts_dbg(ipts, "output kernel load\n"); ++ break; ++ case OUTPUT_BUFFER_PAYLOAD_FEEDBACK_BUFFER: ++ { ++ /* send feedback data for raw data mode */ ++ fb_buf = ipts_get_feedback_buffer(ipts, ++ parallel_idx); ++ transaction_id = out_buf_hdr-> ++ hid_private_data.transaction_id; ++ memcpy(fb_buf->addr, payload, payload_size); ++ break; ++ } ++ case OUTPUT_BUFFER_PAYLOAD_ERROR: ++ { ++ kernel_output_payload_error_t *err_payload; ++ ++ if (payload_size == 0) ++ break; ++ ++ err_payload = ++ (kernel_output_payload_error_t*)payload; ++ ++ ipts_err(ipts, "error : severity : %d," ++ " source : %d," ++ " code : %d:%d:%d:%d\n" ++ "string %s\n", ++ err_payload->severity, ++ err_payload->source, ++ err_payload->code[0], ++ err_payload->code[1], ++ err_payload->code[2], ++ err_payload->code[3], ++ err_payload->string); ++ ++ break; ++ } ++ default: ++ ipts_err(ipts, "invalid output buffer payload\n"); ++ break; ++ } ++ } ++ ++ if (fb_buf) { ++ ret = ipts_send_feedback(ipts, parallel_idx, transaction_id); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int handle_output_buffers(ipts_info_t *ipts, int cur_idx, int end_idx) ++{ ++ int max_num_of_buffers = ipts_get_num_of_parallel_buffers(ipts); ++ ++ do { ++ cur_idx++; /* cur_idx has last completed so starts with +1 */ ++ cur_idx %= max_num_of_buffers; ++ handle_outputs(ipts, cur_idx); ++ } while (cur_idx != end_idx); ++ ++ return 0; ++} ++ ++int ipts_handle_processed_data(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ int current_buffer_idx; ++ int last_buffer_idx; ++ ++ current_buffer_idx = *ipts->last_submitted_id; ++ last_buffer_idx = ipts->last_buffer_completed; ++ ++ if (current_buffer_idx == last_buffer_idx) ++ return 0; ++ ++ ipts->last_buffer_completed = current_buffer_idx; ++ handle_output_buffers(ipts, last_buffer_idx, current_buffer_idx); ++ ++ return ret; ++} +diff --git a/drivers/misc/ipts/ipts-hid.h b/drivers/misc/ipts/ipts-hid.h +new file mode 100644 +index 000000000000..f1b22c912df7 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-hid.h +@@ -0,0 +1,34 @@ ++/* ++ * Intel Precise Touch & Stylus HID definition ++ * ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#ifndef _IPTS_HID_H_ ++#define _IPTS_HID_H_ ++ ++#define BUS_MEI 0x44 ++ ++#if 0 /* TODO : we have special report ID. will implement them */ ++#define WRITE_CHANNEL_REPORT_ID 0xa ++#define READ_CHANNEL_REPORT_ID 0xb ++#define CONFIG_CHANNEL_REPORT_ID 0xd ++#define VENDOR_INFO_REPORT_ID 0xF ++#define SINGLE_TOUCH_REPORT_ID 0x40 ++#endif ++ ++int ipts_hid_init(ipts_info_t *ipts); ++void ipts_hid_release(ipts_info_t *ipts); ++int ipts_handle_hid_data(ipts_info_t *ipts, ++ touch_sensor_hid_ready_for_data_rsp_data_t *hid_rsp); ++ ++#endif /* _IPTS_HID_H_ */ +diff --git a/drivers/misc/ipts/ipts-kernel.c b/drivers/misc/ipts/ipts-kernel.c +new file mode 100644 +index 000000000000..86fd359d2eed +--- /dev/null ++++ b/drivers/misc/ipts/ipts-kernel.c +@@ -0,0 +1,1050 @@ ++#include ++#include ++#include ++#include ++ ++#include "ipts.h" ++#include "ipts-resource.h" ++#include "ipts-binary-spec.h" ++#include "ipts-state.h" ++#include "ipts-msg-handler.h" ++#include "ipts-gfx.h" ++ ++#define MAX_IOCL_FILE_NAME_LEN 80 ++#define MAX_IOCL_FILE_PATH_LEN 256 ++ ++#pragma pack(1) ++typedef struct bin_data_file_info { ++ u32 io_buffer_type; ++ u32 flags; ++ char file_name[MAX_IOCL_FILE_NAME_LEN]; ++} bin_data_file_info_t; ++ ++typedef struct bin_fw_info { ++ char fw_name[MAX_IOCL_FILE_NAME_LEN]; ++ ++ /* list of parameters to load a kernel */ ++ s32 vendor_output; /* output index. -1 for no use */ ++ u32 num_of_data_files; ++ bin_data_file_info_t data_file[]; ++} bin_fw_info_t; ++ ++typedef struct bin_fw_list { ++ u32 num_of_fws; ++ bin_fw_info_t fw_info[]; ++} bin_fw_list_t; ++#pragma pack() ++ ++/* OpenCL kernel */ ++typedef struct bin_workload { ++ int cmdbuf_index; ++ int iobuf_input; ++ int iobuf_output[MAX_NUM_OUTPUT_BUFFERS]; ++} bin_workload_t; ++ ++typedef struct bin_buffer { ++ unsigned int handle; ++ intel_ipts_mapbuffer_t *buf; ++ bool no_unmap; /* only releasing vendor kernel unmaps output buffers */ ++} bin_buffer_t; ++ ++typedef struct bin_alloc_info { ++ bin_buffer_t *buffs; ++ int num_of_allocations; ++ int num_of_outputs; ++ ++ int num_of_buffers; ++} bin_alloc_info_t; ++ ++typedef struct bin_guc_wq_item { ++ unsigned int batch_offset; ++ unsigned int size; ++ char data[]; ++} bin_guc_wq_item_t; ++ ++typedef struct bin_kernel_info { ++ bin_workload_t *wl; ++ bin_alloc_info_t *alloc_info; ++ bin_guc_wq_item_t *guc_wq_item; ++ ipts_bin_bufid_patch_t bufid_patch; ++ ++ bool is_vendor; /* 1: vendor, 0: postprocessing */ ++} bin_kernel_info_t; ++ ++typedef struct bin_kernel_list { ++ intel_ipts_mapbuffer_t *bufid_buf; ++ int num_of_kernels; ++ bin_kernel_info_t kernels[]; ++} bin_kernel_list_t; ++ ++typedef struct bin_parse_info { ++ u8 *data; ++ int size; ++ int parsed; ++ ++ bin_fw_info_t *fw_info; ++ ++ /* only used by postprocessing */ ++ bin_kernel_info_t *vendor_kernel; ++ u32 interested_vendor_output; /* interested vendor output index */ ++} bin_parse_info_t; ++ ++#define BDW_SURFACE_BASE_ADDRESS 0x6101000e ++#define SURFACE_STATE_OFFSET_WORD 4 ++#define SBA_OFFSET_BYTES 16384 ++#define LASTSUBMITID_DEFAULT_VALUE -1 ++ ++#define IPTS_FW_PATH_FMT "intel/ipts/%s" ++#define IPTS_FW_CONFIG_FILE "intel/ipts/ipts_fw_config.bin" ++ ++MODULE_FIRMWARE(IPTS_FW_CONFIG_FILE); ++ ++#define IPTS_INPUT_ON ((u32)1 << IPTS_INPUT) ++#define IPTS_OUTPUT_ON ((u32)1 << IPTS_OUTPUT) ++#define IPTS_CONFIGURATION_ON ((u32)1 << IPTS_CONFIGURATION) ++#define IPTS_CALIBRATION_ON ((u32)1 << IPTS_CALIBRATION) ++#define IPTS_FEATURE_ON ((u32)1 << IPTS_FEATURE) ++ ++#define DATA_FILE_FLAG_SHARE 0x00000001 ++#define DATA_FILE_FLAG_ALLOC_CONTIGUOUS 0x00000002 ++ ++static int bin_read_fw(ipts_info_t *ipts, const char *fw_name, ++ u8* data, int size) ++{ ++ const struct firmware *fw = NULL; ++ char fw_path[MAX_IOCL_FILE_PATH_LEN]; ++ int ret = 0; ++ ++ snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_FW_PATH_FMT, fw_name); ++ ret = request_firmware(&fw, fw_path, &ipts->cldev->dev); ++ if (ret) { ++ ipts_err(ipts, "cannot read fw %s\n", fw_path); ++ return ret; ++ } ++ ++ if (fw->size > size) { ++ ipts_dbg(ipts, "too small buffer to contain fw data\n"); ++ ret = -EINVAL; ++ goto rel_return; ++ } ++ ++ memcpy(data, fw->data, fw->size); ++ ++rel_return: ++ release_firmware(fw); ++ ++ return ret; ++} ++ ++ ++static bin_data_file_info_t* bin_get_data_file_info(bin_fw_info_t* fw_info, ++ u32 io_buffer_type) ++{ ++ int i; ++ ++ for (i = 0; i < fw_info->num_of_data_files; i++) { ++ if (fw_info->data_file[i].io_buffer_type == io_buffer_type) ++ break; ++ } ++ ++ if (i == fw_info->num_of_data_files) ++ return NULL; ++ ++ return &fw_info->data_file[i]; ++} ++ ++static inline bool is_shared_data(const bin_data_file_info_t *data_file) ++{ ++ if (data_file) ++ return (!!(data_file->flags & DATA_FILE_FLAG_SHARE)); ++ ++ return false; ++} ++ ++static inline bool is_alloc_cont_data(const bin_data_file_info_t *data_file) ++{ ++ if (data_file) ++ return (!!(data_file->flags & DATA_FILE_FLAG_ALLOC_CONTIGUOUS)); ++ ++ return false; ++} ++ ++static inline bool is_parsing_vendor_kernel(const bin_parse_info_t *parse_info) ++{ ++ /* vendor_kernel == null while loading itself(vendor kernel) */ ++ return parse_info->vendor_kernel == NULL; ++} ++ ++static int bin_read_allocation_list(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ bin_alloc_info_t *alloc_info) ++{ ++ ipts_bin_alloc_list_t *alloc_list; ++ int alloc_idx, parallel_idx, num_of_parallels, buf_idx, num_of_buffers; ++ int parsed, size; ++ ++ parsed = parse_info->parsed; ++ size = parse_info->size; ++ ++ alloc_list = (ipts_bin_alloc_list_t *)&parse_info->data[parsed]; ++ ++ /* validation check */ ++ if (sizeof(alloc_list->num) > size - parsed) ++ return -EINVAL; ++ ++ /* read the number of aloocations */ ++ parsed += sizeof(alloc_list->num); ++ ++ /* validation check */ ++ if (sizeof(alloc_list->alloc[0]) * alloc_list->num > size - parsed) ++ return -EINVAL; ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ num_of_buffers = num_of_parallels * alloc_list->num + num_of_parallels; ++ ++ alloc_info->buffs = vmalloc(sizeof(bin_buffer_t) * num_of_buffers); ++ if (alloc_info->buffs == NULL) ++ return -ENOMEM; ++ ++ memset(alloc_info->buffs, 0, sizeof(bin_buffer_t) * num_of_buffers); ++ for (alloc_idx = 0; alloc_idx < alloc_list->num; alloc_idx++) { ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; ++ parallel_idx++) { ++ buf_idx = alloc_idx + (parallel_idx * alloc_list->num); ++ alloc_info->buffs[buf_idx].handle = ++ alloc_list->alloc[alloc_idx].handle; ++ ++ } ++ ++ parsed += sizeof(alloc_list->alloc[0]); ++ } ++ ++ parse_info->parsed = parsed; ++ alloc_info->num_of_allocations = alloc_list->num; ++ alloc_info->num_of_buffers = num_of_buffers; ++ ++ ipts_dbg(ipts, "number of allocations = %d, buffers = %d\n", ++ alloc_info->num_of_allocations, ++ alloc_info->num_of_buffers); ++ ++ return 0; ++} ++ ++static void patch_SBA(u32 *buf_addr, u64 gpu_addr, int size) ++{ ++ u64 *stateBase; ++ u64 SBA; ++ u32 inst; ++ int i; ++ ++ SBA = gpu_addr + SBA_OFFSET_BYTES; ++ ++ for (i = 0; i < size/4; i++) { ++ inst = buf_addr[i]; ++ if (inst == BDW_SURFACE_BASE_ADDRESS) { ++ stateBase = (u64*)&buf_addr[i + SURFACE_STATE_OFFSET_WORD]; ++ *stateBase |= SBA; ++ *stateBase |= 0x01; // enable ++ break; ++ } ++ } ++} ++ ++static int bin_read_cmd_buffer(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ bin_alloc_info_t *alloc_info, ++ bin_workload_t *wl) ++{ ++ ipts_bin_cmdbuf_t *cmd; ++ intel_ipts_mapbuffer_t *buf; ++ int cmdbuf_idx, size, parsed, parallel_idx, num_of_parallels; ++ ++ size = parse_info->size; ++ parsed = parse_info->parsed; ++ ++ cmd = (ipts_bin_cmdbuf_t *)&parse_info->data[parsed]; ++ ++ if (sizeof(cmd->size) > size - parsed) ++ return -EINVAL; ++ ++ parsed += sizeof(cmd->size); ++ if (cmd->size > size - parsed) ++ return -EINVAL; ++ ++ ipts_dbg(ipts, "cmd buf size = %d\n", cmd->size); ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ /* command buffers are located after the other allocations */ ++ cmdbuf_idx = num_of_parallels * alloc_info->num_of_allocations; ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { ++ buf = ipts_map_buffer(ipts, cmd->size, 0); ++ if (buf == NULL) ++ return -ENOMEM; ++ ++ ipts_dbg(ipts, "cmd_idx[%d] = %d, g:0x%p, c:0x%p\n", parallel_idx, ++ cmdbuf_idx, buf->gfx_addr, buf->cpu_addr); ++ ++ memcpy((void *)buf->cpu_addr, &(cmd->data[0]), cmd->size); ++ patch_SBA(buf->cpu_addr, (u64)buf->gfx_addr, cmd->size); ++ alloc_info->buffs[cmdbuf_idx].buf = buf; ++ wl[parallel_idx].cmdbuf_index = cmdbuf_idx; ++ ++ cmdbuf_idx++; ++ } ++ ++ parsed += cmd->size; ++ parse_info->parsed = parsed; ++ ++ return 0; ++} ++ ++static int bin_find_alloc(ipts_info_t *ipts, ++ bin_alloc_info_t *alloc_info, ++ u32 handle) ++{ ++ int i; ++ ++ for (i = 0; i < alloc_info->num_of_allocations; i++) { ++ if (alloc_info->buffs[i].handle == handle) ++ return i; ++ } ++ ++ return -1; ++} ++ ++static intel_ipts_mapbuffer_t* bin_get_vendor_kernel_output( ++ bin_parse_info_t *parse_info, ++ int parallel_idx) ++{ ++ bin_kernel_info_t *vendor = parse_info->vendor_kernel; ++ bin_alloc_info_t *alloc_info; ++ int buf_idx, vendor_output_idx; ++ ++ alloc_info = vendor->alloc_info; ++ vendor_output_idx = parse_info->interested_vendor_output; ++ ++ if (vendor_output_idx >= alloc_info->num_of_outputs) ++ return NULL; ++ ++ buf_idx = vendor->wl[parallel_idx].iobuf_output[vendor_output_idx]; ++ return alloc_info->buffs[buf_idx].buf; ++} ++ ++static int bin_read_res_list(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ bin_alloc_info_t *alloc_info, ++ bin_workload_t *wl) ++{ ++ ipts_bin_res_list_t *res_list; ++ ipts_bin_res_t *res; ++ intel_ipts_mapbuffer_t *buf; ++ bin_data_file_info_t *data_file; ++ u8 *bin_data; ++ int i, size, parsed, parallel_idx, num_of_parallels, output_idx = -1; ++ int buf_idx, num_of_alloc; ++ u32 buf_size, flags, io_buf_type; ++ bool initialize; ++ ++ parsed = parse_info->parsed; ++ size = parse_info->size; ++ bin_data = parse_info->data; ++ ++ res_list = (ipts_bin_res_list_t *)&parse_info->data[parsed]; ++ if (sizeof(res_list->num) > (size - parsed)) ++ return -EINVAL; ++ parsed += sizeof(res_list->num); ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ ++ ipts_dbg(ipts, "number of resources %u\n", res_list->num); ++ for (i = 0; i < res_list->num; i++) { ++ initialize = false; ++ io_buf_type = 0; ++ flags = 0; ++ ++ /* initial data */ ++ data_file = NULL; ++ ++ res = (ipts_bin_res_t *)(&(bin_data[parsed])); ++ if (sizeof(res[0]) > (size - parsed)) { ++ return -EINVAL; ++ } ++ ++ ipts_dbg(ipts, "Resource(%d):handle 0x%08x type %u init %u" ++ " size %u alsigned %u\n", ++ i, res->handle, res->type, res->initialize, ++ res->size, res->aligned_size); ++ parsed += sizeof(res[0]); ++ ++ if (res->initialize) { ++ if (res->size > (size - parsed)) { ++ return -EINVAL; ++ } ++ parsed += res->size; ++ } ++ ++ initialize = res->initialize; ++ if (initialize && res->size > sizeof(ipts_bin_io_header_t)) { ++ ipts_bin_io_header_t *io_hdr; ++ io_hdr = (ipts_bin_io_header_t *)(&res->data[0]); ++ if (strncmp(io_hdr->str, "INTELTOUCH", 10) == 0) { ++ data_file = bin_get_data_file_info( ++ parse_info->fw_info, ++ (u32)io_hdr->type); ++ switch (io_hdr->type) { ++ case IPTS_INPUT: ++ ipts_dbg(ipts, "input detected\n"); ++ io_buf_type = IPTS_INPUT_ON; ++ flags = IPTS_BUF_FLAG_CONTIGUOUS; ++ break; ++ case IPTS_OUTPUT: ++ ipts_dbg(ipts, "output detected\n"); ++ io_buf_type = IPTS_OUTPUT_ON; ++ output_idx++; ++ break; ++ default: ++ if ((u32)io_hdr->type > 31) { ++ ipts_err(ipts, ++ "invalid io buffer : %u\n", ++ (u32)io_hdr->type); ++ continue; ++ } ++ ++ if (is_alloc_cont_data(data_file)) ++ flags = IPTS_BUF_FLAG_CONTIGUOUS; ++ ++ io_buf_type = ((u32)1 << (u32)io_hdr->type); ++ ipts_dbg(ipts, "special io buffer %u\n", ++ io_hdr->type); ++ break; ++ } ++ ++ initialize = false; ++ } ++ } ++ ++ num_of_alloc = alloc_info->num_of_allocations; ++ buf_idx = bin_find_alloc(ipts, alloc_info, res->handle); ++ if (buf_idx == -1) { ++ ipts_dbg(ipts, "cannot find alloc info\n"); ++ return -EINVAL; ++ } ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; ++ parallel_idx++, buf_idx += num_of_alloc) { ++ if (!res->aligned_size) ++ continue; ++ ++ if (!(parallel_idx == 0 || ++ (io_buf_type && !is_shared_data(data_file)))) ++ continue; ++ ++ buf_size = res->aligned_size; ++ if (io_buf_type & IPTS_INPUT_ON) { ++ buf_size = max_t(u32, ++ ipts->device_info.frame_size, ++ buf_size); ++ wl[parallel_idx].iobuf_input = buf_idx; ++ } else if (io_buf_type & IPTS_OUTPUT_ON) { ++ wl[parallel_idx].iobuf_output[output_idx] = buf_idx; ++ ++ if (!is_parsing_vendor_kernel(parse_info) && ++ output_idx > 0) { ++ ipts_err(ipts, ++ "postproc with more than one inout" ++ " is not supported : %d\n", output_idx); ++ return -EINVAL; ++ } ++ } ++ ++ if (!is_parsing_vendor_kernel(parse_info) && ++ io_buf_type & IPTS_OUTPUT_ON) { ++ buf = bin_get_vendor_kernel_output( ++ parse_info, ++ parallel_idx); ++ alloc_info->buffs[buf_idx].no_unmap = true; ++ } else ++ buf = ipts_map_buffer(ipts, buf_size, flags); ++ ++ if (buf == NULL) { ++ ipts_dbg(ipts, "ipts_map_buffer failed\n"); ++ return -ENOMEM; ++ } ++ ++ if (initialize) { ++ memcpy((void *)buf->cpu_addr, &(res->data[0]), ++ res->size); ++ } else { ++ if (data_file && strlen(data_file->file_name)) { ++ bin_read_fw(ipts, data_file->file_name, ++ buf->cpu_addr, buf_size); ++ } else if (is_parsing_vendor_kernel(parse_info) || ++ !(io_buf_type & IPTS_OUTPUT_ON)) { ++ memset((void *)buf->cpu_addr, 0, res->size); ++ } ++ } ++ ++ alloc_info->buffs[buf_idx].buf = buf; ++ } ++ } ++ ++ alloc_info->num_of_outputs = output_idx + 1; ++ parse_info->parsed = parsed; ++ ++ return 0; ++} ++ ++static int bin_read_patch_list(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ bin_alloc_info_t *alloc_info, ++ bin_workload_t *wl) ++{ ++ ipts_bin_patch_list_t *patch_list; ++ ipts_bin_patch_t *patch; ++ intel_ipts_mapbuffer_t *cmd = NULL; ++ u8 *batch; ++ int parsed, size, i, parallel_idx, num_of_parallels, cmd_idx, buf_idx; ++ unsigned int gtt_offset; ++ ++ parsed = parse_info->parsed; ++ size = parse_info->size; ++ patch_list = (ipts_bin_patch_list_t *)&parse_info->data[parsed]; ++ ++ if (sizeof(patch_list->num) > (size - parsed)) { ++ return -EFAULT; ++ } ++ parsed += sizeof(patch_list->num); ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ patch = (ipts_bin_patch_t *)(&patch_list->patch[0]); ++ for (i = 0; i < patch_list->num; i++) { ++ if (sizeof(patch_list->patch[0]) > (size - parsed)) { ++ return -EFAULT; ++ } ++ ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; ++ parallel_idx++) { ++ cmd_idx = wl[parallel_idx].cmdbuf_index; ++ buf_idx = patch[i].index + parallel_idx * ++ alloc_info->num_of_allocations; ++ ++ if (alloc_info->buffs[buf_idx].buf == NULL) { ++ /* buffer shared */ ++ buf_idx = patch[i].index; ++ } ++ ++ cmd = alloc_info->buffs[cmd_idx].buf; ++ batch = (char *)(u64)cmd->cpu_addr; ++ ++ gtt_offset = 0; ++ if(alloc_info->buffs[buf_idx].buf != NULL) { ++ gtt_offset = (u32)(u64) ++ alloc_info->buffs[buf_idx].buf->gfx_addr; ++ } ++ gtt_offset += patch[i].alloc_offset; ++ ++ batch += patch[i].patch_offset; ++ *(u32*)batch = gtt_offset; ++ } ++ ++ parsed += sizeof(patch_list->patch[0]); ++ } ++ ++ parse_info->parsed = parsed; ++ ++ return 0; ++} ++ ++static int bin_read_guc_wq_item(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ bin_guc_wq_item_t **guc_wq_item) ++{ ++ ipts_bin_guc_wq_info_t *bin_guc_wq; ++ bin_guc_wq_item_t *item; ++ u8 *wi_data; ++ int size, parsed, hdr_size, wi_size; ++ int i, batch_offset; ++ ++ parsed = parse_info->parsed; ++ size = parse_info->size; ++ bin_guc_wq = (ipts_bin_guc_wq_info_t *)&parse_info->data[parsed]; ++ ++ wi_size = bin_guc_wq->size; ++ wi_data = bin_guc_wq->data; ++ batch_offset = bin_guc_wq->batch_offset; ++ ipts_dbg(ipts, "wi size = %d, bt offset = %d\n", wi_size, batch_offset); ++ for (i = 0; i < wi_size / sizeof(u32); i++) { ++ ipts_dbg(ipts, "wi[%d] = 0x%08x\n", i, *((u32*)wi_data + i)); ++ } ++ hdr_size = sizeof(bin_guc_wq->size) + sizeof(bin_guc_wq->batch_offset); ++ ++ if (hdr_size > (size - parsed)) { ++ return -EINVAL; ++ } ++ parsed += hdr_size; ++ ++ item = vmalloc(sizeof(bin_guc_wq_item_t) + wi_size); ++ if (item == NULL) ++ return -ENOMEM; ++ ++ item->size = wi_size; ++ item->batch_offset = batch_offset; ++ memcpy(item->data, wi_data, wi_size); ++ ++ *guc_wq_item = item; ++ ++ parsed += wi_size; ++ parse_info->parsed = parsed; ++ ++ return 0; ++} ++ ++static int bin_setup_guc_workqueue(ipts_info_t *ipts, ++ bin_kernel_list_t *kernel_list) ++{ ++ bin_alloc_info_t *alloc_info; ++ bin_workload_t *wl; ++ bin_kernel_info_t *kernel; ++ u8 *wq_start, *wq_addr, *wi_data; ++ bin_buffer_t *bin_buf; ++ int wq_size, wi_size, parallel_idx, cmd_idx, k_idx, iter_size; ++ int i, num_of_parallels, batch_offset, k_num, total_workload; ++ ++ wq_addr = (u8*)ipts->resource.wq_info.wq_addr; ++ wq_size = ipts->resource.wq_info.wq_size; ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ total_workload = ipts_get_wq_item_size(ipts); ++ k_num = kernel_list->num_of_kernels; ++ ++ iter_size = total_workload * num_of_parallels; ++ if (wq_size % iter_size) { ++ ipts_err(ipts, "wq item cannot fit into wq\n"); ++ return -EINVAL; ++ } ++ ++ wq_start = wq_addr; ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; ++ parallel_idx++) { ++ kernel = &kernel_list->kernels[0]; ++ for (k_idx = 0; k_idx < k_num; k_idx++, kernel++) { ++ wl = kernel->wl; ++ alloc_info = kernel->alloc_info; ++ ++ batch_offset = kernel->guc_wq_item->batch_offset; ++ wi_size = kernel->guc_wq_item->size; ++ wi_data = &kernel->guc_wq_item->data[0]; ++ ++ cmd_idx = wl[parallel_idx].cmdbuf_index; ++ bin_buf = &alloc_info->buffs[cmd_idx]; ++ ++ /* Patch the WQ Data with proper batch buffer offset */ ++ *(u32*)(wi_data + batch_offset) = ++ (u32)(unsigned long)(bin_buf->buf->gfx_addr); ++ ++ memcpy(wq_addr, wi_data, wi_size); ++ ++ wq_addr += wi_size; ++ } ++ } ++ ++ for (i = 0; i < (wq_size / iter_size) - 1; i++) { ++ memcpy(wq_addr, wq_start, iter_size); ++ wq_addr += iter_size; ++ } ++ ++ return 0; ++} ++ ++static int bin_read_bufid_patch(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ ipts_bin_bufid_patch_t *bufid_patch) ++{ ++ ipts_bin_bufid_patch_t *patch; ++ int size, parsed; ++ ++ parsed = parse_info->parsed; ++ size = parse_info->size; ++ patch = (ipts_bin_bufid_patch_t *)&parse_info->data[parsed]; ++ ++ if (sizeof(ipts_bin_bufid_patch_t) > (size - parsed)) { ++ ipts_dbg(ipts, "invalid bufid info\n"); ++ return -EINVAL; ++ } ++ parsed += sizeof(ipts_bin_bufid_patch_t); ++ ++ memcpy(bufid_patch, patch, sizeof(ipts_bin_bufid_patch_t)); ++ ++ parse_info->parsed = parsed; ++ ++ return 0; ++} ++ ++static int bin_setup_bufid_buffer(ipts_info_t *ipts, bin_kernel_list_t *kernel_list) ++{ ++ intel_ipts_mapbuffer_t *buf, *cmd_buf; ++ bin_kernel_info_t *last_kernel; ++ bin_alloc_info_t *alloc_info; ++ bin_workload_t *wl; ++ u8 *batch; ++ int parallel_idx, num_of_parallels, cmd_idx; ++ u32 mem_offset, imm_offset; ++ ++ buf = ipts_map_buffer(ipts, PAGE_SIZE, 0); ++ if (!buf) { ++ return -ENOMEM; ++ } ++ ++ last_kernel = &kernel_list->kernels[kernel_list->num_of_kernels - 1]; ++ ++ mem_offset = last_kernel->bufid_patch.mem_offset; ++ imm_offset = last_kernel->bufid_patch.imm_offset; ++ wl = last_kernel->wl; ++ alloc_info = last_kernel->alloc_info; ++ ++ /* Initialize the buffer with default value */ ++ *((u32*)buf->cpu_addr) = LASTSUBMITID_DEFAULT_VALUE; ++ ipts->current_buffer_index = LASTSUBMITID_DEFAULT_VALUE; ++ ipts->last_buffer_completed = LASTSUBMITID_DEFAULT_VALUE; ++ ipts->last_submitted_id = (int*)buf->cpu_addr; ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { ++ cmd_idx = wl[parallel_idx].cmdbuf_index; ++ cmd_buf = alloc_info->buffs[cmd_idx].buf; ++ batch = (u8*)(u64)cmd_buf->cpu_addr; ++ ++ *((u32*)(batch + mem_offset)) = (u32)(u64)(buf->gfx_addr); ++ *((u32*)(batch + imm_offset)) = parallel_idx; ++ } ++ ++ kernel_list->bufid_buf = buf; ++ ++ return 0; ++} ++ ++static void unmap_buffers(ipts_info_t *ipts, bin_alloc_info_t *alloc_info) ++{ ++ bin_buffer_t *buffs; ++ int i, num_of_buffers; ++ ++ num_of_buffers = alloc_info->num_of_buffers; ++ buffs = &alloc_info->buffs[0]; ++ ++ for (i = 0; i < num_of_buffers; i++) { ++ if (buffs[i].no_unmap != true && buffs[i].buf != NULL) ++ ipts_unmap_buffer(ipts, buffs[i].buf); ++ } ++} ++ ++static int load_kernel(ipts_info_t *ipts, bin_parse_info_t *parse_info, ++ bin_kernel_info_t *kernel) ++{ ++ ipts_bin_header_t *hdr; ++ bin_workload_t *wl; ++ bin_alloc_info_t *alloc_info; ++ bin_guc_wq_item_t *guc_wq_item = NULL; ++ ipts_bin_bufid_patch_t bufid_patch; ++ int num_of_parallels, ret; ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ ++ /* check header version and magic numbers */ ++ hdr = (ipts_bin_header_t *)parse_info->data; ++ if (hdr->version != IPTS_BIN_HEADER_VERSION || ++ strncmp(hdr->str, "IOCL", 4) != 0) { ++ ipts_err(ipts, "binary header is not correct version = %d, " ++ "string = %c%c%c%c\n", hdr->version, ++ hdr->str[0], hdr->str[1], ++ hdr->str[2], hdr->str[3] ); ++ return -EINVAL; ++ } ++ ++ parse_info->parsed = sizeof(ipts_bin_header_t); ++ wl = vmalloc(sizeof(bin_workload_t) * num_of_parallels); ++ if (wl == NULL) ++ return -ENOMEM; ++ memset(wl, 0, sizeof(bin_workload_t) * num_of_parallels); ++ ++ alloc_info = vmalloc(sizeof(bin_alloc_info_t)); ++ if (alloc_info == NULL) { ++ vfree(wl); ++ return -ENOMEM; ++ } ++ memset(alloc_info, 0, sizeof(bin_alloc_info_t)); ++ ++ ipts_dbg(ipts, "kernel setup(size : %d)\n", parse_info->size); ++ ++ ret = bin_read_allocation_list(ipts, parse_info, alloc_info); ++ if (ret) { ++ ipts_dbg(ipts, "error read_allocation_list\n"); ++ goto setup_error; ++ } ++ ++ ret = bin_read_cmd_buffer(ipts, parse_info, alloc_info, wl); ++ if (ret) { ++ ipts_dbg(ipts, "error read_cmd_buffer\n"); ++ goto setup_error; ++ } ++ ++ ret = bin_read_res_list(ipts, parse_info, alloc_info, wl); ++ if (ret) { ++ ipts_dbg(ipts, "error read_res_list\n"); ++ goto setup_error; ++ } ++ ++ ret = bin_read_patch_list(ipts, parse_info, alloc_info, wl); ++ if (ret) { ++ ipts_dbg(ipts, "error read_patch_list\n"); ++ goto setup_error; ++ } ++ ++ ret = bin_read_guc_wq_item(ipts, parse_info, &guc_wq_item); ++ if (ret) { ++ ipts_dbg(ipts, "error read_guc_workqueue\n"); ++ goto setup_error; ++ } ++ ++ memset(&bufid_patch, 0, sizeof(bufid_patch)); ++ ret = bin_read_bufid_patch(ipts, parse_info, &bufid_patch); ++ if (ret) { ++ ipts_dbg(ipts, "error read_bufid_patch\n"); ++ goto setup_error; ++ } ++ ++ kernel->wl = wl; ++ kernel->alloc_info = alloc_info; ++ kernel->is_vendor = is_parsing_vendor_kernel(parse_info); ++ kernel->guc_wq_item = guc_wq_item; ++ memcpy(&kernel->bufid_patch, &bufid_patch, sizeof(bufid_patch)); ++ ++ return 0; ++ ++setup_error: ++ vfree(guc_wq_item); ++ ++ unmap_buffers(ipts, alloc_info); ++ ++ vfree(alloc_info->buffs); ++ vfree(alloc_info); ++ vfree(wl); ++ ++ return ret; ++} ++ ++void bin_setup_input_output(ipts_info_t *ipts, bin_kernel_list_t *kernel_list) ++{ ++ bin_kernel_info_t *vendor_kernel; ++ bin_workload_t *wl; ++ intel_ipts_mapbuffer_t *buf; ++ bin_alloc_info_t *alloc_info; ++ int parallel_idx, num_of_parallels, i, buf_idx; ++ ++ vendor_kernel = &kernel_list->kernels[0]; ++ ++ wl = vendor_kernel->wl; ++ alloc_info = vendor_kernel->alloc_info; ++ ipts->resource.num_of_outputs = alloc_info->num_of_outputs; ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { ++ buf_idx = wl[parallel_idx].iobuf_input; ++ buf = alloc_info->buffs[buf_idx].buf; ++ ++ ipts_dbg(ipts, "in_buf[%d](%d) c:%p, p:%p, g:%p\n", ++ parallel_idx, buf_idx, (void*)buf->cpu_addr, ++ (void*)buf->phy_addr, (void*)buf->gfx_addr); ++ ++ ipts_set_input_buffer(ipts, parallel_idx, buf->cpu_addr, ++ buf->phy_addr); ++ ++ for (i = 0; i < alloc_info->num_of_outputs; i++) { ++ buf_idx = wl[parallel_idx].iobuf_output[i]; ++ buf = alloc_info->buffs[buf_idx].buf; ++ ++ ipts_dbg(ipts, "out_buf[%d][%d] c:%p, p:%p, g:%p\n", ++ parallel_idx, i, (void*)buf->cpu_addr, ++ (void*)buf->phy_addr, (void*)buf->gfx_addr); ++ ++ ipts_set_output_buffer(ipts, parallel_idx, i, ++ buf->cpu_addr, buf->phy_addr); ++ } ++ } ++} ++ ++static void unload_kernel(ipts_info_t *ipts, bin_kernel_info_t *kernel) ++{ ++ bin_alloc_info_t *alloc_info = kernel->alloc_info; ++ bin_guc_wq_item_t *guc_wq_item = kernel->guc_wq_item; ++ ++ if (guc_wq_item) { ++ vfree(guc_wq_item); ++ } ++ ++ if (alloc_info) { ++ unmap_buffers(ipts, alloc_info); ++ ++ vfree(alloc_info->buffs); ++ vfree(alloc_info); ++ } ++} ++ ++static int setup_kernel(ipts_info_t *ipts, bin_fw_list_t *fw_list) ++{ ++ bin_kernel_list_t *kernel_list = NULL; ++ bin_kernel_info_t *kernel = NULL; ++ const struct firmware *fw = NULL; ++ bin_workload_t *wl; ++ bin_fw_info_t *fw_info; ++ char *fw_name, *fw_data; ++ bin_parse_info_t parse_info; ++ int ret = 0, kernel_idx = 0, num_of_kernels = 0; ++ int vendor_output_idx, total_workload = 0; ++ char fw_path[MAX_IOCL_FILE_PATH_LEN]; ++ ++ num_of_kernels = fw_list->num_of_fws; ++ kernel_list = vmalloc(sizeof(*kernel) * num_of_kernels + sizeof(*kernel_list)); ++ if (kernel_list == NULL) ++ return -ENOMEM; ++ ++ memset(kernel_list, 0, sizeof(*kernel) * num_of_kernels + sizeof(*kernel_list)); ++ kernel_list->num_of_kernels = num_of_kernels; ++ kernel = &kernel_list->kernels[0]; ++ ++ fw_data = (char *)&fw_list->fw_info[0]; ++ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { ++ fw_info = (bin_fw_info_t *)fw_data; ++ fw_name = &fw_info->fw_name[0]; ++ vendor_output_idx = fw_info->vendor_output; ++ snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_FW_PATH_FMT, fw_name); ++ ret = request_firmware(&fw, (const char *)fw_path, &ipts->cldev->dev); ++ if (ret) { ++ ipts_err(ipts, "cannot read fw %s\n", fw_path); ++ goto error_exit; ++ } ++ ++ parse_info.data = (u8*)fw->data; ++ parse_info.size = fw->size; ++ parse_info.parsed = 0; ++ parse_info.fw_info = fw_info; ++ parse_info.vendor_kernel = (kernel_idx == 0) ? NULL : &kernel[0]; ++ parse_info.interested_vendor_output = vendor_output_idx; ++ ++ ret = load_kernel(ipts, &parse_info, &kernel[kernel_idx]); ++ if (ret) { ++ ipts_err(ipts, "do_setup_kernel error : %d\n", ret); ++ release_firmware(fw); ++ goto error_exit; ++ } ++ ++ release_firmware(fw); ++ ++ total_workload += kernel[kernel_idx].guc_wq_item->size; ++ ++ /* advance to the next kernel */ ++ fw_data += sizeof(bin_fw_info_t); ++ fw_data += sizeof(bin_data_file_info_t) * fw_info->num_of_data_files; ++ } ++ ++ ipts_set_wq_item_size(ipts, total_workload); ++ ++ ret = bin_setup_guc_workqueue(ipts, kernel_list); ++ if (ret) { ++ ipts_dbg(ipts, "error setup_guc_workqueue\n"); ++ goto error_exit; ++ } ++ ++ ret = bin_setup_bufid_buffer(ipts, kernel_list); ++ if (ret) { ++ ipts_dbg(ipts, "error setup_lastbubmit_buffer\n"); ++ goto error_exit; ++ } ++ ++ bin_setup_input_output(ipts, kernel_list); ++ ++ /* workload is not needed during run-time so free them */ ++ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { ++ wl = kernel[kernel_idx].wl; ++ vfree(wl); ++ } ++ ++ ipts->kernel_handle = (u64)kernel_list; ++ ++ return 0; ++ ++error_exit: ++ ++ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { ++ wl = kernel[kernel_idx].wl; ++ vfree(wl); ++ unload_kernel(ipts, &kernel[kernel_idx]); ++ } ++ ++ vfree(kernel_list); ++ ++ return ret; ++} ++ ++ ++static void release_kernel(ipts_info_t *ipts) ++{ ++ bin_kernel_list_t *kernel_list; ++ bin_kernel_info_t *kernel; ++ int k_idx, k_num; ++ ++ kernel_list = (bin_kernel_list_t *)ipts->kernel_handle; ++ k_num = kernel_list->num_of_kernels; ++ kernel = &kernel_list->kernels[0]; ++ ++ for (k_idx = 0; k_idx < k_num; k_idx++) { ++ unload_kernel(ipts, kernel); ++ kernel++; ++ } ++ ++ ipts_unmap_buffer(ipts, kernel_list->bufid_buf); ++ ++ vfree(kernel_list); ++ ipts->kernel_handle = 0; ++} ++ ++int ipts_init_kernels(ipts_info_t *ipts) ++{ ++ const struct firmware *config_fw = NULL; ++ const char *config_fw_path = IPTS_FW_CONFIG_FILE; ++ bin_fw_list_t *fw_list; ++ int ret; ++ ++ ret = ipts_open_gpu(ipts); ++ if (ret) { ++ ipts_err(ipts, "open gpu error : %d\n", ret); ++ return ret; ++ } ++ ++ ret = request_firmware(&config_fw, config_fw_path, &ipts->cldev->dev); ++ if (ret) { ++ ipts_err(ipts, "request firmware error : %d\n", ret); ++ goto close_gpu; ++ } ++ ++ fw_list = (bin_fw_list_t *)config_fw->data; ++ ret = setup_kernel(ipts, fw_list); ++ if (ret) { ++ ipts_err(ipts, "setup kernel error : %d\n", ret); ++ goto close_firmware; ++ } ++ ++ release_firmware(config_fw); ++ ++ return ret; ++ ++close_firmware: ++ release_firmware(config_fw); ++ ++close_gpu: ++ ipts_close_gpu(ipts); ++ ++ return ret; ++} ++ ++void ipts_release_kernels(ipts_info_t *ipts) ++{ ++ release_kernel(ipts); ++ ipts_close_gpu(ipts); ++} +diff --git a/drivers/misc/ipts/ipts-kernel.h b/drivers/misc/ipts/ipts-kernel.h +new file mode 100644 +index 000000000000..0e7f1393b807 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-kernel.h +@@ -0,0 +1,23 @@ ++/* ++ * ++ * Intel Precise Touch & Stylus Linux driver ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#ifndef _ITPS_GFX_H ++#define _ITPS_GFX_H ++ ++int ipts_init_kernels(ipts_info_t *ipts); ++void ipts_release_kernels(ipts_info_t *ipts); ++ ++#endif +diff --git a/drivers/misc/ipts/ipts-mei-msgs.h b/drivers/misc/ipts/ipts-mei-msgs.h +new file mode 100644 +index 000000000000..8ca146800a47 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-mei-msgs.h +@@ -0,0 +1,585 @@ ++/* ++ * Precise Touch HECI Message ++ * ++ * Copyright (c) 2013-2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#ifndef _IPTS_MEI_MSGS_H_ ++#define _IPTS_MEI_MSGS_H_ ++ ++#include "ipts-sensor-regs.h" ++ ++#pragma pack(1) ++ ++ ++// Initial protocol version ++#define TOUCH_HECI_CLIENT_PROTOCOL_VERSION 10 ++ ++// GUID that identifies the Touch HECI client. ++#define TOUCH_HECI_CLIENT_GUID \ ++ {0x3e8d0870, 0x271a, 0x4208, {0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04}} ++ ++ ++// define C_ASSERT macro to check structure size and fail compile for unexpected mismatch ++#ifndef C_ASSERT ++#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] ++#endif ++ ++ ++// General Type Defines for compatibility with HID driver and BIOS ++#ifndef BIT0 ++#define BIT0 1 ++#endif ++#ifndef BIT1 ++#define BIT1 2 ++#endif ++#ifndef BIT2 ++#define BIT2 4 ++#endif ++ ++ ++#define TOUCH_SENSOR_GET_DEVICE_INFO_CMD 0x00000001 ++#define TOUCH_SENSOR_GET_DEVICE_INFO_RSP 0x80000001 ++ ++ ++#define TOUCH_SENSOR_SET_MODE_CMD 0x00000002 ++#define TOUCH_SENSOR_SET_MODE_RSP 0x80000002 ++ ++ ++#define TOUCH_SENSOR_SET_MEM_WINDOW_CMD 0x00000003 ++#define TOUCH_SENSOR_SET_MEM_WINDOW_RSP 0x80000003 ++ ++ ++#define TOUCH_SENSOR_QUIESCE_IO_CMD 0x00000004 ++#define TOUCH_SENSOR_QUIESCE_IO_RSP 0x80000004 ++ ++ ++#define TOUCH_SENSOR_HID_READY_FOR_DATA_CMD 0x00000005 ++#define TOUCH_SENSOR_HID_READY_FOR_DATA_RSP 0x80000005 ++ ++ ++#define TOUCH_SENSOR_FEEDBACK_READY_CMD 0x00000006 ++#define TOUCH_SENSOR_FEEDBACK_READY_RSP 0x80000006 ++ ++ ++#define TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD 0x00000007 ++#define TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP 0x80000007 ++ ++ ++#define TOUCH_SENSOR_NOTIFY_DEV_READY_CMD 0x00000008 ++#define TOUCH_SENSOR_NOTIFY_DEV_READY_RSP 0x80000008 ++ ++ ++#define TOUCH_SENSOR_SET_POLICIES_CMD 0x00000009 ++#define TOUCH_SENSOR_SET_POLICIES_RSP 0x80000009 ++ ++ ++#define TOUCH_SENSOR_GET_POLICIES_CMD 0x0000000A ++#define TOUCH_SENSOR_GET_POLICIES_RSP 0x8000000A ++ ++ ++#define TOUCH_SENSOR_RESET_CMD 0x0000000B ++#define TOUCH_SENSOR_RESET_RSP 0x8000000B ++ ++ ++#define TOUCH_SENSOR_READ_ALL_REGS_CMD 0x0000000C ++#define TOUCH_SENSOR_READ_ALL_REGS_RSP 0x8000000C ++ ++ ++#define TOUCH_SENSOR_CMD_ERROR_RSP 0x8FFFFFFF // M2H: ME sends this message to indicate previous command was unrecognized/unsupported ++ ++ ++ ++//******************************************************************* ++// ++// Touch Sensor Status Codes ++// ++//******************************************************************* ++typedef enum touch_status ++{ ++ TOUCH_STATUS_SUCCESS = 0, // 0 Requested operation was successful ++ TOUCH_STATUS_INVALID_PARAMS, // 1 Invalid parameter(s) sent ++ TOUCH_STATUS_ACCESS_DENIED, // 2 Unable to validate address range ++ TOUCH_STATUS_CMD_SIZE_ERROR, // 3 HECI message incorrect size for specified command ++ TOUCH_STATUS_NOT_READY, // 4 Memory window not set or device is not armed for operation ++ TOUCH_STATUS_REQUEST_OUTSTANDING, // 5 There is already an outstanding message of the same type, must wait for response before sending another request of that type ++ TOUCH_STATUS_NO_SENSOR_FOUND, // 6 Sensor could not be found. Either no sensor is connected, the sensor has not yet initialized, or the system is improperly configured. ++ TOUCH_STATUS_OUT_OF_MEMORY, // 7 Not enough memory/storage for requested operation ++ TOUCH_STATUS_INTERNAL_ERROR, // 8 Unexpected error occurred ++ TOUCH_STATUS_SENSOR_DISABLED, // 9 Used in TOUCH_SENSOR_HID_READY_FOR_DATA_RSP to indicate sensor has been disabled or reset and must be reinitialized. ++ TOUCH_STATUS_COMPAT_CHECK_FAIL, // 10 Used to indicate compatibility revision check between sensor and ME failed, or protocol ver between ME/HID/Kernels failed. ++ TOUCH_STATUS_SENSOR_EXPECTED_RESET, // 11 Indicates sensor went through a reset initiated by ME ++ TOUCH_STATUS_SENSOR_UNEXPECTED_RESET, // 12 Indicates sensor went through an unexpected reset ++ TOUCH_STATUS_RESET_FAILED, // 13 Requested sensor reset failed to complete ++ TOUCH_STATUS_TIMEOUT, // 14 Operation timed out ++ TOUCH_STATUS_TEST_MODE_FAIL, // 15 Test mode pattern did not match expected values ++ TOUCH_STATUS_SENSOR_FAIL_FATAL, // 16 Indicates sensor reported fatal error during reset sequence. Further progress is not possible. ++ TOUCH_STATUS_SENSOR_FAIL_NONFATAL, // 17 Indicates sensor reported non-fatal error during reset sequence. HID/BIOS logs error and attempts to continue. ++ TOUCH_STATUS_INVALID_DEVICE_CAPS, // 18 Indicates sensor reported invalid capabilities, such as not supporting required minimum frequency or I/O mode. ++ TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS, // 19 Indicates that command cannot be complete until ongoing Quiesce I/O flow has completed. ++ TOUCH_STATUS_MAX // 20 Invalid value, never returned ++} touch_status_t; ++C_ASSERT(sizeof(touch_status_t) == 4); ++ ++ ++ ++//******************************************************************* ++// ++// Defines for message structures used for Host to ME communication ++// ++//******************************************************************* ++ ++ ++typedef enum touch_sensor_mode ++{ ++ TOUCH_SENSOR_MODE_HID = 0, // Set mode to HID mode ++ TOUCH_SENSOR_MODE_RAW_DATA, // Set mode to Raw Data mode ++ TOUCH_SENSOR_MODE_SENSOR_DEBUG = 4, // Used like TOUCH_SENSOR_MODE_HID but data coming from sensor is not necessarily a HID packet. ++ TOUCH_SENSOR_MODE_MAX // Invalid value ++} touch_sensor_mode_t; ++C_ASSERT(sizeof(touch_sensor_mode_t) == 4); ++ ++typedef struct touch_sensor_set_mode_cmd_data ++{ ++ touch_sensor_mode_t sensor_mode; // Indicate desired sensor mode ++ u32 Reserved[3]; // For future expansion ++} touch_sensor_set_mode_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_set_mode_cmd_data_t) == 16); ++ ++ ++#define TOUCH_SENSOR_MAX_DATA_BUFFERS 16 ++#define TOUCH_HID_2_ME_BUFFER_ID TOUCH_SENSOR_MAX_DATA_BUFFERS ++#define TOUCH_HID_2_ME_BUFFER_SIZE_MAX 1024 ++#define TOUCH_INVALID_BUFFER_ID 0xFF ++ ++typedef struct touch_sensor_set_mem_window_cmd_data ++{ ++ u32 touch_data_buffer_addr_lower[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Lower 32 bits of Touch Data Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FrameSize ++ u32 touch_data_buffer_addr_upper[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Upper 32 bits of Touch Data Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FrameSize ++ u32 tail_offset_addr_lower; // Lower 32 bits of Tail Offset physical address ++ u32 tail_offset_addr_upper; // Upper 32 bits of Tail Offset physical address, always 32 bit, increment by WorkQueueItemSize ++ u32 doorbell_cookie_addr_lower; // Lower 32 bits of Doorbell register physical address ++ u32 doorbell_cookie_addr_upper; // Upper 32 bits of Doorbell register physical address, always 32 bit, increment as integer, rollover to 1 ++ u32 feedback_buffer_addr_lower[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Lower 32 bits of Feedback Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FeedbackSize ++ u32 feedback_buffer_addr_upper[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Upper 32 bits of Feedback Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FeedbackSize ++ u32 hid2me_buffer_addr_lower; // Lower 32 bits of dedicated HID to ME communication buffer. Size is Hid2MeBufferSize. ++ u32 hid2me_buffer_addr_upper; // Upper 32 bits of dedicated HID to ME communication buffer. Size is Hid2MeBufferSize. ++ u32 hid2me_buffer_size; // Size in bytes of Hid2MeBuffer, can be no bigger than TOUCH_HID_2_ME_BUFFER_SIZE_MAX ++ u8 reserved1; // For future expansion ++ u8 work_queue_item_size; // Size in bytes of the GuC Work Queue Item pointed to by TailOffset ++ u16 work_queue_size; // Size in bytes of the entire GuC Work Queue ++ u32 reserved[8]; // For future expansion ++} touch_sensor_set_mem_window_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_set_mem_window_cmd_data_t) == 320); ++ ++ ++#define TOUCH_SENSOR_QUIESCE_FLAG_GUC_RESET BIT0 // indicates GuC got reset and ME must re-read GuC data such as TailOffset and Doorbell Cookie values ++ ++typedef struct touch_sensor_quiesce_io_cmd_data ++{ ++ u32 quiesce_flags; // Optionally set TOUCH_SENSOR_QUIESCE_FLAG_GUC_RESET ++ u32 reserved[2]; ++} touch_sensor_quiesce_io_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_quiesce_io_cmd_data_t) == 12); ++ ++ ++typedef struct touch_sensor_feedback_ready_cmd_data ++{ ++ u8 feedback_index; // Index value from 0 to TOUCH_HID_2_ME_BUFFER_ID used to indicate which Feedback Buffer to use. Using special value TOUCH_HID_2_ME_BUFFER_ID ++ // is an indication to ME to get feedback data from the Hid2Me buffer instead of one of the standard Feedback buffers. ++ u8 reserved1[3]; // For future expansion ++ u32 transaction_id; // Transaction ID that was originally passed to host in TOUCH_HID_PRIVATE_DATA. Used to track round trip of a given transaction for performance measurements. ++ u32 reserved2[2]; // For future expansion ++} touch_sensor_feedback_ready_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_feedback_ready_cmd_data_t) == 16); ++ ++ ++#define TOUCH_DEFAULT_DOZE_TIMER_SECONDS 30 ++ ++typedef enum touch_freq_override ++{ ++ TOUCH_FREQ_OVERRIDE_NONE, // Do not apply any override ++ TOUCH_FREQ_OVERRIDE_10MHZ, // Force frequency to 10MHz (not currently supported) ++ TOUCH_FREQ_OVERRIDE_17MHZ, // Force frequency to 17MHz ++ TOUCH_FREQ_OVERRIDE_30MHZ, // Force frequency to 30MHz ++ TOUCH_FREQ_OVERRIDE_50MHZ, // Force frequency to 50MHz (not currently supported) ++ TOUCH_FREQ_OVERRIDE_MAX // Invalid value ++} touch_freq_override_t; ++C_ASSERT(sizeof(touch_freq_override_t) == 4); ++ ++typedef enum touch_spi_io_mode_override ++{ ++ TOUCH_SPI_IO_MODE_OVERRIDE_NONE, // Do not apply any override ++ TOUCH_SPI_IO_MODE_OVERRIDE_SINGLE, // Force Single I/O ++ TOUCH_SPI_IO_MODE_OVERRIDE_DUAL, // Force Dual I/O ++ TOUCH_SPI_IO_MODE_OVERRIDE_QUAD, // Force Quad I/O ++ TOUCH_SPI_IO_MODE_OVERRIDE_MAX // Invalid value ++} touch_spi_io_mode_override_t; ++C_ASSERT(sizeof(touch_spi_io_mode_override_t) == 4); ++ ++// Debug Policy bits used by TOUCH_POLICY_DATA.DebugOverride ++#define TOUCH_DBG_POLICY_OVERRIDE_STARTUP_TIMER_DIS BIT0 // Disable sensor startup timer ++#define TOUCH_DBG_POLICY_OVERRIDE_SYNC_BYTE_DIS BIT1 // Disable Sync Byte check ++#define TOUCH_DBG_POLICY_OVERRIDE_ERR_RESET_DIS BIT2 // Disable error resets ++ ++typedef struct touch_policy_data ++{ ++ u32 reserved0; // For future expansion. ++ u32 doze_timer :16; // Value in seconds, after which ME will put the sensor into Doze power state if no activity occurs. Set ++ // to 0 to disable Doze mode (not recommended). Value will be set to TOUCH_DEFAULT_DOZE_TIMER_SECONDS by ++ // default. ++ touch_freq_override_t freq_override :3; // Override frequency requested by sensor ++ touch_spi_io_mode_override_t spi_io_override :3; // Override IO mode requested by sensor ++ u32 reserved1 :10; // For future expansion ++ u32 reserved2; // For future expansion ++ u32 debug_override; // Normally all bits will be zero. Bits will be defined as needed for enabling special debug features ++} touch_policy_data_t; ++C_ASSERT(sizeof(touch_policy_data_t) == 16); ++ ++typedef struct touch_sensor_set_policies_cmd_data ++{ ++ touch_policy_data_t policy_data; // Contains the desired policy to be set ++} touch_sensor_set_policies_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_set_policies_cmd_data_t) == 16); ++ ++ ++typedef enum touch_sensor_reset_type ++{ ++ TOUCH_SENSOR_RESET_TYPE_HARD, // Hardware Reset using dedicated GPIO pin ++ TOUCH_SENSOR_RESET_TYPE_SOFT, // Software Reset using command written over SPI interface ++ TOUCH_SENSOR_RESET_TYPE_MAX // Invalid value ++} touch_sensor_reset_type_t; ++C_ASSERT(sizeof(touch_sensor_reset_type_t) == 4); ++ ++typedef struct touch_sensor_reset_cmd_data ++{ ++ touch_sensor_reset_type_t reset_type; // Indicate desired reset type ++ u32 reserved; // For future expansion ++} touch_sensor_reset_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_reset_cmd_data_t) == 8); ++ ++ ++// ++// Host to ME message ++// ++typedef struct touch_sensor_msg_h2m ++{ ++ u32 command_code; ++ union ++ { ++ touch_sensor_set_mode_cmd_data_t set_mode_cmd_data; ++ touch_sensor_set_mem_window_cmd_data_t set_window_cmd_data; ++ touch_sensor_quiesce_io_cmd_data_t quiesce_io_cmd_data; ++ touch_sensor_feedback_ready_cmd_data_t feedback_ready_cmd_data; ++ touch_sensor_set_policies_cmd_data_t set_policies_cmd_data; ++ touch_sensor_reset_cmd_data_t reset_cmd_data; ++ } h2m_data; ++} touch_sensor_msg_h2m_t; ++C_ASSERT(sizeof(touch_sensor_msg_h2m_t) == 324); ++ ++ ++//******************************************************************* ++// ++// Defines for message structures used for ME to Host communication ++// ++//******************************************************************* ++ ++// I/O mode values used by TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. ++typedef enum touch_spi_io_mode ++{ ++ TOUCH_SPI_IO_MODE_SINGLE = 0, // Sensor set for Single I/O SPI ++ TOUCH_SPI_IO_MODE_DUAL, // Sensor set for Dual I/O SPI ++ TOUCH_SPI_IO_MODE_QUAD, // Sensor set for Quad I/O SPI ++ TOUCH_SPI_IO_MODE_MAX // Invalid value ++} touch_spi_io_mode_t; ++C_ASSERT(sizeof(touch_spi_io_mode_t) == 4); ++ ++// ++// TOUCH_SENSOR_GET_DEVICE_INFO_RSP code is sent in response to TOUCH_SENSOR_GET_DEVICE_INFO_CMD. This code will be followed ++// by TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor details are reported. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_NO_SENSOR_FOUND: Sensor has not yet been detected. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_DEVICE_CAPS: Indicates sensor does not support minimum required Frequency or I/O Mode. ME firmware will choose best possible option for the errant ++// field. Caller should attempt to continue. ++// TOUCH_STATUS_COMPAT_CHECK_FAIL: Indicates TouchIC/ME compatibility mismatch. Caller should attempt to continue. ++// ++typedef struct touch_sensor_get_device_info_rsp_data ++{ ++ u16 vendor_id; // Touch Sensor vendor ID ++ u16 device_id; // Touch Sensor device ID ++ u32 hw_rev; // Touch Sensor Hardware Revision ++ u32 fw_rev; // Touch Sensor Firmware Revision ++ u32 frame_size; // Max size of one frame returned by Touch IC in bytes. This data will be TOUCH_RAW_DATA_HDR followed ++ // by a payload. The payload can be raw data or a HID structure depending on mode. ++ u32 feedback_size; // Max size of one Feedback structure in bytes ++ touch_sensor_mode_t sensor_mode; // Current operating mode of the sensor ++ u32 max_touch_points :8; // Maximum number of simultaneous touch points that can be reported by sensor ++ touch_freq_t spi_frequency :8; // SPI bus Frequency supported by sensor and ME firmware ++ touch_spi_io_mode_t spi_io_mode :8; // SPI bus I/O Mode supported by sensor and ME firmware ++ u32 reserved0 :8; // For future expansion ++ u8 sensor_minor_eds_rev; // Minor version number of EDS spec supported by sensor (from Compat Rev ID Reg) ++ u8 sensor_major_eds_rev; // Major version number of EDS spec supported by sensor (from Compat Rev ID Reg) ++ u8 me_minor_eds_rev; // Minor version number of EDS spec supported by ME ++ u8 me_major_eds_rev; // Major version number of EDS spec supported by ME ++ u8 sensor_eds_intf_rev; // EDS Interface Revision Number supported by sensor (from Compat Rev ID Reg) ++ u8 me_eds_intf_rev; // EDS Interface Revision Number supported by ME ++ u8 kernel_compat_ver; // EU Kernel Compatibility Version (from Compat Rev ID Reg) ++ u8 reserved1; // For future expansion ++ u32 reserved2[2]; // For future expansion ++} touch_sensor_get_device_info_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_get_device_info_rsp_data_t) == 44); ++ ++ ++// ++// TOUCH_SENSOR_SET_MODE_RSP code is sent in response to TOUCH_SENSOR_SET_MODE_CMD. This code will be followed ++// by TOUCH_SENSOR_SET_MODE_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and mode was set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// ++typedef struct touch_sensor_set_mode_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_set_mode_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_set_mode_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_SET_MEM_WINDOW_RSP code is sent in response to TOUCH_SENSOR_SET_MEM_WINDOW_CMD. This code will be followed ++// by TOUCH_SENSOR_SET_MEM_WINDOW_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and memory window was set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// TOUCH_STATUS_ACCESS_DENIED: Unable to map host address ranges for DMA. ++// TOUCH_STATUS_OUT_OF_MEMORY: Unable to allocate enough space for needed buffers. ++// ++typedef struct touch_sensor_set_mem_window_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_set_mem_window_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_set_mem_window_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_QUIESCE_IO_RSP code is sent in response to TOUCH_SENSOR_QUIESCE_IO_CMD. This code will be followed ++// by TOUCH_SENSOR_QUIESCE_IO_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and touch flow has stopped. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. ++// TOUCH_STATIS_TIMEOUT: Indicates ME timed out waiting for Quiesce I/O flow to complete. ++// ++typedef struct touch_sensor_quiesce_io_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_quiesce_io_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_quiesce_io_rsp_data_t) == 12); ++ ++ ++// Reset Reason values used in TOUCH_SENSOR_HID_READY_FOR_DATA_RSP_DATA ++typedef enum touch_reset_reason ++{ ++ TOUCH_RESET_REASON_UNKNOWN = 0, // Reason for sensor reset is not known ++ TOUCH_RESET_REASON_FEEDBACK_REQUEST, // Reset was requested as part of TOUCH_SENSOR_FEEDBACK_READY_CMD ++ TOUCH_RESET_REASON_HECI_REQUEST, // Reset was requested via TOUCH_SENSOR_RESET_CMD ++ TOUCH_RESET_REASON_MAX ++} touch_reset_reason_t; ++C_ASSERT(sizeof(touch_reset_reason_t) == 4); ++ ++// ++// TOUCH_SENSOR_HID_READY_FOR_DATA_RSP code is sent in response to TOUCH_SENSOR_HID_READY_FOR_DATA_CMD. This code will be followed ++// by TOUCH_SENSOR_HID_READY_FOR_DATA_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and HID data was sent by DMA. This will only be sent in HID mode. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_REQUEST_OUTSTANDING: Previous request is still outstanding, ME FW cannot handle another request for the same command. ++// TOUCH_STATUS_NOT_READY: Indicates memory window has not yet been set by BIOS/HID. ++// TOUCH_STATUS_SENSOR_DISABLED: Indicates that ME to HID communication has been stopped either by TOUCH_SENSOR_QUIESCE_IO_CMD or TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD. ++// TOUCH_STATUS_SENSOR_UNEXPECTED_RESET: Sensor signaled a Reset Interrupt. ME did not expect this and has no info about why this occurred. ++// TOUCH_STATUS_SENSOR_EXPECTED_RESET: Sensor signaled a Reset Interrupt. ME either directly requested this reset, or it was expected as part of a defined flow in the EDS. ++// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. ++// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. ++// ++typedef struct touch_sensor_hid_ready_for_data_rsp_data ++{ ++ u32 data_size; // Size of the data the ME DMA'd into a RawDataBuffer. Valid only when Status == TOUCH_STATUS_SUCCESS ++ u8 touch_data_buffer_index; // Index to indicate which RawDataBuffer was used. Valid only when Status == TOUCH_STATUS_SUCCESS ++ u8 reset_reason; // If Status is TOUCH_STATUS_SENSOR_EXPECTED_RESET, ME will provide the cause. See TOUCH_RESET_REASON. ++ u8 reserved1[2]; // For future expansion ++ u32 reserved2[5]; // For future expansion ++} touch_sensor_hid_ready_for_data_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_hid_ready_for_data_rsp_data_t) == 28); ++ ++ ++// ++// TOUCH_SENSOR_FEEDBACK_READY_RSP code is sent in response to TOUCH_SENSOR_FEEDBACK_READY_CMD. This code will be followed ++// by TOUCH_SENSOR_FEEDBACK_READY_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and any feedback or commands were sent to sensor. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// TOUCH_STATUS_COMPAT_CHECK_FAIL Indicates ProtocolVer does not match ME supported version. (non-fatal error) ++// TOUCH_STATUS_INTERNAL_ERROR: Unexpected error occurred. This should not normally be seen. ++// TOUCH_STATUS_OUT_OF_MEMORY: Insufficient space to store Calibration Data ++// ++typedef struct touch_sensor_feedback_ready_rsp_data ++{ ++ u8 feedback_index; // Index value from 0 to TOUCH_SENSOR_MAX_DATA_BUFFERS used to indicate which Feedback Buffer to use ++ u8 reserved1[3]; // For future expansion ++ u32 reserved2[6]; // For future expansion ++} touch_sensor_feedback_ready_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_feedback_ready_rsp_data_t) == 28); ++ ++ ++// ++// TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP code is sent in response to TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD. This code will be followed ++// by TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and memory window was set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. ++// ++typedef struct touch_sensor_clear_mem_window_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_clear_mem_window_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_clear_mem_window_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_NOTIFY_DEV_READY_RSP code is sent in response to TOUCH_SENSOR_NOTIFY_DEV_READY_CMD. This code will be followed ++// by TOUCH_SENSOR_NOTIFY_DEV_READY_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor has been detected by ME FW. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. ++// TOUCH_STATUS_REQUEST_OUTSTANDING: Previous request is still outstanding, ME FW cannot handle another request for the same command. ++// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. ++// TOUCH_STATUS_SENSOR_FAIL_FATAL: Sensor indicated a fatal error, further operation is not possible. Error details can be found in ErrReg. ++// TOUCH_STATUS_SENSOR_FAIL_NONFATAL: Sensor indicated a non-fatal error. Error should be logged by caller and init flow can continue. Error details can be found in ErrReg. ++// ++typedef struct touch_sensor_notify_dev_ready_rsp_data ++{ ++ touch_err_reg_t err_reg; // Value of sensor Error Register, field is only valid for Status == TOUCH_STATUS_SENSOR_FAIL_FATAL or TOUCH_STATUS_SENSOR_FAIL_NONFATAL ++ u32 reserved[2]; // For future expansion ++} touch_sensor_notify_dev_ready_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_notify_dev_ready_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_SET_POLICIES_RSP code is sent in response to TOUCH_SENSOR_SET_POLICIES_CMD. This code will be followed ++// by TOUCH_SENSOR_SET_POLICIES_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// ++typedef struct touch_sensor_set_policies_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_set_policies_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_set_policies_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_GET_POLICIES_RSP code is sent in response to TOUCH_SENSOR_GET_POLICIES_CMD. This code will be followed ++// by TOUCH_SENSOR_GET_POLICIES_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// ++typedef struct touch_sensor_get_policies_rsp_data ++{ ++ touch_policy_data_t policy_data; // Contains the current policy ++} touch_sensor_get_policies_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_get_policies_rsp_data_t) == 16); ++ ++ ++// ++// TOUCH_SENSOR_RESET_RSP code is sent in response to TOUCH_SENSOR_RESET_CMD. This code will be followed ++// by TOUCH_SENSOR_RESET_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor reset was completed. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. ++// TOUCH_STATUS_RESET_FAILED: Sensor generated an invalid or unexpected interrupt. ++// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. ++// ++typedef struct touch_sensor_reset_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_reset_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_reset_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_READ_ALL_REGS_RSP code is sent in response to TOUCH_SENSOR_READ_ALL_REGS_CMD. This code will be followed ++// by TOUCH_SENSOR_READ_ALL_REGS_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// ++typedef struct touch_sensor_read_all_regs_rsp_data ++{ ++ touch_reg_block_t sensor_regs; // Returns first 64 bytes of register space used for normal touch operation. Does not include test mode register. ++ u32 reserved[4]; ++} touch_sensor_read_all_regs_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_read_all_regs_rsp_data_t) == 80); ++ ++// ++// ME to Host Message ++// ++typedef struct touch_sensor_msg_m2h ++{ ++ u32 command_code; ++ touch_status_t status; ++ union ++ { ++ touch_sensor_get_device_info_rsp_data_t device_info_rsp_data; ++ touch_sensor_set_mode_rsp_data_t set_mode_rsp_data; ++ touch_sensor_set_mem_window_rsp_data_t set_mem_window_rsp_data; ++ touch_sensor_quiesce_io_rsp_data_t quiesce_io_rsp_data; ++ touch_sensor_hid_ready_for_data_rsp_data_t hid_ready_for_data_rsp_data; ++ touch_sensor_feedback_ready_rsp_data_t feedback_ready_rsp_data; ++ touch_sensor_clear_mem_window_rsp_data_t clear_mem_window_rsp_data; ++ touch_sensor_notify_dev_ready_rsp_data_t notify_dev_ready_rsp_data; ++ touch_sensor_set_policies_rsp_data_t set_policies_rsp_data; ++ touch_sensor_get_policies_rsp_data_t get_policies_rsp_data; ++ touch_sensor_reset_rsp_data_t reset_rsp_data; ++ touch_sensor_read_all_regs_rsp_data_t read_all_regs_rsp_data; ++ } m2h_data; ++} touch_sensor_msg_m2h_t; ++C_ASSERT(sizeof(touch_sensor_msg_m2h_t) == 88); ++ ++ ++#define TOUCH_MSG_SIZE_MAX_BYTES (MAX(sizeof(touch_sensor_msg_m2h_t), sizeof(touch_sensor_msg_h2m_t))) ++ ++#pragma pack() ++ ++#endif // _IPTS_MEI_MSGS_H_ +diff --git a/drivers/misc/ipts/ipts-mei.c b/drivers/misc/ipts/ipts-mei.c +new file mode 100644 +index 000000000000..199e49cb8d70 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-mei.c +@@ -0,0 +1,282 @@ ++/* ++ * MEI client driver for Intel Precise Touch and Stylus ++ * ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ipts.h" ++#include "ipts-hid.h" ++#include "ipts-msg-handler.h" ++#include "ipts-mei-msgs.h" ++#include "ipts-binary-spec.h" ++#include "ipts-state.h" ++ ++#define IPTS_DRIVER_NAME "ipts" ++#define IPTS_MEI_UUID UUID_LE(0x3e8d0870, 0x271a, 0x4208, \ ++ 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04) ++ ++static struct mei_cl_device_id ipts_mei_cl_tbl[] = { ++ { "", IPTS_MEI_UUID, MEI_CL_VERSION_ANY}, ++ {} ++}; ++ ++static ssize_t sensor_mode_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ipts_info_t *ipts; ++ ipts = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", ipts->sensor_mode); ++} ++ ++//TODO: Verify the function implementation ++static ssize_t sensor_mode_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t count) ++{ ++ int ret; ++ long val; ++ ipts_info_t *ipts; ++ ++ ipts = dev_get_drvdata(dev); ++ ret = kstrtol(buf, 10, &val); ++ if (ret) ++ return ret; ++ ++ ipts_dbg(ipts, "try sensor mode = %ld\n", val); ++ ++ switch (val) { ++ case TOUCH_SENSOR_MODE_HID: ++ break; ++ case TOUCH_SENSOR_MODE_RAW_DATA: ++ break; ++ default: ++ ipts_err(ipts, "sensor mode %ld is not supported\n", val); ++ } ++ ++ return count; ++} ++ ++static ssize_t device_info_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ipts_info_t *ipts; ++ ++ ipts = dev_get_drvdata(dev); ++ return sprintf(buf, "vendor id = 0x%04hX\n" ++ "device id = 0x%04hX\n" ++ "HW rev = 0x%08X\n" ++ "firmware rev = 0x%08X\n", ++ ipts->device_info.vendor_id, ipts->device_info.device_id, ++ ipts->device_info.hw_rev, ipts->device_info.fw_rev); ++} ++ ++static DEVICE_ATTR_RW(sensor_mode); ++static DEVICE_ATTR_RO(device_info); ++ ++static struct attribute *ipts_attrs[] = { ++ &dev_attr_sensor_mode.attr, ++ &dev_attr_device_info.attr, ++ NULL ++}; ++ ++static const struct attribute_group ipts_grp = { ++ .attrs = ipts_attrs, ++}; ++ ++MODULE_DEVICE_TABLE(mei, ipts_mei_cl_tbl); ++ ++static void raw_data_work_func(struct work_struct *work) ++{ ++ ipts_info_t *ipts = container_of(work, ipts_info_t, raw_data_work); ++ ++ ipts_handle_processed_data(ipts); ++} ++ ++static void gfx_status_work_func(struct work_struct *work) ++{ ++ ipts_info_t *ipts = container_of(work, ipts_info_t, gfx_status_work); ++ ipts_state_t state; ++ int status = ipts->gfx_status; ++ ++ ipts_dbg(ipts, "notify gfx status : %d\n", status); ++ ++ state = ipts_get_state(ipts); ++ ++ if (state == IPTS_STA_RAW_DATA_STARTED || state == IPTS_STA_HID_STARTED) { ++ if (status == IPTS_NOTIFY_STA_BACKLIGHT_ON && ++ ipts->display_status == false) { ++ ipts_send_sensor_clear_mem_window_cmd(ipts); ++ ipts->display_status = true; ++ } else if (status == IPTS_NOTIFY_STA_BACKLIGHT_OFF && ++ ipts->display_status == true) { ++ ipts_send_sensor_quiesce_io_cmd(ipts); ++ ipts->display_status = false; ++ } ++ } ++} ++ ++/* event loop */ ++static int ipts_mei_cl_event_thread(void *data) ++{ ++ ipts_info_t *ipts = (ipts_info_t *)data; ++ struct mei_cl_device *cldev = ipts->cldev; ++ ssize_t msg_len; ++ touch_sensor_msg_m2h_t m2h_msg; ++ ++ while (!kthread_should_stop()) { ++ msg_len = mei_cldev_recv(cldev, (u8*)&m2h_msg, sizeof(m2h_msg)); ++ if (msg_len <= 0) { ++ ipts_err(ipts, "error in reading m2h msg\n"); ++ continue; ++ } ++ ++ if (ipts_handle_resp(ipts, &m2h_msg, msg_len) != 0) { ++ ipts_err(ipts, "error in handling resp msg\n"); ++ } ++ } ++ ++ ipts_dbg(ipts, "!! end event loop !!\n"); ++ ++ return 0; ++} ++ ++static void init_work_func(struct work_struct *work) ++{ ++ ipts_info_t *ipts = container_of(work, ipts_info_t, init_work); ++ ++ ipts->sensor_mode = TOUCH_SENSOR_MODE_RAW_DATA; ++ ipts->display_status = true; ++ ++ ipts_start(ipts); ++} ++ ++static int ipts_mei_cl_probe(struct mei_cl_device *cldev, ++ const struct mei_cl_device_id *id) ++{ ++ int ret = 0; ++ ipts_info_t *ipts = NULL; ++ ++ pr_info("probing Intel Precise Touch & Stylus\n"); ++ ++ // setup the DMA BIT mask, the system will choose the best possible ++ if (dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64)) == 0) { ++ pr_info("IPTS using DMA_BIT_MASK(64)\n"); ++ } else if (dma_coerce_mask_and_coherent(&cldev->dev, ++ DMA_BIT_MASK(32)) == 0) { ++ pr_info("IPTS using DMA_BIT_MASK(32)\n"); ++ } else { ++ pr_err("IPTS: No suitable DMA available\n"); ++ return -EFAULT; ++ } ++ ++ ret = mei_cldev_enable(cldev); ++ if (ret < 0) { ++ pr_err("cannot enable IPTS\n"); ++ return ret; ++ } ++ ++ ipts = devm_kzalloc(&cldev->dev, sizeof(ipts_info_t), GFP_KERNEL); ++ if (ipts == NULL) { ++ ret = -ENOMEM; ++ goto disable_mei; ++ } ++ ipts->cldev = cldev; ++ mei_cldev_set_drvdata(cldev, ipts); ++ ++ ipts->event_loop = kthread_run(ipts_mei_cl_event_thread, (void*)ipts, ++ "ipts_event_thread"); ++ ++ if(ipts_dbgfs_register(ipts, "ipts")) ++ pr_debug("cannot register debugfs for IPTS\n"); ++ ++ INIT_WORK(&ipts->init_work, init_work_func); ++ INIT_WORK(&ipts->raw_data_work, raw_data_work_func); ++ INIT_WORK(&ipts->gfx_status_work, gfx_status_work_func); ++ ++ ret = sysfs_create_group(&cldev->dev.kobj, &ipts_grp); ++ if (ret != 0) { ++ pr_debug("cannot create sysfs for IPTS\n"); ++ } ++ ++ schedule_work(&ipts->init_work); ++ ++ return 0; ++ ++disable_mei : ++ mei_cldev_disable(cldev); ++ ++ return ret; ++} ++ ++static int ipts_mei_cl_remove(struct mei_cl_device *cldev) ++{ ++ ipts_info_t *ipts = mei_cldev_get_drvdata(cldev); ++ ++ ipts_stop(ipts); ++ ++ sysfs_remove_group(&cldev->dev.kobj, &ipts_grp); ++ ipts_hid_release(ipts); ++ ipts_dbgfs_deregister(ipts); ++ mei_cldev_disable(cldev); ++ ++ kthread_stop(ipts->event_loop); ++ ++ pr_info("IPTS removed\n"); ++ ++ return 0; ++} ++ ++static struct mei_cl_driver ipts_mei_cl_driver = { ++ .id_table = ipts_mei_cl_tbl, ++ .name = IPTS_DRIVER_NAME, ++ .probe = ipts_mei_cl_probe, ++ .remove = ipts_mei_cl_remove, ++}; ++ ++static int ipts_mei_cl_init(void) ++{ ++ int ret; ++ ++ pr_info("IPTS %s() is called\n", __func__); ++ ++ ret = mei_cldev_driver_register(&ipts_mei_cl_driver); ++ if (ret) { ++ pr_err("unable to register IPTS mei client driver\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void __exit ipts_mei_cl_exit(void) ++{ ++ pr_info("IPTS %s() is called\n", __func__); ++ ++ mei_cldev_driver_unregister(&ipts_mei_cl_driver); ++} ++ ++module_init(ipts_mei_cl_init); ++module_exit(ipts_mei_cl_exit); ++ ++MODULE_DESCRIPTION ++ ("Intel(R) Management Engine Interface Client Driver for "\ ++ "Intel Precision Touch and Sylus"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/misc/ipts/ipts-msg-handler.c b/drivers/misc/ipts/ipts-msg-handler.c +new file mode 100644 +index 000000000000..8b214f975c03 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-msg-handler.c +@@ -0,0 +1,431 @@ ++#include ++ ++#include "ipts.h" ++#include "ipts-hid.h" ++#include "ipts-resource.h" ++#include "ipts-mei-msgs.h" ++ ++int ipts_handle_cmd(ipts_info_t *ipts, u32 cmd, void *data, int data_size) ++{ ++ int ret = 0; ++ touch_sensor_msg_h2m_t h2m_msg; ++ int len = 0; ++ ++ memset(&h2m_msg, 0, sizeof(h2m_msg)); ++ ++ h2m_msg.command_code = cmd; ++ len = sizeof(h2m_msg.command_code) + data_size; ++ if (data != NULL && data_size != 0) ++ memcpy(&h2m_msg.h2m_data, data, data_size); /* copy payload */ ++ ++ ret = mei_cldev_send(ipts->cldev, (u8*)&h2m_msg, len); ++ if (ret < 0) { ++ ipts_err(ipts, "mei_cldev_send() error 0x%X:%d\n", ++ cmd, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++int ipts_send_feedback(ipts_info_t *ipts, int buffer_idx, u32 transaction_id) ++{ ++ int ret; ++ int cmd_len; ++ touch_sensor_feedback_ready_cmd_data_t fb_ready_cmd; ++ ++ cmd_len = sizeof(touch_sensor_feedback_ready_cmd_data_t); ++ memset(&fb_ready_cmd, 0, cmd_len); ++ ++ fb_ready_cmd.feedback_index = buffer_idx; ++ fb_ready_cmd.transaction_id = transaction_id; ++ ++ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_FEEDBACK_READY_CMD, ++ &fb_ready_cmd, cmd_len); ++ ++ return ret; ++} ++ ++int ipts_send_sensor_quiesce_io_cmd(ipts_info_t *ipts) ++{ ++ int ret; ++ int cmd_len; ++ touch_sensor_quiesce_io_cmd_data_t quiesce_io_cmd; ++ ++ cmd_len = sizeof(touch_sensor_quiesce_io_cmd_data_t); ++ memset(&quiesce_io_cmd, 0, cmd_len); ++ ++ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_QUIESCE_IO_CMD, ++ &quiesce_io_cmd, cmd_len); ++ ++ return ret; ++} ++ ++int ipts_send_sensor_hid_ready_for_data_cmd(ipts_info_t *ipts) ++{ ++ return ipts_handle_cmd(ipts, TOUCH_SENSOR_HID_READY_FOR_DATA_CMD, NULL, 0); ++} ++ ++int ipts_send_sensor_clear_mem_window_cmd(ipts_info_t *ipts) ++{ ++ return ipts_handle_cmd(ipts, TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD, NULL, 0); ++} ++ ++static int check_validity(touch_sensor_msg_m2h_t *m2h_msg, u32 msg_len) ++{ ++ int ret = 0; ++ int valid_msg_len = sizeof(m2h_msg->command_code); ++ u32 cmd_code = m2h_msg->command_code; ++ ++ switch (cmd_code) { ++ case TOUCH_SENSOR_SET_MODE_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_set_mode_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_SET_MEM_WINDOW_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_set_mem_window_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_QUIESCE_IO_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_quiesce_io_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_HID_READY_FOR_DATA_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_hid_ready_for_data_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_FEEDBACK_READY_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_feedback_ready_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_clear_mem_window_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_NOTIFY_DEV_READY_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_notify_dev_ready_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_SET_POLICIES_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_set_policies_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_GET_POLICIES_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_get_policies_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_RESET_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_reset_rsp_data_t); ++ break; ++ } ++ ++ if (valid_msg_len != msg_len) { ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++int ipts_start(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ /* TODO : check if we need to do SET_POLICIES_CMD ++ we need to do this when protocol version doesn't match with reported one ++ how we keep vendor specific data is the first thing to solve */ ++ ++ ipts_set_state(ipts, IPTS_STA_INIT); ++ ipts->num_of_parallel_data_buffers = TOUCH_SENSOR_MAX_DATA_BUFFERS; ++ ++ ipts->sensor_mode = TOUCH_SENSOR_MODE_RAW_DATA; /* start with RAW_DATA */ ++ ++ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_NOTIFY_DEV_READY_CMD, NULL, 0); ++ ++ return ret; ++} ++ ++void ipts_stop(ipts_info_t *ipts) ++{ ++ ipts_state_t old_state; ++ ++ old_state = ipts_get_state(ipts); ++ ipts_set_state(ipts, IPTS_STA_STOPPING); ++ ++ if (old_state < IPTS_STA_RESOURCE_READY) ++ return; ++ ++ if (old_state == IPTS_STA_RAW_DATA_STARTED || ++ old_state == IPTS_STA_HID_STARTED) { ++ ipts_free_default_resource(ipts); ++ ipts_free_raw_data_resource(ipts); ++ ++ return; ++ } ++} ++ ++int ipts_restart(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ ++ ipts_dbg(ipts, "ipts restart\n"); ++ ++ ipts_stop(ipts); ++ ++ ipts->retry++; ++ if (ipts->retry == IPTS_MAX_RETRY && ++ ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) { ++ /* try with HID mode */ ++ ipts->sensor_mode = TOUCH_SENSOR_MODE_HID; ++ } else if (ipts->retry > IPTS_MAX_RETRY) { ++ return -EPERM; ++ } ++ ++ ipts_send_sensor_quiesce_io_cmd(ipts); ++ ipts->restart = true; ++ ++ return ret; ++} ++ ++int ipts_switch_sensor_mode(ipts_info_t *ipts, int new_sensor_mode) ++{ ++ int ret = 0; ++ ++ ipts->new_sensor_mode = new_sensor_mode; ++ ipts->switch_sensor_mode = true; ++ ret = ipts_send_sensor_quiesce_io_cmd(ipts); ++ ++ return ret; ++} ++ ++#define rsp_failed(ipts, cmd, status) ipts_err(ipts, \ ++ "0x%08x failed status = %d\n", cmd, status); ++ ++int ipts_handle_resp(ipts_info_t *ipts, touch_sensor_msg_m2h_t *m2h_msg, ++ u32 msg_len) ++{ ++ int ret = 0; ++ int rsp_status = 0; ++ int cmd_status = 0; ++ int cmd_len = 0; ++ u32 cmd; ++ ++ if (!check_validity(m2h_msg, msg_len)) { ++ ipts_err(ipts, "wrong rsp\n"); ++ return -EINVAL; ++ } ++ ++ rsp_status = m2h_msg->status; ++ cmd = m2h_msg->command_code; ++ ++ switch (cmd) { ++ case TOUCH_SENSOR_NOTIFY_DEV_READY_RSP: ++ if (rsp_status != 0 && ++ rsp_status != TOUCH_STATUS_SENSOR_FAIL_NONFATAL) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ cmd_status = ipts_handle_cmd(ipts, ++ TOUCH_SENSOR_GET_DEVICE_INFO_CMD, ++ NULL, 0); ++ break; ++ case TOUCH_SENSOR_GET_DEVICE_INFO_RSP: ++ if (rsp_status != 0 && ++ rsp_status != TOUCH_STATUS_COMPAT_CHECK_FAIL) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ memcpy(&ipts->device_info, ++ &m2h_msg->m2h_data.device_info_rsp_data, ++ sizeof(touch_sensor_get_device_info_rsp_data_t)); ++ ++ /* ++ TODO : support raw_request during HID init. ++ Although HID init happens here, technically most of ++ reports (for both direction) can be issued only ++ after SET_MEM_WINDOWS_CMD since they may require ++ ME or touch IC. If ipts vendor requires raw_request ++ during HID init, we need to consider to move HID init. ++ */ ++ if (ipts->hid_desc_ready == false) { ++ ret = ipts_hid_init(ipts); ++ if (ret) ++ break; ++ } ++ ++ cmd_status = ipts_send_sensor_clear_mem_window_cmd(ipts); ++ ++ break; ++ case TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP: ++ { ++ touch_sensor_set_mode_cmd_data_t sensor_mode_cmd; ++ ++ if (rsp_status != 0 && ++ rsp_status != TOUCH_STATUS_TIMEOUT) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ /* allocate default resource : common & hid only */ ++ if (!ipts_is_default_resource_ready(ipts)) { ++ ret = ipts_allocate_default_resource(ipts); ++ if (ret) ++ break; ++ } ++ ++ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA && ++ !ipts_is_raw_data_resource_ready(ipts)) { ++ ret = ipts_allocate_raw_data_resource(ipts); ++ if (ret) { ++ ipts_free_default_resource(ipts); ++ break; ++ } ++ } ++ ++ ipts_set_state(ipts, IPTS_STA_RESOURCE_READY); ++ ++ cmd_len = sizeof(touch_sensor_set_mode_cmd_data_t); ++ memset(&sensor_mode_cmd, 0, cmd_len); ++ sensor_mode_cmd.sensor_mode = ipts->sensor_mode; ++ cmd_status = ipts_handle_cmd(ipts, ++ TOUCH_SENSOR_SET_MODE_CMD, ++ &sensor_mode_cmd, cmd_len); ++ break; ++ } ++ case TOUCH_SENSOR_SET_MODE_RSP: ++ { ++ touch_sensor_set_mem_window_cmd_data_t smw_cmd; ++ ++ if (rsp_status != 0) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ cmd_len = sizeof(touch_sensor_set_mem_window_cmd_data_t); ++ memset(&smw_cmd, 0, cmd_len); ++ ipts_get_set_mem_window_cmd_data(ipts, &smw_cmd); ++ cmd_status = ipts_handle_cmd(ipts, ++ TOUCH_SENSOR_SET_MEM_WINDOW_CMD, ++ &smw_cmd, cmd_len); ++ break; ++ } ++ case TOUCH_SENSOR_SET_MEM_WINDOW_RSP: ++ if (rsp_status != 0) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ cmd_status = ipts_send_sensor_hid_ready_for_data_cmd(ipts); ++ if (cmd_status) ++ break; ++ ++ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) { ++ ipts_set_state(ipts, IPTS_STA_HID_STARTED); ++ } else if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) { ++ ipts_set_state(ipts, IPTS_STA_RAW_DATA_STARTED); ++ } ++ ++ ipts_err(ipts, "touch enabled %d\n", ipts_get_state(ipts)); ++ ++ break; ++ case TOUCH_SENSOR_HID_READY_FOR_DATA_RSP: ++ { ++ touch_sensor_hid_ready_for_data_rsp_data_t *hid_data; ++ ipts_state_t state; ++ ++ if (rsp_status != 0 && ++ rsp_status != TOUCH_STATUS_SENSOR_DISABLED) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ state = ipts_get_state(ipts); ++ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID && ++ state == IPTS_STA_HID_STARTED) { ++ ++ hid_data = &m2h_msg->m2h_data.hid_ready_for_data_rsp_data; ++ ++ /* HID mode only uses buffer 0 */ ++ if (hid_data->touch_data_buffer_index != 0) ++ break; ++ ++ /* handle hid data */ ++ ipts_handle_hid_data(ipts, hid_data); ++ } ++ ++ break; ++ } ++ case TOUCH_SENSOR_FEEDBACK_READY_RSP: ++ if (rsp_status != 0 && ++ rsp_status != TOUCH_STATUS_COMPAT_CHECK_FAIL) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ if (m2h_msg->m2h_data.feedback_ready_rsp_data. ++ feedback_index == TOUCH_HID_2_ME_BUFFER_ID) ++ break; ++ ++ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) { ++ cmd_status = ipts_handle_cmd(ipts, ++ TOUCH_SENSOR_HID_READY_FOR_DATA_CMD, ++ NULL, 0); ++ } ++ ++ /* reset retry since we are getting touch data */ ++ ipts->retry = 0; ++ ++ break; ++ case TOUCH_SENSOR_QUIESCE_IO_RSP: ++ { ++ ipts_state_t state; ++ ++ if (rsp_status != 0) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ state = ipts_get_state(ipts); ++ if (state == IPTS_STA_STOPPING && ipts->restart) { ++ ipts_dbg(ipts, "restart\n"); ++ ipts_start(ipts); ++ ipts->restart = 0; ++ break; ++ } ++ ++ /* support sysfs debug node for switch sensor mode */ ++ if (ipts->switch_sensor_mode) { ++ ipts_set_state(ipts, IPTS_STA_INIT); ++ ipts->sensor_mode = ipts->new_sensor_mode; ++ ipts->switch_sensor_mode = false; ++ ++ ipts_send_sensor_clear_mem_window_cmd(ipts); ++ } ++ ++ break; ++ } ++ } ++ ++ /* handle error in rsp_status */ ++ if (rsp_status != 0) { ++ switch (rsp_status) { ++ case TOUCH_STATUS_SENSOR_EXPECTED_RESET: ++ case TOUCH_STATUS_SENSOR_UNEXPECTED_RESET: ++ ipts_dbg(ipts, "sensor reset %d\n", rsp_status); ++ ipts_restart(ipts); ++ break; ++ default: ++ ipts_dbg(ipts, "cmd : 0x%08x, status %d\n", ++ cmd, ++ rsp_status); ++ break; ++ } ++ } ++ ++ if (cmd_status) { ++ ipts_restart(ipts); ++ } ++ ++ return ret; ++} +diff --git a/drivers/misc/ipts/ipts-msg-handler.h b/drivers/misc/ipts/ipts-msg-handler.h +new file mode 100644 +index 000000000000..15038814dfec +--- /dev/null ++++ b/drivers/misc/ipts/ipts-msg-handler.h +@@ -0,0 +1,32 @@ ++/* ++ * ++ * Intel Precise Touch & Stylus ME message handler ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#ifndef _IPTS_MSG_HANDLER_H ++#define _IPTS_MSG_HANDLER_H ++ ++int ipts_handle_cmd(ipts_info_t *ipts, u32 cmd, void *data, int data_size); ++int ipts_start(ipts_info_t *ipts); ++void ipts_stop(ipts_info_t *ipts); ++int ipts_switch_sensor_mode(ipts_info_t *ipts, int new_sensor_mode); ++int ipts_handle_resp(ipts_info_t *ipts, touch_sensor_msg_m2h_t *m2h_msg, ++ u32 msg_len); ++int ipts_handle_processed_data(ipts_info_t *ipts); ++int ipts_send_feedback(ipts_info_t *ipts, int buffer_idx, u32 transaction_id); ++int ipts_send_sensor_quiesce_io_cmd(ipts_info_t *ipts); ++int ipts_send_sensor_hid_ready_for_data_cmd(ipts_info_t *ipts); ++int ipts_send_sensor_clear_mem_window_cmd(ipts_info_t *ipts); ++ ++#endif /* _IPTS_MSG_HANDLER_H */ +diff --git a/drivers/misc/ipts/ipts-resource.c b/drivers/misc/ipts/ipts-resource.c +new file mode 100644 +index 000000000000..47607ef7c461 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-resource.c +@@ -0,0 +1,277 @@ ++#include ++ ++#include "ipts.h" ++#include "ipts-mei-msgs.h" ++#include "ipts-kernel.h" ++ ++static void free_common_resource(ipts_info_t *ipts) ++{ ++ char *addr; ++ ipts_buffer_info_t *feedback_buffer; ++ dma_addr_t dma_addr; ++ u32 buffer_size; ++ int i, num_of_parallels; ++ ++ if (ipts->resource.me2hid_buffer) { ++ devm_kfree(&ipts->cldev->dev, ipts->resource.me2hid_buffer); ++ ipts->resource.me2hid_buffer = 0; ++ } ++ ++ addr = ipts->resource.hid2me_buffer.addr; ++ dma_addr = ipts->resource.hid2me_buffer.dma_addr; ++ buffer_size = ipts->resource.hid2me_buffer_size; ++ ++ if (ipts->resource.hid2me_buffer.addr) { ++ dmam_free_coherent(&ipts->cldev->dev, buffer_size, addr, dma_addr); ++ ipts->resource.hid2me_buffer.addr = 0; ++ ipts->resource.hid2me_buffer.dma_addr = 0; ++ ipts->resource.hid2me_buffer_size = 0; ++ } ++ ++ feedback_buffer = ipts->resource.feedback_buffer; ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ for (i = 0; i < num_of_parallels; i++) { ++ if (feedback_buffer[i].addr) { ++ dmam_free_coherent(&ipts->cldev->dev, ++ ipts->device_info.feedback_size, ++ feedback_buffer[i].addr, ++ feedback_buffer[i].dma_addr); ++ feedback_buffer[i].addr = 0; ++ feedback_buffer[i].dma_addr = 0; ++ } ++ } ++} ++ ++static int allocate_common_resource(ipts_info_t *ipts) ++{ ++ char *addr, *me2hid_addr; ++ ipts_buffer_info_t *feedback_buffer; ++ dma_addr_t dma_addr; ++ int i, ret = 0, num_of_parallels; ++ u32 buffer_size; ++ ++ buffer_size = ipts->device_info.feedback_size; ++ ++ addr = dmam_alloc_coherent(&ipts->cldev->dev, ++ buffer_size, ++ &dma_addr, ++ GFP_ATOMIC|__GFP_ZERO); ++ if (addr == NULL) ++ return -ENOMEM; ++ ++ me2hid_addr = devm_kzalloc(&ipts->cldev->dev, buffer_size, GFP_KERNEL); ++ if (me2hid_addr == NULL) { ++ ret = -ENOMEM; ++ goto release_resource; ++ } ++ ++ ipts->resource.hid2me_buffer.addr = addr; ++ ipts->resource.hid2me_buffer.dma_addr = dma_addr; ++ ipts->resource.hid2me_buffer_size = buffer_size; ++ ipts->resource.me2hid_buffer = me2hid_addr; ++ ++ feedback_buffer = ipts->resource.feedback_buffer; ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ for (i = 0; i < num_of_parallels; i++) { ++ feedback_buffer[i].addr = dmam_alloc_coherent(&ipts->cldev->dev, ++ ipts->device_info.feedback_size, ++ &feedback_buffer[i].dma_addr, ++ GFP_ATOMIC|__GFP_ZERO); ++ ++ if (feedback_buffer[i].addr == NULL) { ++ ret = -ENOMEM; ++ goto release_resource; ++ } ++ } ++ ++ return 0; ++ ++release_resource: ++ free_common_resource(ipts); ++ ++ return ret; ++} ++ ++void ipts_free_raw_data_resource(ipts_info_t *ipts) ++{ ++ if (ipts_is_raw_data_resource_ready(ipts)) { ++ ipts->resource.raw_data_resource_ready = false; ++ ++ ipts_release_kernels(ipts); ++ } ++} ++ ++static int allocate_hid_resource(ipts_info_t *ipts) ++{ ++ ipts_buffer_info_t *buffer_hid; ++ ++ /* hid mode uses only one touch data buffer */ ++ buffer_hid = &ipts->resource.touch_data_buffer_hid; ++ buffer_hid->addr = dmam_alloc_coherent(&ipts->cldev->dev, ++ ipts->device_info.frame_size, ++ &buffer_hid->dma_addr, ++ GFP_ATOMIC|__GFP_ZERO); ++ if (buffer_hid->addr == NULL) { ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void free_hid_resource(ipts_info_t *ipts) ++{ ++ ipts_buffer_info_t *buffer_hid; ++ ++ buffer_hid = &ipts->resource.touch_data_buffer_hid; ++ if (buffer_hid->addr) { ++ dmam_free_coherent(&ipts->cldev->dev, ++ ipts->device_info.frame_size, ++ buffer_hid->addr, ++ buffer_hid->dma_addr); ++ buffer_hid->addr = 0; ++ buffer_hid->dma_addr = 0; ++ } ++} ++ ++int ipts_allocate_default_resource(ipts_info_t *ipts) ++{ ++ int ret; ++ ++ ret = allocate_common_resource(ipts); ++ if (ret) { ++ ipts_dbg(ipts, "cannot allocate common resource\n"); ++ return ret; ++ } ++ ++ ret = allocate_hid_resource(ipts); ++ if (ret) { ++ ipts_dbg(ipts, "cannot allocate hid resource\n"); ++ free_common_resource(ipts); ++ return ret; ++ } ++ ++ ipts->resource.default_resource_ready = true; ++ ++ return 0; ++} ++ ++void ipts_free_default_resource(ipts_info_t *ipts) ++{ ++ if (ipts_is_default_resource_ready(ipts)) { ++ ipts->resource.default_resource_ready = false; ++ ++ free_hid_resource(ipts); ++ free_common_resource(ipts); ++ } ++} ++ ++int ipts_allocate_raw_data_resource(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ ++ ret = ipts_init_kernels(ipts); ++ if (ret) { ++ return ret; ++ } ++ ++ ipts->resource.raw_data_resource_ready = true; ++ ++ return 0; ++} ++ ++static void get_hid_only_smw_cmd_data(ipts_info_t *ipts, ++ touch_sensor_set_mem_window_cmd_data_t *data, ++ ipts_resource_t *resrc) ++{ ++ ipts_buffer_info_t *touch_buf; ++ ipts_buffer_info_t *feedback_buf; ++ ++ touch_buf = &resrc->touch_data_buffer_hid; ++ feedback_buf = &resrc->feedback_buffer[0]; ++ ++ data->touch_data_buffer_addr_lower[0] = ++ lower_32_bits(touch_buf->dma_addr); ++ data->touch_data_buffer_addr_upper[0] = ++ upper_32_bits(touch_buf->dma_addr); ++ data->feedback_buffer_addr_lower[0] = ++ lower_32_bits(feedback_buf->dma_addr); ++ data->feedback_buffer_addr_upper[0] = ++ upper_32_bits(feedback_buf->dma_addr); ++} ++ ++static void get_raw_data_only_smw_cmd_data(ipts_info_t *ipts, ++ touch_sensor_set_mem_window_cmd_data_t *data, ++ ipts_resource_t *resrc) ++{ ++ u64 wq_tail_phy_addr; ++ u64 cookie_phy_addr; ++ ipts_buffer_info_t *touch_buf; ++ ipts_buffer_info_t *feedback_buf; ++ int i, num_of_parallels; ++ ++ touch_buf = resrc->touch_data_buffer_raw; ++ feedback_buf = resrc->feedback_buffer; ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ for (i = 0; i < num_of_parallels; i++) { ++ data->touch_data_buffer_addr_lower[i] = ++ lower_32_bits(touch_buf[i].dma_addr); ++ data->touch_data_buffer_addr_upper[i] = ++ upper_32_bits(touch_buf[i].dma_addr); ++ data->feedback_buffer_addr_lower[i] = ++ lower_32_bits(feedback_buf[i].dma_addr); ++ data->feedback_buffer_addr_upper[i] = ++ upper_32_bits(feedback_buf[i].dma_addr); ++ } ++ ++ wq_tail_phy_addr = resrc->wq_info.wq_tail_phy_addr; ++ data->tail_offset_addr_lower = lower_32_bits(wq_tail_phy_addr); ++ data->tail_offset_addr_upper = upper_32_bits(wq_tail_phy_addr); ++ ++ cookie_phy_addr = resrc->wq_info.db_phy_addr + ++ resrc->wq_info.db_cookie_offset; ++ data->doorbell_cookie_addr_lower = lower_32_bits(cookie_phy_addr); ++ data->doorbell_cookie_addr_upper = upper_32_bits(cookie_phy_addr); ++ data->work_queue_size = resrc->wq_info.wq_size; ++ ++ data->work_queue_item_size = resrc->wq_item_size; ++} ++ ++void ipts_get_set_mem_window_cmd_data(ipts_info_t *ipts, ++ touch_sensor_set_mem_window_cmd_data_t *data) ++{ ++ ipts_resource_t *resrc = &ipts->resource; ++ ++ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) ++ get_raw_data_only_smw_cmd_data(ipts, data, resrc); ++ else if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) ++ get_hid_only_smw_cmd_data(ipts, data, resrc); ++ ++ /* hid2me is common for "raw data" and "hid" */ ++ data->hid2me_buffer_addr_lower = ++ lower_32_bits(resrc->hid2me_buffer.dma_addr); ++ data->hid2me_buffer_addr_upper = ++ upper_32_bits(resrc->hid2me_buffer.dma_addr); ++ data->hid2me_buffer_size = resrc->hid2me_buffer_size; ++} ++ ++void ipts_set_input_buffer(ipts_info_t *ipts, int parallel_idx, ++ u8* cpu_addr, u64 dma_addr) ++{ ++ ipts_buffer_info_t *touch_buf; ++ ++ touch_buf = ipts->resource.touch_data_buffer_raw; ++ touch_buf[parallel_idx].dma_addr = dma_addr; ++ touch_buf[parallel_idx].addr = cpu_addr; ++} ++ ++void ipts_set_output_buffer(ipts_info_t *ipts, int parallel_idx, int output_idx, ++ u8* cpu_addr, u64 dma_addr) ++{ ++ ipts_buffer_info_t *output_buf; ++ ++ output_buf = &ipts->resource.raw_data_mode_output_buffer[parallel_idx][output_idx]; ++ ++ output_buf->dma_addr = dma_addr; ++ output_buf->addr = cpu_addr; ++} +diff --git a/drivers/misc/ipts/ipts-resource.h b/drivers/misc/ipts/ipts-resource.h +new file mode 100644 +index 000000000000..7d66ac72b475 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-resource.h +@@ -0,0 +1,30 @@ ++/* ++ * Intel Precise Touch & Stylus state codes ++ * ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#ifndef _IPTS_RESOURCE_H_ ++#define _IPTS_RESOURCE_H_ ++ ++int ipts_allocate_default_resource(ipts_info_t *ipts); ++void ipts_free_default_resource(ipts_info_t *ipts); ++int ipts_allocate_raw_data_resource(ipts_info_t *ipts); ++void ipts_free_raw_data_resource(ipts_info_t *ipts); ++void ipts_get_set_mem_window_cmd_data(ipts_info_t *ipts, ++ touch_sensor_set_mem_window_cmd_data_t *data); ++void ipts_set_input_buffer(ipts_info_t *ipts, int parallel_idx, ++ u8* cpu_addr, u64 dma_addr); ++void ipts_set_output_buffer(ipts_info_t *ipts, int parallel_idx, int output_idx, ++ u8* cpu_addr, u64 dma_addr); ++ ++#endif // _IPTS_RESOURCE_H_ +diff --git a/drivers/misc/ipts/ipts-sensor-regs.h b/drivers/misc/ipts/ipts-sensor-regs.h +new file mode 100644 +index 000000000000..96812b0eb980 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-sensor-regs.h +@@ -0,0 +1,700 @@ ++/* ++ * Touch Sensor Register definition ++ * ++ * Copyright (c) 2013-2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++ ++#ifndef _TOUCH_SENSOR_REGS_H ++#define _TOUCH_SENSOR_REGS_H ++ ++#pragma pack(1) ++ ++// define C_ASSERT macro to check structure size and fail compile for unexpected mismatch ++#ifndef C_ASSERT ++#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] ++#endif ++ ++// ++// Compatibility versions for this header file ++// ++#define TOUCH_EDS_REV_MINOR 0 ++#define TOUCH_EDS_REV_MAJOR 1 ++#define TOUCH_EDS_INTF_REV 1 ++#define TOUCH_PROTOCOL_VER 0 ++ ++ ++// ++// Offset 00h: TOUCH_STS: Status Register ++// This register is read by the SPI Controller immediately following an interrupt. ++// ++#define TOUCH_STS_REG_OFFSET 0x00 ++ ++typedef enum touch_sts_reg_int_type ++{ ++ TOUCH_STS_REG_INT_TYPE_DATA_AVAIL = 0, // Touch Data Available ++ TOUCH_STS_REG_INT_TYPE_RESET_OCCURRED, // Reset Occurred ++ TOUCH_STS_REG_INT_TYPE_ERROR_OCCURRED, // Error Occurred ++ TOUCH_STS_REG_INT_TYPE_VENDOR_DATA, // Vendor specific data, treated same as raw frame ++ TOUCH_STS_REG_INT_TYPE_GET_FEATURES, // Get Features response data available ++ TOUCH_STS_REG_INT_TYPE_MAX ++} touch_sts_reg_int_type_t; ++C_ASSERT(sizeof(touch_sts_reg_int_type_t) == 4); ++ ++typedef enum touch_sts_reg_pwr_state ++{ ++ TOUCH_STS_REG_PWR_STATE_SLEEP = 0, // Sleep ++ TOUCH_STS_REG_PWR_STATE_DOZE, // Doze ++ TOUCH_STS_REG_PWR_STATE_ARMED, // Armed ++ TOUCH_STS_REG_PWR_STATE_SENSING, // Sensing ++ TOUCH_STS_REG_PWR_STATE_MAX ++} touch_sts_reg_pwr_state_t; ++C_ASSERT(sizeof(touch_sts_reg_pwr_state_t) == 4); ++ ++typedef enum touch_sts_reg_init_state ++{ ++ TOUCH_STS_REG_INIT_STATE_READY_FOR_OP = 0, // Ready for normal operation ++ TOUCH_STS_REG_INIT_STATE_FW_NEEDED, // Touch IC needs its Firmware loaded ++ TOUCH_STS_REG_INIT_STATE_DATA_NEEDED, // Touch IC needs its Data loaded ++ TOUCH_STS_REG_INIT_STATE_INIT_ERROR, // Error info in TOUCH_ERR_REG ++ TOUCH_STS_REG_INIT_STATE_MAX ++} touch_sts_reg_init_state_t; ++C_ASSERT(sizeof(touch_sts_reg_init_state_t) == 4); ++ ++#define TOUCH_SYNC_BYTE_VALUE 0x5A ++ ++typedef union touch_sts_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // When set, this indicates the hardware has data that needs to be read. ++ u32 int_status :1; ++ // see TOUCH_STS_REG_INT_TYPE ++ u32 int_type :4; ++ // see TOUCH_STS_REG_PWR_STATE ++ u32 pwr_state :2; ++ // see TOUCH_STS_REG_INIT_STATE ++ u32 init_state :2; ++ // Busy bit indicates that sensor cannot accept writes at this time ++ u32 busy :1; ++ // Reserved ++ u32 reserved :14; ++ // Synchronization bit, should always be TOUCH_SYNC_BYTE_VALUE ++ u32 sync_byte :8; ++ } fields; ++} touch_sts_reg_t; ++C_ASSERT(sizeof(touch_sts_reg_t) == 4); ++ ++ ++// ++// Offset 04h: TOUCH_FRAME_CHAR: Frame Characteristics Register ++// This registers describes the characteristics of each data frame read by the SPI Controller in ++// response to a touch interrupt. ++// ++#define TOUCH_FRAME_CHAR_REG_OFFSET 0x04 ++ ++typedef union touch_frame_char_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Micro-Frame Size (MFS): Indicates the size of a touch micro-frame in byte increments. ++ // When a micro-frame is to be read for processing (in data mode), this is the total number of ++ // bytes that must be read per interrupt, split into multiple read commands no longer than RPS. ++ // Maximum micro-frame size is 256KB. ++ u32 microframe_size :18; ++ // Micro-Frames per Frame (MFPF): Indicates the number of micro-frames per frame. If a ++ // sensor's frame does not contain micro-frames this value will be 1. Valid values are 1-31. ++ u32 microframes_per_frame :5; ++ // Micro-Frame Index (MFI): Indicates the index of the micro-frame within a frame. This allows ++ // the SPI Controller to maintain synchronization with the sensor and determine when the final ++ // micro-frame has arrived. Valid values are 1-31. ++ u32 microframe_index :5; ++ // HID/Raw Data: This bit describes whether the data from the sensor is Raw data or a HID ++ // report. When set, the data is a HID report. ++ u32 hid_report :1; ++ // Reserved ++ u32 reserved :3; ++ } fields; ++} touch_frame_char_reg_t; ++C_ASSERT(sizeof(touch_frame_char_reg_t) == 4); ++ ++ ++// ++// Offset 08h: Touch Error Register ++// ++#define TOUCH_ERR_REG_OFFSET 0x08 ++ ++// bit definition is vendor specific ++typedef union touch_err_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ u32 invalid_fw :1; ++ u32 invalid_data :1; ++ u32 self_test_failed :1; ++ u32 reserved :12; ++ u32 fatal_error :1; ++ u32 vendor_errors :16; ++ } fields; ++} touch_err_reg_t; ++C_ASSERT(sizeof(touch_err_reg_t) == 4); ++ ++ ++// ++// Offset 0Ch: RESERVED ++// This register is reserved for future use. ++// ++ ++ ++// ++// Offset 10h: Touch Identification Register ++// ++#define TOUCH_ID_REG_OFFSET 0x10 ++ ++#define TOUCH_ID_REG_VALUE 0x43495424 ++ ++// expected value is "$TIC" or 0x43495424 ++typedef u32 touch_id_reg_t; ++C_ASSERT(sizeof(touch_id_reg_t) == 4); ++ ++ ++// ++// Offset 14h: TOUCH_DATA_SZ: Touch Data Size Register ++// This register describes the maximum size of frames and feedback data ++// ++#define TOUCH_DATA_SZ_REG_OFFSET 0x14 ++ ++#define TOUCH_MAX_FRAME_SIZE_INCREMENT 64 ++#define TOUCH_MAX_FEEDBACK_SIZE_INCREMENT 64 ++ ++#define TOUCH_SENSOR_MAX_FRAME_SIZE (32 * 1024) // Max allowed frame size 32KB ++#define TOUCH_SENSOR_MAX_FEEDBACK_SIZE (16 * 1024) // Max allowed feedback size 16KB ++ ++typedef union touch_data_sz_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // This value describes the maximum frame size in 64byte increments. ++ u32 max_frame_size :12; ++ // This value describes the maximum feedback size in 64byte increments. ++ u32 max_feedback_size :8; ++ // Reserved ++ u32 reserved :12; ++ } fields; ++} touch_data_sz_reg_t; ++C_ASSERT(sizeof(touch_data_sz_reg_t) == 4); ++ ++ ++// ++// Offset 18h: TOUCH_CAPABILITIES: Touch Capabilities Register ++// This register informs the host as to the capabilities of the touch IC. ++// ++#define TOUCH_CAPS_REG_OFFSET 0x18 ++ ++typedef enum touch_caps_reg_read_delay_time ++{ ++ TOUCH_CAPS_REG_READ_DELAY_TIME_0, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_10uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_50uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_100uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_150uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_250uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_500uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_1mS, ++} touch_caps_reg_read_delay_time_t; ++C_ASSERT(sizeof(touch_caps_reg_read_delay_time_t) == 4); ++ ++#define TOUCH_BULK_DATA_MAX_WRITE_INCREMENT 64 ++ ++typedef union touch_caps_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Reserved for future frequency ++ u32 reserved0 :1; ++ // 17 MHz (14 MHz on Atom) Supported: 0b - Not supported, 1b - Supported ++ u32 supported_17Mhz :1; ++ // 30 MHz (25MHz on Atom) Supported: 0b - Not supported, 1b - Supported ++ u32 supported_30Mhz :1; ++ // 50 MHz Supported: 0b - Not supported, 1b - Supported ++ u32 supported_50Mhz :1; ++ // Reserved ++ u32 reserved1 :4; ++ // Single I/O Supported: 0b - Not supported, 1b - Supported ++ u32 supported_single_io :1; ++ // Dual I/O Supported: 0b - Not supported, 1b - Supported ++ u32 supported_dual_io :1; ++ // Quad I/O Supported: 0b - Not supported, 1b - Supported ++ u32 supported_quad_io :1; ++ // Bulk Data Area Max Write Size: The amount of data the SPI Controller can write to the bulk ++ // data area before it has to poll the busy bit. This field is in multiples of 64 bytes. The ++ // SPI Controller will write the amount of data specified in this field, then check and wait ++ // for the Status.Busy bit to be zero before writing the next data chunk. This field is 6 bits ++ // long, allowing for 4KB of contiguous writes w/o a poll of the busy bit. If this field is ++ // 0x00 the Touch IC has no limit in the amount of data the SPI Controller can write to the ++ // bulk data area. ++ u32 bulk_data_max_write :6; ++ // Read Delay Timer Value: This field describes the delay the SPI Controller will initiate when ++ // a read interrupt follows a write data command. Uses values from TOUCH_CAPS_REG_READ_DELAY_TIME ++ u32 read_delay_timer_value :3; ++ // Reserved ++ u32 reserved2 :4; ++ // Maximum Touch Points: A byte value based on the HID descriptor definition. ++ u32 max_touch_points :8; ++ } fields; ++} touch_caps_reg_t; ++C_ASSERT(sizeof(touch_caps_reg_t) == 4); ++ ++ ++// ++// Offset 1Ch: TOUCH_CFG: Touch Configuration Register ++// This register allows the SPI Controller to configure the touch sensor as needed during touch ++// operations. ++// ++#define TOUCH_CFG_REG_OFFSET 0x1C ++ ++typedef enum touch_cfg_reg_bulk_xfer_size ++{ ++ TOUCH_CFG_REG_BULK_XFER_SIZE_4B = 0, // Bulk Data Transfer Size is 4 bytes ++ TOUCH_CFG_REG_BULK_XFER_SIZE_8B, // Bulk Data Transfer Size is 8 bytes ++ TOUCH_CFG_REG_BULK_XFER_SIZE_16B, // Bulk Data Transfer Size is 16 bytes ++ TOUCH_CFG_REG_BULK_XFER_SIZE_32B, // Bulk Data Transfer Size is 32 bytes ++ TOUCH_CFG_REG_BULK_XFER_SIZE_64B, // Bulk Data Transfer Size is 64 bytes ++ TOUCH_CFG_REG_BULK_XFER_SIZE_MAX ++} touch_cfg_reg_bulk_xfer_size_t; ++C_ASSERT(sizeof(touch_cfg_reg_bulk_xfer_size_t) == 4); ++ ++// Frequency values used by TOUCH_CFG_REG and TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. ++typedef enum touch_freq ++{ ++ TOUCH_FREQ_RSVD = 0, // Reserved value ++ TOUCH_FREQ_17MHZ, // Sensor set for 17MHz operation (14MHz on Atom) ++ TOUCH_FREQ_30MHZ, // Sensor set for 30MHz operation (25MHz on Atom) ++ TOUCH_FREQ_MAX // Invalid value ++} touch_freq_t; ++C_ASSERT(sizeof(touch_freq_t) == 4); ++ ++typedef union touch_cfg_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Touch Enable (TE): This bit is used as a HW semaphore for the Touch IC to guarantee to the ++ // SPI Controller to that (when 0) no sensing operations will occur and only the Reset ++ // interrupt will be generated. When TE is cleared by the SPI Controller: ++ // - TICs must flush all output buffers ++ // - TICs must De-assert any pending interrupt ++ // - ME must throw away any partial frame and pending interrupt must be cleared/not serviced. ++ // The SPI Controller will only modify the configuration of the TIC when TE is cleared. TE is ++ // defaulted to 0h on a power-on reset. ++ u32 touch_enable :1; ++ // Data/HID Packet Mode (DHPM): Raw Data Mode: 0h, HID Packet Mode: 1h ++ u32 dhpm :1; ++ // Bulk Data Transfer Size: This field represents the amount of data written to the Bulk Data ++ // Area (SPI Offset 0x1000-0x2FFF) in a single SPI write protocol ++ u32 bulk_xfer_size :4; ++ // Frequency Select: Frequency for the TouchIC to run at. Use values from TOUCH_FREQ ++ u32 freq_select :3; ++ // Reserved ++ u32 reserved :23; ++ } fields; ++} touch_cfg_reg_t; ++C_ASSERT(sizeof(touch_cfg_reg_t) == 4); ++ ++ ++// ++// Offset 20h: TOUCH_CMD: Touch Command Register ++// This register is used for sending commands to the Touch IC. ++// ++#define TOUCH_CMD_REG_OFFSET 0x20 ++ ++typedef enum touch_cmd_reg_code ++{ ++ TOUCH_CMD_REG_CODE_NOP = 0, // No Operation ++ TOUCH_CMD_REG_CODE_SOFT_RESET, // Soft Reset ++ TOUCH_CMD_REG_CODE_PREP_4_READ, // Prepare All Registers for Read ++ TOUCH_CMD_REG_CODE_GEN_TEST_PACKETS, // Generate Test Packets according to value in TOUCH_TEST_CTRL_REG ++ TOUCH_CMD_REG_CODE_MAX ++} touch_cmd_reg_code_t; ++C_ASSERT(sizeof(touch_cmd_reg_code_t) == 4); ++ ++typedef union touch_cmd_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Command Code: See TOUCH_CMD_REG_CODE ++ u32 command_code :8; ++ // Reserved ++ u32 reserved :24; ++ } fields; ++} touch_cmd_reg_t; ++C_ASSERT(sizeof(touch_cmd_reg_t) == 4); ++ ++ ++// ++// Offset 24h: Power Management Control ++// This register is used for active power management. The Touch IC is allowed to mover from Doze or ++// Armed to Sensing after a touch has occurred. All other transitions will be made at the request ++// of the SPI Controller. ++// ++#define TOUCH_PWR_MGMT_CTRL_REG_OFFSET 0x24 ++ ++typedef enum touch_pwr_mgmt_ctrl_reg_cmd ++{ ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_NOP = 0, // No change to power state ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_SLEEP, // Sleep - set when the system goes into connected standby ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_DOZE, // Doze - set after 300 seconds of inactivity ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_ARMED, // Armed - Set by FW when a "finger off" message is received from the EUs ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_SENSING, // Sensing - not typically set by FW ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_MAX // Values will result in no change to the power state of the Touch IC ++} touch_pwr_mgmt_ctrl_reg_cmd_t; ++C_ASSERT(sizeof(touch_pwr_mgmt_ctrl_reg_cmd_t) == 4); ++ ++typedef union touch_pwr_mgmt_ctrl_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Power State Command: See TOUCH_PWR_MGMT_CTRL_REG_CMD ++ u32 pwr_state_cmd :3; ++ // Reserved ++ u32 reserved :29; ++ } fields; ++} touch_pwr_mgmt_ctrl_reg_t; ++C_ASSERT(sizeof(touch_pwr_mgmt_ctrl_reg_t) == 4); ++ ++ ++// ++// Offset 28h: Vendor HW Information Register ++// This register is used to relay Intel-assigned vendor ID information to the SPI Controller, which ++// may be forwarded to SW running on the host CPU. ++// ++#define TOUCH_VEN_HW_INFO_REG_OFFSET 0x28 ++ ++typedef union touch_ven_hw_info_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Touch Sensor Vendor ID ++ u32 vendor_id :16; ++ // Touch Sensor Device ID ++ u32 device_id :16; ++ } fields; ++} touch_ven_hw_info_reg_t; ++C_ASSERT(sizeof(touch_ven_hw_info_reg_t) == 4); ++ ++ ++// ++// Offset 2Ch: HW Revision ID Register ++// This register is used to relay vendor HW revision information to the SPI Controller which may be ++// forwarded to SW running on the host CPU. ++// ++#define TOUCH_HW_REV_REG_OFFSET 0x2C ++ ++typedef u32 touch_hw_rev_reg_t; // bit definition is vendor specific ++C_ASSERT(sizeof(touch_hw_rev_reg_t) == 4); ++ ++ ++// ++// Offset 30h: FW Revision ID Register ++// This register is used to relay vendor FW revision information to the SPI Controller which may be ++// forwarded to SW running on the host CPU. ++// ++#define TOUCH_FW_REV_REG_OFFSET 0x30 ++ ++typedef u32 touch_fw_rev_reg_t; // bit definition is vendor specific ++C_ASSERT(sizeof(touch_fw_rev_reg_t) == 4); ++ ++ ++// ++// Offset 34h: Compatibility Revision ID Register ++// This register is used to relay vendor compatibility information to the SPI Controller which may ++// be forwarded to SW running on the host CPU. Compatibility Information is a numeric value given ++// by Intel to the Touch IC vendor based on the major and minor revision of the EDS supported. From ++// a nomenclature point of view in an x.y revision number of the EDS, the major version is the value ++// of x and the minor version is the value of y. For example, a Touch IC supporting an EDS version ++// of 0.61 would contain a major version of 0 and a minor version of 61 in the register. ++// ++#define TOUCH_COMPAT_REV_REG_OFFSET 0x34 ++ ++typedef union touch_compat_rev_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // EDS Minor Revision ++ u8 minor; ++ // EDS Major Revision ++ u8 major; ++ // Interface Revision Number (from EDS) ++ u8 intf_rev; ++ // EU Kernel Compatibility Version - vendor specific value ++ u8 kernel_compat_ver; ++ } fields; ++} touch_compat_rev_reg_t; ++C_ASSERT(sizeof(touch_compat_rev_reg_t) == 4); ++ ++ ++// ++// Touch Register Block is the full set of registers from offset 0x00h to 0x3F ++// This is the entire set of registers needed for normal touch operation. It does not include test ++// registers such as TOUCH_TEST_CTRL_REG ++// ++#define TOUCH_REG_BLOCK_OFFSET TOUCH_STS_REG_OFFSET ++ ++typedef struct touch_reg_block ++{ ++ touch_sts_reg_t sts_reg; // 0x00 ++ touch_frame_char_reg_t frame_char_reg; // 0x04 ++ touch_err_reg_t error_reg; // 0x08 ++ u32 reserved0; // 0x0C ++ touch_id_reg_t id_reg; // 0x10 ++ touch_data_sz_reg_t data_size_reg; // 0x14 ++ touch_caps_reg_t caps_reg; // 0x18 ++ touch_cfg_reg_t cfg_reg; // 0x1C ++ touch_cmd_reg_t cmd_reg; // 0x20 ++ touch_pwr_mgmt_ctrl_reg_t pwm_mgme_ctrl_reg; // 0x24 ++ touch_ven_hw_info_reg_t ven_hw_info_reg; // 0x28 ++ touch_hw_rev_reg_t hw_rev_reg; // 0x2C ++ touch_fw_rev_reg_t fw_rev_reg; // 0x30 ++ touch_compat_rev_reg_t compat_rev_reg; // 0x34 ++ u32 reserved1; // 0x38 ++ u32 reserved2; // 0x3C ++} touch_reg_block_t; ++C_ASSERT(sizeof(touch_reg_block_t) == 64); ++ ++ ++// ++// Offset 40h: Test Control Register ++// This register ++// ++#define TOUCH_TEST_CTRL_REG_OFFSET 0x40 ++ ++typedef union touch_test_ctrl_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Size of Test Frame in Raw Data Mode: This field specifies the test frame size in raw data ++ // mode in multiple of 64 bytes. For example, if this field value is 16, the test frame size ++ // will be 16x64 = 1K. ++ u32 raw_test_frame_size :16; ++ // Number of Raw Data Frames or HID Report Packets Generation. This field represents the number ++ // of test frames or HID reports to be generated when test mode is enabled. When multiple ++ // packets/frames are generated, they need be generated at 100 Hz frequency, i.e. 10ms per ++ // packet/frame. ++ u32 num_test_frames :16; ++ } fields; ++} touch_test_ctrl_reg_t; ++C_ASSERT(sizeof(touch_test_ctrl_reg_t) == 4); ++ ++ ++// ++// Offsets 0x000 to 0xFFF are reserved for Intel-defined Registers ++// ++#define TOUCH_REGISTER_LIMIT 0xFFF ++ ++ ++// ++// Data Window: Address 0x1000-0x1FFFF ++// The data window is reserved for writing and reading large quantities of data to and from the ++// sensor. ++// ++#define TOUCH_DATA_WINDOW_OFFSET 0x1000 ++#define TOUCH_DATA_WINDOW_LIMIT 0x1FFFF ++ ++#define TOUCH_SENSOR_MAX_OFFSET TOUCH_DATA_WINDOW_LIMIT ++ ++ ++// ++// The following data structures represent the headers defined in the Data Structures chapter of the ++// Intel Integrated Touch EDS ++// ++ ++// Enumeration used in TOUCH_RAW_DATA_HDR ++typedef enum touch_raw_data_types ++{ ++ TOUCH_RAW_DATA_TYPE_FRAME = 0, ++ TOUCH_RAW_DATA_TYPE_ERROR, // RawData will be the TOUCH_ERROR struct below ++ TOUCH_RAW_DATA_TYPE_VENDOR_DATA, // Set when InterruptType is Vendor Data ++ TOUCH_RAW_DATA_TYPE_HID_REPORT, ++ TOUCH_RAW_DATA_TYPE_GET_FEATURES, ++ TOUCH_RAW_DATA_TYPE_MAX ++} touch_raw_data_types_t; ++C_ASSERT(sizeof(touch_raw_data_types_t) == 4); ++ ++// Private data structure. Kernels must copy to HID driver buffer ++typedef struct touch_hid_private_data ++{ ++ u32 transaction_id; ++ u8 reserved[28]; ++} touch_hid_private_data_t; ++C_ASSERT(sizeof(touch_hid_private_data_t) == 32); ++ ++// This is the data structure sent from the PCH FW to the EU kernel ++typedef struct touch_raw_data_hdr ++{ ++ u32 data_type; // use values from TOUCH_RAW_DATA_TYPES ++ u32 raw_data_size_bytes; // The size in bytes of the raw data read from the ++ // sensor, does not include TOUCH_RAW_DATA_HDR. Will ++ // be the sum of all uFrames, or size of TOUCH_ERROR ++ // for if DataType is TOUCH_RAW_DATA_TYPE_ERROR ++ u32 buffer_id; // An ID to qualify with the feedback data to track ++ // buffer usage ++ u32 protocol_ver; // Must match protocol version of the EDS ++ u8 kernel_compat_id; // Copied from the Compatibility Revision ID Reg ++ u8 reserved[15]; // Padding to extend header to full 64 bytes and ++ // allow for growth ++ touch_hid_private_data_t hid_private_data; // Private data structure. Kernels must copy to HID ++ // driver buffer ++} touch_raw_data_hdr_t; ++C_ASSERT(sizeof(touch_raw_data_hdr_t) == 64); ++ ++typedef struct touch_raw_data ++{ ++ touch_raw_data_hdr_t header; ++ u8 raw_data[1]; // used to access the raw data as an array and keep the ++ // compilers happy. Actual size of this array is ++ // Header.RawDataSizeBytes ++} touch_raw_data_t; ++ ++ ++// The following section describes the data passed in TOUCH_RAW_DATA.RawData when DataType equals ++// TOUCH_RAW_DATA_TYPE_ERROR ++// Note: This data structure is also applied to HID mode ++typedef enum touch_err_types ++{ ++ TOUCH_RAW_DATA_ERROR = 0, ++ TOUCH_RAW_ERROR_MAX ++} touch_err_types_t; ++C_ASSERT(sizeof(touch_err_types_t) == 4); ++ ++typedef union touch_me_fw_error ++{ ++ u32 value; ++ ++ struct ++ { ++ u32 invalid_frame_characteristics : 1; ++ u32 microframe_index_invalid : 1; ++ u32 reserved : 30; ++ } fields; ++} touch_me_fw_error_t; ++C_ASSERT(sizeof(touch_me_fw_error_t) == 4); ++ ++typedef struct touch_error ++{ ++ u8 touch_error_type; // This must be a value from TOUCH_ERROR_TYPES ++ u8 reserved[3]; ++ touch_me_fw_error_t touch_me_fw_error; ++ touch_err_reg_t touch_error_register; // Contains the value copied from the Touch Error Reg ++} touch_error_t; ++C_ASSERT(sizeof(touch_error_t) == 12); ++ ++// Enumeration used in TOUCH_FEEDBACK_BUFFER ++typedef enum touch_feedback_cmd_types ++{ ++ TOUCH_FEEDBACK_CMD_TYPE_NONE = 0, ++ TOUCH_FEEDBACK_CMD_TYPE_SOFT_RESET, ++ TOUCH_FEEDBACK_CMD_TYPE_GOTO_ARMED, ++ TOUCH_FEEDBACK_CMD_TYPE_GOTO_SENSING, ++ TOUCH_FEEDBACK_CMD_TYPE_GOTO_SLEEP, ++ TOUCH_FEEDBACK_CMD_TYPE_GOTO_DOZE, ++ TOUCH_FEEDBACK_CMD_TYPE_HARD_RESET, ++ TOUCH_FEEDBACK_CMD_TYPE_MAX ++} touch_feedback_cmd_types_t; ++C_ASSERT(sizeof(touch_feedback_cmd_types_t) == 4); ++ ++// Enumeration used in TOUCH_FEEDBACK_HDR ++typedef enum touch_feedback_data_types ++{ ++ TOUCH_FEEDBACK_DATA_TYPE_FEEDBACK = 0, // This is vendor specific feedback to be written to the sensor ++ TOUCH_FEEDBACK_DATA_TYPE_SET_FEATURES, // This is a set features command to be written to the sensor ++ TOUCH_FEEDBACK_DATA_TYPE_GET_FEATURES, // This is a get features command to be written to the sensor ++ TOUCH_FEEDBACK_DATA_TYPE_OUTPUT_REPORT, // This is a HID output report to be written to the sensor ++ TOUCH_FEEDBACK_DATA_TYPE_STORE_DATA, // This is calibration data to be written to system flash ++ TOUCH_FEEDBACK_DATA_TYPE_MAX ++} touch_feedback_data_types_t; ++C_ASSERT(sizeof(touch_feedback_data_types_t) == 4); ++ ++// This is the data structure sent from the EU kernels back to the ME FW. ++// In addition to "feedback" data, the FW can execute a "command" described by the command type parameter. ++// Any payload data will always be sent to the TIC first, then any command will be issued. ++typedef struct touch_feedback_hdr ++{ ++ u32 feedback_cmd_type; // use values from TOUCH_FEEDBACK_CMD_TYPES ++ u32 payload_size_bytes; // The amount of data to be written to the sensor, not including the header ++ u32 buffer_id; // The ID of the raw data buffer that generated this feedback data ++ u32 protocol_ver; // Must match protocol version of the EDS ++ u32 feedback_data_type; // use values from TOUCH_FEEDBACK_DATA_TYPES. This is not relevant if PayloadSizeBytes is 0 ++ u32 spi_offest; // The offset from TOUCH_DATA_WINDOW_OFFSET at which to write the Payload data. Maximum offset is 0x1EFFF. ++ u8 reserved[40]; // Padding to extend header to full 64 bytes and allow for growth ++} touch_feedback_hdr_t; ++C_ASSERT(sizeof(touch_feedback_hdr_t) == 64); ++ ++typedef struct touch_feedback_buffer ++{ ++ touch_feedback_hdr_t Header; ++ u8 feedback_data[1]; // used to access the feedback data as an array and keep the compilers happy. Actual size of this array is Header.PayloadSizeBytes ++} touch_feedback_buffer_t; ++ ++ ++// ++// This data structure describes the header prepended to all data ++// written to the touch IC at the bulk data write (TOUCH_DATA_WINDOW_OFFSET + TOUCH_FEEDBACK_HDR.SpiOffest) address. ++typedef enum touch_write_data_type ++{ ++ TOUCH_WRITE_DATA_TYPE_FW_LOAD = 0, ++ TOUCH_WRITE_DATA_TYPE_DATA_LOAD, ++ TOUCH_WRITE_DATA_TYPE_FEEDBACK, ++ TOUCH_WRITE_DATA_TYPE_SET_FEATURES, ++ TOUCH_WRITE_DATA_TYPE_GET_FEATURES, ++ TOUCH_WRITE_DATA_TYPE_OUTPUT_REPORT, ++ TOUCH_WRITE_DATA_TYPE_NO_DATA_USE_DEFAULTS, ++ TOUCH_WRITE_DATA_TYPE_MAX ++} touch_write_data_type_t; ++C_ASSERT(sizeof(touch_write_data_type_t) == 4); ++ ++typedef struct touch_write_hdr ++{ ++ u32 write_data_type; // Use values from TOUCH_WRITE_DATA_TYPE ++ u32 write_data_len; // This field designates the amount of data to follow ++} touch_write_hdr_t; ++C_ASSERT(sizeof(touch_write_hdr_t) == 8); ++ ++typedef struct touch_write_data ++{ ++ touch_write_hdr_t header; ++ u8 write_data[1]; // used to access the write data as an array and keep the compilers happy. Actual size of this array is Header.WriteDataLen ++} touch_write_data_t; ++ ++#pragma pack() ++ ++#endif // _TOUCH_SENSOR_REGS_H +diff --git a/drivers/misc/ipts/ipts-state.h b/drivers/misc/ipts/ipts-state.h +new file mode 100644 +index 000000000000..39a2eaf5f004 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-state.h +@@ -0,0 +1,29 @@ ++/* ++ * Intel Precise Touch & Stylus state codes ++ * ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#ifndef _IPTS_STATE_H_ ++#define _IPTS_STATE_H_ ++ ++/* ipts driver states */ ++typedef enum ipts_state { ++ IPTS_STA_NONE, ++ IPTS_STA_INIT, ++ IPTS_STA_RESOURCE_READY, ++ IPTS_STA_HID_STARTED, ++ IPTS_STA_RAW_DATA_STARTED, ++ IPTS_STA_STOPPING ++} ipts_state_t; ++ ++#endif // _IPTS_STATE_H_ +diff --git a/drivers/misc/ipts/ipts.h b/drivers/misc/ipts/ipts.h +new file mode 100644 +index 000000000000..1fcd02146b50 +--- /dev/null ++++ b/drivers/misc/ipts/ipts.h +@@ -0,0 +1,200 @@ ++/* ++ * ++ * Intel Management Engine Interface (Intel MEI) Client Driver for IPTS ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#ifndef _IPTS_H_ ++#define _IPTS_H_ ++ ++#include ++#include ++#include ++#include ++ ++#include "ipts-mei-msgs.h" ++#include "ipts-state.h" ++#include "ipts-binary-spec.h" ++ ++//#define ENABLE_IPTS_DEBUG /* enable IPTS debug */ ++ ++#ifdef ENABLE_IPTS_DEBUG ++ ++#define ipts_info(ipts, format, arg...) do {\ ++ dev_info(&ipts->cldev->dev, format, ##arg);\ ++} while (0) ++ ++#define ipts_dbg(ipts, format, arg...) do {\ ++ dev_info(&ipts->cldev->dev, format, ##arg);\ ++} while (0) ++ ++//#define RUN_DBG_THREAD ++ ++#else ++ ++#define ipts_info(ipts, format, arg...) do {} while(0); ++#define ipts_dbg(ipts, format, arg...) do {} while(0); ++ ++#endif ++ ++#define ipts_err(ipts, format, arg...) do {\ ++ dev_err(&ipts->cldev->dev, format, ##arg);\ ++} while (0) ++ ++#define HID_PARALLEL_DATA_BUFFERS TOUCH_SENSOR_MAX_DATA_BUFFERS ++ ++#define IPTS_MAX_RETRY 3 ++ ++typedef struct ipts_buffer_info { ++ char *addr; ++ dma_addr_t dma_addr; ++} ipts_buffer_info_t; ++ ++typedef struct ipts_gfx_info { ++ u64 gfx_handle; ++ intel_ipts_ops_t ipts_ops; ++} ipts_gfx_info_t; ++ ++typedef struct ipts_resource { ++ /* ME & Gfx resource */ ++ ipts_buffer_info_t touch_data_buffer_raw[HID_PARALLEL_DATA_BUFFERS]; ++ ipts_buffer_info_t touch_data_buffer_hid; ++ ++ ipts_buffer_info_t feedback_buffer[HID_PARALLEL_DATA_BUFFERS]; ++ ++ ipts_buffer_info_t hid2me_buffer; ++ u32 hid2me_buffer_size; ++ ++ u8 wq_item_size; ++ intel_ipts_wq_info_t wq_info; ++ ++ /* ME2HID buffer */ ++ char *me2hid_buffer; ++ ++ /* Gfx specific resource */ ++ ipts_buffer_info_t raw_data_mode_output_buffer ++ [HID_PARALLEL_DATA_BUFFERS][MAX_NUM_OUTPUT_BUFFERS]; ++ ++ int num_of_outputs; ++ ++ bool default_resource_ready; ++ bool raw_data_resource_ready; ++} ipts_resource_t; ++ ++typedef struct ipts_info { ++ struct mei_cl_device *cldev; ++ struct hid_device *hid; ++ ++ struct work_struct init_work; ++ struct work_struct raw_data_work; ++ struct work_struct gfx_status_work; ++ ++ struct task_struct *event_loop; ++ ++#if IS_ENABLED(CONFIG_DEBUG_FS) ++ struct dentry *dbgfs_dir; ++#endif ++ ++ ipts_state_t state; ++ ++ touch_sensor_mode_t sensor_mode; ++ touch_sensor_get_device_info_rsp_data_t device_info; ++ ipts_resource_t resource; ++ u8 hid_input_report[HID_MAX_BUFFER_SIZE]; ++ int num_of_parallel_data_buffers; ++ bool hid_desc_ready; ++ ++ int current_buffer_index; ++ int last_buffer_completed; ++ int *last_submitted_id; ++ ++ ipts_gfx_info_t gfx_info; ++ u64 kernel_handle; ++ int gfx_status; ++ bool display_status; ++ ++ bool switch_sensor_mode; ++ touch_sensor_mode_t new_sensor_mode; ++ ++ int retry; ++ bool restart; ++} ipts_info_t; ++ ++#if IS_ENABLED(CONFIG_DEBUG_FS) ++int ipts_dbgfs_register(ipts_info_t *ipts, const char *name); ++void ipts_dbgfs_deregister(ipts_info_t *ipts); ++#else ++static int ipts_dbgfs_register(ipts_info_t *ipts, const char *name); ++static void ipts_dbgfs_deregister(ipts_info_t *ipts); ++#endif /* CONFIG_DEBUG_FS */ ++ ++/* inline functions */ ++static inline void ipts_set_state(ipts_info_t *ipts, ipts_state_t state) ++{ ++ ipts->state = state; ++} ++ ++static inline ipts_state_t ipts_get_state(const ipts_info_t *ipts) ++{ ++ return ipts->state; ++} ++ ++static inline bool ipts_is_default_resource_ready(const ipts_info_t *ipts) ++{ ++ return ipts->resource.default_resource_ready; ++} ++ ++static inline bool ipts_is_raw_data_resource_ready(const ipts_info_t *ipts) ++{ ++ return ipts->resource.raw_data_resource_ready; ++} ++ ++static inline ipts_buffer_info_t* ipts_get_feedback_buffer(ipts_info_t *ipts, ++ int buffer_idx) ++{ ++ return &ipts->resource.feedback_buffer[buffer_idx]; ++} ++ ++static inline ipts_buffer_info_t* ipts_get_touch_data_buffer_hid(ipts_info_t *ipts) ++{ ++ return &ipts->resource.touch_data_buffer_hid; ++} ++ ++static inline ipts_buffer_info_t* ipts_get_output_buffers_by_parallel_id( ++ ipts_info_t *ipts, ++ int parallel_idx) ++{ ++ return &ipts->resource.raw_data_mode_output_buffer[parallel_idx][0]; ++} ++ ++static inline ipts_buffer_info_t* ipts_get_hid2me_buffer(ipts_info_t *ipts) ++{ ++ return &ipts->resource.hid2me_buffer; ++} ++ ++static inline void ipts_set_wq_item_size(ipts_info_t *ipts, u8 size) ++{ ++ ipts->resource.wq_item_size = size; ++} ++ ++static inline u8 ipts_get_wq_item_size(const ipts_info_t *ipts) ++{ ++ return ipts->resource.wq_item_size; ++} ++ ++static inline int ipts_get_num_of_parallel_buffers(const ipts_info_t *ipts) ++{ ++ return ipts->num_of_parallel_data_buffers; ++} ++ ++#endif // _IPTS_H_ +diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h +index e4b10b2d1a08..883b185c9dbe 100644 +--- a/drivers/misc/mei/hw-me-regs.h ++++ b/drivers/misc/mei/hw-me-regs.h +@@ -119,6 +119,7 @@ + + #define MEI_DEV_ID_SPT 0x9D3A /* Sunrise Point */ + #define MEI_DEV_ID_SPT_2 0x9D3B /* Sunrise Point 2 */ ++#define MEI_DEV_ID_SPT_4 0x9D3E /* Sunrise Point 4 */ + #define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */ + #define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */ + +diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c +index ea4e152270a3..4d301ba3f867 100644 +--- a/drivers/misc/mei/pci-me.c ++++ b/drivers/misc/mei/pci-me.c +@@ -86,6 +86,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { + + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, MEI_ME_PCH8_CFG)}, ++ {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_4, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, MEI_ME_PCH8_SPS_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, MEI_ME_PCH8_SPS_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, MEI_ME_PCH8_CFG)}, +diff --git a/include/linux/intel_ipts_if.h b/include/linux/intel_ipts_if.h +new file mode 100644 +index 000000000000..f329bbfb8079 +--- /dev/null ++++ b/include/linux/intel_ipts_if.h +@@ -0,0 +1,75 @@ ++/* ++ * ++ * GFX interface to support Intel Precise Touch & Stylus ++ * Copyright (c) 2016 Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#ifndef INTEL_IPTS_IF_H ++#define INTEL_IPTS_IF_H ++ ++enum { ++ IPTS_INTERFACE_V1 = 1, ++}; ++ ++#define IPTS_BUF_FLAG_CONTIGUOUS 0x01 ++ ++#define IPTS_NOTIFY_STA_BACKLIGHT_OFF 0x00 ++#define IPTS_NOTIFY_STA_BACKLIGHT_ON 0x01 ++ ++typedef struct intel_ipts_mapbuffer { ++ u32 size; ++ u32 flags; ++ void *gfx_addr; ++ void *cpu_addr; ++ u64 buf_handle; ++ u64 phy_addr; ++} intel_ipts_mapbuffer_t; ++ ++typedef struct intel_ipts_wq_info { ++ u64 db_addr; ++ u64 db_phy_addr; ++ u32 db_cookie_offset; ++ u32 wq_size; ++ u64 wq_addr; ++ u64 wq_phy_addr; ++ u64 wq_head_addr; /* head of wq is managed by GPU */ ++ u64 wq_head_phy_addr; /* head of wq is managed by GPU */ ++ u64 wq_tail_addr; /* tail of wq is managed by CSME */ ++ u64 wq_tail_phy_addr; /* tail of wq is managed by CSME */ ++} intel_ipts_wq_info_t; ++ ++typedef struct intel_ipts_ops { ++ int (*get_wq_info)(uint64_t gfx_handle, intel_ipts_wq_info_t *wq_info); ++ int (*map_buffer)(uint64_t gfx_handle, intel_ipts_mapbuffer_t *mapbuffer); ++ int (*unmap_buffer)(uint64_t gfx_handle, uint64_t buf_handle); ++} intel_ipts_ops_t; ++ ++typedef struct intel_ipts_callback { ++ void (*workload_complete)(void *data); ++ void (*notify_gfx_status)(u32 status, void *data); ++} intel_ipts_callback_t; ++ ++typedef struct intel_ipts_connect { ++ intel_ipts_callback_t ipts_cb; /* input : callback addresses */ ++ void *data; /* input : callback data */ ++ u32 if_version; /* input : interface version */ ++ ++ u32 gfx_version; /* output : gfx version */ ++ u64 gfx_handle; /* output : gfx handle */ ++ intel_ipts_ops_t ipts_ops; /* output : gfx ops for IPTS */ ++} intel_ipts_connect_t; ++ ++int intel_ipts_connect(intel_ipts_connect_t *ipts_connect); ++void intel_ipts_disconnect(uint64_t gfx_handle); ++ ++#endif // INTEL_IPTS_IF_H +-- +2.17.1 + diff --git a/linux_surface/patches/0006-hid.patch b/linux_surface/patches/0006-hid.patch new file mode 100644 index 0000000..a91dc19 --- /dev/null +++ b/linux_surface/patches/0006-hid.patch @@ -0,0 +1,144 @@ +From 0ba33cc0e4f65bfc9250027f810e2a78e251bb92 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Sun, 27 Jan 2019 10:52:52 -0500 +Subject: [PATCH 06/11] hid + +--- + drivers/hid/hid-ids.h | 20 ++++++++++---- + drivers/hid/hid-microsoft.c | 3 ++- + drivers/hid/hid-multitouch.c | 52 ++++++++++++++++++++++++++++++++++++ + drivers/hid/hid-quirks.c | 10 +++++++ + 4 files changed, 79 insertions(+), 6 deletions(-) + +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +index b7870e7e41d4..2ad517f1c1fe 100644 +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -800,11 +800,21 @@ + #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1 0x0732 + #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_600 0x0750 + #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c +-#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3 +-#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 +-#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 +-#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 +-#define USB_DEVICE_ID_MS_POWER_COVER 0x07da ++#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3 ++#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 ++#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 ++#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 ++#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1 0x07de ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2 ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 0x07e8 ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1 0x07e4 ++#define USB_DEVICE_ID_MS_SURFACE_BOOK 0x07cd ++#define USB_DEVICE_ID_MS_SURFACE_BOOK_2 0x0922 ++#define USB_DEVICE_ID_MS_SURFACE_VHF 0xf001 ++#define USB_DEVICE_ID_MS_POWER_COVER 0x07da + #define USB_DEVICE_ID_MS_PIXART_MOUSE 0x00cb + + #define USB_VENDOR_ID_MOJO 0x8282 +diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c +index 72d983626afd..133395b45022 100644 +--- a/drivers/hid/hid-microsoft.c ++++ b/drivers/hid/hid-microsoft.c +@@ -313,7 +313,8 @@ static const struct hid_device_id ms_devices[] = { + .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD), + .driver_data = MS_ERGONOMY}, +- ++ { HID_DEVICE(BUS_VIRTUAL, 0, USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_VHF), ++ .driver_data = MS_HIDINPUT}, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), + .driver_data = MS_PRESENTER }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B), +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index 831617c386e6..1953d21398cf 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -1978,6 +1978,58 @@ static const struct hid_device_id mt_devices[] = { + HID_USB_DEVICE(USB_VENDOR_ID_LG, + USB_DEVICE_ID_LG_MELFAS_MT) }, + ++ /* Microsoft Touch Cover */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TOUCH_COVER_2) }, ++ ++ /* Microsoft Type Cover */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_2) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_3) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_4) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1) }, ++ ++ /* Microsoft Surface Book */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_SURFACE_BOOK) }, ++ ++ /* Microsoft Surface Book 2 */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_SURFACE_BOOK_2) }, ++ ++ /* Microsoft Surface Laptop */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, ++ USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_SURFACE_VHF) }, ++ ++ /* Microsoft Power Cover */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_POWER_COVER) }, ++ + /* MosArt panels */ + { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, + MT_USB_DEVICE(USB_VENDOR_ID_ASUS, +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index 77316f022c5a..2d4aafb92d85 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -111,6 +111,16 @@ static const struct hid_device_id hid_quirks[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK_2), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_DEVICE(BUS_VIRTUAL, 0, USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_VHF), HID_QUIRK_ALWAYS_POLL }, + { HID_USB_DEVICE(USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER), HID_QUIRK_MULTI_INPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD), HID_QUIRK_MULTI_INPUT }, +-- +2.17.1 + diff --git a/linux_surface/patches/0007-sdcard-reader.patch b/linux_surface/patches/0007-sdcard-reader.patch new file mode 100644 index 0000000..7e756c1 --- /dev/null +++ b/linux_surface/patches/0007-sdcard-reader.patch @@ -0,0 +1,26 @@ +From 9100ebd6198c2db95b041a6ffcca6f8070308aa1 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Sun, 27 Jan 2019 10:53:11 -0500 +Subject: [PATCH 07/11] sdcard-reader + +--- + drivers/usb/core/hub.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +index cc62707c0251..caed42b72daf 100644 +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -4150,7 +4150,8 @@ void usb_enable_lpm(struct usb_device *udev) + if (!udev || !udev->parent || + udev->speed < USB_SPEED_SUPER || + !udev->lpm_capable || +- udev->state < USB_STATE_DEFAULT) ++ udev->state < USB_STATE_DEFAULT || ++ !udev->bos || !udev->bos->ss_cap) + return; + + udev->lpm_disable_count--; +-- +2.17.1 + diff --git a/linux_surface/patches/0008-wifi.patch b/linux_surface/patches/0008-wifi.patch new file mode 100644 index 0000000..1d8c90a --- /dev/null +++ b/linux_surface/patches/0008-wifi.patch @@ -0,0 +1,272 @@ +From fc19b38f066347b2acffe213bfe6c96e745698b2 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Sun, 27 Jan 2019 10:53:31 -0500 +Subject: [PATCH 08/11] wifi + +--- + drivers/net/wireless/marvell/mwifiex/11n_aggr.c | 3 +-- + drivers/net/wireless/marvell/mwifiex/cfg80211.c | 5 ++++- + drivers/net/wireless/marvell/mwifiex/cmdevt.c | 2 ++ + drivers/net/wireless/marvell/mwifiex/fw.h | 1 + + drivers/net/wireless/marvell/mwifiex/main.c | 17 +++++++++++++---- + drivers/net/wireless/marvell/mwifiex/main.h | 2 ++ + drivers/net/wireless/marvell/mwifiex/pcie.c | 9 +++++++++ + drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 8 ++++---- + .../net/wireless/marvell/mwifiex/sta_cmdresp.c | 11 ++++++++--- + drivers/net/wireless/marvell/mwifiex/usb.c | 2 ++ + scripts/leaking_addresses.pl | 0 + 11 files changed, 46 insertions(+), 14 deletions(-) + mode change 100755 => 100644 scripts/leaking_addresses.pl + +diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c +index 042a1d07f686..fc9041f58e9f 100644 +--- a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c ++++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c +@@ -200,8 +200,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, + + do { + /* Check if AMSDU can accommodate this MSDU */ +- if ((skb_aggr->len + skb_src->len + LLC_SNAP_LEN) > +- adapter->tx_buf_size) ++ if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN)) + break; + + skb_src = skb_dequeue(&pra_list->skb_head); +diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c +index adc88433faa8..7376ffa82c63 100644 +--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c ++++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c +@@ -428,7 +428,10 @@ mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, + mwifiex_dbg(priv->adapter, INFO, + "info: ignore timeout value for IEEE Power Save\n"); + +- ps_mode = enabled; ++ //ps_mode = enabled; ++ ++ mwifiex_dbg(priv->adapter, INFO, "overriding ps_mode to false\n"); ++ ps_mode = 0; + + return mwifiex_drv_set_power(priv, &ps_mode); + } +diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c +index 60db2b969e20..a15675ef31bd 100644 +--- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c ++++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c +@@ -1000,6 +1000,7 @@ mwifiex_cmd_timeout_func(struct timer_list *t) + if (cmd_node->wait_q_enabled) { + adapter->cmd_wait_q.status = -ETIMEDOUT; + mwifiex_cancel_pending_ioctl(adapter); ++ adapter->cmd_sent = false; + } + } + if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { +@@ -1577,6 +1578,7 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, + adapter->key_api_minor_ver); + break; + case FW_API_VER_ID: ++ case FW_KEY_API_VER_ID: + adapter->fw_api_ver = + api_rev->major_ver; + mwifiex_dbg(adapter, INFO, +diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h +index b73f99dc5a72..d96a0ffc9649 100644 +--- a/drivers/net/wireless/marvell/mwifiex/fw.h ++++ b/drivers/net/wireless/marvell/mwifiex/fw.h +@@ -1052,6 +1052,7 @@ struct host_cmd_ds_802_11_ps_mode_enh { + enum API_VER_ID { + KEY_API_VER_ID = 1, + FW_API_VER_ID = 2, ++ FW_KEY_API_VER_ID = 4, + }; + + struct hw_spec_api_rev { +diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c +index 20cee5c397fb..13e49a3ae812 100644 +--- a/drivers/net/wireless/marvell/mwifiex/main.c ++++ b/drivers/net/wireless/marvell/mwifiex/main.c +@@ -163,6 +163,7 @@ void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) + spin_lock_irqsave(&adapter->main_proc_lock, flags); + if (adapter->mwifiex_processing) { + adapter->more_task_flag = true; ++ adapter->more_rx_task_flag = true; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + } else { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); +@@ -171,18 +172,20 @@ void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) + } + EXPORT_SYMBOL_GPL(mwifiex_queue_main_work); + +-static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) ++void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) + { + unsigned long flags; + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + if (adapter->rx_processing) { ++ adapter->more_rx_task_flag = true; + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + } else { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + queue_work(adapter->rx_workqueue, &adapter->rx_work); + } + } ++EXPORT_SYMBOL_GPL(mwifiex_queue_rx_work); + + static int mwifiex_process_rx(struct mwifiex_adapter *adapter) + { +@@ -192,6 +195,7 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + if (adapter->rx_processing || adapter->rx_locked) { ++ adapter->more_rx_task_flag = true; + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + goto exit_rx_proc; + } else { +@@ -199,6 +203,7 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + } + ++rx_process_start: + /* Check for Rx data */ + while ((skb = skb_dequeue(&adapter->rx_data_q))) { + atomic_dec(&adapter->rx_pending); +@@ -220,6 +225,11 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) + } + } + spin_lock_irqsave(&adapter->rx_proc_lock, flags); ++ if (adapter->more_rx_task_flag) { ++ adapter->more_rx_task_flag = false; ++ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); ++ goto rx_process_start; ++ } + adapter->rx_processing = false; + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + +@@ -283,11 +293,10 @@ int mwifiex_main_process(struct mwifiex_adapter *adapter) + mwifiex_process_hs_config(adapter); + if (adapter->if_ops.process_int_status) + adapter->if_ops.process_int_status(adapter); ++ if (adapter->rx_work_enabled && adapter->data_received) ++ mwifiex_queue_rx_work(adapter); + } + +- if (adapter->rx_work_enabled && adapter->data_received) +- mwifiex_queue_rx_work(adapter); +- + /* Need to wake up the card ? */ + if ((adapter->ps_state == PS_STATE_SLEEP) && + (adapter->pm_wakeup_card_req && +diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h +index b025ba164412..d4027a803079 100644 +--- a/drivers/net/wireless/marvell/mwifiex/main.h ++++ b/drivers/net/wireless/marvell/mwifiex/main.h +@@ -908,6 +908,7 @@ struct mwifiex_adapter { + spinlock_t main_proc_lock; + u32 mwifiex_processing; + u8 more_task_flag; ++ u8 more_rx_task_flag; + u16 tx_buf_size; + u16 curr_tx_buf_size; + /* sdio single port rx aggregation capability */ +@@ -1694,6 +1695,7 @@ void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter); + void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags); + void mwifiex_fw_dump_event(struct mwifiex_private *priv); + void mwifiex_queue_main_work(struct mwifiex_adapter *adapter); ++void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter); + int mwifiex_get_wakeup_reason(struct mwifiex_private *priv, u16 action, + int cmd_type, + struct mwifiex_ds_wakeup_reason *wakeup_reason); +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c +index 3fe81b2a929a..6e734a83e6bf 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie.c +@@ -1743,6 +1743,15 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) + } + + rx_len = get_unaligned_le16(skb->data); ++ ++ if (rx_len == 0) { ++ mwifiex_dbg(adapter, ERROR, ++ "0 byte cmdrsp\n"); ++ mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, ++ PCI_DMA_FROMDEVICE); ++ return 0; ++ } ++ + skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); + skb_trim(skb, rx_len); + +diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +index 4ed10cf82f9a..f17af83f5669 100644 +--- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c ++++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +@@ -2339,7 +2339,7 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) + if (ret) + return -1; + +- if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { ++ if (0 && priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { + /* Enable IEEE PS by default */ + priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; + ret = mwifiex_send_cmd(priv, +@@ -2362,8 +2362,8 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) + return -1; + } + +- mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REGION_CFG, +- HostCmd_ACT_GEN_GET, 0, NULL, true); ++ //mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REGION_CFG, ++ // HostCmd_ACT_GEN_GET, 0, NULL, true); + } + + /* get tx rate */ +@@ -2395,7 +2395,7 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) + if (ret) + return -1; + +- if (!disable_auto_ds && first_sta && ++ if (0 && !disable_auto_ds && first_sta && + priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { + /* Enable auto deep sleep */ + auto_ds.auto_ds = DEEP_SLEEP_ON; +diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c +index 69e3b624adbb..884bad677cc3 100644 +--- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c ++++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c +@@ -48,9 +48,14 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv, + struct host_cmd_ds_802_11_ps_mode_enh *pm; + unsigned long flags; + +- mwifiex_dbg(adapter, ERROR, +- "CMD_RESP: cmd %#x error, result=%#x\n", +- resp->command, resp->result); ++ if (resp->command == 271 && resp->result == 2){ ++ // ignore this command as the firmware does not support it ++ } ++ else { ++ mwifiex_dbg(adapter, ERROR, ++ "CMD_RESP: cmd %#x error, result=%#x\n", ++ resp->command, resp->result); ++ } + + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status = -1; +diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c +index 433c6a16870b..a2ff1ee3d230 100644 +--- a/drivers/net/wireless/marvell/mwifiex/usb.c ++++ b/drivers/net/wireless/marvell/mwifiex/usb.c +@@ -144,6 +144,8 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, + skb_queue_tail(&adapter->rx_data_q, skb); + adapter->data_received = true; + atomic_inc(&adapter->rx_pending); ++ if (adapter->rx_work_enabled) ++ mwifiex_queue_rx_work(adapter); + break; + default: + mwifiex_dbg(adapter, ERROR, +diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl +old mode 100755 +new mode 100644 +-- +2.17.1 + diff --git a/linux_surface/patches/0009-surface3-power.patch b/linux_surface/patches/0009-surface3-power.patch new file mode 100644 index 0000000..b4ecf5a --- /dev/null +++ b/linux_surface/patches/0009-surface3-power.patch @@ -0,0 +1,753 @@ +From a6204ef155dc2c4ad1fd5bb32e06fd48f43f8572 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Sun, 27 Jan 2019 10:53:53 -0500 +Subject: [PATCH 09/11] surface3-power + +--- + drivers/acpi/Kconfig | 6 + + drivers/acpi/Makefile | 2 + + drivers/acpi/surface3_power.c | 702 ++++++++++++++++++++++++++++++++++ + 3 files changed, 710 insertions(+) + create mode 100644 drivers/acpi/surface3_power.c + +diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig +index dd1eea90f67f..b5a978e6d487 100644 +--- a/drivers/acpi/Kconfig ++++ b/drivers/acpi/Kconfig +@@ -498,6 +498,12 @@ config ACPI_EXTLOG + driver adds support for that functionality with corresponding + tracepoint which carries that information to userspace. + ++config ACPI_SURFACE3_POWER_OPREGION ++ tristate "Surface 3 battery platform operation region support" ++ help ++ Select this option to enable support for ACPI operation ++ region of the Surface 3 battery platform driver. ++ + menuconfig PMIC_OPREGION + bool "PMIC (Power Management Integrated Circuit) operation region support" + help +diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile +index 6d59aa109a91..a6f02538cd65 100644 +--- a/drivers/acpi/Makefile ++++ b/drivers/acpi/Makefile +@@ -103,6 +103,8 @@ obj-$(CONFIG_ACPI_APEI) += apei/ + + obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o + ++obj-$(CONFIG_ACPI_SURFACE3_POWER_OPREGION) += surface3_power.o ++ + obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o + obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o + obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o +diff --git a/drivers/acpi/surface3_power.c b/drivers/acpi/surface3_power.c +new file mode 100644 +index 000000000000..6d59c7f6e4a5 +--- /dev/null ++++ b/drivers/acpi/surface3_power.c +@@ -0,0 +1,702 @@ ++/* ++ * Supports for the power IC on the Surface 3 tablet. ++ * ++ * (C) Copyright 2016-2017 Red Hat, Inc ++ * (C) Copyright 2016-2017 Benjamin Tissoires ++ * (C) Copyright 2016 Stephen Just ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 ++ * of the License. ++ */ ++ ++/* ++ * This driver has been reverse-engineered by parsing the DSDT of the Surface 3 ++ * and looking at the registers of the chips. ++ * ++ * The DSDT allowed to find out that: ++ * - the driver is required for the ACPI BAT0 device to communicate to the chip ++ * through an operation region. ++ * - the various defines for the operation region functions to communicate with ++ * this driver ++ * - the DSM 3f99e367-6220-4955-8b0f-06ef2ae79412 allows to trigger ACPI ++ * events to BAT0 (the code is all available in the DSDT). ++ * ++ * Further findings regarding the 2 chips declared in the MSHW0011 are: ++ * - there are 2 chips declared: ++ * . 0x22 seems to control the ADP1 line status (and probably the charger) ++ * . 0x55 controls the battery directly ++ * - the battery chip uses a SMBus protocol (using plain SMBus allows non ++ * destructive commands): ++ * . the commands/registers used are in the range 0x00..0x7F ++ * . if bit 8 (0x80) is set in the SMBus command, the returned value is the ++ * same as when it is not set. There is a high chance this bit is the ++ * read/write ++ * . the various registers semantic as been deduced by observing the register ++ * dumps. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define POLL_INTERVAL (2 * HZ) ++ ++static bool dump_registers; ++module_param_named(dump_registers, dump_registers, bool, 0644); ++MODULE_PARM_DESC(dump_registers, ++ "Dump the SMBus register at probe (debugging only)."); ++ ++struct mshw0011_data { ++ struct i2c_client *adp1; ++ struct i2c_client *bat0; ++ unsigned short notify_version; ++ struct task_struct *poll_task; ++ bool kthread_running; ++ ++ bool charging; ++ bool bat_charging; ++ u8 trip_point; ++ s32 full_capacity; ++}; ++ ++struct mshw0011_lookup { ++ struct mshw0011_data *cdata; ++ unsigned int n; ++ unsigned int index; ++ int addr; ++}; ++ ++struct mshw0011_handler_data { ++ struct acpi_connection_info info; ++ struct i2c_client *client; ++}; ++ ++struct bix { ++ u32 revision; ++ u32 power_unit; ++ u32 design_capacity; ++ u32 last_full_charg_capacity; ++ u32 battery_technology; ++ u32 design_voltage; ++ u32 design_capacity_of_warning; ++ u32 design_capacity_of_low; ++ u32 cycle_count; ++ u32 measurement_accuracy; ++ u32 max_sampling_time; ++ u32 min_sampling_time; ++ u32 max_average_interval; ++ u32 min_average_interval; ++ u32 battery_capacity_granularity_1; ++ u32 battery_capacity_granularity_2; ++ char model[10]; ++ char serial[10]; ++ char type[10]; ++ char OEM[10]; ++} __packed; ++ ++struct bst { ++ u32 battery_state; ++ s32 battery_present_rate; ++ u32 battery_remaining_capacity; ++ u32 battery_present_voltage; ++} __packed; ++ ++struct gsb_command { ++ u8 arg0; ++ u8 arg1; ++ u8 arg2; ++} __packed; ++ ++struct gsb_buffer { ++ u8 status; ++ u8 len; ++ u8 ret; ++ union { ++ struct gsb_command cmd; ++ struct bst bst; ++ struct bix bix; ++ } __packed; ++} __packed; ++ ++#define ACPI_BATTERY_STATE_DISCHARGING 0x1 ++#define ACPI_BATTERY_STATE_CHARGING 0x2 ++#define ACPI_BATTERY_STATE_CRITICAL 0x4 ++ ++#define MSHW0011_CMD_DEST_BAT0 0x01 ++#define MSHW0011_CMD_DEST_ADP1 0x03 ++ ++#define MSHW0011_CMD_BAT0_STA 0x01 ++#define MSHW0011_CMD_BAT0_BIX 0x02 ++#define MSHW0011_CMD_BAT0_BCT 0x03 ++#define MSHW0011_CMD_BAT0_BTM 0x04 ++#define MSHW0011_CMD_BAT0_BST 0x05 ++#define MSHW0011_CMD_BAT0_BTP 0x06 ++#define MSHW0011_CMD_ADP1_PSR 0x07 ++#define MSHW0011_CMD_BAT0_PSOC 0x09 ++#define MSHW0011_CMD_BAT0_PMAX 0x0a ++#define MSHW0011_CMD_BAT0_PSRC 0x0b ++#define MSHW0011_CMD_BAT0_CHGI 0x0c ++#define MSHW0011_CMD_BAT0_ARTG 0x0d ++ ++#define MSHW0011_NOTIFY_GET_VERSION 0x00 ++#define MSHW0011_NOTIFY_ADP1 0x01 ++#define MSHW0011_NOTIFY_BAT0_BST 0x02 ++#define MSHW0011_NOTIFY_BAT0_BIX 0x05 ++ ++#define MSHW0011_ADP1_REG_PSR 0x04 ++ ++#define MSHW0011_BAT0_REG_CAPACITY 0x0c ++#define MSHW0011_BAT0_REG_FULL_CHG_CAPACITY 0x0e ++#define MSHW0011_BAT0_REG_DESIGN_CAPACITY 0x40 ++#define MSHW0011_BAT0_REG_VOLTAGE 0x08 ++#define MSHW0011_BAT0_REG_RATE 0x14 ++#define MSHW0011_BAT0_REG_OEM 0x45 ++#define MSHW0011_BAT0_REG_TYPE 0x4e ++#define MSHW0011_BAT0_REG_SERIAL_NO 0x56 ++#define MSHW0011_BAT0_REG_CYCLE_CNT 0x6e ++ ++#define MSHW0011_EV_2_5 0x1ff ++ ++static int mshw0011_i2c_read_block(struct i2c_client *client, u8 reg, u8 *buf, ++ int len) ++{ ++ int status, i; ++ ++ for (i = 0; i < len; i++) { ++ status = i2c_smbus_read_byte_data(client, reg + i); ++ if (status < 0) { ++ buf[i] = 0xff; ++ continue; ++ } ++ ++ buf[i] = (u8)status; ++ } ++ ++ return 0; ++} ++ ++static int ++mshw0011_notify(struct mshw0011_data *cdata, u8 arg1, u8 arg2, ++ unsigned int *ret_value) ++{ ++ static const uuid_le mshw0011_guid = ++ GUID_INIT(0x3F99E367, 0x6220, 0x4955, ++ 0x8B, 0x0F, 0x06, 0xEF, 0x2A, 0xE7, 0x94, 0x12); ++ union acpi_object *obj; ++ struct acpi_device *adev; ++ acpi_handle handle; ++ unsigned int i; ++ ++ handle = ACPI_HANDLE(&cdata->adp1->dev); ++ if (!handle || acpi_bus_get_device(handle, &adev)) ++ return -ENODEV; ++ ++ obj = acpi_evaluate_dsm_typed(handle, &mshw0011_guid, arg1, arg2, NULL, ++ ACPI_TYPE_BUFFER); ++ if (!obj) { ++ dev_err(&cdata->adp1->dev, "device _DSM execution failed\n"); ++ return -ENODEV; ++ } ++ ++ *ret_value = 0; ++ for (i = 0; i < obj->buffer.length; i++) ++ *ret_value |= obj->buffer.pointer[i] << (i * 8); ++ ++ ACPI_FREE(obj); ++ return 0; ++} ++ ++static const struct bix default_bix = { ++ .revision = 0x00, ++ .power_unit = 0x01, ++ .design_capacity = 0x1dca, ++ .last_full_charg_capacity = 0x1dca, ++ .battery_technology = 0x01, ++ .design_voltage = 0x10df, ++ .design_capacity_of_warning = 0x8f, ++ .design_capacity_of_low = 0x47, ++ .cycle_count = 0xffffffff, ++ .measurement_accuracy = 0x00015f90, ++ .max_sampling_time = 0x03e8, ++ .min_sampling_time = 0x03e8, ++ .max_average_interval = 0x03e8, ++ .min_average_interval = 0x03e8, ++ .battery_capacity_granularity_1 = 0x45, ++ .battery_capacity_granularity_2 = 0x11, ++ .model = "P11G8M", ++ .serial = "", ++ .type = "LION", ++ .OEM = "", ++}; ++ ++static int mshw0011_bix(struct mshw0011_data *cdata, struct bix *bix) ++{ ++ struct i2c_client *client = cdata->bat0; ++ char buf[10]; ++ int ret; ++ ++ *bix = default_bix; ++ ++ /* get design capacity */ ++ ret = i2c_smbus_read_word_data(client, ++ MSHW0011_BAT0_REG_DESIGN_CAPACITY); ++ if (ret < 0) { ++ dev_err(&client->dev, "Error reading design capacity: %d\n", ++ ret); ++ return ret; ++ } ++ bix->design_capacity = le16_to_cpu(ret); ++ ++ /* get last full charge capacity */ ++ ret = i2c_smbus_read_word_data(client, ++ MSHW0011_BAT0_REG_FULL_CHG_CAPACITY); ++ if (ret < 0) { ++ dev_err(&client->dev, ++ "Error reading last full charge capacity: %d\n", ret); ++ return ret; ++ } ++ bix->last_full_charg_capacity = le16_to_cpu(ret); ++ ++ /* get serial number */ ++ ret = mshw0011_i2c_read_block(client, MSHW0011_BAT0_REG_SERIAL_NO, ++ buf, 10); ++ if (ret) { ++ dev_err(&client->dev, "Error reading serial no: %d\n", ret); ++ return ret; ++ } ++ memcpy(bix->serial, buf + 7, 3); ++ memcpy(bix->serial + 3, buf, 6); ++ bix->serial[9] = '\0'; ++ ++ /* get cycle count */ ++ ret = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_CYCLE_CNT); ++ if (ret < 0) { ++ dev_err(&client->dev, "Error reading cycle count: %d\n", ret); ++ return ret; ++ } ++ bix->cycle_count = le16_to_cpu(ret); ++ ++ /* get OEM name */ ++ ret = mshw0011_i2c_read_block(client, MSHW0011_BAT0_REG_OEM, buf, 4); ++ if (ret) { ++ dev_err(&client->dev, "Error reading cycle count: %d\n", ret); ++ return ret; ++ } ++ memcpy(bix->OEM, buf, 3); ++ bix->OEM[4] = '\0'; ++ ++ return 0; ++} ++ ++static int mshw0011_bst(struct mshw0011_data *cdata, struct bst *bst) ++{ ++ struct i2c_client *client = cdata->bat0; ++ int rate, capacity, voltage, state; ++ s16 tmp; ++ ++ rate = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_RATE); ++ if (rate < 0) ++ return rate; ++ ++ capacity = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_CAPACITY); ++ if (capacity < 0) ++ return capacity; ++ ++ voltage = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_VOLTAGE); ++ if (voltage < 0) ++ return voltage; ++ ++ tmp = le16_to_cpu(rate); ++ bst->battery_present_rate = abs((s32)tmp); ++ ++ state = 0; ++ if ((s32) tmp > 0) ++ state |= ACPI_BATTERY_STATE_CHARGING; ++ else if ((s32) tmp < 0) ++ state |= ACPI_BATTERY_STATE_DISCHARGING; ++ bst->battery_state = state; ++ ++ bst->battery_remaining_capacity = le16_to_cpu(capacity); ++ bst->battery_present_voltage = le16_to_cpu(voltage); ++ ++ return 0; ++} ++ ++static int mshw0011_adp_psr(struct mshw0011_data *cdata) ++{ ++ struct i2c_client *client = cdata->adp1; ++ int ret; ++ ++ ret = i2c_smbus_read_byte_data(client, MSHW0011_ADP1_REG_PSR); ++ if (ret < 0) ++ return ret; ++ ++ return ret; ++} ++ ++static int mshw0011_isr(struct mshw0011_data *cdata) ++{ ++ struct bst bst; ++ struct bix bix; ++ int ret; ++ bool status, bat_status; ++ ++ ret = mshw0011_adp_psr(cdata); ++ if (ret < 0) ++ return ret; ++ ++ status = ret; ++ ++ if (status != cdata->charging) ++ mshw0011_notify(cdata, cdata->notify_version, ++ MSHW0011_NOTIFY_ADP1, &ret); ++ ++ cdata->charging = status; ++ ++ ret = mshw0011_bst(cdata, &bst); ++ if (ret < 0) ++ return ret; ++ ++ bat_status = bst.battery_state; ++ ++ if (bat_status != cdata->bat_charging) ++ mshw0011_notify(cdata, cdata->notify_version, ++ MSHW0011_NOTIFY_BAT0_BST, &ret); ++ ++ cdata->bat_charging = bat_status; ++ ++ ret = mshw0011_bix(cdata, &bix); ++ if (ret < 0) ++ return ret; ++ if (bix.last_full_charg_capacity != cdata->full_capacity) ++ mshw0011_notify(cdata, cdata->notify_version, ++ MSHW0011_NOTIFY_BAT0_BIX, &ret); ++ ++ cdata->full_capacity = bix.last_full_charg_capacity; ++ ++ return 0; ++} ++ ++static int mshw0011_poll_task(void *data) ++{ ++ struct mshw0011_data *cdata = data; ++ int ret = 0; ++ ++ cdata->kthread_running = true; ++ ++ set_freezable(); ++ ++ while (!kthread_should_stop()) { ++ schedule_timeout_interruptible(POLL_INTERVAL); ++ try_to_freeze(); ++ ret = mshw0011_isr(data); ++ if (ret) ++ break; ++ } ++ ++ cdata->kthread_running = false; ++ return ret; ++} ++ ++static acpi_status ++mshw0011_space_handler(u32 function, acpi_physical_address command, ++ u32 bits, u64 *value64, ++ void *handler_context, void *region_context) ++{ ++ struct gsb_buffer *gsb = (struct gsb_buffer *)value64; ++ struct mshw0011_handler_data *data = handler_context; ++ struct acpi_connection_info *info = &data->info; ++ struct acpi_resource_i2c_serialbus *sb; ++ struct i2c_client *client = data->client; ++ struct mshw0011_data *cdata = i2c_get_clientdata(client); ++ struct acpi_resource *ares; ++ u32 accessor_type = function >> 16; ++ acpi_status ret; ++ int status = 1; ++ ++ ret = acpi_buffer_to_resource(info->connection, info->length, &ares); ++ if (ACPI_FAILURE(ret)) ++ return ret; ++ ++ if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) { ++ ret = AE_BAD_PARAMETER; ++ goto err; ++ } ++ ++ sb = &ares->data.i2c_serial_bus; ++ if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) { ++ ret = AE_BAD_PARAMETER; ++ goto err; ++ } ++ ++ if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) { ++ ret = AE_BAD_PARAMETER; ++ goto err; ++ } ++ ++ if (gsb->cmd.arg0 == MSHW0011_CMD_DEST_ADP1 && ++ gsb->cmd.arg1 == MSHW0011_CMD_ADP1_PSR) { ++ ret = mshw0011_adp_psr(cdata); ++ if (ret >= 0) { ++ status = ret; ++ ret = 0; ++ } ++ goto out; ++ } ++ ++ if (gsb->cmd.arg0 != MSHW0011_CMD_DEST_BAT0) { ++ ret = AE_BAD_PARAMETER; ++ goto err; ++ } ++ ++ switch (gsb->cmd.arg1) { ++ case MSHW0011_CMD_BAT0_STA: ++ ret = 0; ++ break; ++ case MSHW0011_CMD_BAT0_BIX: ++ ret = mshw0011_bix(cdata, &gsb->bix); ++ break; ++ case MSHW0011_CMD_BAT0_BTP: ++ ret = 0; ++ cdata->trip_point = gsb->cmd.arg2; ++ break; ++ case MSHW0011_CMD_BAT0_BST: ++ ret = mshw0011_bst(cdata, &gsb->bst); ++ break; ++ default: ++ pr_info("command(0x%02x) is not supported.\n", gsb->cmd.arg1); ++ ret = AE_BAD_PARAMETER; ++ goto err; ++ } ++ ++ out: ++ gsb->ret = status; ++ gsb->status = 0; ++ ++ err: ++ ACPI_FREE(ares); ++ return ret; ++} ++ ++static int mshw0011_install_space_handler(struct i2c_client *client) ++{ ++ acpi_handle handle; ++ struct mshw0011_handler_data *data; ++ acpi_status status; ++ ++ handle = ACPI_HANDLE(&client->dev); ++ ++ if (!handle) ++ return -ENODEV; ++ ++ data = kzalloc(sizeof(struct mshw0011_handler_data), ++ GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->client = client; ++ status = acpi_bus_attach_private_data(handle, (void *)data); ++ if (ACPI_FAILURE(status)) { ++ kfree(data); ++ return -ENOMEM; ++ } ++ ++ status = acpi_install_address_space_handler(handle, ++ ACPI_ADR_SPACE_GSBUS, ++ &mshw0011_space_handler, ++ NULL, ++ data); ++ if (ACPI_FAILURE(status)) { ++ dev_err(&client->dev, "Error installing i2c space handler\n"); ++ acpi_bus_detach_private_data(handle); ++ kfree(data); ++ return -ENOMEM; ++ } ++ ++ acpi_walk_dep_device_list(handle); ++ return 0; ++} ++ ++static void mshw0011_remove_space_handler(struct i2c_client *client) ++{ ++ acpi_handle handle = ACPI_HANDLE(&client->dev); ++ struct mshw0011_handler_data *data; ++ acpi_status status; ++ ++ if (!handle) ++ return; ++ ++ acpi_remove_address_space_handler(handle, ++ ACPI_ADR_SPACE_GSBUS, ++ &mshw0011_space_handler); ++ ++ status = acpi_bus_get_private_data(handle, (void **)&data); ++ if (ACPI_SUCCESS(status)) ++ kfree(data); ++ ++ acpi_bus_detach_private_data(handle); ++} ++ ++static int acpi_find_i2c(struct acpi_resource *ares, void *data) ++{ ++ struct mshw0011_lookup *lookup = data; ++ ++ if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) ++ return 1; ++ ++ if (lookup->n++ == lookup->index && !lookup->addr) ++ lookup->addr = ares->data.i2c_serial_bus.slave_address; ++ ++ return 1; ++} ++ ++static int mshw0011_i2c_resource_lookup(struct mshw0011_data *cdata, ++ unsigned int index) ++{ ++ struct i2c_client *client = cdata->adp1; ++ struct acpi_device *adev = ACPI_COMPANION(&client->dev); ++ struct mshw0011_lookup lookup = { ++ .cdata = cdata, ++ .index = index, ++ }; ++ struct list_head res_list; ++ int ret; ++ ++ INIT_LIST_HEAD(&res_list); ++ ++ ret = acpi_dev_get_resources(adev, &res_list, acpi_find_i2c, &lookup); ++ if (ret < 0) ++ return ret; ++ ++ acpi_dev_free_resource_list(&res_list); ++ ++ if (!lookup.addr) ++ return -ENOENT; ++ ++ return lookup.addr; ++} ++ ++static void mshw0011_dump_registers(struct i2c_client *client, ++ struct i2c_client *bat0, u8 end_register) ++{ ++ char *rd_buf; ++ char prefix[128]; ++ unsigned int length = end_register + 1; ++ int error; ++ ++ snprintf(prefix, ARRAY_SIZE(prefix), "%s: ", bat0->name); ++ prefix[127] = '\0'; ++ ++ rd_buf = kzalloc(length, GFP_KERNEL); ++ error = mshw0011_i2c_read_block(bat0, 0, rd_buf, length); ++ print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, 16, 1, ++ rd_buf, length, true); ++ ++ kfree(rd_buf); ++} ++ ++static int mshw0011_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &client->dev; ++ struct i2c_client *bat0; ++ struct mshw0011_data *data; ++ int error, version, addr; ++ ++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->adp1 = client; ++ i2c_set_clientdata(client, data); ++ ++ addr = mshw0011_i2c_resource_lookup(data, 1); ++ if (addr < 0) ++ return addr; ++ ++ bat0 = i2c_new_dummy(client->adapter, addr); ++ if (!bat0) ++ return -ENOMEM; ++ ++ data->bat0 = bat0; ++ i2c_set_clientdata(bat0, data); ++ ++ if (dump_registers) { ++ mshw0011_dump_registers(client, client, 0xFF); ++ mshw0011_dump_registers(client, bat0, 0x80); ++ } ++ ++ error = mshw0011_notify(data, 1, MSHW0011_NOTIFY_GET_VERSION, &version); ++ if (error) ++ goto out_err; ++ ++ data->notify_version = version == MSHW0011_EV_2_5; ++ ++ data->poll_task = kthread_run(mshw0011_poll_task, data, "mshw0011_adp"); ++ if (IS_ERR(data->poll_task)) { ++ error = PTR_ERR(data->poll_task); ++ dev_err(&client->dev, "Unable to run kthread err %d\n", error); ++ goto out_err; ++ } ++ ++ error = mshw0011_install_space_handler(client); ++ if (error) ++ goto out_err; ++ ++ return 0; ++ ++out_err: ++ if (data->kthread_running) ++ kthread_stop(data->poll_task); ++ i2c_unregister_device(data->bat0); ++ return error; ++} ++ ++static int mshw0011_remove(struct i2c_client *client) ++{ ++ struct mshw0011_data *cdata = i2c_get_clientdata(client); ++ ++ mshw0011_remove_space_handler(client); ++ ++ if (cdata->kthread_running) ++ kthread_stop(cdata->poll_task); ++ ++ i2c_unregister_device(cdata->bat0); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id mshw0011_id[] = { ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, mshw0011_id); ++ ++#ifdef CONFIG_ACPI ++static const struct acpi_device_id mshw0011_acpi_match[] = { ++ { "MSHW0011", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(acpi, mshw0011_acpi_match); ++#endif ++ ++static struct i2c_driver mshw0011_driver = { ++ .probe = mshw0011_probe, ++ .remove = mshw0011_remove, ++ .id_table = mshw0011_id, ++ .driver = { ++ .name = "mshw0011", ++ .acpi_match_table = ACPI_PTR(mshw0011_acpi_match), ++ }, ++}; ++module_i2c_driver(mshw0011_driver); ++ ++MODULE_AUTHOR("Benjamin Tissoires "); ++MODULE_DESCRIPTION("mshw0011 driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.17.1 + diff --git a/linux_surface/patches/0010-surface-dock.patch b/linux_surface/patches/0010-surface-dock.patch new file mode 100644 index 0000000..9d3630b --- /dev/null +++ b/linux_surface/patches/0010-surface-dock.patch @@ -0,0 +1,25 @@ +From 9e407a993e67743172e1ad895072fc74b2289f8e Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Sun, 27 Jan 2019 10:54:15 -0500 +Subject: [PATCH 10/11] surface-dock + +--- + drivers/usb/core/quirks.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c +index 8bc35d53408b..11fa56cbd769 100644 +--- a/drivers/usb/core/quirks.c ++++ b/drivers/usb/core/quirks.c +@@ -212,6 +212,8 @@ static const struct usb_device_id usb_quirk_list[] = { + /* Cherry Stream G230 2.0 (G85-231) and 3.0 (G85-232) */ + { USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME }, + ++ { USB_DEVICE(0x045e, 0x07c6), .driver_info = USB_QUIRK_NO_LPM }, ++ + /* Logitech HD Pro Webcams C920, C920-C, C925e and C930e */ + { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT }, + { USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT }, +-- +2.17.1 + diff --git a/linux_surface/patches/0011-mwlwifi.patch b/linux_surface/patches/0011-mwlwifi.patch new file mode 100644 index 0000000..1f1abfd --- /dev/null +++ b/linux_surface/patches/0011-mwlwifi.patch @@ -0,0 +1,19755 @@ +From 2a1eb7e8d76dd753663ef0e240ce83b144487ce8 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Sun, 27 Jan 2019 10:54:37 -0500 +Subject: [PATCH 11/11] mwlwifi + +--- + drivers/net/wireless/marvell/Kconfig | 1 + + drivers/net/wireless/marvell/Makefile | 1 + + drivers/net/wireless/marvell/mwlwifi/Kconfig | 23 + + drivers/net/wireless/marvell/mwlwifi/Makefile | 19 + + .../wireless/marvell/mwlwifi/Makefile.module | 28 + + .../net/wireless/marvell/mwlwifi/README.md | 142 + + drivers/net/wireless/marvell/mwlwifi/core.c | 1086 +++++ + drivers/net/wireless/marvell/mwlwifi/core.h | 517 +++ + .../net/wireless/marvell/mwlwifi/debugfs.c | 2201 ++++++++++ + .../net/wireless/marvell/mwlwifi/debugfs.h | 24 + + .../net/wireless/marvell/mwlwifi/hif/fwcmd.c | 3852 +++++++++++++++++ + .../net/wireless/marvell/mwlwifi/hif/fwcmd.h | 285 ++ + .../wireless/marvell/mwlwifi/hif/hif-ops.h | 297 ++ + .../net/wireless/marvell/mwlwifi/hif/hif.h | 81 + + .../wireless/marvell/mwlwifi/hif/hostcmd.h | 1285 ++++++ + .../wireless/marvell/mwlwifi/hif/pcie/dev.h | 1032 +++++ + .../wireless/marvell/mwlwifi/hif/pcie/fwdl.c | 274 ++ + .../wireless/marvell/mwlwifi/hif/pcie/fwdl.h | 24 + + .../wireless/marvell/mwlwifi/hif/pcie/pcie.c | 1645 +++++++ + .../wireless/marvell/mwlwifi/hif/pcie/rx.c | 540 +++ + .../wireless/marvell/mwlwifi/hif/pcie/rx.h | 25 + + .../marvell/mwlwifi/hif/pcie/rx_ndp.c | 612 +++ + .../marvell/mwlwifi/hif/pcie/rx_ndp.h | 26 + + .../marvell/mwlwifi/hif/pcie/sc4_ddr.h | 965 +++++ + .../wireless/marvell/mwlwifi/hif/pcie/tx.c | 1396 ++++++ + .../wireless/marvell/mwlwifi/hif/pcie/tx.h | 38 + + .../marvell/mwlwifi/hif/pcie/tx_ndp.c | 693 +++ + .../marvell/mwlwifi/hif/pcie/tx_ndp.h | 30 + + ...-workaround-for-80+80-and-160-MHz-channels | 32 + + .../wireless/marvell/mwlwifi/hostapd/README | 26 + + .../net/wireless/marvell/mwlwifi/mac80211.c | 933 ++++ + .../net/wireless/marvell/mwlwifi/mu_mimo.c | 21 + + .../net/wireless/marvell/mwlwifi/mu_mimo.h | 23 + + .../net/wireless/marvell/mwlwifi/sysadpt.h | 86 + + .../net/wireless/marvell/mwlwifi/thermal.c | 182 + + .../net/wireless/marvell/mwlwifi/thermal.h | 42 + + drivers/net/wireless/marvell/mwlwifi/utils.c | 576 +++ + drivers/net/wireless/marvell/mwlwifi/utils.h | 158 + + .../net/wireless/marvell/mwlwifi/vendor_cmd.c | 136 + + .../net/wireless/marvell/mwlwifi/vendor_cmd.h | 60 + + 40 files changed, 19417 insertions(+) + create mode 100644 drivers/net/wireless/marvell/mwlwifi/Kconfig + create mode 100644 drivers/net/wireless/marvell/mwlwifi/Makefile + create mode 100644 drivers/net/wireless/marvell/mwlwifi/Makefile.module + create mode 100644 drivers/net/wireless/marvell/mwlwifi/README.md + create mode 100644 drivers/net/wireless/marvell/mwlwifi/core.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/core.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/debugfs.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/debugfs.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/hif-ops.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/hif.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/hostcmd.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/dev.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/pcie.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/sc4_ddr.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hostapd/700-interoperability-workaround-for-80+80-and-160-MHz-channels + create mode 100644 drivers/net/wireless/marvell/mwlwifi/hostapd/README + create mode 100644 drivers/net/wireless/marvell/mwlwifi/mac80211.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/mu_mimo.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/mu_mimo.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/sysadpt.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/thermal.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/thermal.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/utils.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/utils.h + create mode 100644 drivers/net/wireless/marvell/mwlwifi/vendor_cmd.c + create mode 100644 drivers/net/wireless/marvell/mwlwifi/vendor_cmd.h + +diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig +index 27038901d3ee..3982c80f6df9 100644 +--- a/drivers/net/wireless/marvell/Kconfig ++++ b/drivers/net/wireless/marvell/Kconfig +@@ -14,6 +14,7 @@ if WLAN_VENDOR_MARVELL + source "drivers/net/wireless/marvell/libertas/Kconfig" + source "drivers/net/wireless/marvell/libertas_tf/Kconfig" + source "drivers/net/wireless/marvell/mwifiex/Kconfig" ++source "drivers/net/wireless/marvell/mwlwifi/Kconfig" + + config MWL8K + tristate "Marvell 88W8xxx PCI/PCIe Wireless support" +diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile +index 1b0a7d2bc8e6..04dff3388a41 100644 +--- a/drivers/net/wireless/marvell/Makefile ++++ b/drivers/net/wireless/marvell/Makefile +@@ -2,5 +2,6 @@ obj-$(CONFIG_LIBERTAS) += libertas/ + + obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ + obj-$(CONFIG_MWIFIEX) += mwifiex/ ++obj-$(CONFIG_MWLWIFI) += mwlwifi/ + + obj-$(CONFIG_MWL8K) += mwl8k.o +diff --git a/drivers/net/wireless/marvell/mwlwifi/Kconfig b/drivers/net/wireless/marvell/mwlwifi/Kconfig +new file mode 100644 +index 000000000000..a9bcb9cd4100 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/Kconfig +@@ -0,0 +1,23 @@ ++config MWLWIFI ++ tristate "Marvell Avastar 88W8864/88W8897 PCIe driver (mac80211 compatible)" ++ depends on PCI && MAC80211 ++ select FW_LOADER ++ ---help--- ++ Select to build the driver supporting the: ++ ++ Marvell Wireless Wi-Fi 88W8864 modules ++ Marvell Wireless Wi-Fi 88W8897 modules ++ ++ This driver uses the kernel's mac80211 subsystem. ++ ++ If you want to compile the driver as a module (= code which can be ++ inserted in and removed from the running kernel whenever you want), ++ say M here and read . The ++ module will be called mwlwifi. ++ ++ NOTE: Selecting this driver may cause conflict with MWIFIEX driver ++ that also operates on the same part number 88W8897. Users should ++ select either MWIFIEX or MWLWIFI, not both. MWIFIEX is fullmac, ++ supporting more comprehensive client functions for laptops/embedded ++ devices. MWLWIFI is mac80211-based for full AP/Wireless Bridge. ++ +diff --git a/drivers/net/wireless/marvell/mwlwifi/Makefile b/drivers/net/wireless/marvell/mwlwifi/Makefile +new file mode 100644 +index 000000000000..061833703c7f +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/Makefile +@@ -0,0 +1,19 @@ ++obj-$(CONFIG_MWLWIFI) += mwlwifi.o ++ ++mwlwifi-objs += core.o ++mwlwifi-objs += mac80211.o ++mwlwifi-objs += mu_mimo.o ++mwlwifi-objs += vendor_cmd.o ++mwlwifi-objs += utils.o ++mwlwifi-$(CONFIG_THERMAL) += thermal.o ++mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o ++mwlwifi-objs += hif/fwcmd.o ++mwlwifi-objs += hif/pcie/pcie.o ++mwlwifi-objs += hif/pcie/fwdl.o ++mwlwifi-objs += hif/pcie/tx.o ++mwlwifi-objs += hif/pcie/rx.o ++mwlwifi-objs += hif/pcie/tx_ndp.o ++mwlwifi-objs += hif/pcie/rx_ndp.o ++ ++ccflags-y += -I$(src) ++ccflags-y += -D__CHECK_ENDIAN__ +diff --git a/drivers/net/wireless/marvell/mwlwifi/Makefile.module b/drivers/net/wireless/marvell/mwlwifi/Makefile.module +new file mode 100644 +index 000000000000..d11a1b88cab6 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/Makefile.module +@@ -0,0 +1,28 @@ ++obj-m += mwlwifi.o ++ ++mwlwifi-objs += core.o ++mwlwifi-objs += mac80211.o ++mwlwifi-objs += mu_mimo.o ++mwlwifi-objs += vendor_cmd.o ++mwlwifi-objs += utils.o ++mwlwifi-$(CONFIG_THERMAL) += thermal.o ++mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o ++mwlwifi-objs += hif/fwcmd.o ++mwlwifi-objs += hif/pcie/pcie.o ++mwlwifi-objs += hif/pcie/fwdl.o ++mwlwifi-objs += hif/pcie/tx.o ++mwlwifi-objs += hif/pcie/rx.o ++mwlwifi-objs += hif/pcie/tx_ndp.o ++mwlwifi-objs += hif/pcie/rx_ndp.o ++ ++ccflags-y += -I$(src) ++ccflags-y += -O2 -funroll-loops -D__CHECK_ENDIAN__ ++ ++all: ++ $(MAKE) -C $(KDIR) M=$(PWD) ++ ++clean: ++ rm -f *.a *.s *.ko *.ko.cmd *.mod.* .mwlwifi.* modules.order Module.symvers ++ rm -rf .tmp_versions ++ find . -name ".*.o.cmd" -exec rm -f {} \; ++ find . -name "*.o" -exec rm -f {} \; +diff --git a/drivers/net/wireless/marvell/mwlwifi/README.md b/drivers/net/wireless/marvell/mwlwifi/README.md +new file mode 100644 +index 000000000000..788c5d4dc80d +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/README.md +@@ -0,0 +1,142 @@ ++# mwlwifi ++mac80211 driver for the Marvell 88W8x64 802.11ac chip ++ ++## Building mwlwifi With OpenWrt/LEDE ++1. Modify `package/kernel/mwlwifi/Makefile`: ++ ``` ++ PKG_VERSION:=10.3.0.17-20160601 ++ PKG_SOURCE_VERSION:=4bb95ba1aeccce506a95499b49b9b844ecfae8a1 ++ ``` ++ ++2. Rename `package/kernel/mwlwifi/patches` to `package/kernel/mwlwifi/patches.tmp`. ++3. Run the following commands: ++ ```sh ++ make package/kernel/mwlwifi/clean ++ make V=s (-jx) ++ ``` ++ ++### Special Considerations ++* After driver 10.3.0.17-20160603, [MAX-MPDU-7991] should be removed from vht_capab command of hostapd. ++ ++* Hostpad must include the following commit for 160 MHz operation: ++ ``` ++ commit 03a72eacda5d9a1837a74387081596a0d5466ec1 ++ Author: Jouni Malinen ++ Date: Thu Dec 17 18:39:19 2015 +0200 ++ ++ VHT: Add an interoperability workaround for 80+80 and 160 MHz channels ++ ++ Number of deployed 80 MHz capable VHT stations that do not support 80+80 ++ and 160 MHz bandwidths seem to misbehave when trying to connect to an AP ++ that advertises 80+80 or 160 MHz channel bandwidth in the VHT Operation ++ element. To avoid such issues with deployed devices, modify the design ++ based on newly proposed IEEE 802.11 standard changes. ++ ++ This allows poorly implemented VHT 80 MHz stations to connect with the ++ AP in 80 MHz mode. 80+80 and 160 MHz capable stations need to support ++ the new workaround mechanism to allow full bandwidth to be used. ++ However, there are more or less no impacted station with 80+80/160 ++ capability deployed. ++ ++ Signed-off-by: Jouni Malinen jouni@qca.qualcomm.com ++ ++ Note: After hostapd package 2016-06-15, this commit is already included. ++ ``` ++ ++* In order to let STA mode to support 160 MHz operation, mac80211 package should be 2016-10-08 or later. ++ ++* WiFi device does not use HT rates when using TKIP as the encryption cipher. If you want to have good performance, please use AES only. ++ ++* DTS parameters for mwlwifi driver (pcie@X,0): ++ ```sh ++ #Disable 2g band ++ marvell,2ghz = <0>; ++ ++ #Disable 5g band ++ marvell,5ghz = <0>; ++ ++ #Specify antenna number, default is 4x4. For WRT1200AC, you must set these values to 2x2. ++ marvell,chainmask = <4 4>; ++ ++ #Specify external power table. If your device needs external power table, you must provide the power table via this parameter, otherwise the Tx power will be pretty low. ++ marvell,powertable ++ ``` ++ ++ To see if your device needs/accepts an external power table or not, run the following: ++ ```sh ++ cat /sys/kernel/debug/ieee80211/phy0/mwlwifi/info ++ ``` ++ ++ You should see a line in the results which looks like the following: ++ ```sh ++ power table loaded from dts: no ++ ``` ++ ++ If it is "no", it does not allow you to load external power table (for newer devices due to FCC regulations). If it is "yes", you must provide power table in DTS file (for older devices). ++ ++* Changing interrupt to different CPU cores: ++ ```sh ++ #Use CPU0: ++ echo 1 > /proc/irq/irq number of phy0 or phy1/smp_affinity ++ ++ #Use CPU1: ++ echo 2 > /proc/irq/irq number of phy0 or phy1/smp_affinity ++ ``` ++ ++* Note for DFS of WRT3200ACM (88W8964): ++ ++ All WRT3200ACM devices are programmed with device power table. Mwlwifi driver will base on region code to set country code for your device and it will not allow you to change country code. There are another wifi (phy2) on WRT3200ACM which is not mwlwifi. It will allow you to change country code. Under this case, country code setting will be conflicted and it will let DFS can't work. ++ ++ There are two ways to resolve this problem: ++ * Please don't change country code and let mwlwifi set it for you. ++ * Remove phy2. Under this case, even though you change country code, mwlwifi will reject it. Because phy2 is not existed, country code setting won't be conflicted. To do this, run the following commands (for OpenWrt/LEDE): ++ ++ ```sh ++ opkg remove kmod-mwifiex-sdio ++ opkg remove mwifiex-sdio-firmware ++ reboot ++ ``` ++ ++ The better way is let mwlwifi set country code for you. ++ ++## Replacing mwlwifi on a Current OpenWrt/LEDE Build ++ ++1. Establish a symbolic link to your working mwlwifi directory with current mwlwifi package name under directory "dl": ++ ```sh ++ ls -l mwlwifi* ++ ``` ++ ++ You should see something like the following: ++ ```sh ++ lrwxrwxrwx 1 dlin dlin 48 mwlwifi-10.3.2.0-20170110 -> /home/dlin/home2/projects/github/mwlwifi ++ ++ -rw-r--r-- 1 dlin dlin 4175136 mwlwifi-10.3.2.0-20170110.tar.xz ++ ``` ++ ++2. Back up original mwlwifi package and tar your working mwlwifi to replace original mwlwifi package: ++ ++ ```sh ++ tar Jcvf mwlwifi-10.3.2.0-20170110.tar.xz mwlwifi-10.3.2.0-20170110/. ++ ``` ++ ++3. You can use `make V=s` to build the whole image or `make V=s package/kernel/mwlwifi/compile` to build mwlwifi package. The generated whole image or mwlwifi package can be found under directory "bin". ++ ++Due to package version being the same as previous one, you need to add option `--force-reinstall` when you use `opkg` to update mwlwifi package on your device. ++ ++## Monitor interface for debug ++ ++1. Create moinitor interface mon0: ++ ```sh ++ iw wlan0/wlan1 interface add mon0 type monitor ++ ifconfig mon0 up ++ ``` ++ ++2. Use tcpdump to dump dhcp packets: ++ ```sh ++ tcpdump -vvvi mon0 -n port 67 and port 68 ++ ``` ++ ++3. Use tcpdump to dump icmp packets: ++ ```sh ++ tcpdump -vvvi mon0 icmp ++ ``` +diff --git a/drivers/net/wireless/marvell/mwlwifi/core.c b/drivers/net/wireless/marvell/mwlwifi/core.c +new file mode 100644 +index 000000000000..9d2b5511607e +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/core.c +@@ -0,0 +1,1086 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements core layer related functions. */ ++ ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "vendor_cmd.h" ++#include "thermal.h" ++#include "debugfs.h" ++#include "hif/fwcmd.h" ++#include "hif/hif-ops.h" ++ ++#define CMD_BUF_SIZE 0x4000 ++#define INVALID_WATCHDOG 0xAA ++ ++static const struct ieee80211_channel mwl_channels_24[] = { ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2412, .hw_value = 1, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2417, .hw_value = 2, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2422, .hw_value = 3, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2427, .hw_value = 4, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2432, .hw_value = 5, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2437, .hw_value = 6, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2442, .hw_value = 7, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2447, .hw_value = 8, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2452, .hw_value = 9, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2457, .hw_value = 10, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2462, .hw_value = 11, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2467, .hw_value = 12, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2472, .hw_value = 13, }, ++ { .band = NL80211_BAND_2GHZ, .center_freq = 2484, .hw_value = 14, }, ++}; ++ ++static const struct ieee80211_rate mwl_rates_24[] = { ++ { .bitrate = 10, .hw_value = 2, }, ++ { .bitrate = 20, .hw_value = 4, }, ++ { .bitrate = 55, .hw_value = 11, }, ++ { .bitrate = 110, .hw_value = 22, }, ++ { .bitrate = 220, .hw_value = 44, }, ++ { .bitrate = 60, .hw_value = 12, }, ++ { .bitrate = 90, .hw_value = 18, }, ++ { .bitrate = 120, .hw_value = 24, }, ++ { .bitrate = 180, .hw_value = 36, }, ++ { .bitrate = 240, .hw_value = 48, }, ++ { .bitrate = 360, .hw_value = 72, }, ++ { .bitrate = 480, .hw_value = 96, }, ++ { .bitrate = 540, .hw_value = 108, }, ++}; ++ ++static const struct ieee80211_channel mwl_channels_50[] = { ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5180, .hw_value = 36, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5260, .hw_value = 52, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5280, .hw_value = 56, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5300, .hw_value = 60, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5320, .hw_value = 64, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5500, .hw_value = 100, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5520, .hw_value = 104, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5540, .hw_value = 108, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5560, .hw_value = 112, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5580, .hw_value = 116, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5600, .hw_value = 120, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5620, .hw_value = 124, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5640, .hw_value = 128, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5660, .hw_value = 132, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5680, .hw_value = 136, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5700, .hw_value = 140, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5720, .hw_value = 144, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5745, .hw_value = 149, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5765, .hw_value = 153, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5785, .hw_value = 157, }, ++ { .band = NL80211_BAND_5GHZ, .center_freq = 5805, .hw_value = 161, }, ++}; ++ ++static const struct ieee80211_rate mwl_rates_50[] = { ++ { .bitrate = 60, .hw_value = 12, }, ++ { .bitrate = 90, .hw_value = 18, }, ++ { .bitrate = 120, .hw_value = 24, }, ++ { .bitrate = 180, .hw_value = 36, }, ++ { .bitrate = 240, .hw_value = 48, }, ++ { .bitrate = 360, .hw_value = 72, }, ++ { .bitrate = 480, .hw_value = 96, }, ++ { .bitrate = 540, .hw_value = 108, }, ++}; ++ ++static const struct ieee80211_iface_limit ap_if_limits[] = { ++ { .max = SYSADPT_NUM_OF_AP, .types = BIT(NL80211_IFTYPE_AP) }, ++#if defined(CPTCFG_MAC80211_MESH) || defined(CONFIG_MAC80211_MESH) ++ { .max = SYSADPT_NUM_OF_MESH, .types = BIT(NL80211_IFTYPE_MESH_POINT) }, ++#endif ++ { .max = SYSADPT_NUM_OF_CLIENT, .types = BIT(NL80211_IFTYPE_STATION) }, ++}; ++ ++static const struct ieee80211_iface_combination ap_if_comb = { ++ .limits = ap_if_limits, ++ .n_limits = ARRAY_SIZE(ap_if_limits), ++ .max_interfaces = SYSADPT_NUM_OF_AP, ++ .num_different_channels = 1, ++ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | ++ BIT(NL80211_CHAN_WIDTH_20) | ++ BIT(NL80211_CHAN_WIDTH_40) | ++ BIT(NL80211_CHAN_WIDTH_80) | ++ BIT(NL80211_CHAN_WIDTH_160), ++}; ++ ++struct region_code_mapping { ++ const char *alpha2; ++ u32 region_code; ++}; ++ ++static const struct region_code_mapping regmap[] = { ++ {"US", 0x10}, /* US FCC */ ++ {"CA", 0x20}, /* Canada */ ++ {"FR", 0x30}, /* France */ ++ {"ES", 0x31}, /* Spain */ ++ {"FR", 0x32}, /* France */ ++ {"JP", 0x40}, /* Japan */ ++ {"TW", 0x80}, /* Taiwan */ ++ {"AU", 0x81}, /* Australia */ ++ {"CN", 0x90}, /* China (Asia) */ ++}; ++ ++static int mwl_prepare_cmd_buf(struct mwl_priv *priv) ++{ ++ priv->pcmd_buf = ++ (unsigned short *)dmam_alloc_coherent(priv->dev, ++ CMD_BUF_SIZE, ++ &priv->pphys_cmd_buf, ++ GFP_KERNEL); ++ if (!priv->pcmd_buf) { ++ wiphy_err(priv->hw->wiphy, ++ "cannot alloc memory for command buffer\n"); ++ goto err; ++ } ++ wiphy_debug(priv->hw->wiphy, ++ "priv->pcmd_buf = %p priv->pphys_cmd_buf = %p\n", ++ priv->pcmd_buf, ++ (void *)priv->pphys_cmd_buf); ++ memset(priv->pcmd_buf, 0x00, CMD_BUF_SIZE); ++ ++ return 0; ++ ++err: ++ wiphy_err(priv->hw->wiphy, "command buffer alloc fail\n"); ++ ++ return -EIO; ++} ++ ++static int mwl_init_firmware(struct mwl_priv *priv, const char *fw_name, ++ const char *cal_name, const char *txpwrlmt_name) ++{ ++ int rc = 0; ++ ++ rc = request_firmware((const struct firmware **)&priv->fw_ucode, ++ fw_name, priv->dev); ++ ++ if (rc) { ++ wiphy_err(priv->hw->wiphy, ++ "cannot find firmware image <%s>\n", fw_name); ++ goto err_load_fw; ++ } ++ ++ rc = mwl_hif_download_firmware(priv->hw); ++ if (rc) { ++ wiphy_err(priv->hw->wiphy, ++ "cannot download firmware image <%s>\n", fw_name); ++ goto err_download_fw; ++ } ++ ++ if (cal_name) { ++ if ((request_firmware((const struct firmware **)&priv->cal_data, ++ cal_name, priv->dev)) < 0) ++ wiphy_debug(priv->hw->wiphy, ++ "cannot find calibtration data\n"); ++ } ++ ++ if (txpwrlmt_name) { ++ if ((request_firmware( ++ (const struct firmware **)&priv->txpwrlmt_file, ++ txpwrlmt_name, priv->dev)) < 0) ++ wiphy_debug(priv->hw->wiphy, ++ "cannot find tx power limit data\n"); ++ } ++ ++ return rc; ++ ++err_download_fw: ++ ++ release_firmware(priv->fw_ucode); ++ ++err_load_fw: ++ ++ wiphy_err(priv->hw->wiphy, "firmware init fail\n"); ++ ++ return rc; ++} ++ ++static void mwl_process_of_dts(struct mwl_priv *priv) ++{ ++#ifdef CONFIG_OF ++ struct property *prop; ++ u32 prop_value; ++ ++ priv->dt_node = ++ of_find_node_by_name(mwl_hif_device_node(priv->hw), ++ "mwlwifi"); ++ if (!priv->dt_node) ++ return; ++ ++ /* look for all matching property names */ ++ for_each_property_of_node(priv->dt_node, prop) { ++ if (strcmp(prop->name, "marvell,2ghz") == 0) ++ priv->disable_2g = true; ++ if (strcmp(prop->name, "marvell,5ghz") == 0) ++ priv->disable_5g = true; ++ if (strcmp(prop->name, "marvell,chainmask") == 0) { ++ prop_value = be32_to_cpu(*((__be32 *)prop->value)); ++ if (prop_value == 2) ++ priv->antenna_tx = ANTENNA_TX_2; ++ else if (prop_value == 3) ++ priv->antenna_tx = ANTENNA_TX_3; ++ ++ prop_value = be32_to_cpu(*((__be32 *) ++ (prop->value + 4))); ++ if (prop_value == 2) ++ priv->antenna_rx = ANTENNA_RX_2; ++ else if (prop_value == 3) ++ priv->antenna_rx = ANTENNA_RX_3; ++ } ++ } ++ ++ priv->pwr_node = of_find_node_by_name(priv->dt_node, ++ "marvell,powertable"); ++#endif ++} ++ ++static void mwl_reg_notifier(struct wiphy *wiphy, ++ struct regulatory_request *request) ++{ ++ struct ieee80211_hw *hw; ++ struct mwl_priv *priv; ++#ifdef CONFIG_OF ++ struct property *prop; ++ struct property *fcc_prop = NULL; ++ struct property *etsi_prop = NULL; ++ struct property *specific_prop = NULL; ++ u32 prop_value; ++ int i, j, k; ++#endif ++ ++ hw = wiphy_to_ieee80211_hw(wiphy); ++ priv = hw->priv; ++ ++ if (priv->forbidden_setting) { ++ if (!priv->regulatory_set) { ++ regulatory_hint(wiphy, priv->fw_alpha2); ++ priv->regulatory_set = true; ++ } else { ++ if (memcmp(priv->fw_alpha2, request->alpha2, 2)) ++ regulatory_hint(wiphy, priv->fw_alpha2); ++ } ++ return; ++ } ++ ++ priv->dfs_region = request->dfs_region; ++ ++#ifdef CONFIG_OF ++ if ((priv->chip_type != MWL8997) && (priv->pwr_node)) { ++ for_each_property_of_node(priv->pwr_node, prop) { ++ if (strcmp(prop->name, "FCC") == 0) ++ fcc_prop = prop; ++ if (strcmp(prop->name, "ETSI") == 0) ++ etsi_prop = prop; ++ if ((prop->name[0] == request->alpha2[0]) && ++ (prop->name[1] == request->alpha2[1])) ++ specific_prop = prop; ++ } ++ ++ prop = NULL; ++ ++ if (specific_prop) { ++ prop = specific_prop; ++ } else { ++ if (priv->dfs_region == NL80211_DFS_ETSI) ++ prop = etsi_prop; ++ else ++ prop = fcc_prop; ++ } ++ ++ if (prop) { ++ /* Reset the whole table */ ++ for (i = 0; i < SYSADPT_MAX_NUM_CHANNELS; i++) ++ memset(&priv->tx_pwr_tbl[i], 0, ++ sizeof(struct mwl_tx_pwr_tbl)); ++ ++ /* Load related power table */ ++ i = 0; ++ j = 0; ++ while (i < prop->length) { ++ prop_value = ++ be32_to_cpu(*(__be32 *) ++ (prop->value + i)); ++ priv->tx_pwr_tbl[j].channel = prop_value; ++ i += 4; ++ prop_value = ++ be32_to_cpu(*(__be32 *) ++ (prop->value + i)); ++ priv->tx_pwr_tbl[j].setcap = prop_value; ++ i += 4; ++ for (k = 0; k < SYSADPT_TX_POWER_LEVEL_TOTAL; ++ k++) { ++ prop_value = ++ be32_to_cpu(*(__be32 *) ++ (prop->value + i)); ++ priv->tx_pwr_tbl[j].tx_power[k] = ++ prop_value; ++ i += 4; ++ } ++ prop_value = ++ be32_to_cpu(*(__be32 *) ++ (prop->value + i)); ++ priv->tx_pwr_tbl[j].cdd = ++ (prop_value == 0) ? false : true; ++ i += 4; ++ prop_value = ++ be32_to_cpu(*(__be32 *) ++ (prop->value + i)); ++ priv->tx_pwr_tbl[j].txantenna2 = prop_value; ++ i += 4; ++ j++; ++ } ++ ++ /* Dump loaded power tabel */ ++ wiphy_debug(hw->wiphy, "regdomain: %s\n", prop->name); ++ for (i = 0; i < SYSADPT_MAX_NUM_CHANNELS; i++) { ++ struct mwl_tx_pwr_tbl *pwr_tbl; ++ char disp_buf[64]; ++ char *disp_ptr; ++ ++ pwr_tbl = &priv->tx_pwr_tbl[i]; ++ if (pwr_tbl->channel == 0) ++ break; ++ wiphy_debug(hw->wiphy, ++ "Channel: %d: 0x%x 0x%x 0x%x\n", ++ pwr_tbl->channel, ++ pwr_tbl->setcap, ++ pwr_tbl->cdd, ++ pwr_tbl->txantenna2); ++ disp_ptr = disp_buf; ++ for (j = 0; j < SYSADPT_TX_POWER_LEVEL_TOTAL; ++ j++) { ++ disp_ptr += ++ sprintf(disp_ptr, "%x ", ++ pwr_tbl->tx_power[j]); ++ } ++ wiphy_debug(hw->wiphy, "%s\n", disp_buf); ++ } ++ } ++ } ++#endif ++} ++ ++static void mwl_regd_init(struct mwl_priv *priv) ++{ ++ u8 region_code; ++ int rc; ++ int i; ++ ++ /* hook regulatory domain change notification */ ++ priv->hw->wiphy->reg_notifier = mwl_reg_notifier; ++ ++ if (priv->chip_type == MWL8964) ++ rc = mwl_fwcmd_get_pwr_tbl_sc4(priv->hw, ++ &priv->device_pwr_tbl[0], ++ ®ion_code, ++ &priv->number_of_channels, ++ 0); ++ else ++ rc = mwl_fwcmd_get_device_pwr_tbl(priv->hw, ++ &priv->device_pwr_tbl[0], ++ ®ion_code, ++ &priv->number_of_channels, ++ 0); ++ if (rc) ++ return; ++ ++ priv->forbidden_setting = true; ++ ++ for (i = 1; i < priv->number_of_channels; i++) { ++ if (priv->chip_type == MWL8964) ++ mwl_fwcmd_get_pwr_tbl_sc4(priv->hw, ++ &priv->device_pwr_tbl[i], ++ ®ion_code, ++ &priv->number_of_channels, ++ i); ++ else ++ mwl_fwcmd_get_device_pwr_tbl(priv->hw, ++ &priv->device_pwr_tbl[i], ++ ®ion_code, ++ &priv->number_of_channels, ++ i); ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(regmap); i++) ++ if (regmap[i].region_code == priv->fw_region_code) { ++ memcpy(priv->fw_alpha2, regmap[i].alpha2, 2); ++ break; ++ } ++} ++ ++static void mwl_set_ht_caps(struct mwl_priv *priv, ++ struct ieee80211_supported_band *band) ++{ ++ struct ieee80211_hw *hw; ++ const u8 ant_rx_no[ANTENNA_RX_MAX] = { 3, 1, 2, 3}; ++ int i; ++ ++ hw = priv->hw; ++ ++ band->ht_cap.ht_supported = 1; ++ if (priv->chip_type == MWL8964) ++ band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; ++ band->ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; ++ band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ++ band->ht_cap.cap |= IEEE80211_HT_CAP_SM_PS; ++ band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; ++ band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; ++ band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; ++ ++ if ((priv->chip_type == MWL8997) && ++ (priv->antenna_tx != ANTENNA_TX_1)) { ++ band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; ++ band->ht_cap.cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); ++ } ++ ++ ieee80211_hw_set(hw, AMPDU_AGGREGATION); ++ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); ++ band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; ++ band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_4; ++ ++ for (i = 0; i < ant_rx_no[priv->antenna_rx]; i++) ++ band->ht_cap.mcs.rx_mask[i] = 0xff; ++ band->ht_cap.mcs.rx_mask[4] = 0x01; ++ ++ band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; ++} ++ ++static void mwl_set_vht_caps(struct mwl_priv *priv, ++ struct ieee80211_supported_band *band) ++{ ++ u32 antenna_num = 4; ++ ++ band->vht_cap.vht_supported = 1; ++ ++ if (priv->chip_type == MWL8964) { ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160; ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; ++ } else ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC; ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80; ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1; ++ if (priv->antenna_tx != ANTENNA_TX_1) { ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; ++ if (priv->chip_type == MWL8964) ++ band->vht_cap.cap |= ++ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; ++ } ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; ++ if (priv->chip_type == MWL8964) ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN; ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; ++ if (priv->chip_type == MWL8997) { ++ if (priv->antenna_tx != ANTENNA_TX_1) ++ band->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC; ++ } ++ ++ if (priv->antenna_rx == ANTENNA_RX_1) ++ band->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0xfffe); ++ else if (priv->antenna_rx == ANTENNA_RX_2) ++ band->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0xfffa); ++ else ++ band->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0xffea); ++ ++ if (priv->antenna_tx == ANTENNA_TX_1) { ++ band->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0xfffe); ++ antenna_num = 1; ++ } else if (priv->antenna_tx == ANTENNA_TX_2) { ++ band->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0xfffa); ++ antenna_num = 2; ++ } else ++ band->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0xffea); ++ ++ if (band->vht_cap.cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | ++ IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) { ++ band->vht_cap.cap |= ++ ((antenna_num - 1) << ++ IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT) & ++ IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; ++ } ++ ++ if (band->vht_cap.cap & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | ++ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) { ++ band->vht_cap.cap |= ++ ((antenna_num - 1) << ++ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT) & ++ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; ++ } ++} ++ ++static void mwl_set_caps(struct mwl_priv *priv) ++{ ++ struct ieee80211_hw *hw; ++ ++ hw = priv->hw; ++ ++ /* set up band information for 2.4G */ ++ if (!priv->disable_2g) { ++ BUILD_BUG_ON(sizeof(priv->channels_24) != ++ sizeof(mwl_channels_24)); ++ memcpy(priv->channels_24, mwl_channels_24, ++ sizeof(mwl_channels_24)); ++ ++ BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl_rates_24)); ++ memcpy(priv->rates_24, mwl_rates_24, sizeof(mwl_rates_24)); ++ ++ priv->band_24.band = NL80211_BAND_2GHZ; ++ priv->band_24.channels = priv->channels_24; ++ priv->band_24.n_channels = ARRAY_SIZE(mwl_channels_24); ++ priv->band_24.bitrates = priv->rates_24; ++ priv->band_24.n_bitrates = ARRAY_SIZE(mwl_rates_24); ++ ++ mwl_set_ht_caps(priv, &priv->band_24); ++ mwl_set_vht_caps(priv, &priv->band_24); ++ ++ hw->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band_24; ++ } ++ ++ /* set up band information for 5G */ ++ if (!priv->disable_5g) { ++ BUILD_BUG_ON(sizeof(priv->channels_50) != ++ sizeof(mwl_channels_50)); ++ memcpy(priv->channels_50, mwl_channels_50, ++ sizeof(mwl_channels_50)); ++ ++ BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl_rates_50)); ++ memcpy(priv->rates_50, mwl_rates_50, sizeof(mwl_rates_50)); ++ ++ priv->band_50.band = NL80211_BAND_5GHZ; ++ priv->band_50.channels = priv->channels_50; ++ priv->band_50.n_channels = ARRAY_SIZE(mwl_channels_50); ++ priv->band_50.bitrates = priv->rates_50; ++ priv->band_50.n_bitrates = ARRAY_SIZE(mwl_rates_50); ++ ++ mwl_set_ht_caps(priv, &priv->band_50); ++ mwl_set_vht_caps(priv, &priv->band_50); ++ ++ hw->wiphy->bands[NL80211_BAND_5GHZ] = &priv->band_50; ++ } ++} ++ ++static void mwl_heartbeat_handle(struct work_struct *work) ++{ ++ struct mwl_priv *priv = ++ container_of(work, struct mwl_priv, heartbeat_handle); ++ u32 val; ++ ++ mwl_fwcmd_get_addr_value(priv->hw, 0, 1, &val, 0); ++ priv->heartbeating = false; ++} ++ ++static void mwl_watchdog_ba_events(struct work_struct *work) ++{ ++ int rc; ++ u8 bitmap = 0, stream_index; ++ struct mwl_ampdu_stream *streams; ++ struct mwl_priv *priv = ++ container_of(work, struct mwl_priv, watchdog_ba_handle); ++ ++ rc = mwl_fwcmd_get_watchdog_bitmap(priv->hw, &bitmap); ++ ++ if (rc) ++ return; ++ ++ spin_lock_bh(&priv->stream_lock); ++ ++ /* the bitmap is the hw queue number. Map it to the ampdu queue. */ ++ if (bitmap != INVALID_WATCHDOG) { ++ if (bitmap == priv->ampdu_num) ++ stream_index = 0; ++ else if (bitmap > priv->ampdu_num) ++ stream_index = bitmap - priv->ampdu_num; ++ else ++ stream_index = bitmap + 3; /** queue 0 is stream 3*/ ++ ++ if (bitmap != 0xFF) { ++ /* Check if the stream is in use before disabling it */ ++ streams = &priv->ampdu[stream_index]; ++ ++ if (streams->state == AMPDU_STREAM_ACTIVE) ++ ieee80211_stop_tx_ba_session(streams->sta, ++ streams->tid); ++ } else { ++ for (stream_index = 0; ++ stream_index < priv->ampdu_num; ++ stream_index++) { ++ streams = &priv->ampdu[stream_index]; ++ ++ if (streams->state != AMPDU_STREAM_ACTIVE) ++ continue; ++ ++ ieee80211_stop_tx_ba_session(streams->sta, ++ streams->tid); ++ } ++ } ++ } ++ ++ spin_unlock_bh(&priv->stream_lock); ++} ++ ++static void mwl_account_handle(struct work_struct *work) ++{ ++ struct mwl_priv *priv = ++ container_of(work, struct mwl_priv, account_handle); ++ ++ mwl_hif_process_account(priv->hw); ++} ++ ++static void mwl_wds_check_handle(struct work_struct *work) ++{ ++ struct mwl_priv *priv = ++ container_of(work, struct mwl_priv, wds_check_handle); ++ struct mwl_sta *sta_info; ++ struct ieee80211_sta *sta; ++ bool wds_sta = false; ++ ++ spin_lock_bh(&priv->sta_lock); ++ list_for_each_entry(sta_info, &priv->sta_list, list) { ++ if (sta_info->wds) ++ continue; ++ sta = container_of((void *)sta_info, struct ieee80211_sta, ++ drv_priv); ++ if (ether_addr_equal(sta->addr, priv->wds_check_sta)) { ++ wds_sta = true; ++ break; ++ } ++ } ++ spin_unlock_bh(&priv->sta_lock); ++ ++ if (wds_sta) { ++ mwl_fwcmd_set_new_stn_wds_sc4(priv->hw, sta->addr); ++ sta_info->wds = true; ++ } ++ ++ priv->wds_check = false; ++} ++ ++static void mwl_chnl_switch_event(struct work_struct *work) ++{ ++ struct mwl_priv *priv = ++ container_of(work, struct mwl_priv, chnl_switch_handle); ++ struct mwl_vif *mwl_vif; ++ struct ieee80211_vif *vif; ++ ++ if (!priv->csa_active) { ++ wiphy_err(priv->hw->wiphy, ++ "csa is not active (got channel switch event)\n"); ++ return; ++ } ++ ++ spin_lock_bh(&priv->vif_lock); ++ list_for_each_entry(mwl_vif, &priv->vif_list, list) { ++ vif = container_of((void *)mwl_vif, struct ieee80211_vif, ++ drv_priv); ++ ++ if (vif->csa_active) ++ ieee80211_csa_finish(vif); ++ } ++ spin_unlock_bh(&priv->vif_lock); ++ ++ wiphy_info(priv->hw->wiphy, "channel switch is done\n"); ++ ++ priv->csa_active = false; ++} ++ ++static irqreturn_t mwl_isr(int irq, void *dev_id) ++{ ++ struct ieee80211_hw *hw = dev_id; ++ ++ return mwl_hif_irq_handler(hw); ++} ++ ++#ifdef timer_setup ++static void timer_routine(struct timer_list *t) ++{ ++ struct mwl_priv *priv = from_timer(priv, t, period_timer); ++ struct ieee80211_hw *hw = priv->hw; ++#else ++static void timer_routine(unsigned long data) ++{ ++ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; ++ struct mwl_priv *priv = hw->priv; ++#endif ++ if (priv->heartbeat) { ++ if ((jiffies - priv->pre_jiffies) >= ++ msecs_to_jiffies(priv->heartbeat * 1000)) { ++ if (!priv->heartbeating) { ++ priv->heartbeating = true; ++ ieee80211_queue_work(hw, ++ &priv->heartbeat_handle); ++ } ++ priv->pre_jiffies = jiffies; ++ } ++ } ++ ++ mwl_hif_timer_routine(hw); ++ ++ mod_timer(&priv->period_timer, jiffies + ++ msecs_to_jiffies(SYSADPT_TIMER_WAKEUP_TIME)); ++} ++ ++static int mwl_wl_init(struct mwl_priv *priv) ++{ ++ struct ieee80211_hw *hw = priv->hw; ++ int rc; ++ u16 addr_num; ++ struct mac_address *mac_addr; ++ u8 last_nibble; ++ ++ hw->extra_tx_headroom = mwl_hif_get_tx_head_room(hw); ++ hw->queues = SYSADPT_TX_WMM_QUEUES; ++ ++ /* Set rssi values to dBm */ ++ ieee80211_hw_set(hw, SIGNAL_DBM); ++ ieee80211_hw_set(hw, HAS_RATE_CONTROL); ++ ++ /* Ask mac80211 not to trigger PS mode ++ * based on PM bit of incoming frames. ++ */ ++ ieee80211_hw_set(hw, AP_LINK_PS); ++ ++ ieee80211_hw_set(hw, SUPPORTS_PER_STA_GTK); ++ ieee80211_hw_set(hw, MFP_CAPABLE); ++ ++ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; ++ hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; ++ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; ++ hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; ++ ++ hw->vif_data_size = sizeof(struct mwl_vif); ++ hw->sta_data_size = sizeof(struct mwl_sta); ++ ++ priv->ap_macids_supported = 0x0000ffff; ++ priv->sta_macids_supported = 0x00010000; ++ priv->macids_used = 0; ++ INIT_LIST_HEAD(&priv->vif_list); ++ INIT_LIST_HEAD(&priv->sta_list); ++ ++ /* Set default radio state, preamble and wmm */ ++ priv->noise = -104; ++ priv->radio_on = false; ++ priv->radio_short_preamble = false; ++ priv->wmm_enabled = false; ++ priv->powinited = 0; ++ priv->wds_check = false; ++ if (priv->chip_type == MWL8997) ++ priv->pwr_level = SYSADPT_TX_GRP_PWR_LEVEL_TOTAL; ++ else ++ priv->pwr_level = SYSADPT_TX_POWER_LEVEL_TOTAL; ++ priv->dfs_test = false; ++ priv->csa_active = false; ++ priv->dfs_chirp_count_min = 5; ++ priv->dfs_chirp_time_interval = 1000; ++ priv->dfs_pw_filter = 0; ++ priv->dfs_min_num_radar = 5; ++ priv->dfs_min_pri_count = 4; ++ priv->bf_type = TXBF_MODE_AUTO; ++ ++ /* Handle watchdog ba events */ ++ INIT_WORK(&priv->heartbeat_handle, mwl_heartbeat_handle); ++ INIT_WORK(&priv->watchdog_ba_handle, mwl_watchdog_ba_events); ++ INIT_WORK(&priv->account_handle, mwl_account_handle); ++ INIT_WORK(&priv->wds_check_handle, mwl_wds_check_handle); ++ INIT_WORK(&priv->chnl_switch_handle, mwl_chnl_switch_event); ++ ++ mutex_init(&priv->fwcmd_mutex); ++ spin_lock_init(&priv->vif_lock); ++ spin_lock_init(&priv->sta_lock); ++ spin_lock_init(&priv->stream_lock); ++ spin_lock_init(&priv->stnid_lock); ++ ++ rc = mwl_thermal_register(priv); ++ if (rc) { ++ wiphy_err(hw->wiphy, "fail to register thermal framework\n"); ++ goto err_thermal_register; ++ } ++ ++ rc = mwl_hif_init(hw); ++ if (rc) { ++ wiphy_err(hw->wiphy, "fail to initialize host interface\n"); ++ goto err_hif_init; ++ } ++ ++ SET_IEEE80211_PERM_ADDR(hw, priv->hw_data.mac_addr); ++ ++ if (priv->chip_type == MWL8964) { ++ addr_num = SYSADPT_NUM_OF_AP + SYSADPT_NUM_OF_CLIENT; ++ hw->wiphy->n_addresses = addr_num; ++ hw->wiphy->addresses = ++ kzalloc(addr_num * sizeof(*mac_addr), GFP_KERNEL); ++ ++ mac_addr = &hw->wiphy->addresses[0]; ++ ether_addr_copy(mac_addr->addr, priv->hw_data.mac_addr); ++ last_nibble = mac_addr->addr[5] & 0x0F; ++ for (addr_num = 0; addr_num < SYSADPT_NUM_OF_AP; addr_num++) { ++ mac_addr = &hw->wiphy->addresses[addr_num + 1]; ++ ether_addr_copy(mac_addr->addr, priv->hw_data.mac_addr); ++ if (!strcmp(wiphy_name(hw->wiphy), "phy0")) { ++ last_nibble++; ++ if (last_nibble == 0x10) ++ last_nibble = 0; ++ } else { ++ last_nibble--; ++ if (last_nibble == 0xFF) ++ last_nibble = 0x0F; ++ } ++ mac_addr->addr[5] = ++ (mac_addr->addr[5] & 0xF0) | last_nibble; ++ mac_addr->addr[0] |= 0x2; ++ } ++ } ++ ++ wiphy_info(hw->wiphy, ++ "firmware version: 0x%x\n", priv->hw_data.fw_release_num); ++ ++ if (priv->chip_type == MWL8997) { ++ mwl_fwcmd_set_cfg_data(hw, 2); ++ mwl_fwcmd_set_txpwrlmt_cfg_data(hw); ++ mwl_fwcmd_get_txpwrlmt_cfg_data(hw); ++ } ++ ++ if (priv->chip_type == MWL8964) ++ rc = mwl_fwcmd_get_fw_region_code_sc4(hw, ++ &priv->fw_region_code); ++ else ++ rc = mwl_fwcmd_get_fw_region_code(hw, &priv->fw_region_code); ++ if (!rc) { ++ priv->fw_device_pwrtbl = true; ++ mwl_regd_init(priv); ++ wiphy_info(hw->wiphy, ++ "firmware region code: %x\n", priv->fw_region_code); ++ } ++ ++ if (priv->chip_type == MWL8997) ++ mwl_fwcmd_dump_otp_data(hw); ++ ++ mwl_fwcmd_radio_disable(hw); ++ mwl_fwcmd_rf_antenna(hw, WL_ANTENNATYPE_TX, priv->antenna_tx); ++ mwl_fwcmd_rf_antenna(hw, WL_ANTENNATYPE_RX, priv->antenna_rx); ++ ++ hw->wiphy->interface_modes = 0; ++ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); ++#if defined(CPTCFG_MAC80211_MESH) || defined(CONFIG_MAC80211_MESH) ++ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT); ++#endif ++ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); ++ hw->wiphy->iface_combinations = &ap_if_comb; ++ hw->wiphy->n_iface_combinations = 1; ++ ++ mwl_set_caps(priv); ++ ++ priv->led_blink_enable = 1; ++ priv->led_blink_rate = LED_BLINK_RATE_MID; ++ mwl_fwcmd_led_ctrl(hw, priv->led_blink_enable, priv->led_blink_rate); ++ ++ vendor_cmd_register(hw->wiphy); ++ ++ rc = ieee80211_register_hw(hw); ++ if (rc) { ++ wiphy_err(hw->wiphy, "fail to register device\n"); ++ goto err_register_hw; ++ } ++ ++ priv->irq = mwl_hif_get_irq_num(hw); ++ rc = request_irq(priv->irq, mwl_isr, IRQF_SHARED, ++ mwl_hif_get_driver_name(hw), hw); ++ if (rc) { ++ priv->irq = -1; ++ wiphy_err(hw->wiphy, "fail to register IRQ handler\n"); ++ goto err_register_irq; ++ } ++#ifdef timer_setup ++ timer_setup(&priv->period_timer, timer_routine, 0); ++#else ++ setup_timer(&priv->period_timer, timer_routine, (unsigned long)hw); ++#endif ++ mod_timer(&priv->period_timer, jiffies + ++ msecs_to_jiffies(SYSADPT_TIMER_WAKEUP_TIME)); ++ ++ return rc; ++ ++err_register_hw: ++err_register_irq: ++ mwl_hif_deinit(hw); ++ ++err_hif_init: ++err_thermal_register: ++ ++ wiphy_err(hw->wiphy, "init fail\n"); ++ ++ return rc; ++} ++ ++static void mwl_wl_deinit(struct mwl_priv *priv) ++{ ++ struct ieee80211_hw *hw = priv->hw; ++ ++ del_timer_sync(&priv->period_timer); ++ ++ if (priv->irq != -1) { ++ free_irq(priv->irq, hw); ++ priv->irq = -1; ++ } ++ ++ if (priv->chip_type == MWL8964) ++ kfree(hw->wiphy->addresses); ++ ieee80211_unregister_hw(hw); ++ mwl_thermal_unregister(priv); ++ cancel_work_sync(&priv->chnl_switch_handle); ++ cancel_work_sync(&priv->account_handle); ++ cancel_work_sync(&priv->wds_check_handle); ++ cancel_work_sync(&priv->watchdog_ba_handle); ++ cancel_work_sync(&priv->heartbeat_handle); ++ mwl_hif_deinit(hw); ++} ++ ++struct ieee80211_hw *mwl_alloc_hw(int bus_type, ++ int chip_type, ++ struct device *dev, ++ const struct mwl_hif_ops *ops, ++ size_t hif_data_len) ++{ ++ struct ieee80211_hw *hw; ++ struct mwl_priv *priv; ++ int priv_size; ++ ++ priv_size = ALIGN(sizeof(*priv), NETDEV_ALIGN) + hif_data_len; ++ ++ hw = ieee80211_alloc_hw(priv_size, &mwl_mac80211_ops); ++ if (!hw) { ++ pr_err("ieee80211 alloc hw failed\n"); ++ return NULL; ++ } ++ ++ priv = hw->priv; ++ priv->hw = hw; ++ priv->dev = dev; ++ priv->chip_type = chip_type; ++ priv->fw_device_pwrtbl = false; ++ priv->forbidden_setting = false; ++ priv->regulatory_set = false; ++ priv->use_short_slot = false; ++ priv->use_short_preamble = false; ++ priv->disable_2g = false; ++ priv->disable_5g = false; ++ priv->tx_amsdu = true; ++ priv->hif.bus = bus_type; ++ priv->hif.ops = ops; ++ priv->hif.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN); ++ priv->ampdu_num = mwl_hif_get_ampdu_num(hw); ++ priv->ampdu = ++ kzalloc(priv->ampdu_num * sizeof(*priv->ampdu), GFP_KERNEL); ++ if (!priv->ampdu) { ++ ieee80211_free_hw(hw); ++ pr_err("alloc ampdu stream failed\n"); ++ return NULL; ++ } ++ ++ if (chip_type == MWL8964) ++ priv->stnid_num = SYSADPT_MAX_STA_SC4; ++ else ++ priv->stnid_num = SYSADPT_MAX_STA; ++ priv->stnid = ++ kzalloc(priv->stnid_num * sizeof(struct mwl_stnid), GFP_KERNEL); ++ if (!priv->stnid) { ++ kfree(priv->ampdu); ++ ieee80211_free_hw(hw); ++ pr_err("alloc stnid failed\n"); ++ return NULL; ++ } ++ priv->available_stnid = 0; ++ ++ SET_IEEE80211_DEV(hw, dev); ++ ++ return hw; ++} ++ ++void mwl_free_hw(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ kfree(priv->stnid); ++ kfree(priv->ampdu); ++ ieee80211_free_hw(hw); ++} ++ ++int mwl_init_hw(struct ieee80211_hw *hw, const char *fw_name, ++ const char *cal_name, const char *txpwrlmt_name) ++{ ++ struct mwl_priv *priv = hw->priv; ++ int rc; ++ int tx_num = 4, rx_num = 4; ++ ++ ++ rc = mwl_prepare_cmd_buf(priv); ++ if (rc) { ++ wiphy_err(hw->wiphy, "fail to prepare command buffer\n"); ++ return -ENOMEM; ++ } ++ ++ rc = mwl_init_firmware(priv, fw_name, cal_name, txpwrlmt_name); ++ if (rc) { ++ wiphy_err(hw->wiphy, "fail to initialize firmware\n"); ++ return -EIO; ++ } ++ ++ /* firmware is loaded to H/W, it can be released now */ ++ release_firmware(priv->fw_ucode); ++ ++ mwl_process_of_dts(priv); ++ ++ rc = mwl_wl_init(priv); ++ if (rc) { ++ wiphy_err(hw->wiphy, "fail to initialize wireless lan\n"); ++ return -EIO; ++ } ++ ++ wiphy_info(priv->hw->wiphy, "2G %s, 5G %s\n", ++ priv->disable_2g ? "disabled" : "enabled", ++ priv->disable_5g ? "disabled" : "enabled"); ++ ++ if (priv->antenna_tx == ANTENNA_TX_2) ++ tx_num = 2; ++ else if (priv->antenna_tx == ANTENNA_TX_3) ++ tx_num = 3; ++ if (priv->antenna_rx == ANTENNA_RX_2) ++ rx_num = 2; ++ else if (priv->antenna_rx == ANTENNA_RX_3) ++ rx_num = 3; ++ wiphy_info(priv->hw->wiphy, "%d TX antennas, %d RX antennas\n", ++ tx_num, rx_num); ++ ++#ifdef CONFIG_DEBUG_FS ++ mwl_debugfs_init(hw); ++#endif ++ ++ return 0; ++} ++ ++void mwl_deinit_hw(struct ieee80211_hw *hw) ++{ ++#ifdef CONFIG_DEBUG_FS ++ mwl_debugfs_remove(hw); ++#endif ++ ++ mwl_wl_deinit(hw->priv); ++} +diff --git a/drivers/net/wireless/marvell/mwlwifi/core.h b/drivers/net/wireless/marvell/mwlwifi/core.h +new file mode 100644 +index 000000000000..00069c4f0b44 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/core.h +@@ -0,0 +1,517 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines core layer related functions. */ ++ ++#ifndef _CORE_H_ ++#define _CORE_H_ ++ ++#include ++#include ++#include ++#include ++ ++#include "hif/hif.h" ++ ++/* antenna control */ ++#define ANTENNA_TX_4_AUTO 0 ++#define ANTENNA_TX_1 1 ++#define ANTENNA_TX_2 3 ++#define ANTENNA_TX_3 7 ++#define ANTENNA_RX_4_AUTO 0 ++#define ANTENNA_RX_1 1 ++#define ANTENNA_RX_2 2 ++#define ANTENNA_RX_3 3 ++#define ANTENNA_RX_MAX 4 ++ ++/* band related constants */ ++#define BAND_24_CHANNEL_NUM 14 ++#define BAND_24_RATE_NUM 13 ++#define BAND_50_CHANNEL_NUM 24 ++#define BAND_50_RATE_NUM 8 ++ ++#define NUM_WEP_KEYS 4 ++#define MWL_MAX_TID 8 ++#define MWL_AMSDU_SIZE_4K 1 ++#define MWL_AMSDU_SIZE_8K 2 ++#define MWL_AMSDU_SIZE_11K 3 ++ ++/* power init */ ++#define MWL_POWER_INIT_1 1 ++#define MWL_POWER_INIT_2 2 ++ ++/* tx rate information constants */ ++#define TX_RATE_FORMAT_LEGACY 0 ++#define TX_RATE_FORMAT_11N 1 ++#define TX_RATE_FORMAT_11AC 2 ++ ++#define TX_RATE_BANDWIDTH_20 0 ++#define TX_RATE_BANDWIDTH_40 1 ++#define TX_RATE_BANDWIDTH_80 2 ++#define TX_RATE_BANDWIDTH_160 3 ++ ++#define TX_RATE_INFO_STD_GI 0 ++#define TX_RATE_INFO_SHORT_GI 1 ++ ++/* tx rate information */ ++/* 0: legacy format 1: 11n format 2: 11ac format */ ++#define MWL_TX_RATE_FORMAT_MASK 0x00000003 ++#define MWL_TX_RATE_STBC_MASK 0x00000004 ++#define MWL_TX_RATE_STBC_SHIFT 2 ++/* 0: 20 MHz 1: 40 MHz 2: 80 MHz 3: 160 MHz */ ++#define MWL_TX_RATE_BANDWIDTH_MASK 0x00000030 ++#define MWL_TX_RATE_BANDWIDTH_SHIFT 4 ++/* 0: normal 1: short */ ++#define MWL_TX_RATE_SHORTGI_MASK 0x00000040 ++#define MWL_TX_RATE_SHORTGI_SHIFT 6 ++#define MWL_TX_RATE_RATEIDMCS_MASK 0x00007F00 ++#define MWL_TX_RATE_RATEIDMCS_SHIFT 8 ++/* 0: long 1: short */ ++#define MWL_TX_RATE_PREAMBLE_MASK 0x00008000 ++#define MWL_TX_RATE_PREAMBLE_SHIFT 15 ++#define MWL_TX_RATE_POWERID_MASK 0x003F0000 ++#define MWL_TX_RATE_POWERID_SHIFT 16 ++#define MWL_TX_RATE_ADVCODING_MASK 0x00400000 ++#define MWL_TX_RATE_ADVCODING_SHIFT 22 ++/* 0: beam forming off 1: beam forming on */ ++#define MWL_TX_RATE_BF_MASK 0x00800000 ++#define MWL_TX_RATE_BF_SHIFT 23 ++#define MWL_TX_RATE_ANTSELECT_MASK 0xFF000000 ++#define MWL_TX_RATE_ANTSELECT_SHIFT 24 ++ ++#define ACNT_BA_SIZE 1000 ++ ++/* Q stats */ ++#define QS_MAX_DATA_RATES_G 14 ++#define QS_NUM_SUPPORTED_11N_BW 2 ++#define QS_NUM_SUPPORTED_GI 2 ++#define QS_NUM_SUPPORTED_MCS 24 ++#define QS_NUM_SUPPORTED_11AC_NSS 3 ++#define QS_NUM_SUPPORTED_11AC_BW 4 ++#define QS_NUM_SUPPORTED_11AC_MCS 10 ++#define TX_RATE_HISTO_CUSTOM_CNT 1 ++#define TX_RATE_HISTO_PER_CNT 5 ++#define MAX_DATA_RATES_G 14 ++#define MAX_SUPPORTED_MCS 24 ++#define MAX_SUPPORTED_11AC_RATES 20 ++/* MAX_DATA_RATES_G + MAX_SUPPORTED_MCS + MAX_SUPPORTED_11AC_RATES */ ++#define MAX_SUPPORTED_RATES 58 ++#define SU_MIMO 0 ++#define MU_MIMO 1 ++#define SU_MU_TYPE_CNT 2 /* traffic type, SU and MU */ ++ ++/* BF operation mode */ ++#define TXBF_MODE_OFF 0x05 ++#define TXBF_MODE_AUTO 0x06 ++#define TXBF_MODE_BFMER_AUTO 0x07 ++ ++static const u8 TX_HISTO_PER_THRES[TX_RATE_HISTO_PER_CNT - 1] = {6, 12, 20, 30}; ++ ++enum { ++ MWL8864 = 0, ++ MWL8897, ++ MWL8964, ++ MWL8997, ++ MWLUNKNOWN, ++}; ++ ++enum mwl_bus { ++ MWL_BUS_PCIE, ++ MWL_BUS_SDIO, ++}; ++ ++enum { ++ AP_MODE_11AC = 0x10, /* generic 11ac indication mode */ ++ AP_MODE_2_4GHZ_11AC_MIXED = 0x17, ++}; ++ ++enum { ++ AMPDU_NO_STREAM = 0, ++ AMPDU_STREAM_NEW, ++ AMPDU_STREAM_IN_PROGRESS, ++ AMPDU_STREAM_ACTIVE, ++}; ++ ++enum { ++ LED_BLINK_RATE_LOW = 0x1, ++ LED_BLINK_RATE_MID, ++ LED_BLINK_RATE_HIGH, ++}; ++ ++struct mwl_chip_info { ++ const char *part_name; ++ const char *fw_image; ++ const char *cal_file; ++ const char *txpwrlmt_file; ++ int antenna_tx; ++ int antenna_rx; ++}; ++ ++struct mwl_device_pwr_tbl { ++ u8 channel; ++ u8 tx_pwr[SYSADPT_TX_PWR_LEVEL_TOTAL_SC4]; ++ u8 dfs_capable; ++ u8 ax_ant; ++ u8 cdd; ++}; ++ ++struct mwl_tx_pwr_tbl { ++ u8 channel; ++ u8 setcap; ++ u16 txantenna2; ++ u16 tx_power[SYSADPT_TX_POWER_LEVEL_TOTAL]; ++ bool cdd; ++}; ++ ++struct mwl_hw_data { ++ u32 fw_release_num; /* MajNbr:MinNbr:SubMin:PatchLevel */ ++ u8 hw_version; /* plain number indicating version */ ++ unsigned char mac_addr[ETH_ALEN]; /* well known -> AA:BB:CC:DD:EE:FF */ ++}; ++ ++struct mwl_ampdu_stream { ++ struct ieee80211_sta *sta; ++ u8 tid; ++ u8 state; ++ int idx; ++}; ++ ++struct mwl_stnid { ++ int macid; /* keep macid for related stnid */ ++ u16 aid; /* keep aid for related stnid */ ++}; ++ ++struct otp_data { ++ u8 buf[SYSADPT_OTP_BUF_SIZE]; ++ u32 len; /* Actual size of data in buf[] */ ++}; ++ ++struct txpwrlmt_cfg_data { ++ u8 buf[SYSADPT_TXPWRLMT_CFG_BUF_SIZE]; ++ u32 len; /* Actual size of data in buf[] */ ++}; ++ ++struct mwl_priv { ++ struct ieee80211_hw *hw; ++ struct device *dev; ++ struct firmware *fw_ucode; ++ struct firmware *cal_data; ++ struct firmware *txpwrlmt_file; ++ struct otp_data otp_data; ++ struct txpwrlmt_cfg_data txpwrlmt_data; ++ bool fw_device_pwrtbl; ++ bool forbidden_setting; ++ bool regulatory_set; ++ u32 fw_region_code; ++ char fw_alpha2[2]; ++ u8 number_of_channels; ++ struct mwl_device_pwr_tbl device_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS]; ++ int chip_type; ++ ++ bool use_short_slot; ++ bool use_short_preamble; ++ ++ struct { ++ enum mwl_bus bus; ++ const struct mwl_hif_ops *ops; ++ void *priv; ++ } hif; ++ ++ struct device_node *dt_node; ++ struct device_node *pwr_node; ++ bool disable_2g; ++ bool disable_5g; ++ int antenna_tx; ++ int antenna_rx; ++ bool tx_amsdu; ++ bool dump_hostcmd; ++ bool dump_probe; ++ ++ struct mwl_tx_pwr_tbl tx_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS]; ++ bool cdd; ++ u16 txantenna2; ++ u8 powinited; ++ u8 pwr_level; ++ u16 max_tx_pow[SYSADPT_TX_GRP_PWR_LEVEL_TOTAL]; /* max tx power (dBm) */ ++ u16 target_powers[SYSADPT_TX_GRP_PWR_LEVEL_TOTAL]; /* target powers */ ++ ++ struct mutex fwcmd_mutex; /* for firmware command */ ++ unsigned short *pcmd_buf; /* pointer to CmdBuf (virtual) */ ++ dma_addr_t pphys_cmd_buf; /* pointer to CmdBuf (physical) */ ++ bool in_send_cmd; ++ bool cmd_timeout; ++ bool rmmod; ++ int heartbeat; ++ u32 pre_jiffies; ++ bool heartbeating; ++ struct work_struct heartbeat_handle; ++ ++ int irq; ++ struct mwl_hw_data hw_data; /* Adapter HW specific info */ ++ ++ struct timer_list period_timer; ++ ++ /* keep survey information */ ++ bool sw_scanning; ++ int survey_info_idx; ++ struct mwl_survey_info survey_info[SYSADPT_MAX_NUM_CHANNELS]; ++ struct mwl_survey_info cur_survey_info; ++ ++ s8 noise; /* Most recently reported noise in dBm */ ++ ++ struct ieee80211_supported_band band_24; ++ struct ieee80211_channel channels_24[BAND_24_CHANNEL_NUM]; ++ struct ieee80211_rate rates_24[BAND_24_RATE_NUM]; ++ struct ieee80211_supported_band band_50; ++ struct ieee80211_channel channels_50[BAND_50_CHANNEL_NUM]; ++ struct ieee80211_rate rates_50[BAND_50_RATE_NUM]; ++ ++ u32 ap_macids_supported; ++ u32 sta_macids_supported; ++ u32 macids_used; ++ u32 running_bsses; /* bitmap of running BSSes */ ++ ++ struct { ++ spinlock_t vif_lock; /* for private interface info */ ++ struct list_head vif_list; /* List of interfaces. */ ++ } ____cacheline_aligned_in_smp; ++ ++ struct { ++ spinlock_t sta_lock; /* for private sta info */ ++ struct list_head sta_list; /* List of stations */ ++ } ____cacheline_aligned_in_smp; ++ ++ /* ampdu stream information */ ++ /* for ampdu stream */ ++ int ampdu_num; ++ struct { ++ spinlock_t stream_lock; /* for BA stream */ ++ struct mwl_ampdu_stream *ampdu; ++ } ____cacheline_aligned_in_smp; ++ struct work_struct watchdog_ba_handle; ++ ++ /* station id */ ++ int stnid_num; ++ struct { ++ spinlock_t stnid_lock; /* for station id */ ++ struct mwl_stnid *stnid; ++ u16 available_stnid; ++ } ____cacheline_aligned_in_smp; ++ ++ bool radio_on; ++ bool radio_short_preamble; ++ bool wmm_enabled; ++ struct ieee80211_tx_queue_params wmm_params[SYSADPT_TX_WMM_QUEUES]; ++ ++ struct work_struct account_handle; ++ ++ bool wds_check; ++ struct work_struct wds_check_handle; ++ u8 wds_check_sta[ETH_ALEN]; ++ ++ bool dfs_test; ++ bool csa_active; ++ struct work_struct chnl_switch_handle; ++ enum nl80211_dfs_regions dfs_region; ++ u16 dfs_chirp_count_min; ++ u16 dfs_chirp_time_interval; ++ u16 dfs_pw_filter; ++ u16 dfs_min_num_radar; ++ u16 dfs_min_pri_count; ++ ++ u8 bf_type; ++ ++ struct thermal_cooling_device *cdev; ++ u32 throttle_state; ++ u32 quiet_period; ++ int temperature; ++ ++ u8 led_blink_enable; ++ u8 led_blink_rate; ++ ++ struct dentry *debugfs_phy; ++ u32 reg_type; ++ u32 reg_offset; ++ u32 reg_value; ++ int ra_aid; ++ int ba_aid; ++ int fixed_rate; ++ bool coredump_text; ++ u32 ra_tx_attempt[2][6]; ++}; ++ ++struct beacon_info { ++ bool valid; ++ u16 cap_info; ++ u8 power_constraint; ++ u8 b_rate_set[SYSADPT_MAX_DATA_RATES_G]; ++ u8 op_rate_set[SYSADPT_MAX_DATA_RATES_G]; ++ u8 ie_list_ht[148]; ++ u8 ie_list_vht[24]; ++ u8 *ie_wmm_ptr; ++ u8 *ie_wsc_ptr; ++ u8 *ie_rsn_ptr; ++ u8 *ie_rsn48_ptr; ++ u8 *ie_mde_ptr; ++ u8 *ie_ht_ptr; ++ u8 *ie_vht_ptr; ++ u8 *ie_country_ptr; ++ u8 *ie_meshid_ptr; ++ u8 *ie_meshcfg_ptr; ++ u8 *ie_meshchsw_ptr; ++ u8 ie_wmm_len; ++ u8 ie_wsc_len; ++ u8 ie_rsn_len; ++ u8 ie_rsn48_len; ++ u8 ie_mde_len; ++ u8 ie_ht_len; ++ u8 ie_vht_len; ++ u8 ie_country_len; ++ u8 ie_meshid_len; ++ u8 ie_meshcfg_len; ++ u8 ie_meshchsw_len; ++}; ++ ++struct mwl_vif { ++ struct list_head list; ++ enum nl80211_iftype type; ++ int macid; /* Firmware macid for this vif. */ ++ u16 seqno; /* Non AMPDU sequence number assigned by driver. */ ++ struct { /* Saved WEP keys */ ++ u8 enabled; ++ u8 key[sizeof(struct ieee80211_key_conf) + WLAN_KEY_LEN_WEP104]; ++ } wep_key_conf[NUM_WEP_KEYS]; ++ u8 bssid[ETH_ALEN]; /* BSSID */ ++ u8 sta_mac[ETH_ALEN]; /* Station mac address */ ++ /* A flag to indicate is HW crypto is enabled for this bssid */ ++ bool is_hw_crypto_enabled; ++ /* Indicate if this is station mode */ ++ struct beacon_info beacon_info; ++ bool set_beacon; ++ int basic_rate_idx; ++ u8 broadcast_ssid; ++ u16 iv16; ++ u32 iv32; ++ s8 keyidx; ++}; ++ ++struct mwl_tx_info { ++ unsigned long start_time; ++ u32 pkts; ++}; ++ ++struct mwl_amsdu_frag { ++ struct sk_buff *skb; ++ u8 *cur_pos; ++ unsigned long jiffies; ++ u8 pad; ++ u8 num; ++}; ++ ++struct mwl_amsdu_ctrl { ++ struct mwl_amsdu_frag frag[SYSADPT_TX_WMM_QUEUES]; ++ u8 cap; ++}; ++ ++struct mwl_tx_ba_stats { ++ u8 ba_hole; /* Total pkt not acked in a BA bitmap */ ++ u8 ba_expected; /* Total Tx pkt expected to be acked */ ++ u8 no_ba; /* No BA is received */ ++ u8 pad; /* Unused */ ++}; ++ ++struct mwl_tx_ba_hist { ++ u16 index; /* Current buffer index */ ++ u8 type; /* 0:SU, 1: MU */ ++ bool enable; ++ struct mwl_tx_ba_stats *ba_stats; ++}; ++ ++struct mwl_tx_hist_data { ++ u32 rateinfo; ++ u32 cnt; ++ /* store according to TX_HISTO_PER_THRES threshold */ ++ u32 per[TX_RATE_HISTO_PER_CNT]; ++}; ++ ++struct mwl_tx_hist { ++ struct mwl_tx_hist_data su_rate[MAX_SUPPORTED_RATES]; ++ struct mwl_tx_hist_data mu_rate ++ [QS_NUM_SUPPORTED_11AC_NSS - 1][QS_NUM_SUPPORTED_11AC_BW] ++ [QS_NUM_SUPPORTED_GI][QS_NUM_SUPPORTED_11AC_MCS]; ++ struct mwl_tx_hist_data custom_rate[TX_RATE_HISTO_CUSTOM_CNT]; ++ /* Current rate for 0:SU, 1:MU */ ++ u32 cur_rate_info[SU_MU_TYPE_CNT]; ++ /* Total tx attempt cnt for 0:SU, 1:MU */ ++ u32 total_tx_cnt[SU_MU_TYPE_CNT]; ++}; ++ ++struct mwl_sta { ++ struct list_head list; ++ struct mwl_vif *mwl_vif; ++ u16 stnid; ++ u16 sta_stnid; ++ bool wds; ++ bool is_mesh_node; ++ bool is_ampdu_allowed; ++ struct mwl_tx_info tx_stats[MWL_MAX_TID]; ++ u32 check_ba_failed[MWL_MAX_TID]; ++ struct mwl_tx_ba_hist ba_hist; ++ bool is_amsdu_allowed; ++ bool is_key_set; ++ /* for amsdu aggregation */ ++ struct { ++ spinlock_t amsdu_lock; /* for amsdu */ ++ struct mwl_amsdu_ctrl amsdu_ctrl; ++ } ____cacheline_aligned_in_smp; ++ struct mwl_tx_hist tx_hist; ++ u32 tx_rate_info; ++ u16 rx_format; ++ u16 rx_nss; ++ u16 rx_bw; ++ u16 rx_gi; ++ u16 rx_rate_mcs; ++ u8 rx_signal; ++ u16 iv16; ++ u32 iv32; ++}; ++ ++static inline struct mwl_vif *mwl_dev_get_vif(const struct ieee80211_vif *vif) ++{ ++ return (struct mwl_vif *)&vif->drv_priv; ++} ++ ++static inline struct mwl_sta *mwl_dev_get_sta(const struct ieee80211_sta *sta) ++{ ++ return (struct mwl_sta *)&sta->drv_priv; ++} ++ ++struct ieee80211_hw *mwl_alloc_hw(int bus_type, ++ int chip_type, ++ struct device *dev, ++ const struct mwl_hif_ops *ops, ++ size_t hif_data_len); ++ ++void mwl_free_hw(struct ieee80211_hw *hw); ++ ++int mwl_init_hw(struct ieee80211_hw *hw, const char *fw_name, ++ const char *cal_name, const char *txpwrlmt_name); ++ ++void mwl_deinit_hw(struct ieee80211_hw *hw); ++ ++/* Defined in mac80211.c. */ ++extern const struct ieee80211_ops mwl_mac80211_ops; ++ ++#endif /* _CORE_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.c b/drivers/net/wireless/marvell/mwlwifi/debugfs.c +new file mode 100644 +index 000000000000..e375806a7111 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.c +@@ -0,0 +1,2201 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements debug fs related functions. */ ++ ++#include ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "utils.h" ++#include "thermal.h" ++#include "hif/fwcmd.h" ++#include "hif/hif-ops.h" ++#include "debugfs.h" ++ ++#define MWLWIFI_DEBUGFS_ADD_FILE(name) do { \ ++ if (!debugfs_create_file(#name, 0644, priv->debugfs_phy, \ ++ priv, &mwl_debugfs_##name##_fops)) \ ++ return; \ ++} while (0) ++ ++#define MWLWIFI_DEBUGFS_FILE_OPS(name) \ ++static const struct file_operations mwl_debugfs_##name##_fops = { \ ++ .read = mwl_debugfs_##name##_read, \ ++ .write = mwl_debugfs_##name##_write, \ ++ .open = simple_open, \ ++} ++ ++#define MWLWIFI_DEBUGFS_FILE_READ_OPS(name) \ ++static const struct file_operations mwl_debugfs_##name##_fops = { \ ++ .read = mwl_debugfs_##name##_read, \ ++ .open = simple_open, \ ++} ++ ++#define MWLWIFI_DEBUGFS_FILE_WRITE_OPS(name) \ ++static const struct file_operations mwl_debugfs_##name##_fops = { \ ++ .write = mwl_debugfs_##name##_write, \ ++ .open = simple_open, \ ++} ++ ++static const char chipname[MWLUNKNOWN][8] = { ++ "88W8864", ++ "88W8897", ++ "88W8964", ++ "88W8997" ++}; ++ ++static void dump_data(char *p, int size, int *len, u8 *data, ++ int data_len, char *title) ++{ ++ int cur_byte = 0; ++ int i; ++ ++ *len += scnprintf(p + *len, size - *len, "%s\n", title); ++ ++ for (cur_byte = 0; cur_byte < data_len; cur_byte += 8) { ++ if ((cur_byte + 8) < data_len) { ++ for (i = 0; i < 8; i++) ++ *len += scnprintf(p + *len, size - *len, ++ "0x%02x ", ++ *(data + cur_byte + i)); ++ *len += scnprintf(p + *len, size - *len, "\n"); ++ } else { ++ for (i = 0; i < (data_len - cur_byte); i++) ++ *len += scnprintf(p + *len, size - *len, ++ "0x%02x ", ++ *(data + cur_byte + i)); ++ *len += scnprintf(p + *len, size - *len, "\n"); ++ break; ++ } ++ } ++} ++ ++static void _dump_tx_hist_mu(char *p, int size, int *len, bool *printed, ++ u32 *total, u8 nss, u8 bw, u8 mcs, u8 sgi, ++ struct mwl_sta *sta_info) ++{ ++ char *bw_str[4] = {"ht20", "ht40", "ht80", "ht160"}; ++ char *sgi_str[2] = {"lgi", "sgi"}; ++ struct mwl_tx_hist_data *tx_hist_data; ++ u32 cnt, rateinfo, per0, per1, per2, per3, per4, ratemask; ++ ++ tx_hist_data = &sta_info->tx_hist.mu_rate[nss][bw][sgi][mcs]; ++ cnt = le32_to_cpu(tx_hist_data->cnt); ++ rateinfo = le32_to_cpu(tx_hist_data->rateinfo); ++ if (cnt && (rateinfo > 0)) { ++ *total += cnt; ++ per4 = le32_to_cpu(tx_hist_data->per[4]); ++ per3 = le32_to_cpu(tx_hist_data->per[3]); ++ per2 = le32_to_cpu(tx_hist_data->per[2]); ++ per1 = le32_to_cpu(tx_hist_data->per[1]); ++ per0 = le32_to_cpu(tx_hist_data->per[0]); ++ if (!*printed) { ++ *len += scnprintf(p + *len, size - *len, ++ "%s %26s <%2d %8s%2d%8s%2d%8s%2d%8s%2d\n", ++ "MU_MIMO rate", " PER%", TX_HISTO_PER_THRES[0], ++ ">=", TX_HISTO_PER_THRES[0], ++ ">=", TX_HISTO_PER_THRES[1], ++ ">=", TX_HISTO_PER_THRES[2], ++ ">=", TX_HISTO_PER_THRES[3]); ++ *len += scnprintf(p + *len, size - *len, ++ "TOTAL MPDU tx pkt: %d\n", ++ sta_info->tx_hist.total_tx_cnt[MU_MIMO]); ++ *printed = true; ++ } ++ if ((rateinfo & 0x3) == 0) ++ ratemask = 0xfff; ++ else ++ ratemask = 0xffff; ++ if ((sta_info->tx_hist.cur_rate_info[MU_MIMO] & ratemask) == ++ (rateinfo & ratemask)) ++ /* mark as current rate */ ++ *len += scnprintf(p + *len, size - *len, "*"); ++ else ++ *len += scnprintf(p + *len, size - *len, " "); ++ *len += scnprintf(p + *len, size - *len, ++ "%5s_%3s_%1dSS_MCS%2d: %10u, %9d, %9d, %9d, %9d, %9d\n", ++ bw_str[bw], sgi_str[sgi], (nss + 1), mcs, cnt, per0, ++ per1, per2, per3, per4); ++ } ++} ++ ++static void dump_tx_hist_mu(char *p, int size, int *len, bool *printed, ++ u32 *total, struct mwl_sta *sta_info) ++{ ++ u8 nss, bw, mcs, sgi; ++ ++ for (nss = 0; nss < (QS_NUM_SUPPORTED_11AC_NSS - 1); nss++) { ++ for (bw = 0; bw < QS_NUM_SUPPORTED_11AC_BW; bw++) { ++ for (mcs = 0; mcs < QS_NUM_SUPPORTED_11AC_MCS; mcs++) { ++ for (sgi = 0; sgi < QS_NUM_SUPPORTED_GI; ++ sgi++) { ++ _dump_tx_hist_mu(p, size, len, printed, ++ total, nss, bw, mcs, ++ sgi, sta_info); ++ } ++ } ++ } ++ } ++} ++ ++ ++static void dump_tx_hist_su(char *p, int size, int *len, bool su, bool *printed, ++ u32 *total, struct mwl_sta *sta_info) ++{ ++ int g_rate[14] = {1, 2, 5, 11, 22, 6, 9, 12, 18, 24, 36, 48, 54, 72}; ++ char *bw_str[4] = {"ht20", "ht40", "ht80", "ht160"}; ++ char *sgi_str[2] = {"lgi", "sgi"}; ++ char title_str[32]; ++ struct mwl_tx_hist *tx_hist; ++ struct mwl_tx_hist_data *tx_hist_data; ++ u32 j, loopcnt; ++ u32 cnt, rateinfo, per0, per1, per2, per3, per4, ratemask; ++ u8 format, bw, sgi, mcs, nss; ++ ++ tx_hist = &sta_info->tx_hist; ++ if (su) { ++ loopcnt = MAX_SUPPORTED_RATES; ++ tx_hist_data = &tx_hist->su_rate[0]; ++ } else { ++ loopcnt = TX_RATE_HISTO_CUSTOM_CNT; ++ tx_hist_data = &tx_hist->custom_rate[0]; ++ } ++ ++ for (j = 0; j < loopcnt; j++) { ++ cnt = le32_to_cpu(tx_hist_data[j].cnt); ++ rateinfo = le32_to_cpu(tx_hist_data[j].rateinfo); ++ if (cnt && (rateinfo > 0)) { ++ *total += cnt; ++ per4 = le32_to_cpu(tx_hist_data[j].per[4]); ++ per3 = le32_to_cpu(tx_hist_data[j].per[3]); ++ per2 = le32_to_cpu(tx_hist_data[j].per[2]); ++ per1 = le32_to_cpu(tx_hist_data[j].per[1]); ++ per0 = le32_to_cpu(tx_hist_data[j].per[0]); ++ if (!*printed) { ++ *len += scnprintf(p + *len, size - *len, ++ "%s %26s <%2d %8s%2d%8s%2d%8s%2d%8s%2d\n", ++ su ? "SU_MIMO rate" : " Custom rate", ++ " PER%", TX_HISTO_PER_THRES[0], ++ ">=", TX_HISTO_PER_THRES[0], ++ ">=", TX_HISTO_PER_THRES[1], ++ ">=", TX_HISTO_PER_THRES[2], ++ ">=", TX_HISTO_PER_THRES[3]); ++ *len += scnprintf(p + *len, size - *len, ++ "TOTAL MPDU tx pkt: %d\n", ++ tx_hist->total_tx_cnt[SU_MIMO]); ++ *printed = true; ++ } ++ format = rateinfo & MWL_TX_RATE_FORMAT_MASK; ++ bw = (rateinfo & MWL_TX_RATE_BANDWIDTH_MASK) >> ++ MWL_TX_RATE_BANDWIDTH_SHIFT; ++ sgi = (rateinfo & MWL_TX_RATE_SHORTGI_MASK) >> ++ MWL_TX_RATE_SHORTGI_SHIFT; ++ mcs = (rateinfo & MWL_TX_RATE_RATEIDMCS_MASK) >> ++ MWL_TX_RATE_RATEIDMCS_SHIFT; ++ if (format == TX_RATE_FORMAT_LEGACY) ++ ratemask = 0xfff; ++ else ++ ratemask = 0xffff; ++ if ((tx_hist->cur_rate_info[SU_MIMO] & ratemask) == ++ (rateinfo & ratemask)) ++ /* mark as current rate */ ++ *len += scnprintf(p + *len, size - *len, "*"); ++ else ++ *len += scnprintf(p + *len, size - *len, " "); ++ if (format == TX_RATE_FORMAT_LEGACY) { ++ if (mcs == 2) { ++ *len += scnprintf(p + *len, size - *len, ++ "%s %10u, %9d, %9d, %9d, %9d, %9d\n", ++ "5.5Mbps :", cnt, per0, ++ per1, per2, per3, per4); ++ } else { ++ sprintf(title_str, ++ "%-3dMbps :", ++ g_rate[mcs]); ++ *len += scnprintf(p + *len, size - *len, ++ "%s %10u, %9d, %9d, %9d, %9d, %9d\n", ++ title_str, cnt, per0, per1, per2, per3, ++ per4); ++ } ++ } else if (format == TX_RATE_FORMAT_11N) { ++ sprintf(title_str, "%4s_%3s_MCS%2d :", ++ bw_str[bw], sgi_str[sgi], mcs); ++ *len += scnprintf(p + *len, size - *len, ++ "%s %10u, %9d, %9d, %9d, %9d, %9d\n", ++ title_str, cnt, per0, per1, per2, per3, ++ per4); ++ } else { ++ nss = (mcs >> 4); ++ sprintf(title_str, "%5s_%3s_%1dSS_MCS%2d :", ++ bw_str[bw], sgi_str[sgi], (nss+1), ++ (mcs & 0xf)); ++ *len += scnprintf(p + *len, size - *len, ++ "%s %10u, %9d, %9d, %9d, %9d, %9d\n", ++ title_str, cnt, per0, per1, per2, per3, ++ per4); ++ } ++ } ++ } ++} ++ ++static void dump_tx_hist(char *p, int size, int *len, struct mwl_sta *sta_info) ++{ ++ int type; ++ bool printed, su; ++ u32 total; ++ ++ for (type = 0; type <= SU_MU_TYPE_CNT; type++) { ++ printed = false; ++ total = 0; ++ if (type == MU_MIMO) { ++ dump_tx_hist_mu(p, size, len, &printed, ++ &total, sta_info); ++ } else { ++ su = (type == SU_MIMO) ? true : false; ++ dump_tx_hist_su(p, size, len, su, &printed, ++ &total, sta_info); ++ } ++ if (printed) ++ *len += scnprintf(p + *len, size - *len, ++ " TOTAL : %10u\n\n", ++ total); ++ } ++} ++ ++static void core_dump_file(u8 *valbuf, u32 length, u32 region, u32 address, ++ u32 append, u32 totallen, bool textmode) ++{ ++ struct file *filp_core = NULL; ++ char file_name[40]; ++ u8 *buf = kmalloc(length * 3, GFP_KERNEL); ++ u8 *data_p = buf; ++ u32 i, j = 0; ++ ++ if (!buf) ++ return; ++ ++ memset(file_name, 0, sizeof(file_name)); ++ sprintf(file_name, "/dev/shm/coredump-%x-%x", ++ region, (region + totallen)); ++ ++ if (append) ++ filp_core = filp_open(file_name, O_RDWR | O_APPEND, 0); ++ else ++ filp_core = filp_open(file_name, O_RDWR | O_CREAT | O_TRUNC, 0); ++ ++ if (!IS_ERR(filp_core)) { ++ if (textmode) { ++ for (i = 0; i < length; i += 4) { ++ u32 val = 0; ++ ++ val = le32_to_cpu(*(__le32 *)(&valbuf[i])); ++ ++ if (i % 16 == 0) { ++ sprintf(buf + j, "\n0x%08x", ++ (int)(address + i)); ++ j = strlen(buf); ++ } ++ sprintf(buf + j, " %08x", val); ++ j = strlen(buf); ++ } ++ data_p = buf + j; ++ data_p += sprintf(data_p, "\n"); ++ __kernel_write(filp_core, buf, strlen(buf), ++ &filp_core->f_pos); ++ } else ++ __kernel_write(filp_core, valbuf, length, ++ &filp_core->f_pos); ++ ++ filp_close(filp_core, current->files); ++ } ++ ++ kfree(buf); ++} ++ ++static ssize_t mwl_debugfs_info_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ int tx_num = 4, rx_num = 4; ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, ++ "driver name: %s\n", ++ mwl_hif_get_driver_name(priv->hw)); ++ len += scnprintf(p + len, size - len, "chip type: %s\n", ++ chipname[priv->chip_type]); ++ len += scnprintf(p + len, size - len, ++ "hw version: %X\n", priv->hw_data.hw_version); ++ len += scnprintf(p + len, size - len, ++ "driver version: %s\n", ++ mwl_hif_get_driver_version(priv->hw)); ++ len += scnprintf(p + len, size - len, "firmware version: 0x%08x\n", ++ priv->hw_data.fw_release_num); ++ len += scnprintf(p + len, size - len, ++ "power table loaded from dts: %s\n", ++ priv->forbidden_setting ? "no" : "yes"); ++ len += scnprintf(p + len, size - len, "firmware region code: 0x%x\n", ++ priv->fw_region_code); ++ len += scnprintf(p + len, size - len, ++ "mac address: %pM\n", priv->hw_data.mac_addr); ++ len += scnprintf(p + len, size - len, ++ "2g: %s\n", priv->disable_2g ? "disable" : "enable"); ++ len += scnprintf(p + len, size - len, ++ "5g: %s\n", priv->disable_5g ? "disable" : "enable"); ++ if (priv->antenna_tx == ANTENNA_TX_2) ++ tx_num = 2; ++ else if (priv->antenna_tx == ANTENNA_TX_3) ++ tx_num = 3; ++ if (priv->antenna_rx == ANTENNA_RX_2) ++ rx_num = 2; ++ else if (priv->antenna_rx == ANTENNA_RX_3) ++ rx_num = 3; ++ len += scnprintf(p + len, size - len, "antenna: %d %d\n", ++ tx_num, rx_num); ++ len += scnprintf(p + len, size - len, "irq number: %d\n", priv->irq); ++ len += scnprintf(p + len, size - len, "ap macid support: %08x\n", ++ priv->ap_macids_supported); ++ len += scnprintf(p + len, size - len, "sta macid support: %08x\n", ++ priv->sta_macids_supported); ++ len += scnprintf(p + len, size - len, ++ "macid used: %08x\n", priv->macids_used); ++ len += scnprintf(p + len, size - len, ++ "radio: %s\n", priv->radio_on ? "enable" : "disable"); ++ len += mwl_hif_get_info(priv->hw, p + len, size - len); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_tx_status_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += mwl_hif_get_tx_status(priv->hw, p + len, size - len); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_rx_status_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += mwl_hif_get_rx_status(priv->hw, p + len, size - len); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_vif_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ struct mwl_vif *mwl_vif; ++ struct ieee80211_vif *vif; ++ char ssid[IEEE80211_MAX_SSID_LEN + 1]; ++ struct cfg80211_chan_def *chan_def; ++ struct beacon_info *beacon_info; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ spin_lock_bh(&priv->vif_lock); ++ list_for_each_entry(mwl_vif, &priv->vif_list, list) { ++ vif = container_of((void *)mwl_vif, struct ieee80211_vif, ++ drv_priv); ++ len += scnprintf(p + len, size - len, ++ "macid: %d\n", mwl_vif->macid); ++ switch (vif->type) { ++ case NL80211_IFTYPE_AP: ++ len += scnprintf(p + len, size - len, "type: ap\n"); ++ memcpy(ssid, vif->bss_conf.ssid, ++ vif->bss_conf.ssid_len); ++ ssid[vif->bss_conf.ssid_len] = 0; ++ len += scnprintf(p + len, size - len, ++ "ssid: %s\n", ssid); ++ len += scnprintf(p + len, size - len, ++ "mac address: %pM\n", mwl_vif->bssid); ++ break; ++ case NL80211_IFTYPE_MESH_POINT: ++ len += scnprintf(p + len, size - len, "type: mesh\n"); ++ len += scnprintf(p + len, size - len, ++ "mac address: %pM\n", mwl_vif->bssid); ++ break; ++ case NL80211_IFTYPE_STATION: ++ len += scnprintf(p + len, size - len, "type: sta\n"); ++ len += scnprintf(p + len, size - len, ++ "mac address: %pM\n", ++ mwl_vif->sta_mac); ++ break; ++ default: ++ len += scnprintf(p + len, size - len, ++ "type: unknown\n"); ++ break; ++ } ++ if (vif->chanctx_conf) { ++ chan_def = &vif->chanctx_conf->def; ++ len += scnprintf(p + len, size - len, ++ "channel: %d: width: %d\n", ++ chan_def->chan->hw_value, ++ chan_def->width); ++ len += scnprintf(p + len, size - len, ++ "freq: %d freq1: %d freq2: %d\n", ++ chan_def->chan->center_freq, ++ chan_def->center_freq1, ++ chan_def->center_freq2); ++ } ++ len += scnprintf(p + len, size - len, "hw_crypto_enabled: %s\n", ++ mwl_vif->is_hw_crypto_enabled ? ++ "true" : "false"); ++ len += scnprintf(p + len, size - len, ++ "key idx: %d\n", mwl_vif->keyidx); ++ len += scnprintf(p + len, size - len, ++ "IV: %08x%04x\n", mwl_vif->iv32, ++ mwl_vif->iv16); ++ beacon_info = &mwl_vif->beacon_info; ++ dump_data(p, size, &len, beacon_info->ie_wmm_ptr, ++ beacon_info->ie_wmm_len, "WMM:"); ++ dump_data(p, size, &len, beacon_info->ie_rsn_ptr, ++ beacon_info->ie_rsn_len, "RSN:"); ++ dump_data(p, size, &len, beacon_info->ie_rsn48_ptr, ++ beacon_info->ie_rsn48_len, "RSN48:"); ++ dump_data(p, size, &len, beacon_info->ie_mde_ptr, ++ beacon_info->ie_mde_len, "MDE:"); ++ dump_data(p, size, &len, beacon_info->ie_ht_ptr, ++ beacon_info->ie_ht_len, "HT:"); ++ dump_data(p, size, &len, beacon_info->ie_vht_ptr, ++ beacon_info->ie_vht_len, "VHT:"); ++ if (vif->type == NL80211_IFTYPE_MESH_POINT) { ++ dump_data(p, size, &len, beacon_info->ie_meshid_ptr, ++ beacon_info->ie_meshid_len, "MESHID:"); ++ dump_data(p, size, &len, beacon_info->ie_meshcfg_ptr, ++ beacon_info->ie_meshcfg_len, "MESHCFG:"); ++ dump_data(p, size, &len, beacon_info->ie_meshchsw_ptr, ++ beacon_info->ie_meshchsw_len, "MESHCHSW:"); ++ } ++ len += scnprintf(p + len, size - len, "\n"); ++ } ++ spin_unlock_bh(&priv->vif_lock); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_sta_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ struct mwl_sta *sta_info; ++ struct ieee80211_sta *sta; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ spin_lock_bh(&priv->sta_lock); ++ list_for_each_entry(sta_info, &priv->sta_list, list) { ++ sta = container_of((void *)sta_info, struct ieee80211_sta, ++ drv_priv); ++ len += scnprintf(p + len, size - len, ++ "mac address: %pM\n", sta->addr); ++ len += scnprintf(p + len, size - len, "aid: %u\n", sta->aid); ++ len += scnprintf(p + len, size - len, "ampdu: %s\n", ++ sta_info->is_ampdu_allowed ? "true" : "false"); ++ len += scnprintf(p + len, size - len, "amsdu: %s\n", ++ sta_info->is_amsdu_allowed ? "true" : "false"); ++ len += scnprintf(p + len, size - len, "wds: %s\n", ++ sta_info->wds ? "true" : "false"); ++ len += scnprintf(p + len, size - len, "ba_hist: %s\n", ++ sta_info->ba_hist.enable ? ++ "enable" : "disable"); ++ if (sta_info->is_amsdu_allowed) { ++ len += scnprintf(p + len, size - len, ++ "amsdu cap: 0x%02x\n", ++ sta_info->amsdu_ctrl.cap); ++ } ++ if (sta->ht_cap.ht_supported) { ++ len += scnprintf(p + len, size - len, ++ "ht_cap: 0x%04x, ampdu: %02x, %02x\n", ++ sta->ht_cap.cap, ++ sta->ht_cap.ampdu_factor, ++ sta->ht_cap.ampdu_density); ++ len += scnprintf(p + len, size - len, ++ "rx_mask: 0x%02x, %02x, %02x, %02x\n", ++ sta->ht_cap.mcs.rx_mask[0], ++ sta->ht_cap.mcs.rx_mask[1], ++ sta->ht_cap.mcs.rx_mask[2], ++ sta->ht_cap.mcs.rx_mask[3]); ++ } ++ if (sta->vht_cap.vht_supported) { ++ len += scnprintf(p + len, size - len, ++ "vht_cap: 0x%08x, mcs: %02x, %02x\n", ++ sta->vht_cap.cap, ++ sta->vht_cap.vht_mcs.rx_mcs_map, ++ sta->vht_cap.vht_mcs.tx_mcs_map); ++ } ++ len += scnprintf(p + len, size - len, "rx_bw: %d, rx_nss: %d\n", ++ sta->bandwidth, sta->rx_nss); ++ len += scnprintf(p + len, size - len, ++ "tdls: %d, tdls_init: %d\n", ++ sta->tdls, sta->tdls_initiator); ++ len += scnprintf(p + len, size - len, "wme: %d, mfp: %d\n", ++ sta->wme, sta->mfp); ++ len += scnprintf(p + len, size - len, "IV: %08x%04x\n", ++ sta_info->iv32, sta_info->iv16); ++ len += scnprintf(p + len, size - len, "\n"); ++ } ++ spin_unlock_bh(&priv->sta_lock); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_ampdu_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ struct mwl_ampdu_stream *stream; ++ int i; ++ struct mwl_sta *sta_info; ++ struct ieee80211_sta *sta; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ spin_lock_bh(&priv->stream_lock); ++ for (i = 0; i < priv->ampdu_num; i++) { ++ stream = &priv->ampdu[i]; ++ if (!stream->state) ++ continue; ++ len += scnprintf(p + len, size - len, "stream: %d\n", i); ++ len += scnprintf(p + len, size - len, "idx: %u\n", stream->idx); ++ len += scnprintf(p + len, size - len, ++ "state: %u\n", stream->state); ++ if (stream->sta) { ++ len += scnprintf(p + len, size - len, ++ "mac address: %pM\n", ++ stream->sta->addr); ++ len += scnprintf(p + len, size - len, ++ "tid: %u\n", stream->tid); ++ } ++ } ++ spin_unlock_bh(&priv->stream_lock); ++ spin_lock_bh(&priv->sta_lock); ++ list_for_each_entry(sta_info, &priv->sta_list, list) { ++ for (i = 0; i < MWL_MAX_TID; i++) { ++ if (sta_info->check_ba_failed[i]) { ++ sta = container_of((void *)sta_info, ++ struct ieee80211_sta, ++ drv_priv); ++ len += scnprintf(p + len, size - len, ++ "%pM(%d): %d\n", ++ sta->addr, i, ++ sta_info->check_ba_failed[i]); ++ } ++ } ++ } ++ spin_unlock_bh(&priv->sta_lock); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_stnid_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ struct mwl_stnid *stnid; ++ int i; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ spin_lock_bh(&priv->stnid_lock); ++ for (i = 0; i < priv->stnid_num; i++) { ++ stnid = &priv->stnid[i]; ++ if (!stnid->aid) ++ continue; ++ len += scnprintf(p + len, size - len, ++ "stnid: %d macid: %d aid: %d\n", ++ i + 1, stnid->macid, stnid->aid); ++ } ++ spin_unlock_bh(&priv->stnid_lock); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_device_pwrtbl_read(struct file *file, ++ char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ int i, j; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, ++ "power table loaded from dts: %s\n", ++ priv->forbidden_setting ? "no" : "yes"); ++ len += scnprintf(p + len, size - len, "firmware region code: 0x%x\n", ++ priv->fw_region_code); ++ len += scnprintf(p + len, size - len, "number of channel: %d\n", ++ priv->number_of_channels); ++ for (i = 0; i < priv->number_of_channels; i++) { ++ len += scnprintf(p + len, size - len, "%3d ", ++ priv->device_pwr_tbl[i].channel); ++ for (j = 0; j < SYSADPT_TX_POWER_LEVEL_TOTAL; j++) ++ len += scnprintf(p + len, size - len, "%3d ", ++ priv->device_pwr_tbl[i].tx_pwr[j]); ++ len += scnprintf(p + len, size - len, "%3d ", ++ priv->device_pwr_tbl[i].dfs_capable); ++ len += scnprintf(p + len, size - len, "%3d ", ++ priv->device_pwr_tbl[i].ax_ant); ++ len += scnprintf(p + len, size - len, "%3d\n", ++ priv->device_pwr_tbl[i].cdd); ++ } ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_txpwrlmt_read(struct file *file, ++ char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ ++ return simple_read_from_buffer(ubuf, count, ppos, ++ priv->txpwrlmt_data.buf, ++ priv->txpwrlmt_data.len); ++} ++ ++static ssize_t mwl_debugfs_tx_amsdu_read(struct file *file, ++ char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, "tx amsdu: %s\n", ++ priv->tx_amsdu ? "enable" : "disable"); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_tx_amsdu_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int value; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ if (kstrtoint(buf, 0, &value)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ priv->tx_amsdu = value ? true : false; ++ ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_dump_hostcmd_read(struct file *file, ++ char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, "dump_hostcmd: %s\n", ++ priv->dump_hostcmd ? "enable" : "disable"); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_dump_hostcmd_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int value; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ if (kstrtoint(buf, 0, &value)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ priv->dump_hostcmd = value ? true : false; ++ ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_dump_probe_read(struct file *file, ++ char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, "dump_probe: %s\n", ++ priv->dump_probe ? "enable" : "disable"); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_dump_probe_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int value; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ if (kstrtoint(buf, 0, &value)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ priv->dump_probe = value ? true : false; ++ ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_heartbeat_read(struct file *file, ++ char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, "heartbeat: %d\n", ++ priv->heartbeat); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_heartbeat_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ if (kstrtoint(buf, 0, &priv->heartbeat)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ priv->pre_jiffies = jiffies; ++ ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_dfs_test_read(struct file *file, ++ char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, "dfs_test: %s\n", ++ priv->dfs_test ? "enable" : "disable"); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_dfs_test_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int value; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ if (kstrtoint(buf, 0, &value)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ priv->dfs_test = value ? true : false; ++ ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_dfs_channel_read(struct file *file, ++ char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ struct ieee80211_supported_band *sband; ++ struct ieee80211_channel *channel; ++ int i; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ sband = priv->hw->wiphy->bands[NL80211_BAND_5GHZ]; ++ if (!sband) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ for (i = 0; i < sband->n_channels; i++) { ++ channel = &sband->channels[i]; ++ if (channel->flags & IEEE80211_CHAN_RADAR) { ++ len += scnprintf(p + len, size - len, ++ "%d(%d): flags: %08x dfs_state: %d\n", ++ channel->hw_value, ++ channel->center_freq, ++ channel->flags, channel->dfs_state); ++ len += scnprintf(p + len, size - len, ++ "cac timer: %d ms\n", ++ channel->dfs_cac_ms); ++ } ++ } ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ ++err: ++ free_page(page); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_dfs_channel_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ struct ieee80211_supported_band *sband; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int dfs_state = 0; ++ int cac_time = -1; ++ struct ieee80211_channel *channel; ++ int i; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ sband = priv->hw->wiphy->bands[NL80211_BAND_5GHZ]; ++ if (!sband) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ ret = sscanf(buf, "%d %d", &dfs_state, &cac_time); ++ ++ if ((ret < 1) || (ret > 2)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ for (i = 0; i < sband->n_channels; i++) { ++ channel = &sband->channels[i]; ++ if (channel->flags & IEEE80211_CHAN_RADAR) { ++ channel->dfs_state = dfs_state; ++ if (cac_time != -1) ++ channel->dfs_cac_ms = cac_time * 1000; ++ } ++ } ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_dfs_radar_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, ++ "csa_active: %d\n", priv->csa_active); ++ len += scnprintf(p + len, size - len, ++ "dfs_region: %d\n", priv->dfs_region); ++ len += scnprintf(p + len, size - len, ++ "chirp_count_min: %d\n", priv->dfs_chirp_count_min); ++ len += scnprintf(p + len, size - len, "chirp_time_interval: %d\n", ++ priv->dfs_chirp_time_interval); ++ len += scnprintf(p + len, size - len, ++ "pw_filter: %d\n", priv->dfs_pw_filter); ++ len += scnprintf(p + len, size - len, ++ "min_num_radar: %d\n", priv->dfs_min_num_radar); ++ len += scnprintf(p + len, size - len, ++ "min_pri_count: %d\n", priv->dfs_min_pri_count); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_dfs_radar_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ ++ wiphy_info(priv->hw->wiphy, "simulate radar detected\n"); ++ ieee80211_radar_detected(priv->hw); ++ ++ return count; ++} ++ ++static ssize_t mwl_debugfs_thermal_read(struct file *file, ++ char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ mwl_fwcmd_get_temp(priv->hw, &priv->temperature); ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, "quiet period: %d\n", ++ priv->quiet_period); ++ len += scnprintf(p + len, size - len, "throttle state: %d\n", ++ priv->throttle_state); ++ len += scnprintf(p + len, size - len, "temperature: %d\n", ++ priv->temperature); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_thermal_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int throttle_state; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ if (kstrtoint(buf, 0, &throttle_state)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (throttle_state > SYSADPT_THERMAL_THROTTLE_MAX) { ++ wiphy_warn(priv->hw->wiphy, ++ "throttle state %d is exceeding the limit %d\n", ++ throttle_state, SYSADPT_THERMAL_THROTTLE_MAX); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ priv->throttle_state = throttle_state; ++ mwl_thermal_set_throttling(priv); ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_led_ctrl_read(struct file *file, ++ char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, "led blink %s\n", ++ priv->led_blink_enable ? "enable" : "disable"); ++ len += scnprintf(p + len, size - len, "led blink rate: %d\n", ++ priv->led_blink_rate); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_led_ctrl_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int enable, rate; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ ret = sscanf(buf, "%x %x", &enable, &rate); ++ ++ if ((ret != 1) && (ret != 2)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (enable && (ret != 2)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ ret = mwl_fwcmd_led_ctrl(priv->hw, enable, rate); ++ ++ if (ret) ++ goto err; ++ ++ priv->led_blink_enable = enable; ++ if (enable) ++ priv->led_blink_rate = rate; ++ else ++ priv->led_blink_rate = 0; ++ ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_regrdwr_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (*ppos) ++ return len; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ if (!priv->reg_type) { ++ /* No command has been given */ ++ len += scnprintf(p + len, size - len, "0"); ++ ret = -EINVAL; ++ goto none; ++ } ++ ++ /* Set command has been given */ ++ if (priv->reg_value != UINT_MAX) { ++ ret = mwl_hif_reg_access(priv->hw, true); ++ goto done; ++ } ++ /* Get command has been given */ ++ ret = mwl_hif_reg_access(priv->hw, false); ++ ++done: ++ if (!ret) ++ len += scnprintf(p + len, size - len, "%u 0x%08x 0x%08x\n", ++ priv->reg_type, priv->reg_offset, ++ priv->reg_value); ++ else ++ len += scnprintf(p + len, size - len, ++ "error: %d(%u 0x%08x 0x%08x)\n", ++ ret, priv->reg_type, priv->reg_offset, ++ priv->reg_value); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ ++none: ++ free_page(page); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_regrdwr_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ ssize_t ret; ++ u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ ret = sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); ++ ++ if (!reg_type) { ++ ret = -EINVAL; ++ goto err; ++ } else { ++ priv->reg_type = reg_type; ++ priv->reg_offset = reg_offset; ++ priv->reg_value = reg_value; ++ ret = count; ++ } ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_ratetable_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ struct mwl_sta *sta_info; ++ struct ieee80211_sta *sta; ++ u8 addr[ETH_ALEN]; ++ int table_size = (sizeof(__le32) * 2 * SYSADPT_MAX_RATE_ADAPT_RATES); ++ u8 *rate_table, *rate_idx; ++ u32 rate_info; ++ u8 fmt, stbc, bw, sgi, mcs, preamble_gf, power_id, ldpc, bf, ant; ++ int idx, rate, nss; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ if (!priv->ra_aid) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ spin_lock_bh(&priv->sta_lock); ++ list_for_each_entry(sta_info, &priv->sta_list, list) { ++ sta = container_of((void *)sta_info, struct ieee80211_sta, ++ drv_priv); ++ if (priv->ra_aid == sta->aid) { ++ ether_addr_copy(addr, sta->addr); ++ break; ++ } ++ } ++ spin_unlock_bh(&priv->sta_lock); ++ ++ rate_table = kzalloc(size, GFP_KERNEL); ++ if (!rate_table) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ret = mwl_fwcmd_get_ratetable(priv->hw, addr, rate_table, ++ table_size, 0); ++ if (ret) { ++ kfree(rate_table); ++ goto err; ++ } ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, ++ "%3s %6s %5s %5s %5s %5s %5s %4s %2s %5s %4s %5s %5s\n", ++ "Num", "Fmt", "STBC", "BW", "SGI", "Nss", "RateId", ++ "GF/Pre", "PId", "LDPC", "BF", "TxAnt", "Rate"); ++ idx = 0; ++ rate_idx = rate_table; ++ rate_info = le32_to_cpu(*(__le32 *)rate_idx); ++ while (rate_info) { ++ fmt = rate_info & MWL_TX_RATE_FORMAT_MASK; ++ stbc = (rate_info & MWL_TX_RATE_STBC_MASK) >> ++ MWL_TX_RATE_STBC_SHIFT; ++ bw = (rate_info & MWL_TX_RATE_BANDWIDTH_MASK) >> ++ MWL_TX_RATE_BANDWIDTH_SHIFT; ++ sgi = (rate_info & MWL_TX_RATE_SHORTGI_MASK) >> ++ MWL_TX_RATE_SHORTGI_SHIFT; ++ mcs = (rate_info & MWL_TX_RATE_RATEIDMCS_MASK) >> ++ MWL_TX_RATE_RATEIDMCS_SHIFT; ++ preamble_gf = (rate_info & MWL_TX_RATE_PREAMBLE_MASK) >> ++ MWL_TX_RATE_PREAMBLE_SHIFT; ++ power_id = (rate_info & MWL_TX_RATE_POWERID_MASK) >> ++ MWL_TX_RATE_POWERID_SHIFT; ++ ldpc = (rate_info & MWL_TX_RATE_ADVCODING_MASK) >> ++ MWL_TX_RATE_ADVCODING_SHIFT; ++ bf = (rate_info & MWL_TX_RATE_BF_MASK) >> ++ MWL_TX_RATE_BF_SHIFT; ++ ant = (rate_info & MWL_TX_RATE_ANTSELECT_MASK) >> ++ MWL_TX_RATE_ANTSELECT_SHIFT; ++ ++ if (fmt == TX_RATE_FORMAT_11AC) { ++ rate = mcs & 0xf; /* 11ac, mcs[3:0]: rate */ ++ nss = mcs >> 4; /* 11ac, mcs[6:4] = nss code */ ++ nss++; /* ddd 1 to correct Nss representation */ ++ } else { ++ rate = mcs; ++ nss = 0; ++ if (fmt == TX_RATE_FORMAT_11N) { ++ if ((mcs >= 0) && (mcs < 8)) ++ nss = 1; ++ else if ((mcs >= 8) && (mcs < 16)) ++ nss = 2; ++ else if ((mcs >= 16) && (mcs < 24)) ++ nss = 3; ++ } ++ } ++ ++ len += scnprintf(p + len, size - len, ++ "%3d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n", ++ idx, (int)fmt, (int)stbc, (int)bw, (int)sgi, nss, rate, ++ (int)preamble_gf, (int)power_id, (int)ldpc, (int)bf, ++ (int)ant, ++ utils_get_phy_rate(fmt, bw, sgi, mcs)); ++ ++ idx++; ++ rate_idx += (2 * sizeof(__le32)); ++ rate_info = le32_to_cpu(*(__le32 *)rate_idx); ++ } ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ kfree(rate_table); ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ ++err: ++ free_page(page); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_ratetable_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int sta_aid; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ if (kstrtoint(buf, 0, &sta_aid)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if ((sta_aid <= 0) || (sta_aid > SYSADPT_MAX_STA_SC4)) { ++ wiphy_warn(priv->hw->wiphy, ++ "station aid is exceeding the limit %d\n", sta_aid); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ priv->ra_aid = sta_aid; ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_tx_hist_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ struct ieee80211_sta *sta; ++ struct mwl_sta *sta_info; ++ ssize_t ret; ++ ++ if (priv->chip_type != MWL8964) ++ return -EPERM; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, ++ "SU: <4:%d >=4:%d >=15:%d >=50:%d >=100:%d >=250:%d\n", ++ priv->ra_tx_attempt[SU_MIMO][0], ++ priv->ra_tx_attempt[SU_MIMO][1], ++ priv->ra_tx_attempt[SU_MIMO][2], ++ priv->ra_tx_attempt[SU_MIMO][3], ++ priv->ra_tx_attempt[SU_MIMO][4], ++ priv->ra_tx_attempt[SU_MIMO][5]); ++ len += scnprintf(p + len, size - len, ++ "MU: <4:%d >=4:%d >=15:%d >=50:%d >=100:%d >=250:%d\n", ++ priv->ra_tx_attempt[MU_MIMO][0], ++ priv->ra_tx_attempt[MU_MIMO][1], ++ priv->ra_tx_attempt[MU_MIMO][2], ++ priv->ra_tx_attempt[MU_MIMO][3], ++ priv->ra_tx_attempt[MU_MIMO][4], ++ priv->ra_tx_attempt[MU_MIMO][5]); ++ spin_lock_bh(&priv->sta_lock); ++ list_for_each_entry(sta_info, &priv->sta_list, list) { ++ sta = container_of((void *)sta_info, struct ieee80211_sta, ++ drv_priv); ++ len += scnprintf(p + len, size - len, "\nSTA %pM\n", sta->addr); ++ len += scnprintf(p + len, size - len, ++ "============================\n"); ++ dump_tx_hist(p, size, &len, sta_info); ++ len += scnprintf(p + len, size - len, ++ "============================\n"); ++ } ++ spin_unlock_bh(&priv->sta_lock); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_tx_hist_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int reset; ++ struct mwl_sta *sta_info; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ if (kstrtoint(buf, 0, &reset)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (!reset) { ++ memset(&priv->ra_tx_attempt, 0, 2 * 6 * sizeof(u32)); ++ spin_lock_bh(&priv->sta_lock); ++ list_for_each_entry(sta_info, &priv->sta_list, list) { ++ memset(&sta_info->tx_hist, 0, ++ sizeof(sta_info->tx_hist)); ++ } ++ spin_unlock_bh(&priv->sta_lock); ++ } ++ ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_ba_hist_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ struct mwl_sta *sta_info; ++ struct mwl_tx_ba_stats *ba_stats; ++ u32 i, data; ++ u32 baholecnt, baexpcnt, bmap0cnt, nobacnt; ++ u8 bmap0flag, nobaflag; ++ char buff[500], file_location[20]; ++ struct file *filp_bahisto; ++ u8 *data_p = buff; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ if (!priv->ba_aid) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ memset(buff, 0, sizeof(buff)); ++ memset(file_location, 0, sizeof(file_location)); ++ sprintf(file_location, "/tmp/ba_histo-%d", priv->ba_aid); ++ ++ filp_bahisto = filp_open(file_location, ++ O_RDWR | O_CREAT | O_TRUNC, 0); ++ ++ if (IS_ERR(filp_bahisto)) { ++ ret = -EIO; ++ goto err; ++ } ++ ++ sta_info = utils_find_sta_by_aid(priv, priv->ba_aid); ++ if (sta_info && sta_info->ba_hist.enable && ++ sta_info->ba_hist.ba_stats) { ++ ba_stats = sta_info->ba_hist.ba_stats; ++ len += scnprintf(p + len, size - len, ++ "BA histogram aid: %d, stnid: %d type: %s\n", ++ priv->ba_aid, sta_info->stnid, ++ sta_info->ba_hist.type ? "MU" : "SU"); ++ data_p += sprintf(data_p, ++ "BA histogram aid: %d, stnid: %d type: %s\n", ++ priv->ba_aid, sta_info->stnid, ++ sta_info->ba_hist.type ? "MU" : "SU"); ++ data_p += sprintf(data_p, "%8s,%8s,%8s,%8s\n", ++ "BAhole", "Expect", "Bmap0", "NoBA"); ++ data = *(u32 *)&ba_stats[0]; ++ baholecnt = 0; ++ baexpcnt = 0; ++ bmap0cnt = 0; ++ nobacnt = 0; ++ for (i = 0; i < ACNT_BA_SIZE && data; i++) { ++ data = *(u32 *)&ba_stats[i]; ++ if (data == 0) ++ break; ++ ++ /* If no BA event does not happen, check BA hole and BA ++ * expected to mark BA bitmap all 0 event ++ */ ++ if (!ba_stats[i].no_ba) ++ bmap0flag = (ba_stats[i].ba_hole == ++ ba_stats[i].ba_expected) ? 1 : 0; ++ else ++ bmap0flag = 0; ++ nobaflag = ba_stats[i].no_ba; ++ ++ /* Buffer is full. Write to file and reset buf */ ++ if ((strlen(buff) + 36) >= 500) { ++ __kernel_write(filp_bahisto, buff, strlen(buff), ++ &filp_bahisto->f_pos); ++ mdelay(2); ++ memset(buff, 0, sizeof(buff)); ++ data_p = buff; ++ } ++ ++ data_p += sprintf(data_p, "%8d,%8d,", ++ ba_stats[i].ba_hole, ++ ba_stats[i].ba_expected); ++ ++ baholecnt += ba_stats[i].ba_hole; ++ baexpcnt += ba_stats[i].ba_expected; ++ if (bmap0flag) { ++ data_p += sprintf(data_p, " #,"); ++ bmap0cnt++; ++ } else ++ data_p += sprintf(data_p, "%8d,", bmap0flag); ++ if (nobaflag) { ++ data_p += sprintf(data_p, " *\n"); ++ nobacnt++; ++ } else ++ data_p += sprintf(data_p, "%8d\n", nobaflag); ++ } ++ ++ __kernel_write(filp_bahisto, buff, strlen(buff), ++ &filp_bahisto->f_pos); ++ len += scnprintf(p + len, size - len, ++ "hole: %d, expect: %d, bmap0: %d, noba: %d\n", ++ baholecnt, baexpcnt, bmap0cnt, nobacnt); ++ len += scnprintf(p + len, size - len, ++ "BA histogram data written to %s\n", ++ file_location); ++ } else ++ len += scnprintf(p + len, size - len, ++ "No BA histogram for sta aid: %d\n", ++ priv->ba_aid); ++ ++ filp_close(filp_bahisto, current->files); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ ++err: ++ free_page(page); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_ba_hist_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int sta_aid; ++ struct mwl_sta *sta_info; ++ int size; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ if (kstrtoint(buf, 0, &sta_aid)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if ((sta_aid <= 0) || (sta_aid > SYSADPT_MAX_STA_SC4)) { ++ wiphy_warn(priv->hw->wiphy, ++ "station aid is exceeding the limit %d\n", sta_aid); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (priv->ba_aid) { ++ sta_info = utils_find_sta_by_aid(priv, priv->ba_aid); ++ if (sta_info) { ++ sta_info->ba_hist.enable = false; ++ kfree(sta_info->ba_hist.ba_stats); ++ } ++ } ++ priv->ba_aid = 0; ++ sta_info = utils_find_sta_by_aid(priv, sta_aid); ++ if (sta_info) { ++ sta_info->ba_hist.enable = true; ++ sta_info->ba_hist.index = 0; ++ size = sizeof(struct mwl_tx_ba_stats) * ACNT_BA_SIZE; ++ sta_info->ba_hist.ba_stats = kmalloc(size, GFP_KERNEL); ++ if (sta_info->ba_hist.ba_stats) { ++ memset(sta_info->ba_hist.ba_stats, 0, size); ++ priv->ba_aid = sta_aid; ++ } ++ ret = count; ++ } else ++ ret = -EINVAL; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_fixed_rate_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ ssize_t ret; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, "fixed rate: 0x%08x\n", ++ priv->fixed_rate); ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ free_page(page); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_fixed_rate_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ ssize_t ret; ++ int fixed_rate = 0, fwcmd_ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ ret = sscanf(buf, "%08x", &fixed_rate); ++ if (!ret) { ++ ret = -EIO; ++ goto err; ++ } ++ ++ priv->fixed_rate = fixed_rate; ++ ++ if (fixed_rate != 0) ++ fwcmd_ret = mwl_fwcmd_set_rate_drop(priv->hw, 3, ++ priv->fixed_rate, 0); ++ else ++ fwcmd_ret = mwl_fwcmd_set_rate_drop(priv->hw, 1, ++ priv->fixed_rate, 0); ++ if (fwcmd_ret) ++ ret = -EIO; ++ else ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_core_dump_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long page = get_zeroed_page(GFP_KERNEL); ++ char *p = (char *)page; ++ int len = 0, size = PAGE_SIZE; ++ struct coredump_cmd *core_dump = NULL; ++ struct coredump *cd = NULL; ++ char *buff = NULL; ++ u32 i, offset; ++ u32 address, length; ++ ssize_t ret; ++ ++ if (priv->chip_type != MWL8964) ++ return -EPERM; ++ ++ if (*ppos) ++ return len; ++ ++ if (!p) ++ return -ENOMEM; ++ ++ core_dump = kmalloc(sizeof(*core_dump), GFP_ATOMIC); ++ if (!core_dump) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ buff = kmalloc(MAX_CORE_DUMP_BUFFER, GFP_ATOMIC); ++ if (!buff) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ memset((char *)buff, 0, MAX_CORE_DUMP_BUFFER); ++ ++ cd = kmalloc(sizeof(*cd), GFP_ATOMIC); ++ if (!cd) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ core_dump->context = 0; ++ core_dump->flags = 0; ++ core_dump->size_kb = 0; ++ if (mwl_fwcmd_get_fw_core_dump(priv->hw, core_dump, buff)) { ++ ret = -EIO; ++ goto err; ++ } ++ memcpy(cd, buff, sizeof(*cd)); ++ ++ len += scnprintf(p + len, size - len, "\n"); ++ len += scnprintf(p + len, size - len, "Major Version : %d\n", ++ cd->version_major); ++ len += scnprintf(p + len, size - len, "Minor Version : %d\n", ++ cd->version_minor); ++ len += scnprintf(p + len, size - len, "Patch Version : %d\n", ++ cd->version_patch); ++ len += scnprintf(p + len, size - len, "Num of Regions: %d\n", ++ cd->num_regions); ++ len += scnprintf(p + len, size - len, "Num of Symbols: %d\n", ++ cd->num_symbols); ++ ++ for (i = 0; i < cd->num_regions; i++) { ++ address = le32_to_cpu(cd->region[i].address); ++ length = le32_to_cpu(cd->region[i].length); ++ len += scnprintf(p + len, size - len, ++ "\ncd.region[%d]: address=%x, length=%x\n", ++ i, address, length); ++ ++ for (offset = 0; offset < length; ++ offset += MAX_CORE_DUMP_BUFFER) { ++ core_dump->context = cpu_to_le32((i << 28) | offset); ++ core_dump->flags = 0; ++ core_dump->size_kb = 0; ++ if (mwl_fwcmd_get_fw_core_dump(priv->hw, ++ core_dump, buff)) { ++ wiphy_info(priv->hw->wiphy, ++ "region:%d offset:%x\n", i, offset); ++ break; ++ } ++ core_dump_file(buff, MAX_CORE_DUMP_BUFFER, ++ address, address + offset, ++ offset, length, priv->coredump_text); ++ } ++ } ++ len += scnprintf(p + len, size - len, "\n"); ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); ++ ++err: ++ kfree(core_dump); ++ kfree(buff); ++ kfree(cd); ++ free_page(page); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_core_dump_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int text_mode; ++ ssize_t ret; ++ ++ if (priv->chip_type != MWL8964) ++ return -EPERM; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ if (kstrtoint(buf, 0, &text_mode)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if ((text_mode < 0) || (text_mode > 1)) { ++ wiphy_warn(priv->hw->wiphy, ++ "text mode should be 0 (false) or 1 (true): %d\n", ++ text_mode); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ mwl_fwcmd_core_dump_diag_mode(priv->hw, 1); ++ priv->coredump_text = text_mode ? true : false; ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_mcast_cts_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ int cts_enable = 0; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ if (kstrtoint(buf, 0, &cts_enable)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ ret = mwl_hif_mcast_cts(priv->hw, cts_enable ? true : false); ++ if (ret) ++ goto err; ++ ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++static ssize_t mwl_debugfs_wmmedcaap_write(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; ++ unsigned long addr = get_zeroed_page(GFP_KERNEL); ++ char *buf = (char *)addr; ++ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); ++ u32 index = 0, cw_min = 0, cw_max = 0, aifsn = 0, txop = 0; ++ ssize_t ret; ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, ubuf, buf_size)) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ ret = sscanf(buf, "%u %x %x %u %x", &index, &cw_min, ++ &cw_max, &aifsn, &txop); ++ if (ret != 5) { ++ ret = -EINVAL; ++ goto err; ++ } ++ wiphy_info(priv->hw->wiphy, "set TCQ%d wmm edca with:\n", index); ++ wiphy_info(priv->hw->wiphy, ++ "cw_min=0x%x, cw_max=0x%x, aifs_num=%d, txop=0x%x\n", ++ cw_min, cw_max, aifsn, txop); ++ ++ ret = mwl_fwcmd_set_edca_params(priv->hw, index, ++ cw_min, cw_max, aifsn, txop); ++ if (ret) ++ goto err; ++ ++ ret = count; ++ ++err: ++ free_page(addr); ++ return ret; ++} ++ ++MWLWIFI_DEBUGFS_FILE_READ_OPS(info); ++MWLWIFI_DEBUGFS_FILE_READ_OPS(tx_status); ++MWLWIFI_DEBUGFS_FILE_READ_OPS(rx_status); ++MWLWIFI_DEBUGFS_FILE_READ_OPS(vif); ++MWLWIFI_DEBUGFS_FILE_READ_OPS(sta); ++MWLWIFI_DEBUGFS_FILE_READ_OPS(ampdu); ++MWLWIFI_DEBUGFS_FILE_READ_OPS(stnid); ++MWLWIFI_DEBUGFS_FILE_READ_OPS(device_pwrtbl); ++MWLWIFI_DEBUGFS_FILE_READ_OPS(txpwrlmt); ++MWLWIFI_DEBUGFS_FILE_OPS(tx_amsdu); ++MWLWIFI_DEBUGFS_FILE_OPS(dump_hostcmd); ++MWLWIFI_DEBUGFS_FILE_OPS(dump_probe); ++MWLWIFI_DEBUGFS_FILE_OPS(heartbeat); ++MWLWIFI_DEBUGFS_FILE_OPS(dfs_test); ++MWLWIFI_DEBUGFS_FILE_OPS(dfs_channel); ++MWLWIFI_DEBUGFS_FILE_OPS(dfs_radar); ++MWLWIFI_DEBUGFS_FILE_OPS(thermal); ++MWLWIFI_DEBUGFS_FILE_OPS(led_ctrl); ++MWLWIFI_DEBUGFS_FILE_OPS(regrdwr); ++MWLWIFI_DEBUGFS_FILE_OPS(ratetable); ++MWLWIFI_DEBUGFS_FILE_OPS(tx_hist); ++MWLWIFI_DEBUGFS_FILE_OPS(ba_hist); ++MWLWIFI_DEBUGFS_FILE_OPS(fixed_rate); ++MWLWIFI_DEBUGFS_FILE_OPS(core_dump); ++MWLWIFI_DEBUGFS_FILE_WRITE_OPS(mcast_cts); ++MWLWIFI_DEBUGFS_FILE_WRITE_OPS(wmmedcaap); ++ ++void mwl_debugfs_init(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (!priv->debugfs_phy) ++ priv->debugfs_phy = debugfs_create_dir("mwlwifi", ++ hw->wiphy->debugfsdir); ++ ++ if (!priv->debugfs_phy) ++ return; ++ ++ MWLWIFI_DEBUGFS_ADD_FILE(info); ++ MWLWIFI_DEBUGFS_ADD_FILE(tx_status); ++ MWLWIFI_DEBUGFS_ADD_FILE(rx_status); ++ MWLWIFI_DEBUGFS_ADD_FILE(vif); ++ MWLWIFI_DEBUGFS_ADD_FILE(sta); ++ MWLWIFI_DEBUGFS_ADD_FILE(ampdu); ++ MWLWIFI_DEBUGFS_ADD_FILE(stnid); ++ MWLWIFI_DEBUGFS_ADD_FILE(device_pwrtbl); ++ MWLWIFI_DEBUGFS_ADD_FILE(txpwrlmt); ++ MWLWIFI_DEBUGFS_ADD_FILE(tx_amsdu); ++ MWLWIFI_DEBUGFS_ADD_FILE(dump_hostcmd); ++ MWLWIFI_DEBUGFS_ADD_FILE(dump_probe); ++ MWLWIFI_DEBUGFS_ADD_FILE(heartbeat); ++ MWLWIFI_DEBUGFS_ADD_FILE(dfs_test); ++ MWLWIFI_DEBUGFS_ADD_FILE(dfs_channel); ++ MWLWIFI_DEBUGFS_ADD_FILE(dfs_radar); ++ MWLWIFI_DEBUGFS_ADD_FILE(thermal); ++ MWLWIFI_DEBUGFS_ADD_FILE(led_ctrl); ++ MWLWIFI_DEBUGFS_ADD_FILE(regrdwr); ++ MWLWIFI_DEBUGFS_ADD_FILE(ratetable); ++ MWLWIFI_DEBUGFS_ADD_FILE(tx_hist); ++ MWLWIFI_DEBUGFS_ADD_FILE(ba_hist); ++ MWLWIFI_DEBUGFS_ADD_FILE(fixed_rate); ++ MWLWIFI_DEBUGFS_ADD_FILE(core_dump); ++ MWLWIFI_DEBUGFS_ADD_FILE(mcast_cts); ++ MWLWIFI_DEBUGFS_ADD_FILE(wmmedcaap); ++} ++ ++void mwl_debugfs_remove(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ debugfs_remove(priv->debugfs_phy); ++ priv->debugfs_phy = NULL; ++} +diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.h b/drivers/net/wireless/marvell/mwlwifi/debugfs.h +new file mode 100644 +index 000000000000..e7595f563348 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.h +@@ -0,0 +1,24 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines debug fs related functions. */ ++ ++#ifndef _MWL_DEBUGFS_H_ ++#define _MWL_DEBUGFS_H_ ++ ++void mwl_debugfs_init(struct ieee80211_hw *hw); ++void mwl_debugfs_remove(struct ieee80211_hw *hw); ++ ++#endif /* _MWL_DEBUGFS_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.c b/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.c +new file mode 100644 +index 000000000000..ff943d6fe447 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.c +@@ -0,0 +1,3852 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements firmware host command related ++ * functions. ++ */ ++ ++#include ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "utils.h" ++#include "hif/fwcmd.h" ++#include "hif/hif-ops.h" ++ ++#define MAX_WAIT_GET_HW_SPECS_ITERATONS 3 ++ ++struct cmd_header { ++ __le16 command; ++ __le16 len; ++} __packed; ++ ++char *mwl_fwcmd_get_cmd_string(unsigned short cmd) ++{ ++ int max_entries = 0; ++ int curr_cmd = 0; ++ ++ static const struct { ++ u16 cmd; ++ char *cmd_string; ++ } cmds[] = { ++ { HOSTCMD_CMD_GET_HW_SPEC, "GetHwSpecifications" }, ++ { HOSTCMD_CMD_SET_HW_SPEC, "SetHwSepcifications" }, ++ { HOSTCMD_CMD_802_11_GET_STAT, "80211GetStat" }, ++ { HOSTCMD_CMD_BBP_REG_ACCESS, "BBPRegAccess" }, ++ { HOSTCMD_CMD_RF_REG_ACCESS, "RFRegAccess" }, ++ { HOSTCMD_CMD_802_11_RADIO_CONTROL, "80211RadioControl" }, ++ { HOSTCMD_CMD_MEM_ADDR_ACCESS, "MEMAddrAccess" }, ++ { HOSTCMD_CMD_802_11_TX_POWER, "80211TxPower" }, ++ { HOSTCMD_CMD_802_11_RF_ANTENNA, "80211RfAntenna" }, ++ { HOSTCMD_CMD_BROADCAST_SSID_ENABLE, "BroadcastSsidEnable" }, ++ { HOSTCMD_CMD_SET_CFG, "SetCfg" }, ++ { HOSTCMD_CMD_SET_RF_CHANNEL, "SetRfChannel" }, ++ { HOSTCMD_CMD_SET_AID, "SetAid" }, ++ { HOSTCMD_CMD_SET_INFRA_MODE, "SetInfraMode" }, ++ { HOSTCMD_CMD_802_11_RTS_THSD, "80211RtsThreshold" }, ++ { HOSTCMD_CMD_SET_EDCA_PARAMS, "SetEDCAParams" }, ++ { HOSTCMD_CMD_802_11H_DETECT_RADAR, "80211hDetectRadar" }, ++ { HOSTCMD_CMD_SET_WMM_MODE, "SetWMMMode" }, ++ { HOSTCMD_CMD_HT_GUARD_INTERVAL, "HtGuardInterval" }, ++ { HOSTCMD_CMD_SET_FIXED_RATE, "SetFixedRate" }, ++ { HOSTCMD_CMD_SET_IES, "SetInformationElements" }, ++ { HOSTCMD_CMD_SET_LINKADAPT_CS_MODE, "LinkAdaptCsMode" }, ++ { HOSTCMD_CMD_DUMP_OTP_DATA, "DumpOtpData" }, ++ { HOSTCMD_CMD_SET_MAC_ADDR, "SetMacAddr" }, ++ { HOSTCMD_CMD_SET_RATE_ADAPT_MODE, "SetRateAdaptationMode" }, ++ { HOSTCMD_CMD_GET_WATCHDOG_BITMAP, "GetWatchdogBitMap" }, ++ { HOSTCMD_CMD_DEL_MAC_ADDR, "DelMacAddr" }, ++ { HOSTCMD_CMD_BSS_START, "BssStart" }, ++ { HOSTCMD_CMD_AP_BEACON, "SetApBeacon" }, ++ { HOSTCMD_CMD_SET_NEW_STN, "SetNewStation" }, ++ { HOSTCMD_CMD_SET_APMODE, "SetApMode" }, ++ { HOSTCMD_CMD_SET_SWITCH_CHANNEL, "SetSwitchChannel" }, ++ { HOSTCMD_CMD_UPDATE_ENCRYPTION, "UpdateEncryption" }, ++ { HOSTCMD_CMD_BASTREAM, "BAStream" }, ++ { HOSTCMD_CMD_SET_SPECTRUM_MGMT, "SetSpectrumMgmt" }, ++ { HOSTCMD_CMD_SET_POWER_CONSTRAINT, "SetPowerConstraint" }, ++ { HOSTCMD_CMD_SET_COUNTRY_CODE, "SetCountryCode" }, ++ { HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL, "SetOptimizationLevel" }, ++ { HOSTCMD_CMD_SET_WSC_IE, "SetWscIE" }, ++ { HOSTCMD_CMD_GET_RATETABLE, "GetRateTable" }, ++ { HOSTCMD_CMD_GET_SEQNO, "GetSeqno" }, ++ { HOSTCMD_CMD_DWDS_ENABLE, "DwdsEnable" }, ++ { HOSTCMD_CMD_FW_FLUSH_TIMER, "FwFlushTimer" }, ++ { HOSTCMD_CMD_SET_CDD, "SetCDD" }, ++ { HOSTCMD_CMD_SET_BFTYPE, "SetBFType" }, ++ { HOSTCMD_CMD_CAU_REG_ACCESS, "CAURegAccess" }, ++ { HOSTCMD_CMD_GET_TEMP, "GetTemp" }, ++ { HOSTCMD_CMD_LED_CTRL, "LedCtrl" }, ++ { HOSTCMD_CMD_GET_FW_REGION_CODE, "GetFwRegionCode" }, ++ { HOSTCMD_CMD_GET_DEVICE_PWR_TBL, "GetDevicePwrTbl" }, ++ { HOSTCMD_CMD_SET_RATE_DROP, "SetRateDrop" }, ++ { HOSTCMD_CMD_NEWDP_DMATHREAD_START, "NewdpDMAThreadStart" }, ++ { HOSTCMD_CMD_GET_FW_REGION_CODE_SC4, "GetFwRegionCodeSC4" }, ++ { HOSTCMD_CMD_GET_DEVICE_PWR_TBL_SC4, "GetDevicePwrTblSC4" }, ++ { HOSTCMD_CMD_QUIET_MODE, "QuietMode" }, ++ { HOSTCMD_CMD_CORE_DUMP_DIAG_MODE, "CoreDumpDiagMode" }, ++ { HOSTCMD_CMD_802_11_SLOT_TIME, "80211SlotTime" }, ++ { HOSTCMD_CMD_GET_FW_CORE_DUMP, "GetFwCoreDump" }, ++ { HOSTCMD_CMD_EDMAC_CTRL, "EDMACCtrl" }, ++ { HOSTCMD_CMD_TXPWRLMT_CFG, "TxpwrlmtCfg" }, ++ { HOSTCMD_CMD_MCAST_CTS, "McastCts" }, ++ }; ++ ++ max_entries = ARRAY_SIZE(cmds); ++ ++ for (curr_cmd = 0; curr_cmd < max_entries; curr_cmd++) ++ if ((cmd & 0x7fff) == cmds[curr_cmd].cmd) ++ return cmds[curr_cmd].cmd_string; ++ ++ return "unknown"; ++} ++ ++static int mwl_fwcmd_802_11_radio_control(struct mwl_priv *priv, ++ bool enable, bool force) ++{ ++ struct hostcmd_cmd_802_11_radio_control *pcmd; ++ ++ if (enable == priv->radio_on && !force) ++ return 0; ++ ++ pcmd = (struct hostcmd_cmd_802_11_radio_control *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RADIO_CONTROL); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le16(WL_SET); ++ pcmd->control = cpu_to_le16(priv->radio_short_preamble ? ++ WL_AUTO_PREAMBLE : WL_LONG_PREAMBLE); ++ pcmd->radio_on = cpu_to_le16(enable ? WL_ENABLE : WL_DISABLE); ++ ++ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_802_11_RADIO_CONTROL)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ priv->radio_on = enable; ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++static int mwl_fwcmd_get_tx_powers(struct mwl_priv *priv, u16 *powlist, ++ u8 action, u16 ch, u16 band, ++ u16 width, u16 sub_ch) ++{ ++ struct hostcmd_cmd_802_11_tx_power *pcmd; ++ int i; ++ ++ pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ if (priv->chip_type == MWL8997) { ++ memset(pcmd, 0x00, ++ sizeof(struct hostcmd_cmd_802_11_tx_power_kf2)); ++ pcmd->cmd_hdr.len = cpu_to_le16( ++ sizeof(struct hostcmd_cmd_802_11_tx_power_kf2)); ++ } else { ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ } ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_TX_POWER); ++ pcmd->action = cpu_to_le16(action); ++ pcmd->ch = cpu_to_le16(ch); ++ pcmd->bw = cpu_to_le16(width); ++ pcmd->band = cpu_to_le16(band); ++ pcmd->sub_ch = cpu_to_le16(sub_ch); ++ ++ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_802_11_TX_POWER)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ for (i = 0; i < priv->pwr_level; i++) ++ powlist[i] = le16_to_cpu(pcmd->power_level_list[i]); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++static int mwl_fwcmd_set_tx_powers(struct mwl_priv *priv, u16 txpow[], ++ u8 action, u16 ch, u16 band, ++ u16 width, u16 sub_ch) ++{ ++ struct hostcmd_cmd_802_11_tx_power *pcmd; ++ int i; ++ ++ pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ if (priv->chip_type == MWL8997) { ++ memset(pcmd, 0x00, ++ sizeof(struct hostcmd_cmd_802_11_tx_power_kf2)); ++ pcmd->cmd_hdr.len = cpu_to_le16( ++ sizeof(struct hostcmd_cmd_802_11_tx_power_kf2)); ++ } else { ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ } ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_TX_POWER); ++ pcmd->action = cpu_to_le16(action); ++ pcmd->ch = cpu_to_le16(ch); ++ pcmd->bw = cpu_to_le16(width); ++ pcmd->band = cpu_to_le16(band); ++ pcmd->sub_ch = cpu_to_le16(sub_ch); ++ ++ for (i = 0; i < priv->pwr_level; i++) ++ pcmd->power_level_list[i] = cpu_to_le16(txpow[i]); ++ ++ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_802_11_TX_POWER)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++static u8 mwl_fwcmd_get_80m_pri_chnl(u8 channel) ++{ ++ u8 act_primary = ACT_PRIMARY_CHAN_0; ++ ++ switch (channel) { ++ case 36: ++ act_primary = ACT_PRIMARY_CHAN_0; ++ break; ++ case 40: ++ act_primary = ACT_PRIMARY_CHAN_1; ++ break; ++ case 44: ++ act_primary = ACT_PRIMARY_CHAN_2; ++ break; ++ case 48: ++ act_primary = ACT_PRIMARY_CHAN_3; ++ break; ++ case 52: ++ act_primary = ACT_PRIMARY_CHAN_0; ++ break; ++ case 56: ++ act_primary = ACT_PRIMARY_CHAN_1; ++ break; ++ case 60: ++ act_primary = ACT_PRIMARY_CHAN_2; ++ break; ++ case 64: ++ act_primary = ACT_PRIMARY_CHAN_3; ++ break; ++ case 100: ++ act_primary = ACT_PRIMARY_CHAN_0; ++ break; ++ case 104: ++ act_primary = ACT_PRIMARY_CHAN_1; ++ break; ++ case 108: ++ act_primary = ACT_PRIMARY_CHAN_2; ++ break; ++ case 112: ++ act_primary = ACT_PRIMARY_CHAN_3; ++ break; ++ case 116: ++ act_primary = ACT_PRIMARY_CHAN_0; ++ break; ++ case 120: ++ act_primary = ACT_PRIMARY_CHAN_1; ++ break; ++ case 124: ++ act_primary = ACT_PRIMARY_CHAN_2; ++ break; ++ case 128: ++ act_primary = ACT_PRIMARY_CHAN_3; ++ break; ++ case 132: ++ act_primary = ACT_PRIMARY_CHAN_0; ++ break; ++ case 136: ++ act_primary = ACT_PRIMARY_CHAN_1; ++ break; ++ case 140: ++ act_primary = ACT_PRIMARY_CHAN_2; ++ break; ++ case 144: ++ act_primary = ACT_PRIMARY_CHAN_3; ++ break; ++ case 149: ++ act_primary = ACT_PRIMARY_CHAN_0; ++ break; ++ case 153: ++ act_primary = ACT_PRIMARY_CHAN_1; ++ break; ++ case 157: ++ act_primary = ACT_PRIMARY_CHAN_2; ++ break; ++ case 161: ++ act_primary = ACT_PRIMARY_CHAN_3; ++ break; ++ } ++ ++ return act_primary; ++} ++ ++static u8 mwl_fwcmd_get_160m_pri_chnl(u8 channel) ++{ ++ u8 act_primary = ACT_PRIMARY_CHAN_0; ++ ++ switch (channel) { ++ case 36: ++ act_primary = ACT_PRIMARY_CHAN_0; ++ break; ++ case 40: ++ act_primary = ACT_PRIMARY_CHAN_1; ++ break; ++ case 44: ++ act_primary = ACT_PRIMARY_CHAN_2; ++ break; ++ case 48: ++ act_primary = ACT_PRIMARY_CHAN_3; ++ break; ++ case 52: ++ act_primary = ACT_PRIMARY_CHAN_4; ++ break; ++ case 56: ++ act_primary = ACT_PRIMARY_CHAN_5; ++ break; ++ case 60: ++ act_primary = ACT_PRIMARY_CHAN_6; ++ break; ++ case 64: ++ act_primary = ACT_PRIMARY_CHAN_7; ++ break; ++ case 100: ++ act_primary = ACT_PRIMARY_CHAN_0; ++ break; ++ case 104: ++ act_primary = ACT_PRIMARY_CHAN_1; ++ break; ++ case 108: ++ act_primary = ACT_PRIMARY_CHAN_2; ++ break; ++ case 112: ++ act_primary = ACT_PRIMARY_CHAN_3; ++ break; ++ case 116: ++ act_primary = ACT_PRIMARY_CHAN_4; ++ break; ++ case 120: ++ act_primary = ACT_PRIMARY_CHAN_5; ++ break; ++ case 124: ++ act_primary = ACT_PRIMARY_CHAN_6; ++ break; ++ case 128: ++ act_primary = ACT_PRIMARY_CHAN_7; ++ break; ++ case 149: ++ act_primary = ACT_PRIMARY_CHAN_0; ++ break; ++ case 153: ++ act_primary = ACT_PRIMARY_CHAN_1; ++ break; ++ case 157: ++ act_primary = ACT_PRIMARY_CHAN_2; ++ break; ++ case 161: ++ act_primary = ACT_PRIMARY_CHAN_3; ++ break; ++ case 165: ++ act_primary = ACT_PRIMARY_CHAN_4; ++ break; ++ case 169: ++ act_primary = ACT_PRIMARY_CHAN_5; ++ break; ++ case 173: ++ act_primary = ACT_PRIMARY_CHAN_6; ++ break; ++ case 177: ++ act_primary = ACT_PRIMARY_CHAN_7; ++ break; ++ } ++ ++ return act_primary; ++} ++ ++static void mwl_fwcmd_parse_beacon(struct mwl_priv *priv, ++ struct mwl_vif *vif, u8 *beacon, int len) ++{ ++ struct ieee80211_mgmt *mgmt; ++ struct beacon_info *beacon_info; ++ int baselen; ++ u8 *pos; ++ size_t left; ++ bool elem_parse_failed; ++ ++ mgmt = (struct ieee80211_mgmt *)beacon; ++ ++ baselen = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt; ++ if (baselen > len) ++ return; ++ ++ beacon_info = &vif->beacon_info; ++ memset(beacon_info, 0, sizeof(struct beacon_info)); ++ beacon_info->valid = false; ++ beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0]; ++ beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0]; ++ ++ beacon_info->cap_info = le16_to_cpu(mgmt->u.beacon.capab_info); ++ beacon_info->power_constraint = 0; ++ ++ pos = (u8 *)mgmt->u.beacon.variable; ++ left = len - baselen; ++ ++ elem_parse_failed = false; ++ ++ while (left >= 2) { ++ u8 id, elen; ++ ++ id = *pos++; ++ elen = *pos++; ++ left -= 2; ++ ++ if (elen > left) { ++ elem_parse_failed = true; ++ break; ++ } ++ ++ switch (id) { ++ case WLAN_EID_COUNTRY: ++ beacon_info->ie_country_len = (elen + 2); ++ beacon_info->ie_country_ptr = (pos - 2); ++ break; ++ case WLAN_EID_SUPP_RATES: ++ case WLAN_EID_EXT_SUPP_RATES: ++ { ++ int idx, bi, oi; ++ u8 rate; ++ ++ for (bi = 0; bi < SYSADPT_MAX_DATA_RATES_G; ++ bi++) { ++ if (beacon_info->b_rate_set[bi] == 0) ++ break; ++ } ++ ++ for (oi = 0; oi < SYSADPT_MAX_DATA_RATES_G; ++ oi++) { ++ if (beacon_info->op_rate_set[oi] == 0) ++ break; ++ } ++ ++ for (idx = 0; idx < elen; idx++) { ++ rate = pos[idx]; ++ if ((rate & 0x80) != 0) { ++ if (bi < SYSADPT_MAX_DATA_RATES_G) ++ beacon_info->b_rate_set[bi++] ++ = rate & 0x7f; ++ else { ++ elem_parse_failed = true; ++ break; ++ } ++ } ++ if (oi < SYSADPT_MAX_DATA_RATES_G) ++ beacon_info->op_rate_set[oi++] = ++ rate & 0x7f; ++ else { ++ elem_parse_failed = true; ++ break; ++ } ++ } ++ } ++ break; ++ case WLAN_EID_PWR_CONSTRAINT: ++ if (elen == 1) ++ beacon_info->power_constraint = *pos; ++ break; ++ case WLAN_EID_RSN: ++ beacon_info->ie_rsn48_len = (elen + 2); ++ beacon_info->ie_rsn48_ptr = (pos - 2); ++ break; ++ case WLAN_EID_MOBILITY_DOMAIN: ++ beacon_info->ie_mde_len = (elen + 2); ++ beacon_info->ie_mde_ptr = (pos - 2); ++ break; ++ case WLAN_EID_HT_CAPABILITY: ++ case WLAN_EID_HT_OPERATION: ++ case WLAN_EID_OVERLAP_BSS_SCAN_PARAM: ++ case WLAN_EID_EXT_CAPABILITY: ++ beacon_info->ie_ht_len += (elen + 2); ++ if (beacon_info->ie_ht_len > ++ sizeof(beacon_info->ie_list_ht)) { ++ elem_parse_failed = true; ++ } else { ++ *beacon_info->ie_ht_ptr++ = id; ++ *beacon_info->ie_ht_ptr++ = elen; ++ memcpy(beacon_info->ie_ht_ptr, pos, elen); ++ beacon_info->ie_ht_ptr += elen; ++ } ++ break; ++ case WLAN_EID_MESH_CONFIG: ++ beacon_info->ie_meshcfg_len = (elen + 2); ++ beacon_info->ie_meshcfg_ptr = (pos - 2); ++ break; ++ case WLAN_EID_MESH_ID: ++ beacon_info->ie_meshid_len = (elen + 2); ++ beacon_info->ie_meshid_ptr = (pos - 2); ++ break; ++ case WLAN_EID_CHAN_SWITCH_PARAM: ++ beacon_info->ie_meshchsw_len = (elen + 2); ++ beacon_info->ie_meshchsw_ptr = (pos - 2); ++ break; ++ case WLAN_EID_VHT_CAPABILITY: ++ case WLAN_EID_VHT_OPERATION: ++ case WLAN_EID_OPMODE_NOTIF: ++ beacon_info->ie_vht_len += (elen + 2); ++ if (beacon_info->ie_vht_len > ++ sizeof(beacon_info->ie_list_vht)) { ++ elem_parse_failed = true; ++ } else { ++ *beacon_info->ie_vht_ptr++ = id; ++ *beacon_info->ie_vht_ptr++ = elen; ++ memcpy(beacon_info->ie_vht_ptr, pos, elen); ++ beacon_info->ie_vht_ptr += elen; ++ } ++ break; ++ case WLAN_EID_VENDOR_SPECIFIC: ++ if ((pos[0] == 0x00) && (pos[1] == 0x50) && ++ (pos[2] == 0xf2)) { ++ if (pos[3] == 0x01) { ++ beacon_info->ie_rsn_len = (elen + 2); ++ beacon_info->ie_rsn_ptr = (pos - 2); ++ } ++ ++ if (pos[3] == 0x02) { ++ beacon_info->ie_wmm_len = (elen + 2); ++ beacon_info->ie_wmm_ptr = (pos - 2); ++ } ++ ++ if (pos[3] == 0x04) { ++ beacon_info->ie_wsc_len = (elen + 2); ++ beacon_info->ie_wsc_ptr = (pos - 2); ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ ++ left -= elen; ++ pos += elen; ++ } ++ ++ if (!elem_parse_failed) { ++ beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0]; ++ beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0]; ++ beacon_info->valid = true; ++ } ++} ++ ++static int mwl_fwcmd_set_ies(struct mwl_priv *priv, struct mwl_vif *mwl_vif) ++{ ++ struct hostcmd_cmd_set_ies *pcmd; ++ struct beacon_info *beacon = &mwl_vif->beacon_info; ++ u16 ie_list_len_proprietary = 0; ++ ++ if (beacon->ie_ht_len > sizeof(pcmd->ie_list_ht)) ++ goto einval; ++ ++ if (beacon->ie_vht_len > sizeof(pcmd->ie_list_vht)) ++ goto einval; ++ ++ pcmd = (struct hostcmd_cmd_set_ies *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_IES); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ++ pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET); ++ ++ memcpy(pcmd->ie_list_ht, beacon->ie_ht_ptr, beacon->ie_ht_len); ++ pcmd->ie_list_len_ht = cpu_to_le16(beacon->ie_ht_len); ++ ++ memcpy(pcmd->ie_list_vht, beacon->ie_vht_ptr, beacon->ie_vht_len); ++ pcmd->ie_list_len_vht = cpu_to_le16(beacon->ie_vht_len); ++ ++ memcpy(pcmd->ie_list_proprietary, beacon->ie_meshid_ptr, ++ beacon->ie_meshid_len); ++ ie_list_len_proprietary = beacon->ie_meshid_len; ++ ++ memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary, ++ beacon->ie_meshcfg_ptr, beacon->ie_meshcfg_len); ++ ie_list_len_proprietary += beacon->ie_meshcfg_len; ++ ++ memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary, ++ beacon->ie_meshchsw_ptr, beacon->ie_meshchsw_len); ++ ie_list_len_proprietary += beacon->ie_meshchsw_len; ++ ++ if (priv->chip_type == MWL8897) { ++ memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary, ++ beacon->ie_wmm_ptr, beacon->ie_wmm_len); ++ ie_list_len_proprietary += mwl_vif->beacon_info.ie_wmm_len; ++ } ++ ++ memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary, ++ beacon->ie_mde_ptr, beacon->ie_mde_len); ++ ie_list_len_proprietary += mwl_vif->beacon_info.ie_mde_len; ++ ++ pcmd->ie_list_len_proprietary = cpu_to_le16(ie_list_len_proprietary); ++ ++ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_SET_IES)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++ ++einval: ++ ++ wiphy_err(priv->hw->wiphy, "length of IE is too long\n"); ++ ++ return -EINVAL; ++} ++ ++static int mwl_fwcmd_set_ap_beacon(struct mwl_priv *priv, ++ struct mwl_vif *mwl_vif, ++ struct ieee80211_bss_conf *bss_conf) ++{ ++ struct hostcmd_cmd_ap_beacon *pcmd; ++ struct ds_params *phy_ds_param_set; ++ ++ /* wmm structure of start command is defined less one byte, ++ * due to following field country is not used, add byte one ++ * to bypass the check. ++ */ ++ if (mwl_vif->beacon_info.ie_wmm_len > ++ (sizeof(pcmd->start_cmd.wmm_param) + 1)) ++ goto ielenerr; ++ ++ if (mwl_vif->beacon_info.ie_rsn_len > sizeof(pcmd->start_cmd.rsn_ie)) ++ goto ielenerr; ++ ++ if (mwl_vif->beacon_info.ie_rsn48_len > ++ sizeof(pcmd->start_cmd.rsn48_ie)) ++ goto ielenerr; ++ ++ pcmd = (struct hostcmd_cmd_ap_beacon *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_AP_BEACON); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ++ ether_addr_copy(pcmd->start_cmd.sta_mac_addr, mwl_vif->bssid); ++ memcpy(pcmd->start_cmd.ssid, bss_conf->ssid, bss_conf->ssid_len); ++ if (priv->chip_type == MWL8997) ++ ether_addr_copy(pcmd->start_cmd.bssid, mwl_vif->bssid); ++ pcmd->start_cmd.bss_type = 1; ++ pcmd->start_cmd.bcn_period = cpu_to_le16(bss_conf->beacon_int); ++ pcmd->start_cmd.dtim_period = bss_conf->dtim_period; /* 8bit */ ++ ++ phy_ds_param_set = &pcmd->start_cmd.phy_param_set.ds_param_set; ++ phy_ds_param_set->elem_id = WLAN_EID_DS_PARAMS; ++ phy_ds_param_set->len = sizeof(phy_ds_param_set->current_chnl); ++ phy_ds_param_set->current_chnl = bss_conf->chandef.chan->hw_value; ++ ++ pcmd->start_cmd.probe_delay = cpu_to_le16(10); ++ pcmd->start_cmd.cap_info = cpu_to_le16(mwl_vif->beacon_info.cap_info); ++ ++ memcpy(&pcmd->start_cmd.wmm_param, mwl_vif->beacon_info.ie_wmm_ptr, ++ mwl_vif->beacon_info.ie_wmm_len); ++ ++ memcpy(&pcmd->start_cmd.rsn_ie, mwl_vif->beacon_info.ie_rsn_ptr, ++ mwl_vif->beacon_info.ie_rsn_len); ++ ++ memcpy(&pcmd->start_cmd.rsn48_ie, mwl_vif->beacon_info.ie_rsn48_ptr, ++ mwl_vif->beacon_info.ie_rsn48_len); ++ ++ memcpy(pcmd->start_cmd.b_rate_set, mwl_vif->beacon_info.b_rate_set, ++ SYSADPT_MAX_DATA_RATES_G); ++ ++ memcpy(pcmd->start_cmd.op_rate_set, mwl_vif->beacon_info.op_rate_set, ++ SYSADPT_MAX_DATA_RATES_G); ++ ++ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_AP_BEACON)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++ ++ielenerr: ++ ++ wiphy_err(priv->hw->wiphy, "length of IE is too long\n"); ++ ++ return -EINVAL; ++} ++ ++static int mwl_fwcmd_set_spectrum_mgmt(struct mwl_priv *priv, bool enable) ++{ ++ struct hostcmd_cmd_set_spectrum_mgmt *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_spectrum_mgmt *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_SPECTRUM_MGMT); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->spectrum_mgmt = cpu_to_le32(enable); ++ ++ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_SET_SPECTRUM_MGMT)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++static int mwl_fwcmd_set_power_constraint(struct mwl_priv *priv, ++ u32 power_constraint) ++{ ++ struct hostcmd_cmd_set_power_constraint *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_power_constraint *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_POWER_CONSTRAINT); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->power_constraint = cpu_to_le32(power_constraint); ++ ++ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_SET_POWER_CONSTRAINT)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++static int mwl_fwcmd_set_country_code(struct mwl_priv *priv, ++ struct mwl_vif *mwl_vif, ++ struct ieee80211_bss_conf *bss_conf) ++{ ++ struct hostcmd_cmd_set_country_code *pcmd; ++ struct beacon_info *b_inf = &mwl_vif->beacon_info; ++ u8 chnl_len; ++ bool a_band; ++ bool enable = false; ++ ++ if (b_inf->ie_country_ptr) { ++ if (bss_conf->chandef.chan->band == NL80211_BAND_2GHZ) ++ a_band = false; ++ else if (bss_conf->chandef.chan->band == NL80211_BAND_5GHZ) ++ a_band = true; ++ else ++ return -EINVAL; ++ ++ chnl_len = b_inf->ie_country_len - 5; ++ if (a_band) { ++ if (chnl_len > sizeof(pcmd->domain_info.domain_entry_a)) ++ return -EINVAL; ++ } else { ++ if (chnl_len > sizeof(pcmd->domain_info.domain_entry_g)) ++ return -EINVAL; ++ } ++ ++ enable = true; ++ } ++ ++ pcmd = (struct hostcmd_cmd_set_country_code *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_COUNTRY_CODE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le32(enable); ++ if (enable) { ++ memcpy(pcmd->domain_info.country_string, ++ b_inf->ie_country_ptr + 2, 3); ++ if (a_band) { ++ pcmd->domain_info.g_chnl_len = 0; ++ pcmd->domain_info.a_chnl_len = chnl_len; ++ memcpy(pcmd->domain_info.domain_entry_a, ++ b_inf->ie_country_ptr + 5, chnl_len); ++ } else { ++ pcmd->domain_info.a_chnl_len = 0; ++ pcmd->domain_info.g_chnl_len = chnl_len; ++ memcpy(pcmd->domain_info.domain_entry_g, ++ b_inf->ie_country_ptr + 5, chnl_len); ++ } ++ } ++ ++ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_SET_COUNTRY_CODE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++static int mwl_fwcmd_encryption_set_cmd_info(struct hostcmd_cmd_set_key *cmd, ++ u8 *addr, ++ struct ieee80211_key_conf *key) ++{ ++ cmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION); ++ cmd->cmd_hdr.len = cpu_to_le16(sizeof(*cmd)); ++ cmd->key_param.length = cpu_to_le16(sizeof(*cmd) - ++ offsetof(struct hostcmd_cmd_set_key, key_param)); ++ cmd->key_param.key_index = cpu_to_le32(key->keyidx); ++ cmd->key_param.key_len = cpu_to_le16(key->keylen); ++ ether_addr_copy(cmd->key_param.mac_addr, addr); ++ ++ switch (key->cipher) { ++ case WLAN_CIPHER_SUITE_WEP40: ++ case WLAN_CIPHER_SUITE_WEP104: ++ cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_WEP); ++ if (key->keyidx == 0) ++ cmd->key_param.key_info = ++ cpu_to_le32(ENCR_KEY_FLAG_WEP_TXKEY); ++ break; ++ case WLAN_CIPHER_SUITE_TKIP: ++ cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_TKIP); ++ cmd->key_param.key_info = ++ (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? ++ cpu_to_le32(ENCR_KEY_FLAG_PAIRWISE) : ++ cpu_to_le32(ENCR_KEY_FLAG_TXGROUPKEY); ++ cmd->key_param.key_info |= ++ cpu_to_le32(ENCR_KEY_FLAG_MICKEY_VALID | ++ ENCR_KEY_FLAG_TSC_VALID); ++ break; ++ case WLAN_CIPHER_SUITE_CCMP: ++ cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_AES); ++ cmd->key_param.key_info = ++ (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? ++ cpu_to_le32(ENCR_KEY_FLAG_PAIRWISE) : ++ cpu_to_le32(ENCR_KEY_FLAG_TXGROUPKEY); ++ break; ++ case WLAN_CIPHER_SUITE_AES_CMAC: ++ return 1; ++ default: ++ return -ENOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static __le16 mwl_fwcmd_parse_cal_cfg(const u8 *src, size_t len, u8 *dst) ++{ ++ const u8 *ptr; ++ u8 *dptr; ++ char byte_str[3]; ++ long res; ++ ++ ptr = src; ++ dptr = dst; ++ byte_str[2] = '\0'; ++ ++ while (ptr - src < len) { ++ if (*ptr && (isspace(*ptr) || iscntrl(*ptr))) { ++ ptr++; ++ continue; ++ } ++ ++ if (isxdigit(*ptr)) { ++ byte_str[0] = *ptr++; ++ byte_str[1] = *ptr++; ++ kstrtol(byte_str, 16, &res); ++ *dptr++ = res; ++ } else { ++ ptr++; ++ } ++ } ++ ++ return cpu_to_le16(dptr - dst); ++} ++ ++static u16 mwl_fwcmd_parse_txpwrlmt_cfg(const u8 *src, size_t len, ++ u16 parse_len, u8 *dst) ++{ ++ const u8 *ptr; ++ u8 *dptr; ++ char byte_str[3]; ++ long res; ++ ++ ptr = src; ++ dptr = dst; ++ byte_str[2] = '\0'; ++ ++ while ((ptr - src < len) && (dptr - dst < parse_len)) { ++ if (*ptr && (isspace(*ptr) || iscntrl(*ptr))) { ++ ptr++; ++ continue; ++ } ++ ++ if (isxdigit(*ptr)) { ++ byte_str[0] = *ptr++; ++ byte_str[1] = *ptr++; ++ kstrtol(byte_str, 16, &res); ++ *dptr++ = res; ++ } else { ++ ptr++; ++ } ++ } ++ ++ return (ptr - src); ++} ++ ++const struct hostcmd_get_hw_spec ++*mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_get_hw_spec *pcmd; ++ int retry; ++ ++ pcmd = (struct hostcmd_cmd_get_hw_spec *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ wiphy_debug(hw->wiphy, "pcmd = %p\n", pcmd); ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ eth_broadcast_addr(pcmd->hw_spec.permanent_addr); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_HW_SPEC); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->hw_spec.fw_awake_cookie = cpu_to_le32(priv->pphys_cmd_buf + 2048); ++ ++ retry = 0; ++ while (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_HW_SPEC)) { ++ if (retry++ > MAX_WAIT_GET_HW_SPECS_ITERATONS) { ++ wiphy_err(hw->wiphy, "can't get hw specs\n"); ++ mutex_unlock(&priv->fwcmd_mutex); ++ return NULL; ++ } ++ ++ msleep(1000); ++ wiphy_debug(hw->wiphy, ++ "repeat command = %p\n", pcmd); ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return &pcmd->hw_spec; ++} ++ ++int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw, ++ struct hostcmd_set_hw_spec *spec) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_hw_spec *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_hw_spec *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_HW_SPEC); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ memcpy(&pcmd->hw_spec, spec, sizeof(*spec)); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_HW_SPEC)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_get_stat(struct ieee80211_hw *hw, ++ struct ieee80211_low_level_stats *stats) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_802_11_get_stat *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_802_11_get_stat *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_GET_STAT); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_802_11_GET_STAT)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ stats->dot11ACKFailureCount = ++ le32_to_cpu(pcmd->ack_failures); ++ stats->dot11RTSFailureCount = ++ le32_to_cpu(pcmd->rts_failures); ++ stats->dot11FCSErrorCount = ++ le32_to_cpu(pcmd->rx_fcs_errors); ++ stats->dot11RTSSuccessCount = ++ le32_to_cpu(pcmd->rts_successes); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_reg_bb(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_bbp_reg_access *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_bbp_reg_access *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BBP_REG_ACCESS); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->offset = cpu_to_le16(reg); ++ pcmd->action = cpu_to_le16(flag); ++ pcmd->value = *val; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BBP_REG_ACCESS)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ *val = pcmd->value; ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_reg_rf(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_rf_reg_access *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_rf_reg_access *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_RF_REG_ACCESS); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->offset = cpu_to_le16(reg); ++ pcmd->action = cpu_to_le16(flag); ++ pcmd->value = *val; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_RF_REG_ACCESS)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ *val = pcmd->value; ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw) ++{ ++ return mwl_fwcmd_802_11_radio_control(hw->priv, true, false); ++} ++ ++int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw) ++{ ++ return mwl_fwcmd_802_11_radio_control(hw->priv, false, false); ++} ++ ++int mwl_fwcmd_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble) ++{ ++ struct mwl_priv *priv = hw->priv; ++ int rc; ++ ++ priv->radio_short_preamble = short_preamble; ++ rc = mwl_fwcmd_802_11_radio_control(priv, true, true); ++ ++ return rc; ++} ++ ++int mwl_fwcmd_get_addr_value(struct ieee80211_hw *hw, u32 addr, u32 len, ++ u32 *val, u16 set) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_mem_addr_access *pcmd; ++ int i; ++ ++ pcmd = (struct hostcmd_cmd_mem_addr_access *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_MEM_ADDR_ACCESS); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->address = cpu_to_le32(addr); ++ pcmd->length = cpu_to_le16(len); ++ pcmd->value[0] = cpu_to_le32(*val); ++ pcmd->reserved = cpu_to_le16(set); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_MEM_ADDR_ACCESS)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ for (i = 0; i < len; i++) ++ val[i] = le32_to_cpu(pcmd->value[i]); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw, ++ struct ieee80211_conf *conf, u8 fraction) ++{ ++ struct ieee80211_channel *channel = conf->chandef.chan; ++ struct mwl_priv *priv = hw->priv; ++ int reduce_val = 0; ++ u16 band = 0, width = 0, sub_ch = 0; ++ u16 maxtxpow[SYSADPT_TX_GRP_PWR_LEVEL_TOTAL]; ++ int i, tmp; ++ int rc = 0; ++ ++ if ((priv->chip_type != MWL8997) && (priv->forbidden_setting)) ++ return rc; ++ ++ switch (fraction) { ++ case 0: ++ reduce_val = 0; /* Max */ ++ break; ++ case 1: ++ reduce_val = 2; /* 75% -1.25db */ ++ break; ++ case 2: ++ reduce_val = 3; /* 50% -3db */ ++ break; ++ case 3: ++ reduce_val = 6; /* 25% -6db */ ++ break; ++ default: ++ /* larger than case 3, pCmd->MaxPowerLevel is min */ ++ reduce_val = 0xff; ++ break; ++ } ++ ++ if (channel->band == NL80211_BAND_2GHZ) ++ band = FREQ_BAND_2DOT4GHZ; ++ else if (channel->band == NL80211_BAND_5GHZ) ++ band = FREQ_BAND_5GHZ; ++ ++ switch (conf->chandef.width) { ++ case NL80211_CHAN_WIDTH_20_NOHT: ++ case NL80211_CHAN_WIDTH_20: ++ width = CH_20_MHZ_WIDTH; ++ sub_ch = NO_EXT_CHANNEL; ++ break; ++ case NL80211_CHAN_WIDTH_40: ++ width = CH_40_MHZ_WIDTH; ++ if (conf->chandef.center_freq1 > channel->center_freq) ++ sub_ch = EXT_CH_ABOVE_CTRL_CH; ++ else ++ sub_ch = EXT_CH_BELOW_CTRL_CH; ++ break; ++ case NL80211_CHAN_WIDTH_80: ++ width = CH_80_MHZ_WIDTH; ++ if (conf->chandef.center_freq1 > channel->center_freq) ++ sub_ch = EXT_CH_ABOVE_CTRL_CH; ++ else ++ sub_ch = EXT_CH_BELOW_CTRL_CH; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (priv->chip_type == MWL8997) { ++ mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow, ++ HOSTCMD_ACT_GET_MAX_TX_PWR, ++ channel->hw_value, band, width, sub_ch); ++ ++ for (i = 0; i < priv->pwr_level; i++) { ++ tmp = priv->max_tx_pow[i]; ++ maxtxpow[i] = ((tmp - reduce_val) > 0) ? ++ (tmp - reduce_val) : 0; ++ } ++ ++ rc = mwl_fwcmd_set_tx_powers(priv, maxtxpow, ++ HOSTCMD_ACT_SET_MAX_TX_PWR, ++ channel->hw_value, band, ++ width, sub_ch); ++ return rc; ++ } ++ ++ if ((priv->powinited & MWL_POWER_INIT_2) == 0) { ++ mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow, ++ HOSTCMD_ACT_GEN_GET_LIST, ++ channel->hw_value, band, width, sub_ch); ++ priv->powinited |= MWL_POWER_INIT_2; ++ } ++ ++ if ((priv->powinited & MWL_POWER_INIT_1) == 0) { ++ mwl_fwcmd_get_tx_powers(priv, priv->target_powers, ++ HOSTCMD_ACT_GEN_GET_LIST, ++ channel->hw_value, band, width, sub_ch); ++ priv->powinited |= MWL_POWER_INIT_1; ++ } ++ ++ for (i = 0; i < priv->pwr_level; i++) { ++ if (priv->target_powers[i] > priv->max_tx_pow[i]) ++ tmp = priv->max_tx_pow[i]; ++ else ++ tmp = priv->target_powers[i]; ++ maxtxpow[i] = ((tmp - reduce_val) > 0) ? (tmp - reduce_val) : 0; ++ } ++ ++ rc = mwl_fwcmd_set_tx_powers(priv, maxtxpow, HOSTCMD_ACT_GEN_SET, ++ channel->hw_value, band, width, sub_ch); ++ ++ return rc; ++} ++ ++int mwl_fwcmd_tx_power(struct ieee80211_hw *hw, ++ struct ieee80211_conf *conf, u8 fraction) ++{ ++ struct ieee80211_channel *channel = conf->chandef.chan; ++ struct mwl_priv *priv = hw->priv; ++ int reduce_val = 0; ++ u16 band = 0, width = 0, sub_ch = 0; ++ u16 txpow[SYSADPT_TX_GRP_PWR_LEVEL_TOTAL]; ++ int index, found = 0; ++ int i, tmp; ++ int rc = 0; ++ ++ if ((priv->chip_type != MWL8997) && (priv->forbidden_setting)) ++ return rc; ++ ++ switch (fraction) { ++ case 0: ++ reduce_val = 0; /* Max */ ++ break; ++ case 1: ++ reduce_val = 2; /* 75% -1.25db */ ++ break; ++ case 2: ++ reduce_val = 3; /* 50% -3db */ ++ break; ++ case 3: ++ reduce_val = 6; /* 25% -6db */ ++ break; ++ default: ++ /* larger than case 3, pCmd->MaxPowerLevel is min */ ++ reduce_val = 0xff; ++ break; ++ } ++ ++ if (channel->band == NL80211_BAND_2GHZ) ++ band = FREQ_BAND_2DOT4GHZ; ++ else if (channel->band == NL80211_BAND_5GHZ) ++ band = FREQ_BAND_5GHZ; ++ ++ switch (conf->chandef.width) { ++ case NL80211_CHAN_WIDTH_20_NOHT: ++ case NL80211_CHAN_WIDTH_20: ++ width = CH_20_MHZ_WIDTH; ++ sub_ch = NO_EXT_CHANNEL; ++ break; ++ case NL80211_CHAN_WIDTH_40: ++ width = CH_40_MHZ_WIDTH; ++ if (conf->chandef.center_freq1 > channel->center_freq) ++ sub_ch = EXT_CH_ABOVE_CTRL_CH; ++ else ++ sub_ch = EXT_CH_BELOW_CTRL_CH; ++ break; ++ case NL80211_CHAN_WIDTH_80: ++ width = CH_80_MHZ_WIDTH; ++ if (conf->chandef.center_freq1 > channel->center_freq) ++ sub_ch = EXT_CH_ABOVE_CTRL_CH; ++ else ++ sub_ch = EXT_CH_BELOW_CTRL_CH; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (priv->chip_type == MWL8997) { ++ mwl_fwcmd_get_tx_powers(priv, priv->target_powers, ++ HOSTCMD_ACT_GET_TARGET_TX_PWR, ++ channel->hw_value, band, width, sub_ch); ++ ++ for (i = 0; i < priv->pwr_level; i++) { ++ tmp = priv->target_powers[i]; ++ txpow[i] = ((tmp - reduce_val) > 0) ? ++ (tmp - reduce_val) : 0; ++ } ++ ++ rc = mwl_fwcmd_set_tx_powers(priv, txpow, ++ HOSTCMD_ACT_SET_TARGET_TX_PWR, ++ channel->hw_value, band, ++ width, sub_ch); ++ ++ return rc; ++ } ++ ++ /* search tx power table if exist */ ++ for (index = 0; index < SYSADPT_MAX_NUM_CHANNELS; index++) { ++ struct mwl_tx_pwr_tbl *tx_pwr; ++ ++ tx_pwr = &priv->tx_pwr_tbl[index]; ++ ++ /* do nothing if table is not loaded */ ++ if (tx_pwr->channel == 0) ++ break; ++ ++ if (tx_pwr->channel == channel->hw_value) { ++ priv->cdd = tx_pwr->cdd; ++ priv->txantenna2 = tx_pwr->txantenna2; ++ ++ if (tx_pwr->setcap) ++ priv->powinited = MWL_POWER_INIT_1; ++ else ++ priv->powinited = MWL_POWER_INIT_2; ++ ++ for (i = 0; i < priv->pwr_level; i++) { ++ if (tx_pwr->setcap) ++ priv->max_tx_pow[i] = ++ tx_pwr->tx_power[i]; ++ else ++ priv->target_powers[i] = ++ tx_pwr->tx_power[i]; ++ } ++ ++ found = 1; ++ break; ++ } ++ } ++ ++ if ((priv->powinited & MWL_POWER_INIT_2) == 0) { ++ mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow, ++ HOSTCMD_ACT_GEN_GET_LIST, ++ channel->hw_value, band, width, sub_ch); ++ ++ priv->powinited |= MWL_POWER_INIT_2; ++ } ++ ++ if ((priv->powinited & MWL_POWER_INIT_1) == 0) { ++ mwl_fwcmd_get_tx_powers(priv, priv->target_powers, ++ HOSTCMD_ACT_GEN_GET_LIST, ++ channel->hw_value, band, width, sub_ch); ++ ++ priv->powinited |= MWL_POWER_INIT_1; ++ } ++ ++ for (i = 0; i < priv->pwr_level; i++) { ++ if (found) { ++ if ((priv->tx_pwr_tbl[index].setcap) && ++ (priv->tx_pwr_tbl[index].tx_power[i] > ++ priv->max_tx_pow[i])) ++ tmp = priv->max_tx_pow[i]; ++ else ++ tmp = priv->tx_pwr_tbl[index].tx_power[i]; ++ } else { ++ if (priv->target_powers[i] > priv->max_tx_pow[i]) ++ tmp = priv->max_tx_pow[i]; ++ else ++ tmp = priv->target_powers[i]; ++ } ++ ++ txpow[i] = ((tmp - reduce_val) > 0) ? (tmp - reduce_val) : 0; ++ } ++ ++ rc = mwl_fwcmd_set_tx_powers(priv, txpow, HOSTCMD_ACT_GEN_SET_LIST, ++ channel->hw_value, band, width, sub_ch); ++ ++ return rc; ++} ++ ++int mwl_fwcmd_rf_antenna(struct ieee80211_hw *hw, int dir, int antenna) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_802_11_rf_antenna *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_802_11_rf_antenna *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RF_ANTENNA); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ pcmd->action = cpu_to_le16(dir); ++ ++ if (dir == WL_ANTENNATYPE_RX) { ++ u8 rx_antenna; ++ ++ if (priv->chip_type == MWL8964) { ++ if (antenna == ANTENNA_RX_4_AUTO) ++ rx_antenna = 0xf; ++ else if (antenna == ANTENNA_RX_3) ++ rx_antenna = 7; ++ else if (antenna == ANTENNA_RX_2) ++ rx_antenna = 4; ++ else ++ rx_antenna = 1; ++ ++ pcmd->antenna_mode = cpu_to_le16(rx_antenna); ++ } else { ++ rx_antenna = 4; ++ ++ if (antenna != 0) ++ pcmd->antenna_mode = cpu_to_le16(antenna); ++ else ++ pcmd->antenna_mode = cpu_to_le16(rx_antenna); ++ } ++ } else { ++ u8 tx_antenna = 0xf; ++ ++ if (antenna != 0) ++ pcmd->antenna_mode = cpu_to_le16(antenna); ++ else ++ pcmd->antenna_mode = cpu_to_le16(tx_antenna); ++ } ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_802_11_RF_ANTENNA)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_broadcast_ssid_enable(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, bool enable) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_broadcast_ssid_enable *pcmd; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_broadcast_ssid_enable *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BROADCAST_SSID_ENABLE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ pcmd->enable = cpu_to_le32(enable); ++ if (priv->chip_type == MWL8997) ++ pcmd->hidden_ssid_info = enable ? 0 : 2; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BROADCAST_SSID_ENABLE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_cfg_data(struct ieee80211_hw *hw, u16 type) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_cfg *pcmd; ++ ++ if (!priv->cal_data) ++ return 0; ++ ++ pcmd = (struct hostcmd_cmd_set_cfg *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->data_len = mwl_fwcmd_parse_cal_cfg(priv->cal_data->data, ++ priv->cal_data->size, ++ pcmd->data); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_CFG); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd) + ++ le16_to_cpu(pcmd->data_len) - sizeof(pcmd->data)); ++ pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET); ++ pcmd->type = cpu_to_le16(type); ++ ++ utils_dump_data_debug("CalData:", pcmd->data, ++ le16_to_cpu(pcmd->data_len)); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_CFG)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ release_firmware(priv->cal_data); ++ priv->cal_data = NULL; ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ release_firmware(priv->cal_data); ++ priv->cal_data = NULL; ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_rf_channel(struct ieee80211_hw *hw, ++ struct ieee80211_conf *conf) ++{ ++ struct ieee80211_channel *channel = conf->chandef.chan; ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_rf_channel *pcmd; ++ u32 chnl_flags, freq_band, chnl_width, act_primary; ++ ++ pcmd = (struct hostcmd_cmd_set_rf_channel *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ if (priv->chip_type == MWL8997) { ++ memset(pcmd, 0x00, ++ sizeof(struct hostcmd_cmd_set_rf_channel_kf2)); ++ pcmd->cmd_hdr.len = cpu_to_le16( ++ sizeof(struct hostcmd_cmd_set_rf_channel_kf2)); ++ } else { ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ } ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_RF_CHANNEL); ++ pcmd->action = cpu_to_le16(WL_SET); ++ pcmd->curr_chnl = channel->hw_value; ++ ++ if (channel->band == NL80211_BAND_2GHZ) { ++ freq_band = FREQ_BAND_2DOT4GHZ; ++ } else if (channel->band == NL80211_BAND_5GHZ) { ++ freq_band = FREQ_BAND_5GHZ; ++ } else { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EINVAL; ++ } ++ ++ switch (conf->chandef.width) { ++ case NL80211_CHAN_WIDTH_20_NOHT: ++ case NL80211_CHAN_WIDTH_20: ++ chnl_width = CH_20_MHZ_WIDTH; ++ act_primary = ACT_PRIMARY_CHAN_0; ++ break; ++ case NL80211_CHAN_WIDTH_40: ++ chnl_width = CH_40_MHZ_WIDTH; ++ if (conf->chandef.center_freq1 > channel->center_freq) ++ act_primary = ACT_PRIMARY_CHAN_0; ++ else ++ act_primary = ACT_PRIMARY_CHAN_1; ++ break; ++ case NL80211_CHAN_WIDTH_80: ++ chnl_width = CH_80_MHZ_WIDTH; ++ act_primary = ++ mwl_fwcmd_get_80m_pri_chnl(pcmd->curr_chnl); ++ break; ++ case NL80211_CHAN_WIDTH_160: ++ chnl_width = CH_160_MHZ_WIDTH; ++ act_primary = ++ mwl_fwcmd_get_160m_pri_chnl(pcmd->curr_chnl); ++ break; ++ default: ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EINVAL; ++ } ++ ++ chnl_flags = (freq_band & FREQ_BAND_MASK) | ++ ((chnl_width << CHNL_WIDTH_SHIFT) & CHNL_WIDTH_MASK) | ++ ((act_primary << ACT_PRIMARY_SHIFT) & ACT_PRIMARY_MASK); ++ ++ pcmd->chnl_flags = cpu_to_le32(chnl_flags); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_RF_CHANNEL)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ if (pcmd->cmd_hdr.result != 0) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EINVAL; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ if (priv->sw_scanning) { ++ priv->survey_info_idx++; ++ mwl_fwcmd_get_survey(hw, priv->survey_info_idx); ++ } else { ++ mwl_fwcmd_get_survey(hw, 0); ++ memset(&priv->cur_survey_info, 0, ++ sizeof(struct mwl_survey_info)); ++ } ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_aid(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *bssid, u16 aid) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_set_aid *pcmd; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_set_aid *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_AID); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ pcmd->aid = cpu_to_le16(aid); ++ ether_addr_copy(pcmd->mac_addr, bssid); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_AID)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_infra_mode(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_set_infra_mode *pcmd; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_set_infra_mode *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_INFRA_MODE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_INFRA_MODE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_rts_threshold(struct ieee80211_hw *hw, int threshold) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_802_11_rts_thsd *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_802_11_rts_thsd *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RTS_THSD); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le16(WL_SET); ++ pcmd->threshold = cpu_to_le16(threshold); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_802_11_RTS_THSD)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_edca_params(struct ieee80211_hw *hw, u8 index, ++ u16 cw_min, u16 cw_max, u8 aifs, u16 txop) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_edca_params *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_edca_params *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_EDCA_PARAMS); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ pcmd->action = cpu_to_le16(0xffff); ++ pcmd->txop = cpu_to_le16(txop); ++ pcmd->cw_max = cpu_to_le32(cw_max); ++ pcmd->cw_min = cpu_to_le32(cw_min); ++ pcmd->aifsn = aifs; ++ pcmd->txq_num = index; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_EDCA_PARAMS)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_radar_detect(struct ieee80211_hw *hw, u16 action) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_802_11h_detect_radar *pcmd; ++ u16 radar_type = RADAR_TYPE_CODE_0; ++ u8 channel = hw->conf.chandef.chan->hw_value; ++ ++ pcmd = (struct hostcmd_cmd_802_11h_detect_radar *)&priv->pcmd_buf[0]; ++ ++ if (priv->dfs_region == NL80211_DFS_JP) { ++ if (channel >= 52 && channel <= 64) ++ radar_type = RADAR_TYPE_CODE_53; ++ else if (channel >= 100 && channel <= 140) ++ radar_type = RADAR_TYPE_CODE_56; ++ else ++ radar_type = RADAR_TYPE_CODE_0; ++ } else if (priv->dfs_region == NL80211_DFS_ETSI) { ++ radar_type = RADAR_TYPE_CODE_ETSI; ++ } ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11H_DETECT_RADAR); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le16(action); ++ pcmd->radar_type_code = cpu_to_le16(radar_type); ++ pcmd->min_chirp_cnt = cpu_to_le16(priv->dfs_chirp_count_min); ++ pcmd->chirp_time_intvl = cpu_to_le16(priv->dfs_chirp_time_interval); ++ pcmd->pw_filter = cpu_to_le16(priv->dfs_pw_filter); ++ pcmd->min_num_radar = cpu_to_le16(priv->dfs_min_num_radar); ++ pcmd->pri_min_num = cpu_to_le16(priv->dfs_min_pri_count); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_802_11H_DETECT_RADAR)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_wmm_mode *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_wmm_mode *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_WMM_MODE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le16(enable ? WL_ENABLE : WL_DISABLE); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_WMM_MODE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_ht_guard_interval(struct ieee80211_hw *hw, u32 gi_type) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_ht_guard_interval *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_ht_guard_interval *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_HT_GUARD_INTERVAL); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le32(WL_SET); ++ pcmd->gi_type = cpu_to_le32(gi_type); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_HT_GUARD_INTERVAL)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw, int mcast, int mgmt) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_fixed_rate *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_fixed_rate *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_FIXED_RATE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ pcmd->action = cpu_to_le32(HOSTCMD_ACT_NOT_USE_FIXED_RATE); ++ pcmd->multicast_rate = mcast; ++ pcmd->management_rate = mgmt; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_FIXED_RATE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_linkadapt_cs_mode(struct ieee80211_hw *hw, u16 cs_mode) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_linkadapt_cs_mode *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_linkadapt_cs_mode *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_LINKADAPT_CS_MODE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET); ++ pcmd->cs_mode = cpu_to_le16(cs_mode); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_LINKADAPT_CS_MODE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_dump_otp_data(struct ieee80211_hw *hw) ++{ ++ int otp_data_len; ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_dump_otp_data *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_dump_otp_data *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_DUMP_OTP_DATA); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_DUMP_OTP_DATA)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ otp_data_len = pcmd->cmd_hdr.len - cpu_to_le16(sizeof(*pcmd)); ++ ++ if (otp_data_len <= SYSADPT_OTP_BUF_SIZE) { ++ wiphy_info(hw->wiphy, "OTP data len = %d\n", otp_data_len); ++ priv->otp_data.len = otp_data_len; ++ memcpy(priv->otp_data.buf, pcmd->pload, otp_data_len); ++ } else { ++ wiphy_err(hw->wiphy, "Driver OTP buf size is less\n"); ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw, u16 mode) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_rate_adapt_mode *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_rate_adapt_mode *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_RATE_ADAPT_MODE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le16(WL_SET); ++ pcmd->rate_adapt_mode = cpu_to_le16(mode); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_RATE_ADAPT_MODE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_mac_addr_client(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *mac_addr) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_set_mac_addr *pcmd; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_MAC_ADDR); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ pcmd->mac_type = cpu_to_le16(WL_MAC_TYPE_SECONDARY_CLIENT); ++ ether_addr_copy(pcmd->mac_addr, mac_addr); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_MAC_ADDR)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_get_watchdog_bitmap *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_get_watchdog_bitmap *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_WATCHDOG_BITMAP); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_WATCHDOG_BITMAP)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ *bitmap = pcmd->watchdog_bitmap; ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_remove_mac_addr(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *mac_addr) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_set_mac_addr *pcmd; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_DEL_MAC_ADDR); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ether_addr_copy(pcmd->mac_addr, mac_addr); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_DEL_MAC_ADDR)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_bss_start(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, bool enable) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_bss_start *pcmd; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ if (enable && (priv->running_bsses & (1 << mwl_vif->macid))) ++ return 0; ++ ++ if (!enable && !(priv->running_bsses & (1 << mwl_vif->macid))) ++ return 0; ++ ++ pcmd = (struct hostcmd_cmd_bss_start *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BSS_START); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ++ if (enable) { ++ pcmd->enable = cpu_to_le32(WL_ENABLE); ++ } else { ++ if (mwl_vif->macid == 0) ++ pcmd->enable = cpu_to_le32(WL_DISABLE); ++ else ++ pcmd->enable = cpu_to_le32(WL_DISABLE_VMAC); ++ } ++ if (priv->chip_type == MWL8964) ++ pcmd->amsdu = MWL_AMSDU_SIZE_11K; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BSS_START)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ if (enable) ++ priv->running_bsses |= (1 << mwl_vif->macid); ++ else ++ priv->running_bsses &= ~(1 << mwl_vif->macid); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_beacon(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *beacon, int len) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct beacon_info *b_inf; ++ int rc; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ b_inf = &mwl_vif->beacon_info; ++ ++ mwl_fwcmd_parse_beacon(priv, mwl_vif, beacon, len); ++ ++ if (!b_inf->valid) ++ goto err; ++ ++ if (mwl_fwcmd_set_ies(priv, mwl_vif)) ++ goto err; ++ ++ if (mwl_fwcmd_set_wsc_ie(hw, b_inf->ie_wsc_len, b_inf->ie_wsc_ptr)) ++ goto err; ++ ++ if (mwl_fwcmd_set_ap_beacon(priv, mwl_vif, &vif->bss_conf)) ++ goto err; ++ ++ if (b_inf->cap_info & WLAN_CAPABILITY_SPECTRUM_MGMT) ++ rc = mwl_fwcmd_set_spectrum_mgmt(priv, true); ++ else ++ rc = mwl_fwcmd_set_spectrum_mgmt(priv, false); ++ if (rc) ++ goto err; ++ ++ if (b_inf->power_constraint) ++ rc = mwl_fwcmd_set_power_constraint(priv, ++ b_inf->power_constraint); ++ if (rc) ++ goto err; ++ ++ if (mwl_fwcmd_set_country_code(priv, mwl_vif, &vif->bss_conf)) ++ goto err; ++ ++ b_inf->valid = false; ++ ++ return 0; ++ ++err: ++ ++ b_inf->valid = false; ++ ++ return -EIO; ++} ++ ++int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct mwl_sta *sta_info; ++ struct hostcmd_cmd_set_new_stn *pcmd; ++ u32 rates; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ sta_info = mwl_dev_get_sta(sta); ++ ++ pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ++ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD); ++ pcmd->aid = cpu_to_le16(sta->aid); ++ pcmd->stn_id = cpu_to_le16(sta_info->stnid); ++ if (priv->chip_type == MWL8997) ++ pcmd->if_type = cpu_to_le16(vif->type); ++ else ++ pcmd->if_type = cpu_to_le16(1); ++ ether_addr_copy(pcmd->mac_addr, sta->addr); ++ ++ if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) ++ rates = sta->supp_rates[NL80211_BAND_2GHZ]; ++ else ++ rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5; ++ pcmd->peer_info.legacy_rate_bitmap = cpu_to_le32(rates); ++ ++ if (sta->ht_cap.ht_supported) { ++ int i; ++ ++ for (i = 0; i < 4; i++) { ++ if (i < sta->rx_nss) { ++ pcmd->peer_info.ht_rates[i] = ++ sta->ht_cap.mcs.rx_mask[i]; ++ } else { ++ pcmd->peer_info.ht_rates[i] = 0; ++ } ++ } ++ pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->ht_cap.cap); ++ pcmd->peer_info.mac_ht_param_info = ++ (sta->ht_cap.ampdu_factor & 3) | ++ ((sta->ht_cap.ampdu_density & 7) << 2); ++ } ++ ++ if (sta->vht_cap.vht_supported) { ++ u32 rx_mcs_map_mask = 0; ++ ++ rx_mcs_map_mask = ((0x0000FFFF) >> (sta->rx_nss * 2)) ++ << (sta->rx_nss * 2); ++ pcmd->peer_info.vht_max_rx_mcs = ++ cpu_to_le32((*((u32 *) ++ &sta->vht_cap.vht_mcs.rx_mcs_map)) | rx_mcs_map_mask); ++ pcmd->peer_info.vht_cap = cpu_to_le32(sta->vht_cap.cap); ++ pcmd->peer_info.vht_rx_channel_width = sta->bandwidth; ++ } ++ ++ pcmd->is_qos_sta = sta->wme; ++ pcmd->qos_info = ((sta->uapsd_queues << 4) | (sta->max_sp << 1)); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ if (vif->type == NL80211_IFTYPE_STATION) { ++ ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac); ++ pcmd->aid = cpu_to_le16(sta->aid + 1); ++ pcmd->stn_id = cpu_to_le16(sta_info->sta_stnid); ++ pcmd->if_type = cpu_to_le16(0); ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_new_stn_add_sc4(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta, ++ u32 wds) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct mwl_sta *sta_info; ++ struct hostcmd_cmd_set_new_stn_sc4 *pcmd; ++ u32 rates; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ sta_info = mwl_dev_get_sta(sta); ++ ++ pcmd = (struct hostcmd_cmd_set_new_stn_sc4 *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ++ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD); ++ pcmd->aid = cpu_to_le16(sta->aid); ++ pcmd->stn_id = cpu_to_le16(sta_info->stnid); ++ ether_addr_copy(pcmd->mac_addr, sta->addr); ++ ++ if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) ++ rates = sta->supp_rates[NL80211_BAND_2GHZ]; ++ else ++ rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5; ++ pcmd->peer_info.legacy_rate_bitmap = cpu_to_le32(rates); ++ ++ if (sta->ht_cap.ht_supported) { ++ int i; ++ ++ for (i = 0; i < 4; i++) { ++ if (i < sta->rx_nss) { ++ pcmd->peer_info.ht_rates[i] = ++ sta->ht_cap.mcs.rx_mask[i]; ++ } else { ++ pcmd->peer_info.ht_rates[i] = 0; ++ } ++ } ++ pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->ht_cap.cap); ++ pcmd->peer_info.mac_ht_param_info = ++ (sta->ht_cap.ampdu_factor & 3) | ++ ((sta->ht_cap.ampdu_density & 7) << 2); ++ } ++ ++ if (sta->vht_cap.vht_supported) { ++ u32 rx_mcs_map_mask = 0; ++ ++ rx_mcs_map_mask = ((0x0000FFFF) >> (sta->rx_nss * 2)) ++ << (sta->rx_nss * 2); ++ pcmd->peer_info.vht_max_rx_mcs = ++ cpu_to_le32((*((u32 *) ++ &sta->vht_cap.vht_mcs.rx_mcs_map)) | rx_mcs_map_mask); ++ pcmd->peer_info.vht_cap = cpu_to_le32(sta->vht_cap.cap); ++ pcmd->peer_info.vht_rx_channel_width = sta->bandwidth; ++ } ++ ++ pcmd->is_qos_sta = sta->wme; ++ pcmd->qos_info = ((sta->uapsd_queues << 4) | (sta->max_sp << 1)); ++ pcmd->wds = cpu_to_le32(wds); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ if (vif->type == NL80211_IFTYPE_STATION) { ++ ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac); ++ pcmd->aid = cpu_to_le16(sta->aid + 1); ++ pcmd->stn_id = cpu_to_le16(sta_info->sta_stnid); ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_new_stn_wds_sc4(struct ieee80211_hw *hw, u8 *addr) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_new_stn_sc4 *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_new_stn_sc4 *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_MODIFY); ++ ether_addr_copy(pcmd->mac_addr, addr); ++ pcmd->wds = cpu_to_le32(WDS_MODE); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_new_stn_add_self(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_set_new_stn *pcmd; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ if (priv->chip_type == MWL8964) { ++ memset(pcmd, 0x00, sizeof(struct hostcmd_cmd_set_new_stn_sc4)); ++ pcmd->cmd_hdr.len = ++ cpu_to_le16(sizeof(struct hostcmd_cmd_set_new_stn_sc4)); ++ } else { ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ } ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ++ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD); ++ ether_addr_copy(pcmd->mac_addr, vif->addr); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_new_stn_del(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *addr) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_set_new_stn *pcmd; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ if (priv->chip_type == MWL8964) { ++ memset(pcmd, 0x00, sizeof(struct hostcmd_cmd_set_new_stn_sc4)); ++ pcmd->cmd_hdr.len = ++ cpu_to_le16(sizeof(struct hostcmd_cmd_set_new_stn_sc4)); ++ } else { ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ } ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ++ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_REMOVE); ++ ether_addr_copy(pcmd->mac_addr, addr); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ if (vif->type == NL80211_IFTYPE_STATION) { ++ ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_apmode(struct ieee80211_hw *hw, u8 apmode) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_apmode *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_apmode *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_APMODE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->apmode = apmode; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_APMODE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_switch_channel(struct ieee80211_hw *hw, ++ struct ieee80211_channel_switch *ch_switch) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_switch_channel *pcmd; ++ struct cfg80211_chan_def *chandef = &ch_switch->chandef; ++ struct ieee80211_channel *channel = chandef->chan; ++ u32 chnl_flags, freq_band, chnl_width, act_primary, sec_chnl_offset; ++ ++ if (priv->csa_active) ++ return 0; ++ ++ if (channel->band == NL80211_BAND_2GHZ) ++ freq_band = FREQ_BAND_2DOT4GHZ; ++ else if (channel->band == NL80211_BAND_5GHZ) ++ freq_band = FREQ_BAND_5GHZ; ++ else ++ return -EINVAL; ++ ++ switch (chandef->width) { ++ case NL80211_CHAN_WIDTH_20_NOHT: ++ case NL80211_CHAN_WIDTH_20: ++ chnl_width = CH_20_MHZ_WIDTH; ++ act_primary = ACT_PRIMARY_CHAN_0; ++ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; ++ break; ++ case NL80211_CHAN_WIDTH_40: ++ chnl_width = CH_40_MHZ_WIDTH; ++ if (chandef->center_freq1 > channel->center_freq) { ++ act_primary = ACT_PRIMARY_CHAN_0; ++ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; ++ } else { ++ act_primary = ACT_PRIMARY_CHAN_1; ++ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; ++ } ++ break; ++ case NL80211_CHAN_WIDTH_80: ++ chnl_width = CH_80_MHZ_WIDTH; ++ act_primary = ++ mwl_fwcmd_get_80m_pri_chnl(channel->hw_value); ++ if ((act_primary == ACT_PRIMARY_CHAN_0) || ++ (act_primary == ACT_PRIMARY_CHAN_2)) ++ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; ++ else ++ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ chnl_flags = (freq_band & FREQ_BAND_MASK) | ++ ((chnl_width << CHNL_WIDTH_SHIFT) & CHNL_WIDTH_MASK) | ++ ((act_primary << ACT_PRIMARY_SHIFT) & ACT_PRIMARY_MASK); ++ ++ pcmd = (struct hostcmd_cmd_set_switch_channel *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_SWITCH_CHANNEL); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->next_11h_chnl = cpu_to_le32(channel->hw_value); ++ pcmd->mode = cpu_to_le32(ch_switch->block_tx); ++ pcmd->init_count = cpu_to_le32(ch_switch->count + 1); ++ pcmd->chnl_flags = cpu_to_le32(chnl_flags); ++ pcmd->next_ht_extchnl_offset = cpu_to_le32(sec_chnl_offset); ++ pcmd->dfs_test_mode = cpu_to_le32(priv->dfs_test); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_SWITCH_CHANNEL)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ priv->csa_active = true; ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_update_encryption_enable(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ u8 *addr, u8 encr_type) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_update_encryption *pcmd; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_update_encryption *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ++ pcmd->action_type = cpu_to_le32(ENCR_ACTION_ENABLE_HW_ENCR); ++ ether_addr_copy(pcmd->mac_addr, addr); ++ pcmd->action_data[0] = encr_type; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_UPDATE_ENCRYPTION)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ if ((vif->type == NL80211_IFTYPE_STATION) && ++ (priv->chip_type != MWL8964)) { ++ if (ether_addr_equal(mwl_vif->bssid, addr)) ++ ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac); ++ else ++ ether_addr_copy(pcmd->mac_addr, mwl_vif->bssid); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_UPDATE_ENCRYPTION)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_encryption_set_key(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *addr, ++ struct ieee80211_key_conf *key) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_set_key *pcmd; ++ int rc; ++ int keymlen; ++ u32 action; ++ u8 idx; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ++ rc = mwl_fwcmd_encryption_set_cmd_info(pcmd, addr, key); ++ if (rc) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ if (rc != 1) ++ wiphy_err(hw->wiphy, "encryption not support\n"); ++ return rc; ++ } ++ ++ idx = key->keyidx; ++ ++ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ++ action = ENCR_ACTION_TYPE_SET_KEY; ++ else { ++ action = ENCR_ACTION_TYPE_SET_GROUP_KEY; ++ if (vif->type == NL80211_IFTYPE_MESH_POINT && ++ !ether_addr_equal(mwl_vif->bssid, addr)) ++ pcmd->key_param.key_info |= ++ cpu_to_le32(ENCR_KEY_FLAG_RXGROUPKEY); ++ } ++ ++ switch (key->cipher) { ++ case WLAN_CIPHER_SUITE_WEP40: ++ case WLAN_CIPHER_SUITE_WEP104: ++ if (!mwl_vif->wep_key_conf[idx].enabled) { ++ memcpy(mwl_vif->wep_key_conf[idx].key, key, ++ sizeof(*key) + key->keylen); ++ mwl_vif->wep_key_conf[idx].enabled = 1; ++ } ++ ++ keymlen = key->keylen; ++ action = ENCR_ACTION_TYPE_SET_KEY; ++ break; ++ case WLAN_CIPHER_SUITE_TKIP: ++ keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH; ++ break; ++ case WLAN_CIPHER_SUITE_CCMP: ++ keymlen = key->keylen; ++ break; ++ default: ++ mutex_unlock(&priv->fwcmd_mutex); ++ wiphy_err(hw->wiphy, "encryption not support\n"); ++ return -ENOTSUPP; ++ } ++ ++ memcpy((void *)&pcmd->key_param.key, key->key, keymlen); ++ pcmd->action_type = cpu_to_le32(action); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_UPDATE_ENCRYPTION)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ if (vif->type == NL80211_IFTYPE_STATION) { ++ if (ether_addr_equal(mwl_vif->bssid, addr)) ++ ether_addr_copy(pcmd->key_param.mac_addr, ++ mwl_vif->sta_mac); ++ else ++ ether_addr_copy(pcmd->key_param.mac_addr, ++ mwl_vif->bssid); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_UPDATE_ENCRYPTION)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_encryption_remove_key(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *addr, ++ struct ieee80211_key_conf *key) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_set_key *pcmd; ++ int rc; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ ++ rc = mwl_fwcmd_encryption_set_cmd_info(pcmd, addr, key); ++ if (rc) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ if (rc != 1) ++ wiphy_err(hw->wiphy, "encryption not support\n"); ++ return rc; ++ } ++ ++ pcmd->action_type = cpu_to_le32(ENCR_ACTION_TYPE_REMOVE_KEY); ++ ++ if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || ++ key->cipher == WLAN_CIPHER_SUITE_WEP104) ++ mwl_vif->wep_key_conf[key->keyidx].enabled = 0; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_UPDATE_ENCRYPTION)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_check_ba(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream, ++ struct ieee80211_vif *vif, ++ u32 direction) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_bastream *pcmd; ++ u32 ba_flags, ba_type, ba_direction; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ pcmd->cmd_hdr.result = cpu_to_le16(0xffff); ++ ++ pcmd->action_type = cpu_to_le32(BA_CHECK_STREAM); ++ ether_addr_copy(&pcmd->ba_info.create_params.peer_mac_addr[0], ++ stream->sta->addr); ++ pcmd->ba_info.create_params.tid = stream->tid; ++ ba_type = BA_FLAG_IMMEDIATE_TYPE; ++ ba_direction = direction; ++ ba_flags = (ba_type & BA_TYPE_MASK) | ++ ((ba_direction << BA_DIRECTION_SHIFT) & BA_DIRECTION_MASK); ++ pcmd->ba_info.create_params.flags = cpu_to_le32(ba_flags); ++ pcmd->ba_info.create_params.queue_id = stream->idx; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BASTREAM)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ if (pcmd->cmd_hdr.result != 0) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EINVAL; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_create_ba(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream, ++ struct ieee80211_vif *vif, ++ u32 direction, u8 buf_size, u16 seqno, bool amsdu) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ struct hostcmd_cmd_bastream *pcmd; ++ u32 ba_flags, ba_type, ba_direction; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_hdr.macid = mwl_vif->macid; ++ pcmd->cmd_hdr.result = cpu_to_le16(0xffff); ++ ++ pcmd->action_type = cpu_to_le32(BA_CREATE_STREAM); ++ pcmd->ba_info.create_params.bar_thrs = cpu_to_le32(buf_size); ++ pcmd->ba_info.create_params.window_size = cpu_to_le32(buf_size); ++ pcmd->ba_info.create_params.idle_thrs = cpu_to_le32(0x22000); ++ ether_addr_copy(&pcmd->ba_info.create_params.peer_mac_addr[0], ++ stream->sta->addr); ++ pcmd->ba_info.create_params.tid = stream->tid; ++ ba_direction = direction; ++ if (priv->chip_type == MWL8964) { ++ ba_type = amsdu ? MWL_AMSDU_SIZE_11K : 0; ++ ba_flags = (ba_type & BA_TYPE_MASK_NDP) | ++ ((ba_direction << BA_DIRECTION_SHIFT_NDP) & ++ BA_DIRECTION_MASK_NDP); ++ } else { ++ ba_type = BA_FLAG_IMMEDIATE_TYPE; ++ ba_flags = (ba_type & BA_TYPE_MASK) | ++ ((ba_direction << BA_DIRECTION_SHIFT) & ++ BA_DIRECTION_MASK); ++ } ++ pcmd->ba_info.create_params.flags = cpu_to_le32(ba_flags); ++ pcmd->ba_info.create_params.queue_id = stream->idx; ++ pcmd->ba_info.create_params.param_info = ++ (stream->sta->ht_cap.ampdu_factor & ++ IEEE80211_HT_AMPDU_PARM_FACTOR) | ++ ((stream->sta->ht_cap.ampdu_density << 2) & ++ IEEE80211_HT_AMPDU_PARM_DENSITY); ++ if (direction == BA_FLAG_DIRECTION_UP) { ++ pcmd->ba_info.create_params.reset_seq_no = 0; ++ pcmd->ba_info.create_params.current_seq = cpu_to_le16(seqno); ++ } else { ++ pcmd->ba_info.create_params.reset_seq_no = 1; ++ pcmd->ba_info.create_params.current_seq = cpu_to_le16(0); ++ } ++ if (priv->chip_type == MWL8964 && ++ stream->sta->vht_cap.vht_supported) { ++ pcmd->ba_info.create_params.vht_rx_factor = ++ cpu_to_le32((stream->sta->vht_cap.cap & ++ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >> ++ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT); ++ } ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BASTREAM)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ if (pcmd->cmd_hdr.result != 0) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ wiphy_err(hw->wiphy, "create ba result error %d\n", ++ le16_to_cpu(pcmd->cmd_hdr.result)); ++ return -EINVAL; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_destroy_ba(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream, ++ u32 direction) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_bastream *pcmd; ++ u32 ba_flags, ba_type, ba_direction; ++ ++ pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ pcmd->action_type = cpu_to_le32(BA_DESTROY_STREAM); ++ ba_type = 0; ++ ba_direction = direction; ++ if (priv->chip_type == MWL8964) ++ ba_flags = (ba_type & BA_TYPE_MASK_NDP) | ++ ((ba_direction << BA_DIRECTION_SHIFT_NDP) & ++ BA_DIRECTION_MASK_NDP); ++ else ++ ba_flags = (ba_type & BA_TYPE_MASK) | ++ ((ba_direction << BA_DIRECTION_SHIFT) & ++ BA_DIRECTION_MASK); ++ pcmd->ba_info.destroy_params.flags = cpu_to_le32(ba_flags); ++ pcmd->ba_info.destroy_params.fw_ba_context.context = ++ cpu_to_le32(stream->idx); ++ pcmd->ba_info.destroy_params.tid = stream->tid; ++ ether_addr_copy(&pcmd->ba_info.destroy_params.peer_mac_addr[0], ++ stream->sta->addr); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BASTREAM)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++/* caller must hold priv->stream_lock when calling the stream functions */ ++struct mwl_ampdu_stream *mwl_fwcmd_add_stream(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta, ++ u8 tid) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_ampdu_stream *stream; ++ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); ++ int idx; ++ ++ if (priv->chip_type == MWL8964) { ++ idx = ((sta_info->stnid - 1) * SYSADPT_MAX_TID) + tid; ++ ++ if (idx < priv->ampdu_num) { ++ stream = &priv->ampdu[idx]; ++ stream->sta = sta; ++ stream->state = AMPDU_STREAM_NEW; ++ stream->tid = tid; ++ stream->idx = idx; ++ return stream; ++ } ++ } else { ++ for (idx = 0; idx < priv->ampdu_num; idx++) { ++ stream = &priv->ampdu[idx]; ++ ++ if (stream->state == AMPDU_NO_STREAM) { ++ stream->sta = sta; ++ stream->state = AMPDU_STREAM_NEW; ++ stream->tid = tid; ++ stream->idx = idx; ++ return stream; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++void mwl_fwcmd_del_sta_streams(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_ampdu_stream *stream; ++ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); ++ int i, idx; ++ ++ spin_lock_bh(&priv->stream_lock); ++ if (priv->chip_type == MWL8964) { ++ idx = (sta_info->stnid - 1) * SYSADPT_MAX_TID; ++ for (i = 0; i < SYSADPT_MAX_TID; i++) { ++ stream = &priv->ampdu[idx + i]; ++ ++ if (stream->sta == sta) { ++ spin_unlock_bh(&priv->stream_lock); ++ mwl_fwcmd_destroy_ba(hw, stream, ++ BA_FLAG_DIRECTION_UP); ++ spin_lock_bh(&priv->stream_lock); ++ mwl_fwcmd_remove_stream(hw, stream); ++ } ++ } ++ } else { ++ for (idx = 0; idx < priv->ampdu_num; idx++) { ++ stream = &priv->ampdu[idx]; ++ ++ if (stream->sta == sta) { ++ spin_unlock_bh(&priv->stream_lock); ++ mwl_fwcmd_destroy_ba(hw, stream, ++ BA_FLAG_DIRECTION_UP); ++ spin_lock_bh(&priv->stream_lock); ++ mwl_fwcmd_remove_stream(hw, stream); ++ } ++ } ++ } ++ spin_unlock_bh(&priv->stream_lock); ++} ++ ++int mwl_fwcmd_start_stream(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream) ++{ ++ /* if the stream has already been started, don't start it again */ ++ if (stream->state != AMPDU_STREAM_NEW) ++ return 0; ++ ++ return ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0); ++} ++ ++void mwl_fwcmd_remove_stream(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream) ++{ ++ memset(stream, 0, sizeof(*stream)); ++} ++ ++struct mwl_ampdu_stream *mwl_fwcmd_lookup_stream(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta, ++ u8 tid) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_ampdu_stream *stream; ++ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); ++ int idx; ++ ++ if (priv->chip_type == MWL8964) { ++ idx = ((sta_info->stnid - 1) * SYSADPT_MAX_TID) + tid; ++ if (idx < priv->ampdu_num) ++ return &priv->ampdu[idx]; ++ } else { ++ for (idx = 0; idx < priv->ampdu_num; idx++) { ++ stream = &priv->ampdu[idx]; ++ if (stream->state == AMPDU_NO_STREAM) ++ continue; ++ ++ if ((stream->sta == sta) && (stream->tid == tid)) ++ return stream; ++ } ++ } ++ ++ return NULL; ++} ++ ++bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid) ++{ ++ struct mwl_sta *sta_info; ++ struct mwl_tx_info *tx_stats; ++ ++ if (WARN_ON(tid >= SYSADPT_MAX_TID)) ++ return false; ++ ++ sta_info = mwl_dev_get_sta(sta); ++ ++ tx_stats = &sta_info->tx_stats[tid]; ++ ++ return (sta_info->is_ampdu_allowed && ++ tx_stats->pkts > SYSADPT_AMPDU_PACKET_THRESHOLD); ++} ++ ++int mwl_fwcmd_set_optimization_level(struct ieee80211_hw *hw, u8 opt_level) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_optimization_level *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_optimization_level *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->opt_level = opt_level; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_wsc_ie(struct ieee80211_hw *hw, u8 len, u8 *data) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_wsc_ie *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_wsc_ie *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_WSC_IE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->len = cpu_to_le16(len); ++ memcpy(pcmd->data, data, len); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_WSC_IE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ pcmd->ie_type = cpu_to_le16(WSC_IE_SET_PROBE_RESPONSE); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_WSC_IE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_get_ratetable(struct ieee80211_hw *hw, u8 *addr, u8 *rate_table, ++ u32 size, u8 type) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_get_ratetable *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_get_ratetable *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_RATETABLE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->type = type; ++ ether_addr_copy(pcmd->addr, addr); ++ memset(rate_table, 0x00, size); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_RATETABLE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ memcpy(rate_table, &pcmd->sorted_rates_idx_map, size); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_get_seqno(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream, u16 *start_seqno) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_get_seqno *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_get_seqno *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_SEQNO); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ether_addr_copy(pcmd->mac_addr, stream->sta->addr); ++ pcmd->tid = stream->tid; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_SEQNO)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ *start_seqno = le16_to_cpu(pcmd->seq_no); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_dwds_enable *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_dwds_enable *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_DWDS_ENABLE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->enable = cpu_to_le32(enable); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_DWDS_ENABLE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_fw_flush_timer(struct ieee80211_hw *hw, u32 value) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_fw_flush_timer *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_fw_flush_timer *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_FW_FLUSH_TIMER); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->value = cpu_to_le32(value); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_FW_FLUSH_TIMER)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_cdd *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_cdd *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_CDD); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->enable = cpu_to_le32(priv->cdd); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_CDD)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_bftype(struct ieee80211_hw *hw, int mode) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_bftype *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_bftype *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_BFTYPE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le32(WL_SET); ++ pcmd->mode = cpu_to_le32(mode); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_BFTYPE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_reg_cau(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_bbp_reg_access *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_bbp_reg_access *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_CAU_REG_ACCESS); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->offset = cpu_to_le16(reg); ++ pcmd->action = cpu_to_le16(flag); ++ pcmd->value = *val; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_CAU_REG_ACCESS)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ *val = pcmd->value; ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_get_temp(struct ieee80211_hw *hw, u32 *temp) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_get_temp *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_get_temp *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_TEMP); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_TEMP)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ *temp = le32_to_cpu(pcmd->celcius); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_led_ctrl(struct ieee80211_hw *hw, u8 enable, u8 rate) ++{ ++ struct hostcmd_cmd_led_ctrl *pcmd; ++ struct mwl_priv *priv = hw->priv; ++ ++ pcmd = (struct hostcmd_cmd_led_ctrl *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_LED_CTRL); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = 1; /* 1: set */ ++ pcmd->led_enable = enable; ++ pcmd->led_control = 1; /* 1: SW */ ++ ++ switch (rate) { ++ case LED_BLINK_RATE_LOW: ++ case LED_BLINK_RATE_MID: ++ case LED_BLINK_RATE_HIGH: ++ pcmd->led_blink_rate = rate; ++ break; ++ default: ++ if (enable) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EINVAL; ++ } ++ break; ++ } ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_LED_CTRL)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_get_fw_region_code(struct ieee80211_hw *hw, ++ u32 *fw_region_code) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_get_fw_region_code *pcmd; ++ u16 cmd; ++ int status; ++ ++ pcmd = (struct hostcmd_cmd_get_fw_region_code *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ cmd = HOSTCMD_CMD_GET_FW_REGION_CODE; ++ pcmd->cmd_hdr.cmd = cpu_to_le16(cmd); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ if (mwl_hif_exec_cmd(hw, cmd)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ if (pcmd->cmd_hdr.result != 0) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EINVAL; ++ } ++ ++ status = le32_to_cpu(pcmd->status); ++ ++ if (!status) ++ *fw_region_code = le32_to_cpu(pcmd->fw_region_code); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_get_device_pwr_tbl(struct ieee80211_hw *hw, ++ struct mwl_device_pwr_tbl *device_ch_pwrtbl, ++ u8 *region_code, ++ u8 *number_of_channels, ++ u32 channel_index) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_get_device_pwr_tbl *pcmd; ++ int status; ++ u16 cmd; ++ ++ pcmd = (struct hostcmd_cmd_get_device_pwr_tbl *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ cmd = HOSTCMD_CMD_GET_DEVICE_PWR_TBL; ++ pcmd->cmd_hdr.cmd = cpu_to_le16(cmd); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->status = cpu_to_le16(cmd); ++ pcmd->current_channel_index = cpu_to_le32(channel_index); ++ ++ if (mwl_hif_exec_cmd(hw, cmd)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ device_ch_pwrtbl->channel = pcmd->channel_pwr_tbl.channel; ++ memcpy(device_ch_pwrtbl->tx_pwr, pcmd->channel_pwr_tbl.tx_pwr, ++ priv->pwr_level); ++ device_ch_pwrtbl->dfs_capable = pcmd->channel_pwr_tbl.dfs_capable; ++ device_ch_pwrtbl->ax_ant = pcmd->channel_pwr_tbl.ax_ant; ++ device_ch_pwrtbl->cdd = pcmd->channel_pwr_tbl.cdd; ++ *region_code = pcmd->region_code; ++ *number_of_channels = pcmd->number_of_channels; ++ status = le16_to_cpu(pcmd->status); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return status; ++} ++ ++int mwl_fwcmd_set_rate_drop(struct ieee80211_hw *hw, int enable, ++ int value, int staid) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_set_rate_drop *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_set_rate_drop *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_RATE_DROP); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->enable = cpu_to_le32(enable); ++ pcmd->rate_index = cpu_to_le32(value); ++ pcmd->sta_index = cpu_to_le32(staid); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_RATE_DROP)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_newdp_dmathread_start(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_newdp_dmathread_start *pcmd; ++ u16 cmd; ++ ++ pcmd = (struct hostcmd_cmd_newdp_dmathread_start *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ cmd = HOSTCMD_CMD_NEWDP_DMATHREAD_START; ++ pcmd->cmd_hdr.cmd = cpu_to_le16(cmd); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ if (mwl_hif_exec_cmd(hw, cmd)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++ ++int mwl_fwcmd_get_fw_region_code_sc4(struct ieee80211_hw *hw, ++ u32 *fw_region_code) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_get_fw_region_code_sc4 *pcmd; ++ u16 cmd; ++ ++ pcmd = (struct hostcmd_cmd_get_fw_region_code_sc4 *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ cmd = HOSTCMD_CMD_GET_FW_REGION_CODE_SC4; ++ pcmd->cmd_hdr.cmd = cpu_to_le16(cmd); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ if (mwl_hif_exec_cmd(hw, cmd)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ if (pcmd->cmd_hdr.result != 0) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EINVAL; ++ } ++ ++ if (pcmd->status) ++ *fw_region_code = (pcmd->status == 1) ? 0 : pcmd->status; ++ else ++ *fw_region_code = le32_to_cpu(pcmd->fw_region_code); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_get_pwr_tbl_sc4(struct ieee80211_hw *hw, ++ struct mwl_device_pwr_tbl *device_ch_pwrtbl, ++ u8 *region_code, ++ u8 *number_of_channels, ++ u32 channel_index) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_get_device_pwr_tbl_sc4 *pcmd; ++ int status; ++ u16 cmd; ++ ++ pcmd = (struct hostcmd_cmd_get_device_pwr_tbl_sc4 *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ cmd = HOSTCMD_CMD_GET_DEVICE_PWR_TBL_SC4; ++ pcmd->cmd_hdr.cmd = cpu_to_le16(cmd); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->status = cpu_to_le16(cmd); ++ pcmd->current_channel_index = cpu_to_le32(channel_index); ++ ++ if (mwl_hif_exec_cmd(hw, cmd)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ device_ch_pwrtbl->channel = pcmd->channel_pwr_tbl.channel; ++ memcpy(device_ch_pwrtbl->tx_pwr, pcmd->channel_pwr_tbl.tx_pwr, ++ SYSADPT_TX_PWR_LEVEL_TOTAL_SC4); ++ device_ch_pwrtbl->dfs_capable = pcmd->channel_pwr_tbl.dfs_capable; ++ device_ch_pwrtbl->ax_ant = pcmd->channel_pwr_tbl.ax_ant; ++ device_ch_pwrtbl->cdd = pcmd->channel_pwr_tbl.cdd; ++ *region_code = pcmd->region_code; ++ *number_of_channels = pcmd->number_of_channels; ++ status = le16_to_cpu(pcmd->status); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return status; ++} ++ ++int mwl_fwcmd_quiet_mode(struct ieee80211_hw *hw, bool enable, u32 period, ++ u32 duration, u32 next_offset) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_quiet_mode *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_quiet_mode *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_QUIET_MODE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le16(WL_SET); ++ pcmd->enable = cpu_to_le32(enable); ++ if (enable) { ++ pcmd->period = cpu_to_le32(period); ++ pcmd->duration = cpu_to_le32(duration); ++ pcmd->next_offset = cpu_to_le32(next_offset); ++ } ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_QUIET_MODE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_core_dump_diag_mode(struct ieee80211_hw *hw, u16 status) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_core_dump_diag_mode *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_core_dump_diag_mode *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_CORE_DUMP_DIAG_MODE); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->status = cpu_to_le16(status); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_CORE_DUMP_DIAG_MODE)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_get_fw_core_dump(struct ieee80211_hw *hw, ++ struct coredump_cmd *core_dump, char *buff) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_get_fw_core_dump *pcmd; ++ ++ if (priv->chip_type != MWL8964) ++ return -EPERM; ++ ++ pcmd = (struct hostcmd_cmd_get_fw_core_dump *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_FW_CORE_DUMP); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->cmd_data.coredump.context = core_dump->context; ++ pcmd->cmd_data.coredump.buffer = cpu_to_le32(priv->pphys_cmd_buf + ++ sizeof(struct hostcmd_cmd_get_fw_core_dump) - ++ sizeof(struct hostcmd_cmd_get_fw_core_dump_)); ++ pcmd->cmd_data.coredump.buffer_len = cpu_to_le32(MAX_CORE_DUMP_BUFFER); ++ pcmd->cmd_data.coredump.size_kb = core_dump->size_kb; ++ pcmd->cmd_data.coredump.flags = core_dump->flags; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_FW_CORE_DUMP)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ /* update core dump buffer */ ++ core_dump->context = pcmd->cmd_data.coredump.context; ++ core_dump->size_kb = pcmd->cmd_data.coredump.size_kb; ++ core_dump->flags = pcmd->cmd_data.coredump.flags; ++ memcpy(buff, ++ (const void *)((u32)pcmd + ++ sizeof(struct hostcmd_cmd_get_fw_core_dump) - ++ sizeof(struct hostcmd_cmd_get_fw_core_dump_)), ++ MAX_CORE_DUMP_BUFFER); ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_slot_time(struct ieee80211_hw *hw, bool short_slot) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_802_11_slot_time *pcmd; ++ ++ wiphy_debug(priv->hw->wiphy, "%s(): short_slot_time=%d\n", ++ __func__, short_slot); ++ ++ pcmd = (struct hostcmd_cmd_802_11_slot_time *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_SLOT_TIME); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le16(WL_SET); ++ pcmd->short_slot = cpu_to_le16(short_slot ? 1 : 0); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_802_11_SLOT_TIME)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_config_EDMACCtrl(struct ieee80211_hw *hw, int EDMAC_Ctrl) ++{ ++ struct hostcmd_cmd_edmac_ctrl *pcmd; ++ struct mwl_priv *priv = hw->priv; ++ ++ pcmd = (struct hostcmd_cmd_edmac_ctrl *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_EDMAC_CTRL); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->action = cpu_to_le16(WL_SET); ++ pcmd->ed_ctrl_2g = cpu_to_le16((EDMAC_Ctrl & EDMAC_2G_ENABLE_MASK) ++ >> EDMAC_2G_ENABLE_SHIFT); ++ pcmd->ed_ctrl_5g = cpu_to_le16((EDMAC_Ctrl & EDMAC_5G_ENABLE_MASK) ++ >> EDMAC_5G_ENABLE_SHIFT); ++ pcmd->ed_offset_2g = cpu_to_le16((EDMAC_Ctrl & ++ EDMAC_2G_THRESHOLD_OFFSET_MASK) ++ >> EDMAC_2G_THRESHOLD_OFFSET_SHIFT); ++ pcmd->ed_offset_5g = cpu_to_le16((EDMAC_Ctrl & ++ EDMAC_5G_THRESHOLD_OFFSET_MASK) ++ >> EDMAC_5G_THRESHOLD_OFFSET_SHIFT); ++ pcmd->ed_bitmap_txq_lock = cpu_to_le16((EDMAC_Ctrl & ++ EDMAC_QLOCK_BITMAP_MASK) ++ >> EDMAC_QLOCK_BITMAP_SHIFT); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_EDMAC_CTRL)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++int mwl_fwcmd_set_txpwrlmt_cfg_data(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_txpwrlmt_cfg *pcmd; ++ struct mwl_txpwrlmt_cfg_entry_hdr hdr; ++ u16 id, parsed_len, size; ++ __le32 txpwr_cfg_sig; ++ u8 version[TXPWRLMT_CFG_VERSION_INFO_LEN]; ++ const u8 *ptr; ++ ++ if (!priv->txpwrlmt_file) ++ return 0; ++ ++ ptr = priv->txpwrlmt_file->data; ++ size = priv->txpwrlmt_file->size; ++ ++ /* Parsing TxPwrLmit Conf file Signature */ ++ parsed_len = mwl_fwcmd_parse_txpwrlmt_cfg(ptr, size, ++ TXPWRLMT_CFG_SIG_LEN, ++ (u8 *)&txpwr_cfg_sig); ++ ptr += parsed_len; ++ size -= parsed_len; ++ ++ if (le32_to_cpu(txpwr_cfg_sig) != TXPWRLMT_CFG_SIGNATURE) { ++ wiphy_err(hw->wiphy, ++ "txpwrlmt config signature mismatch\n"); ++ release_firmware(priv->txpwrlmt_file); ++ priv->txpwrlmt_file = NULL; ++ return 0; ++ } ++ ++ /* Parsing TxPwrLmit Conf file Version */ ++ parsed_len = mwl_fwcmd_parse_txpwrlmt_cfg(ptr, size, ++ TXPWRLMT_CFG_VERSION_INFO_LEN, ++ version); ++ ptr += parsed_len; ++ size -= parsed_len; ++ ++ for (id = 0; id < TXPWRLMT_CFG_MAX_SUBBAND_INFO; id++) { ++ u16 data_len; ++ ++ /*Parsing tx pwr cfg subband header info*/ ++ parsed_len = sizeof(struct mwl_txpwrlmt_cfg_entry_hdr); ++ parsed_len = mwl_fwcmd_parse_txpwrlmt_cfg(ptr, size, ++ parsed_len, ++ (u8 *)&hdr); ++ ptr += parsed_len; ++ size -= parsed_len; ++ data_len = le16_to_cpu(hdr.len) - ++ sizeof(struct mwl_txpwrlmt_cfg_entry_hdr); ++ ++ pcmd = (struct hostcmd_cmd_txpwrlmt_cfg *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET); ++ pcmd->subband_id = hdr.id; ++ pcmd->data_len = cpu_to_le16(data_len); ++ pcmd->num_entries = hdr.num_entries; ++ ++ /* Parsing tx pwr cfg subband header info */ ++ parsed_len = mwl_fwcmd_parse_txpwrlmt_cfg(ptr, size, ++ data_len, pcmd->data); ++ ptr += parsed_len; ++ size -= parsed_len; ++ ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_TXPWRLMT_CFG); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd) + ++ data_len - sizeof(pcmd->data)); ++ ++ if (size < sizeof(struct mwl_txpwrlmt_cfg_entry_hdr)) ++ pcmd->cfgComplete = 1; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_TXPWRLMT_CFG)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ release_firmware(priv->txpwrlmt_file); ++ priv->txpwrlmt_file = NULL; ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ } ++ ++ release_firmware(priv->txpwrlmt_file); ++ priv->txpwrlmt_file = NULL; ++ ++ return 0; ++} ++ ++int mwl_fwcmd_get_txpwrlmt_cfg_data(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_txpwrlmt_cfg *pcmd; ++ u16 subband_len, total_len = 0; ++ u8 id; ++ ++ for (id = 0; id < TXPWRLMT_CFG_MAX_SUBBAND_INFO; id++) { ++ pcmd = (struct hostcmd_cmd_txpwrlmt_cfg *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->action = 0; ++ pcmd->subband_id = id; ++ pcmd->data_len = 0; ++ pcmd->num_entries = 0; ++ ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_TXPWRLMT_CFG); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_TXPWRLMT_CFG)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ subband_len = le16_to_cpu(pcmd->cmd_hdr.len) - ++ sizeof(struct hostcmd_header) - 2; ++ if (total_len <= SYSADPT_TXPWRLMT_CFG_BUF_SIZE) { ++ wiphy_debug(hw->wiphy, "Subband len = %d\n", ++ subband_len); ++ memcpy(priv->txpwrlmt_data.buf + total_len, ++ &pcmd->subband_id, subband_len); ++ total_len += subband_len; ++ priv->txpwrlmt_data.buf[total_len] = '\n'; ++ total_len++; ++ priv->txpwrlmt_data.len = total_len; ++ } else { ++ wiphy_err(hw->wiphy, ++ "TxPwrLmt cfg buf size is not enough\n"); ++ } ++ } ++ ++ return 0; ++} ++ ++int mwl_fwcmd_mcast_cts(struct ieee80211_hw *hw, u8 enable) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct hostcmd_cmd_mcast_cts *pcmd; ++ ++ pcmd = (struct hostcmd_cmd_mcast_cts *)&priv->pcmd_buf[0]; ++ ++ mutex_lock(&priv->fwcmd_mutex); ++ ++ memset(pcmd, 0x00, sizeof(*pcmd)); ++ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_MCAST_CTS); ++ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); ++ pcmd->enable = enable; ++ ++ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_MCAST_CTS)) { ++ mutex_unlock(&priv->fwcmd_mutex); ++ return -EIO; ++ } ++ ++ mutex_unlock(&priv->fwcmd_mutex); ++ ++ return 0; ++} ++ ++void mwl_fwcmd_get_survey(struct ieee80211_hw *hw, int idx) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct ieee80211_conf *conf = &hw->conf; ++ struct mwl_survey_info *survey_info; ++ ++ if (idx) ++ survey_info = &priv->survey_info[idx - 1]; ++ else ++ survey_info = &priv->cur_survey_info; ++ ++ memcpy(&survey_info->channel, conf->chandef.chan, ++ sizeof(struct ieee80211_channel)); ++ mwl_hif_get_survey(hw, survey_info); ++} +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.h b/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.h +new file mode 100644 +index 000000000000..9565cc447dc6 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.h +@@ -0,0 +1,285 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines firmware host command related ++ * functions. ++ */ ++ ++#ifndef _FWCMD_H_ ++#define _FWCMD_H_ ++ ++#include "hif/hostcmd.h" ++ ++/* Define OpMode for SoftAP/Station mode ++ * ++ * The following mode signature has to be written to PCI scratch register#0 ++ * right after successfully downloading the last block of firmware and ++ * before waiting for firmware ready signature ++ */ ++ ++#define HOSTCMD_STA_MODE 0x5A ++#define HOSTCMD_SOFTAP_MODE 0xA5 ++ ++#define HOSTCMD_STA_FWRDY_SIGNATURE 0xF0F1F2F4 ++#define HOSTCMD_SOFTAP_FWRDY_SIGNATURE 0xF1F2F4A5 ++ ++#define GUARD_INTERVAL_STANDARD 1 ++#define GUARD_INTERVAL_SHORT 2 ++#define GUARD_INTERVAL_AUTO 3 ++ ++#define LINK_CS_STATE_CONSERV 0 ++#define LINK_CS_STATE_AGGR 1 ++#define LINK_CS_STATE_AUTO 2 ++#define LINK_CS_STATE_AUTO_DISABLED 3 ++ ++#define STOP_DETECT_RADAR 0 ++#define CAC_START 1 ++#define MONITOR_START 3 ++ ++#define WDS_MODE 4 ++ ++enum { ++ WL_ANTENNATYPE_RX = 1, ++ WL_ANTENNATYPE_TX = 2, ++}; ++ ++enum encr_type { ++ ENCR_TYPE_WEP = 0, ++ ENCR_TYPE_DISABLE = 1, ++ ENCR_TYPE_TKIP = 4, ++ ENCR_TYPE_AES = 6, ++ ENCR_TYPE_MIX = 7, ++}; ++ ++char *mwl_fwcmd_get_cmd_string(unsigned short cmd); ++ ++const struct hostcmd_get_hw_spec ++*mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw); ++ ++int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw, ++ struct hostcmd_set_hw_spec *spec); ++ ++int mwl_fwcmd_get_stat(struct ieee80211_hw *hw, ++ struct ieee80211_low_level_stats *stats); ++ ++int mwl_fwcmd_reg_bb(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val); ++ ++int mwl_fwcmd_reg_rf(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val); ++ ++int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw); ++ ++int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw); ++ ++int mwl_fwcmd_set_radio_preamble(struct ieee80211_hw *hw, ++ bool short_preamble); ++ ++int mwl_fwcmd_get_addr_value(struct ieee80211_hw *hw, u32 addr, u32 len, ++ u32 *val, u16 set); ++ ++int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw, ++ struct ieee80211_conf *conf, u8 fraction); ++ ++int mwl_fwcmd_tx_power(struct ieee80211_hw *hw, ++ struct ieee80211_conf *conf, u8 fraction); ++ ++int mwl_fwcmd_rf_antenna(struct ieee80211_hw *hw, int dir, int antenna); ++ ++int mwl_fwcmd_broadcast_ssid_enable(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, bool enable); ++ ++int mwl_fwcmd_set_cfg_data(struct ieee80211_hw *hw, u16 type); ++ ++int mwl_fwcmd_set_rf_channel(struct ieee80211_hw *hw, ++ struct ieee80211_conf *conf); ++ ++int mwl_fwcmd_set_aid(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *bssid, u16 aid); ++ ++int mwl_fwcmd_set_infra_mode(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif); ++ ++int mwl_fwcmd_set_rts_threshold(struct ieee80211_hw *hw, ++ int threshold); ++ ++int mwl_fwcmd_set_edca_params(struct ieee80211_hw *hw, u8 index, ++ u16 cw_min, u16 cw_max, u8 aifs, u16 txop); ++ ++int mwl_fwcmd_set_radar_detect(struct ieee80211_hw *hw, u16 action); ++ ++int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable); ++ ++int mwl_fwcmd_ht_guard_interval(struct ieee80211_hw *hw, u32 gi_type); ++ ++int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw, ++ int mcast, int mgmt); ++ ++int mwl_fwcmd_set_linkadapt_cs_mode(struct ieee80211_hw *hw, ++ u16 cs_mode); ++ ++int mwl_fwcmd_dump_otp_data(struct ieee80211_hw *hw); ++ ++int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw, ++ u16 mode); ++ ++int mwl_fwcmd_set_mac_addr_client(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *mac_addr); ++ ++int mwl_fwcmd_get_watchdog_bitmap(struct ieee80211_hw *hw, ++ u8 *bitmap); ++ ++int mwl_fwcmd_remove_mac_addr(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *mac_addr); ++ ++int mwl_fwcmd_bss_start(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, bool enable); ++ ++int mwl_fwcmd_set_beacon(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *beacon, int len); ++ ++int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta); ++ ++int mwl_fwcmd_set_new_stn_add_sc4(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta, ++ u32 wds); ++ ++int mwl_fwcmd_set_new_stn_wds_sc4(struct ieee80211_hw *hw, u8 *addr); ++ ++int mwl_fwcmd_set_new_stn_add_self(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif); ++ ++int mwl_fwcmd_set_new_stn_del(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *addr); ++ ++int mwl_fwcmd_set_apmode(struct ieee80211_hw *hw, u8 apmode); ++ ++int mwl_fwcmd_set_switch_channel(struct ieee80211_hw *hw, ++ struct ieee80211_channel_switch *ch_switch); ++ ++int mwl_fwcmd_update_encryption_enable(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ u8 *addr, u8 encr_type); ++ ++int mwl_fwcmd_encryption_set_key(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *addr, ++ struct ieee80211_key_conf *key); ++ ++int mwl_fwcmd_encryption_remove_key(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u8 *addr, ++ struct ieee80211_key_conf *key); ++ ++int mwl_fwcmd_check_ba(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream, ++ struct ieee80211_vif *vif, ++ u32 direction); ++ ++int mwl_fwcmd_create_ba(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream, ++ struct ieee80211_vif *vif, ++ u32 direction, u8 buf_size, u16 seqno, bool amsdu); ++ ++int mwl_fwcmd_destroy_ba(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream, ++ u32 direction); ++ ++struct mwl_ampdu_stream *mwl_fwcmd_add_stream(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta, ++ u8 tid); ++ ++void mwl_fwcmd_del_sta_streams(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta); ++ ++int mwl_fwcmd_start_stream(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream); ++ ++void mwl_fwcmd_remove_stream(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream); ++ ++struct mwl_ampdu_stream *mwl_fwcmd_lookup_stream(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta, ++ u8 tid); ++ ++bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid); ++ ++int mwl_fwcmd_set_optimization_level(struct ieee80211_hw *hw, u8 opt_level); ++ ++int mwl_fwcmd_set_wsc_ie(struct ieee80211_hw *hw, u8 len, u8 *data); ++ ++int mwl_fwcmd_get_ratetable(struct ieee80211_hw *hw, u8 *addr, u8 *rate_table, ++ u32 size, u8 type); ++ ++int mwl_fwcmd_get_seqno(struct ieee80211_hw *hw, ++ struct mwl_ampdu_stream *stream, u16 *start_seqno); ++ ++int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable); ++ ++int mwl_fwcmd_set_fw_flush_timer(struct ieee80211_hw *hw, u32 value); ++ ++int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw); ++ ++int mwl_fwcmd_set_bftype(struct ieee80211_hw *hw, int mode); ++ ++int mwl_fwcmd_reg_cau(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val); ++ ++int mwl_fwcmd_get_temp(struct ieee80211_hw *hw, u32 *temp); ++ ++int mwl_fwcmd_led_ctrl(struct ieee80211_hw *hw, u8 enable, u8 rate); ++ ++int mwl_fwcmd_get_fw_region_code(struct ieee80211_hw *hw, ++ u32 *fw_region_code); ++ ++int mwl_fwcmd_get_device_pwr_tbl(struct ieee80211_hw *hw, ++ struct mwl_device_pwr_tbl *device_ch_pwrtbl, ++ u8 *region_code, ++ u8 *number_of_channels, ++ u32 channel_index); ++ ++int mwl_fwcmd_set_rate_drop(struct ieee80211_hw *hw, int enable, ++ int value, int staid); ++ ++int mwl_fwcmd_newdp_dmathread_start(struct ieee80211_hw *hw); ++ ++int mwl_fwcmd_get_fw_region_code_sc4(struct ieee80211_hw *hw, ++ u32 *fw_region_code); ++ ++int mwl_fwcmd_get_pwr_tbl_sc4(struct ieee80211_hw *hw, ++ struct mwl_device_pwr_tbl *device_ch_pwrtbl, ++ u8 *region_code, ++ u8 *number_of_channels, ++ u32 channel_index); ++ ++int mwl_fwcmd_quiet_mode(struct ieee80211_hw *hw, bool enable, u32 period, ++ u32 duration, u32 next_offset); ++ ++int mwl_fwcmd_core_dump_diag_mode(struct ieee80211_hw *hw, u16 status); ++ ++int mwl_fwcmd_get_fw_core_dump(struct ieee80211_hw *hw, ++ struct coredump_cmd *core_dump, char *buff); ++ ++int mwl_fwcmd_set_slot_time(struct ieee80211_hw *hw, bool short_slot); ++ ++int mwl_fwcmd_config_EDMACCtrl(struct ieee80211_hw *hw, int EDMAC_Ctrl); ++ ++int mwl_fwcmd_set_txpwrlmt_cfg_data(struct ieee80211_hw *hw); ++ ++int mwl_fwcmd_get_txpwrlmt_cfg_data(struct ieee80211_hw *hw); ++ ++int mwl_fwcmd_mcast_cts(struct ieee80211_hw *hw, u8 enable); ++ ++void mwl_fwcmd_get_survey(struct ieee80211_hw *hw, int idx); ++ ++#endif /* _FWCMD_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/hif-ops.h b/drivers/net/wireless/marvell/mwlwifi/hif/hif-ops.h +new file mode 100644 +index 000000000000..f5c7144b3c1b +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/hif-ops.h +@@ -0,0 +1,297 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines host interface related operations. */ ++ ++#ifndef _HIF_OPS_H_ ++#define _HIF_OPS_H_ ++static inline const char *mwl_hif_get_driver_name(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ return priv->hif.ops->driver_name; ++} ++ ++static inline const char *mwl_hif_get_driver_version(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ return priv->hif.ops->driver_version; ++} ++ ++static inline unsigned int mwl_hif_get_tx_head_room(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ return priv->hif.ops->tx_head_room; ++} ++ ++static inline unsigned int mwl_hif_get_ampdu_num(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ return priv->hif.ops->ampdu_num; ++} ++ ++static inline void mwl_hif_reset(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->reset) ++ priv->hif.ops->reset(hw); ++} ++ ++static inline int mwl_hif_init(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->init) ++ return priv->hif.ops->init(hw); ++ else ++ return -ENOTSUPP; ++} ++ ++static inline void mwl_hif_deinit(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->deinit) ++ priv->hif.ops->deinit(hw); ++} ++ ++static inline int mwl_hif_get_info(struct ieee80211_hw *hw, ++ char *buf, size_t size) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->get_info) ++ return priv->hif.ops->get_info(hw, buf, size); ++ else ++ return 0; ++} ++ ++static inline int mwl_hif_get_tx_status(struct ieee80211_hw *hw, ++ char *buf, size_t size) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->get_tx_status) ++ return priv->hif.ops->get_tx_status(hw, buf, size); ++ else ++ return 0; ++} ++ ++static inline int mwl_hif_get_rx_status(struct ieee80211_hw *hw, ++ char *buf, size_t size) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->get_rx_status) ++ return priv->hif.ops->get_rx_status(hw, buf, size); ++ else ++ return 0; ++} ++ ++static inline void mwl_hif_enable_data_tasks(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->enable_data_tasks) ++ priv->hif.ops->enable_data_tasks(hw); ++} ++ ++static inline void mwl_hif_disable_data_tasks(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->disable_data_tasks) ++ priv->hif.ops->disable_data_tasks(hw); ++} ++ ++static inline int mwl_hif_exec_cmd(struct ieee80211_hw *hw, unsigned short cmd) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->exec_cmd) ++ return priv->hif.ops->exec_cmd(hw, cmd); ++ else ++ return -ENOTSUPP; ++} ++ ++static inline int mwl_hif_get_irq_num(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->get_irq_num) ++ return priv->hif.ops->get_irq_num(hw); ++ else ++ return -ENOTSUPP; ++} ++ ++static inline irqreturn_t mwl_hif_irq_handler(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->irq_handler) ++ return priv->hif.ops->irq_handler(hw); ++ else ++ return -ENOTSUPP; ++} ++ ++static inline void mwl_hif_irq_enable(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->irq_enable) ++ priv->hif.ops->irq_enable(hw); ++} ++ ++static inline void mwl_hif_irq_disable(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->irq_disable) ++ priv->hif.ops->irq_disable(hw); ++} ++ ++static inline int mwl_hif_download_firmware(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->download_firmware) ++ return priv->hif.ops->download_firmware(hw); ++ else ++ return -ENOTSUPP; ++} ++ ++static inline void mwl_hif_timer_routine(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->timer_routine) ++ priv->hif.ops->timer_routine(hw); ++} ++ ++static inline void mwl_hif_tx_xmit(struct ieee80211_hw *hw, ++ struct ieee80211_tx_control *control, ++ struct sk_buff *skb) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->tx_xmit) ++ priv->hif.ops->tx_xmit(hw, control, skb); ++} ++ ++static inline void mwl_hif_tx_del_pkts_via_vif(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->tx_del_pkts_via_vif) ++ priv->hif.ops->tx_del_pkts_via_vif(hw, vif); ++} ++ ++static inline void mwl_hif_tx_del_pkts_via_sta(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->tx_del_pkts_via_sta) ++ priv->hif.ops->tx_del_pkts_via_sta(hw, sta); ++} ++ ++static inline void mwl_hif_tx_del_ampdu_pkts(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta, u8 tid) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->tx_del_ampdu_pkts) ++ priv->hif.ops->tx_del_ampdu_pkts(hw, sta, tid); ++} ++ ++static inline void mwl_hif_tx_del_sta_amsdu_pkts(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->tx_del_sta_amsdu_pkts) ++ priv->hif.ops->tx_del_sta_amsdu_pkts(hw, sta); ++} ++ ++static inline void mwl_hif_tx_return_pkts(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->tx_return_pkts) ++ priv->hif.ops->tx_return_pkts(hw); ++} ++ ++static inline struct device_node *mwl_hif_device_node(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->get_device_node) ++ return priv->hif.ops->get_device_node(hw); ++ else ++ return NULL; ++} ++ ++static inline void mwl_hif_get_survey(struct ieee80211_hw *hw, ++ struct mwl_survey_info *survey_info) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->get_survey) ++ priv->hif.ops->get_survey(hw, survey_info); ++} ++ ++static inline int mwl_hif_reg_access(struct ieee80211_hw *hw, bool write) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->reg_access) ++ return priv->hif.ops->reg_access(hw, write); ++ else ++ return -ENOTSUPP; ++} ++ ++static inline void mwl_hif_set_sta_id(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta, ++ bool sta_mode, bool set) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->set_sta_id) ++ priv->hif.ops->set_sta_id(hw, sta, sta_mode, set); ++} ++ ++static inline void mwl_hif_process_account(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->process_account) ++ priv->hif.ops->process_account(hw); ++} ++ ++static inline int mwl_hif_mcast_cts(struct ieee80211_hw *hw, bool enable) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->hif.ops->mcast_cts) ++ return priv->hif.ops->mcast_cts(hw, enable); ++ else ++ return -ENOTSUPP; ++} ++#endif /* _HIF_OPS_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/hif.h b/drivers/net/wireless/marvell/mwlwifi/hif/hif.h +new file mode 100644 +index 000000000000..6ea6192ac5e0 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/hif.h +@@ -0,0 +1,81 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines host interface data structure. */ ++ ++#ifndef _HIF_H_ ++#define _HIF_H_ ++ ++/* memory/register access */ ++#define MWL_ACCESS_MAC 1 ++#define MWL_ACCESS_RF 2 ++#define MWL_ACCESS_BBP 3 ++#define MWL_ACCESS_CAU 4 ++#define MWL_ACCESS_ADDR0 5 ++#define MWL_ACCESS_ADDR1 6 ++#define MWL_ACCESS_ADDR 7 ++ ++struct mwl_survey_info { ++ struct ieee80211_channel channel; ++ u32 filled; ++ u32 time_period; ++ u32 time_busy; ++ u32 time_tx; ++ s8 noise; ++}; ++ ++struct mwl_hif_ops { ++ const char *driver_name; ++ const char *driver_version; ++ unsigned int tx_head_room; ++ int ampdu_num; ++ void (*reset)(struct ieee80211_hw *hw); ++ int (*init)(struct ieee80211_hw *hw); ++ void (*deinit)(struct ieee80211_hw *hw); ++ int (*get_info)(struct ieee80211_hw *hw, char *buf, size_t size); ++ int (*get_tx_status)(struct ieee80211_hw *hw, char *buf, size_t size); ++ int (*get_rx_status)(struct ieee80211_hw *hw, char *buf, size_t size); ++ void (*enable_data_tasks)(struct ieee80211_hw *hw); ++ void (*disable_data_tasks)(struct ieee80211_hw *hw); ++ int (*exec_cmd)(struct ieee80211_hw *hw, unsigned short cmd); ++ int (*get_irq_num)(struct ieee80211_hw *hw); ++ irqreturn_t (*irq_handler)(struct ieee80211_hw *hw); ++ void (*irq_enable)(struct ieee80211_hw *hw); ++ void (*irq_disable)(struct ieee80211_hw *hw); ++ int (*download_firmware)(struct ieee80211_hw *hw); ++ void (*timer_routine)(struct ieee80211_hw *hw); ++ void (*tx_xmit)(struct ieee80211_hw *hw, ++ struct ieee80211_tx_control *control, ++ struct sk_buff *skb); ++ void (*tx_del_pkts_via_vif)(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif); ++ void (*tx_del_pkts_via_sta)(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta); ++ void (*tx_del_ampdu_pkts)(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta, u8 tid); ++ void (*tx_del_sta_amsdu_pkts)(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta); ++ void (*tx_return_pkts)(struct ieee80211_hw *hw); ++ struct device_node *(*get_device_node)(struct ieee80211_hw *hw); ++ void (*get_survey)(struct ieee80211_hw *hw, ++ struct mwl_survey_info *survey_info); ++ int (*reg_access)(struct ieee80211_hw *hw, bool write); ++ void (*set_sta_id)(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta, ++ bool sta_mode, bool set); ++ void (*process_account)(struct ieee80211_hw *hw); ++ int (*mcast_cts)(struct ieee80211_hw *hw, bool enable); ++}; ++#endif /* _HIF_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/hostcmd.h b/drivers/net/wireless/marvell/mwlwifi/hif/hostcmd.h +new file mode 100644 +index 000000000000..b14f161f1410 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/hostcmd.h +@@ -0,0 +1,1285 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines firmware host command related ++ * structure. ++ */ ++ ++#ifndef _HOSTCMD_H_ ++#define _HOSTCMD_H_ ++ ++/* 16 bit host command code */ ++#define HOSTCMD_CMD_GET_HW_SPEC 0x0003 ++#define HOSTCMD_CMD_SET_HW_SPEC 0x0004 ++#define HOSTCMD_CMD_802_11_GET_STAT 0x0014 ++#define HOSTCMD_CMD_BBP_REG_ACCESS 0x001a ++#define HOSTCMD_CMD_RF_REG_ACCESS 0x001b ++#define HOSTCMD_CMD_802_11_RADIO_CONTROL 0x001c ++#define HOSTCMD_CMD_MEM_ADDR_ACCESS 0x001d ++#define HOSTCMD_CMD_802_11_TX_POWER 0x001f ++#define HOSTCMD_CMD_802_11_RF_ANTENNA 0x0020 ++#define HOSTCMD_CMD_BROADCAST_SSID_ENABLE 0x0050 /* per-vif */ ++#define HOSTCMD_CMD_SET_CFG 0x008f ++#define HOSTCMD_CMD_SET_RF_CHANNEL 0x010a ++#define HOSTCMD_CMD_SET_AID 0x010d /* per-vif */ ++#define HOSTCMD_CMD_SET_INFRA_MODE 0x010e /* per-vif */ ++#define HOSTCMD_CMD_802_11_RTS_THSD 0x0113 ++#define HOSTCMD_CMD_SET_EDCA_PARAMS 0x0115 ++#define HOSTCMD_CMD_802_11H_DETECT_RADAR 0x0120 ++#define HOSTCMD_CMD_SET_WMM_MODE 0x0123 ++#define HOSTCMD_CMD_HT_GUARD_INTERVAL 0x0124 ++#define HOSTCMD_CMD_SET_FIXED_RATE 0x0126 ++#define HOSTCMD_CMD_SET_IES 0x0127 ++#define HOSTCMD_CMD_SET_LINKADAPT_CS_MODE 0x0129 ++#define HOSTCMD_CMD_DUMP_OTP_DATA 0x0142 ++#define HOSTCMD_CMD_SET_MAC_ADDR 0x0202 /* per-vif */ ++#define HOSTCMD_CMD_SET_RATE_ADAPT_MODE 0x0203 ++#define HOSTCMD_CMD_GET_WATCHDOG_BITMAP 0x0205 ++#define HOSTCMD_CMD_DEL_MAC_ADDR 0x0206 /* per-vif */ ++#define HOSTCMD_CMD_BSS_START 0x1100 /* per-vif */ ++#define HOSTCMD_CMD_AP_BEACON 0x1101 /* per-vif */ ++#define HOSTCMD_CMD_SET_NEW_STN 0x1111 /* per-vif */ ++#define HOSTCMD_CMD_SET_APMODE 0x1114 ++#define HOSTCMD_CMD_SET_SWITCH_CHANNEL 0x1121 ++#define HOSTCMD_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */ ++#define HOSTCMD_CMD_BASTREAM 0x1125 ++#define HOSTCMD_CMD_SET_SPECTRUM_MGMT 0x1128 ++#define HOSTCMD_CMD_SET_POWER_CONSTRAINT 0x1129 ++#define HOSTCMD_CMD_SET_COUNTRY_CODE 0x1130 ++#define HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL 0x1133 ++#define HOSTCMD_CMD_SET_WSC_IE 0x1136 /* per-vif */ ++#define HOSTCMD_CMD_GET_RATETABLE 0x1137 ++#define HOSTCMD_CMD_GET_SEQNO 0x1143 ++#define HOSTCMD_CMD_DWDS_ENABLE 0x1144 ++#define HOSTCMD_CMD_FW_FLUSH_TIMER 0x1148 ++#define HOSTCMD_CMD_SET_CDD 0x1150 ++#define HOSTCMD_CMD_SET_BFTYPE 0x1155 ++#define HOSTCMD_CMD_CAU_REG_ACCESS 0x1157 ++#define HOSTCMD_CMD_GET_TEMP 0x1159 ++#define HOSTCMD_CMD_LED_CTRL 0x1169 ++#define HOSTCMD_CMD_GET_FW_REGION_CODE 0x116A ++#define HOSTCMD_CMD_GET_DEVICE_PWR_TBL 0x116B ++#define HOSTCMD_CMD_SET_RATE_DROP 0x1172 ++#define HOSTCMD_CMD_NEWDP_DMATHREAD_START 0x1189 ++#define HOSTCMD_CMD_GET_FW_REGION_CODE_SC4 0x118A ++#define HOSTCMD_CMD_GET_DEVICE_PWR_TBL_SC4 0x118B ++#define HOSTCMD_CMD_QUIET_MODE 0x1201 ++#define HOSTCMD_CMD_CORE_DUMP_DIAG_MODE 0x1202 ++#define HOSTCMD_CMD_802_11_SLOT_TIME 0x1203 ++#define HOSTCMD_CMD_GET_FW_CORE_DUMP 0x1203 ++#define HOSTCMD_CMD_EDMAC_CTRL 0x1204 ++#define HOSTCMD_CMD_TXPWRLMT_CFG 0x1211 ++#define HOSTCMD_CMD_MCAST_CTS 0x4001 ++ ++/* Define general result code for each command */ ++#define HOSTCMD_RESULT_OK 0x0000 ++/* General error */ ++#define HOSTCMD_RESULT_ERROR 0x0001 ++/* Command is not valid */ ++#define HOSTCMD_RESULT_NOT_SUPPORT 0x0002 ++/* Command is pending (will be processed) */ ++#define HOSTCMD_RESULT_PENDING 0x0003 ++/* System is busy (command ignored) */ ++#define HOSTCMD_RESULT_BUSY 0x0004 ++/* Data buffer is not big enough */ ++#define HOSTCMD_RESULT_PARTIAL_DATA 0x0005 ++ ++/* Define channel related constants */ ++#define FREQ_BAND_2DOT4GHZ 0x1 ++#define FREQ_BAND_4DOT9GHZ 0x2 ++#define FREQ_BAND_5GHZ 0x4 ++#define FREQ_BAND_5DOT2GHZ 0x8 ++#define CH_AUTO_WIDTH 0 ++#define CH_10_MHZ_WIDTH 0x1 ++#define CH_20_MHZ_WIDTH 0x2 ++#define CH_40_MHZ_WIDTH 0x4 ++#define CH_80_MHZ_WIDTH 0x5 ++#define CH_160_MHZ_WIDTH 0x6 ++#define EXT_CH_ABOVE_CTRL_CH 0x1 ++#define EXT_CH_AUTO 0x2 ++#define EXT_CH_BELOW_CTRL_CH 0x3 ++#define NO_EXT_CHANNEL 0x0 ++ ++#define ACT_PRIMARY_CHAN_0 0 ++#define ACT_PRIMARY_CHAN_1 1 ++#define ACT_PRIMARY_CHAN_2 2 ++#define ACT_PRIMARY_CHAN_3 3 ++#define ACT_PRIMARY_CHAN_4 4 ++#define ACT_PRIMARY_CHAN_5 5 ++#define ACT_PRIMARY_CHAN_6 6 ++#define ACT_PRIMARY_CHAN_7 7 ++ ++/* Define rate related constants */ ++#define HOSTCMD_ACT_NOT_USE_FIXED_RATE 0x0002 ++ ++/* Define station related constants */ ++#define HOSTCMD_ACT_STA_ACTION_ADD 0 ++#define HOSTCMD_ACT_STA_ACTION_MODIFY 1 ++#define HOSTCMD_ACT_STA_ACTION_REMOVE 2 ++ ++/* Define key related constants */ ++#define MAX_ENCR_KEY_LENGTH 16 ++#define MIC_KEY_LENGTH 8 ++ ++#define KEY_TYPE_ID_WEP 0x00 ++#define KEY_TYPE_ID_TKIP 0x01 ++#define KEY_TYPE_ID_AES 0x02 ++ ++/* Group key for RX only */ ++#define ENCR_KEY_FLAG_RXGROUPKEY 0x00000002 ++#define ENCR_KEY_FLAG_TXGROUPKEY 0x00000004 ++#define ENCR_KEY_FLAG_PAIRWISE 0x00000008 ++#define ENCR_KEY_FLAG_TSC_VALID 0x00000040 ++#define ENCR_KEY_FLAG_WEP_TXKEY 0x01000000 ++#define ENCR_KEY_FLAG_MICKEY_VALID 0x02000000 ++ ++/* Define block ack related constants */ ++#define BA_FLAG_IMMEDIATE_TYPE 1 ++#define BA_FLAG_DIRECTION_UP 0 ++#define BA_FLAG_DIRECTION_DOWN 1 ++ ++/* Define general purpose action */ ++#define HOSTCMD_ACT_GEN_SET 0x0001 ++#define HOSTCMD_ACT_GEN_SET_LIST 0x0002 ++#define HOSTCMD_ACT_GEN_GET_LIST 0x0003 ++ ++/* Define TXPower control action*/ ++#define HOSTCMD_ACT_GET_TARGET_TX_PWR 0x0000 ++#define HOSTCMD_ACT_GET_MAX_TX_PWR 0x0001 ++#define HOSTCMD_ACT_SET_TARGET_TX_PWR 0x0002 ++#define HOSTCMD_ACT_SET_MAX_TX_PWR 0x0003 ++ ++/* Misc */ ++#define WSC_IE_MAX_LENGTH 251 ++#define WSC_IE_SET_BEACON 0 ++#define WSC_IE_SET_PROBE_RESPONSE 1 ++ ++#define HW_SET_PARMS_FEATURES_HOST_PROBE_RESP 0x00000020 ++ ++#define EDMAC_2G_ENABLE_MASK 0x00000001 ++#define EDMAC_2G_ENABLE_SHIFT 0x0 ++#define EDMAC_5G_ENABLE_MASK 0x00000002 ++#define EDMAC_5G_ENABLE_SHIFT 0x1 ++#define EDMAC_2G_THRESHOLD_OFFSET_MASK 0x00000FF0 ++#define EDMAC_2G_THRESHOLD_OFFSET_SHIFT 0x4 ++#define EDMAC_5G_THRESHOLD_OFFSET_MASK 0x000FF000 ++#define EDMAC_5G_THRESHOLD_OFFSET_SHIFT 0xC ++#define EDMAC_QLOCK_BITMAP_MASK 0x0FF00000 ++#define EDMAC_QLOCK_BITMAP_SHIFT 0x14 ++ ++enum { ++ WL_DISABLE = 0, ++ WL_ENABLE = 1, ++ WL_DISABLE_VMAC = 0x80, ++}; ++ ++enum { ++ WL_GET = 0, ++ WL_SET = 1, ++ WL_RESET = 2, ++}; ++ ++enum { ++ WL_LONG_PREAMBLE = 1, ++ WL_SHORT_PREAMBLE = 3, ++ WL_AUTO_PREAMBLE = 5, ++}; ++ ++enum encr_action_type { ++ /* request to enable/disable HW encryption */ ++ ENCR_ACTION_ENABLE_HW_ENCR, ++ /* request to set encryption key */ ++ ENCR_ACTION_TYPE_SET_KEY, ++ /* request to remove one or more keys */ ++ ENCR_ACTION_TYPE_REMOVE_KEY, ++ ENCR_ACTION_TYPE_SET_GROUP_KEY, ++}; ++ ++enum ba_action_type { ++ BA_CREATE_STREAM, ++ BA_UPDATE_STREAM, ++ BA_DESTROY_STREAM, ++ BA_FLUSH_STREAM, ++ BA_CHECK_STREAM, ++}; ++ ++enum mac_type { ++ WL_MAC_TYPE_PRIMARY_CLIENT, ++ WL_MAC_TYPE_SECONDARY_CLIENT, ++ WL_MAC_TYPE_PRIMARY_AP, ++ WL_MAC_TYPE_SECONDARY_AP, ++}; ++ ++/* General host command header */ ++struct hostcmd_header { ++ __le16 cmd; ++ __le16 len; ++ u8 seq_num; ++ u8 macid; ++ __le16 result; ++} __packed; ++ ++/* HOSTCMD_CMD_GET_HW_SPEC */ ++struct hostcmd_get_hw_spec { ++ u8 version; /* version of the HW */ ++ u8 host_if; /* host interface */ ++ __le16 num_wcb; /* Max. number of WCB FW can handle */ ++ __le16 num_mcast_addr; /* MaxNbr of MC addresses FW can handle */ ++ u8 permanent_addr[ETH_ALEN]; /* MAC address programmed in HW */ ++ __le16 region_code; ++ __le16 num_antenna; /* Number of antenna used */ ++ __le32 fw_release_num; /* 4 byte of FW release number */ ++ __le32 wcb_base0; ++ __le32 rxpd_wr_ptr; ++ __le32 rxpd_rd_ptr; ++ __le32 fw_awake_cookie; ++ __le32 wcb_base[SYSADPT_TOTAL_TX_QUEUES - 1]; ++} __packed; ++ ++struct hostcmd_cmd_get_hw_spec { ++ struct hostcmd_header cmd_hdr; ++ struct hostcmd_get_hw_spec hw_spec; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_HW_SPEC */ ++struct hostcmd_set_hw_spec { ++ /* HW revision */ ++ u8 version; ++ /* Host interface */ ++ u8 host_if; ++ /* Max. number of Multicast address FW can handle */ ++ __le16 num_mcast_addr; ++ /* MAC address */ ++ u8 permanent_addr[ETH_ALEN]; ++ /* Region Code */ ++ __le16 region_code; ++ /* 4 byte of FW release number, example 0x1234=1.2.3.4 */ ++ __le32 fw_release_num; ++ /* Firmware awake cookie - used to ensure that the device ++ * is not in sleep mode ++ */ ++ __le32 fw_awake_cookie; ++ /* Device capabilities (see above) */ ++ __le32 device_caps; ++ /* Rx shared memory queue */ ++ __le32 rxpd_wr_ptr; ++ /* Actual number of TX queues in WcbBase array */ ++ __le32 num_tx_queues; ++ /* TX WCB Rings */ ++ __le32 wcb_base[4 + SYSADPT_NUM_OF_AP]; ++ /* Max AMSDU size (00 - AMSDU Disabled, ++ * 01 - 4K, 10 - 8K, 11 - not defined) ++ */ ++ __le32 features; ++ __le32 tx_wcb_num_per_queue; ++ __le32 total_rx_wcb; ++ __le32 acnt_buf_size; ++ __le32 acnt_base_addr; ++} __packed; ++ ++struct hostcmd_cmd_set_hw_spec { ++ struct hostcmd_header cmd_hdr; ++ struct hostcmd_set_hw_spec hw_spec; ++} __packed; ++ ++/* HOSTCMD_CMD_802_11_GET_STAT */ ++struct hostcmd_cmd_802_11_get_stat { ++ struct hostcmd_header cmd_hdr; ++ __le32 tx_retry_successes; ++ __le32 tx_multiple_retry_successes; ++ __le32 tx_failures; ++ __le32 rts_successes; ++ __le32 rts_failures; ++ __le32 ack_failures; ++ __le32 rx_duplicate_frames; ++ __le32 rx_fcs_errors; ++ __le32 tx_watchdog_timeouts; ++ __le32 rx_overflows; ++ __le32 rx_frag_errors; ++ __le32 rx_mem_errors; ++ __le32 pointer_errors; ++ __le32 tx_underflows; ++ __le32 tx_done; ++ __le32 tx_done_buf_try_put; ++ __le32 tx_done_buf_put; ++ /* Put size of requested buffer in here */ ++ __le32 wait_for_tx_buf; ++ __le32 tx_attempts; ++ __le32 tx_successes; ++ __le32 tx_fragments; ++ __le32 tx_multicasts; ++ __le32 rx_non_ctl_pkts; ++ __le32 rx_multicasts; ++ __le32 rx_undecryptable_frames; ++ __le32 rx_icv_errors; ++ __le32 rx_excluded_frames; ++ __le32 rx_weak_iv_count; ++ __le32 rx_unicasts; ++ __le32 rx_bytes; ++ __le32 rx_errors; ++ __le32 rx_rts_count; ++ __le32 tx_cts_count; ++} __packed; ++ ++/* HOSTCMD_CMD_BBP_REG_ACCESS */ ++struct hostcmd_cmd_bbp_reg_access { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ __le16 offset; ++ u8 value; ++ u8 reserverd[3]; ++} __packed; ++ ++/* HOSTCMD_CMD_RF_REG_ACCESS */ ++struct hostcmd_cmd_rf_reg_access { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ __le16 offset; ++ u8 value; ++ u8 reserverd[3]; ++} __packed; ++ ++/* HOSTCMD_CMD_802_11_RADIO_CONTROL */ ++struct hostcmd_cmd_802_11_radio_control { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ /* @bit0: 1/0,on/off, @bit1: 1/0, long/short @bit2: 1/0,auto/fix */ ++ __le16 control; ++ __le16 radio_on; ++} __packed; ++ ++/* HOSTCMD_CMD_MEM_ADDR_ACCESS */ ++struct hostcmd_cmd_mem_addr_access { ++ struct hostcmd_header cmd_hdr; ++ __le32 address; ++ __le16 length; ++ __le16 reserved; ++ __le32 value[64]; ++} __packed; ++ ++/* HOSTCMD_CMD_802_11_TX_POWER */ ++struct hostcmd_cmd_802_11_tx_power { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ __le16 band; ++ __le16 ch; ++ __le16 bw; ++ __le16 sub_ch; ++ __le16 power_level_list[SYSADPT_TX_POWER_LEVEL_TOTAL]; ++} __packed; ++ ++struct hostcmd_cmd_802_11_tx_power_kf2 { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ __le16 band; ++ __le16 ch; ++ __le16 bw; ++ __le16 sub_ch; ++ __le16 power_level_list[SYSADPT_TX_GRP_PWR_LEVEL_TOTAL]; ++} __packed; ++ ++/* HOSTCMD_CMD_802_11_RF_ANTENNA */ ++struct hostcmd_cmd_802_11_rf_antenna { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ __le16 antenna_mode; /* Number of antennas or 0xffff(diversity) */ ++} __packed; ++ ++/* HOSTCMD_CMD_BROADCAST_SSID_ENABLE */ ++struct hostcmd_cmd_broadcast_ssid_enable { ++ struct hostcmd_header cmd_hdr; ++ __le32 enable; ++ __le32 hidden_ssid_info; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_CFG */ ++struct hostcmd_cmd_set_cfg { ++ struct hostcmd_header cmd_hdr; ++ /* Action */ ++ __le16 action; ++ /* Type */ ++ __le16 type; ++ /* Data length */ ++ __le16 data_len; ++ /* Data */ ++ u8 data[1]; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_RF_CHANNEL */ ++#define FREQ_BAND_MASK 0x0000003f ++#define CHNL_WIDTH_MASK 0x000007c0 ++#define CHNL_WIDTH_SHIFT 6 ++#define ACT_PRIMARY_MASK 0x00003800 ++#define ACT_PRIMARY_SHIFT 11 ++ ++struct hostcmd_cmd_set_rf_channel { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ u8 curr_chnl; ++ __le32 chnl_flags; ++} __packed; ++ ++struct hostcmd_cmd_set_rf_channel_kf2 { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ u8 curr_chnl; ++ __le32 chnl_flags; ++ u8 remain_on_chan; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_AID */ ++struct hostcmd_cmd_set_aid { ++ struct hostcmd_header cmd_hdr; ++ __le16 aid; ++ u8 mac_addr[ETH_ALEN]; /* AP's Mac Address(BSSID) */ ++ __le32 gprotect; ++ u8 ap_rates[SYSADPT_MAX_DATA_RATES_G]; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_INFRA_MODE */ ++struct hostcmd_cmd_set_infra_mode { ++ struct hostcmd_header cmd_hdr; ++} __packed; ++ ++/* HOSTCMD_CMD_802_11_RTS_THSD */ ++struct hostcmd_cmd_802_11_rts_thsd { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ __le16 threshold; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_EDCA_PARAMS */ ++struct hostcmd_cmd_set_edca_params { ++ struct hostcmd_header cmd_hdr; ++ /* 0 = get all, 0x1 =set CWMin/Max, 0x2 = set TXOP , 0x4 =set AIFSN */ ++ __le16 action; ++ __le16 txop; /* in unit of 32 us */ ++ __le32 cw_max; /* 0~15 */ ++ __le32 cw_min; /* 0~15 */ ++ u8 aifsn; ++ u8 txq_num; /* Tx Queue number. */ ++} __packed; ++ ++/* HOSTCMD_CMD_802_11H_DETECT_RADAR */ ++#define RADAR_TYPE_CODE_0 0 ++#define RADAR_TYPE_CODE_53 53 ++#define RADAR_TYPE_CODE_56 56 ++#define RADAR_TYPE_CODE_ETSI 151 ++ ++struct hostcmd_cmd_802_11h_detect_radar { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ __le16 radar_type_code; ++ __le16 min_chirp_cnt; ++ __le16 chirp_time_intvl; ++ __le16 pw_filter; ++ __le16 min_num_radar; ++ __le16 pri_min_num; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_WMM_MODE */ ++struct hostcmd_cmd_set_wmm_mode { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; /* 0->unset, 1->set */ ++} __packed; ++ ++/* HOSTCMD_CMD_HT_GUARD_INTERVAL */ ++struct hostcmd_cmd_ht_guard_interval { ++ struct hostcmd_header cmd_hdr; ++ __le32 action; ++ __le32 gi_type; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_FIXED_RATE */ ++struct fix_rate_flag { /* lower rate after the retry count */ ++ /* 0: legacy, 1: HT */ ++ __le32 fix_rate_type; ++ /* 0: retry count is not valid, 1: use retry count specified */ ++ __le32 retry_count_valid; ++} __packed; ++ ++struct fix_rate_entry { ++ struct fix_rate_flag fix_rate_type_flags; ++ /* depending on the flags above, this can be either a legacy ++ * rate(not index) or an MCS code. ++ */ ++ __le32 fixed_rate; ++ __le32 retry_count; ++} __packed; ++ ++struct hostcmd_cmd_set_fixed_rate { ++ struct hostcmd_header cmd_hdr; ++ /* HOSTCMD_ACT_NOT_USE_FIXED_RATE 0x0002 */ ++ __le32 action; ++ /* use fixed rate specified but firmware can drop to */ ++ __le32 allow_rate_drop; ++ __le32 entry_count; ++ struct fix_rate_entry fixed_rate_table[4]; ++ u8 multicast_rate; ++ u8 multi_rate_tx_type; ++ u8 management_rate; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_IES */ ++struct hostcmd_cmd_set_ies { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; /* 0->unset, 1->set */ ++ __le16 ie_list_len_ht; ++ __le16 ie_list_len_vht; ++ __le16 ie_list_len_proprietary; ++ /*Buffer size same as Generic_Beacon*/ ++ u8 ie_list_ht[148]; ++ u8 ie_list_vht[24]; ++ u8 ie_list_proprietary[112]; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_LINKADAPT_CS_MODE */ ++struct hostcmd_cmd_set_linkadapt_cs_mode { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ __le16 cs_mode; ++} __packed; ++ ++/* HOSTCMD_CMD_DUMP_OTP_DATA */ ++struct hostcmd_cmd_dump_otp_data { ++ struct hostcmd_header cmd_hdr; ++ u8 pload[0]; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_MAC_ADDR, HOSTCMD_CMD_DEL_MAC_ADDR */ ++struct hostcmd_cmd_set_mac_addr { ++ struct hostcmd_header cmd_hdr; ++ __le16 mac_type; ++ u8 mac_addr[ETH_ALEN]; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_RATE_ADAPT_MODE */ ++struct hostcmd_cmd_set_rate_adapt_mode { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ __le16 rate_adapt_mode; /* 0:Indoor, 1:Outdoor */ ++} __packed; ++ ++/* HOSTCMD_CMD_GET_WATCHDOG_BITMAP */ ++struct hostcmd_cmd_get_watchdog_bitmap { ++ struct hostcmd_header cmd_hdr; ++ u8 watchdog_bitmap; /* for SW/BA */ ++} __packed; ++ ++/* HOSTCMD_CMD_BSS_START */ ++struct hostcmd_cmd_bss_start { ++ struct hostcmd_header cmd_hdr; ++ __le32 enable; /* FALSE: Disable or TRUE: Enable */ ++ u8 amsdu; ++} __packed; ++ ++/* HOSTCMD_CMD_AP_BEACON */ ++struct cf_params { ++ u8 elem_id; ++ u8 len; ++ u8 cfp_cnt; ++ u8 cfp_period; ++ __le16 cfp_max_duration; ++ __le16 cfp_duration_remaining; ++} __packed; ++ ++struct ibss_params { ++ u8 elem_id; ++ u8 len; ++ __le16 atim_window; ++} __packed; ++ ++union ss_params { ++ struct cf_params cf_param_set; ++ struct ibss_params ibss_param_set; ++} __packed; ++ ++struct fh_params { ++ u8 elem_id; ++ u8 len; ++ __le16 dwell_time; ++ u8 hop_set; ++ u8 hop_pattern; ++ u8 hop_index; ++} __packed; ++ ++struct ds_params { ++ u8 elem_id; ++ u8 len; ++ u8 current_chnl; ++} __packed; ++ ++union phy_params { ++ struct fh_params fh_param_set; ++ struct ds_params ds_param_set; ++} __packed; ++ ++struct rsn_ie { ++ u8 elem_id; ++ u8 len; ++ u8 oui_type[4]; /* 00:50:f2:01 */ ++ u8 ver[2]; ++ u8 grp_key_cipher[4]; ++ u8 pws_key_cnt[2]; ++ u8 pws_key_cipher_list[4]; ++ u8 auth_key_cnt[2]; ++ u8 auth_key_list[4]; ++} __packed; ++ ++struct rsn48_ie { ++ u8 elem_id; ++ u8 len; ++ u8 ver[2]; ++ u8 grp_key_cipher[4]; ++ u8 pws_key_cnt[2]; ++ u8 pws_key_cipher_list[4]; ++ u8 auth_key_cnt[2]; ++ u8 auth_key_list[4]; ++ u8 rsn_cap[2]; ++ u8 pmk_id_cnt[2]; ++ u8 pmk_id_list[16]; /* Should modify to 16 * S */ ++ u8 reserved[8]; ++} __packed; ++ ++struct ac_param_rcd { ++ u8 aci_aifsn; ++ u8 ecw_min_max; ++ __le16 txop_lim; ++} __packed; ++ ++struct wmm_param_elem { ++ u8 elem_id; ++ u8 len; ++ u8 oui[3]; ++ u8 type; ++ u8 sub_type; ++ u8 version; ++ u8 qos_info; ++ u8 rsvd; ++ struct ac_param_rcd ac_be; ++ struct ac_param_rcd ac_bk; ++ struct ac_param_rcd ac_vi; ++ struct ac_param_rcd ac_vo; ++} __packed; ++ ++struct channel_info { ++ u8 first_channel_num; ++ u8 num_channels; ++ u8 max_tx_pwr_level; ++} __packed; ++ ++struct country { ++ u8 elem_id; ++ u8 len; ++ u8 country_str[3]; ++ struct channel_info channel_info[40]; ++} __packed; ++ ++struct start_cmd { ++ u8 sta_mac_addr[ETH_ALEN]; ++ u8 ssid[IEEE80211_MAX_SSID_LEN]; ++ u8 bss_type; ++ __le16 bcn_period; ++ u8 dtim_period; ++ union ss_params ss_param_set; ++ union phy_params phy_param_set; ++ __le16 probe_delay; ++ __le16 cap_info; ++ u8 b_rate_set[SYSADPT_MAX_DATA_RATES_G]; ++ u8 op_rate_set[SYSADPT_MAX_DATA_RATES_G]; ++ struct rsn_ie rsn_ie; ++ struct rsn48_ie rsn48_ie; ++ struct wmm_param_elem wmm_param; ++ struct country country; ++ __le32 ap_rf_type; /* 0->B, 1->G, 2->Mixed, 3->A, 4->11J */ ++ u8 rsvd[3]; ++ u8 bssid[ETH_ALEN]; /* only for 88W8997 */ ++} __packed; ++ ++struct hostcmd_cmd_ap_beacon { ++ struct hostcmd_header cmd_hdr; ++ struct start_cmd start_cmd; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_NEW_STN */ ++struct add_ht_info { ++ u8 control_chnl; ++ u8 add_chnl; ++ __le16 op_mode; ++ __le16 stbc; ++} __packed; ++ ++struct peer_info { ++ __le32 legacy_rate_bitmap; ++ u8 ht_rates[4]; ++ __le16 cap_info; ++ __le16 ht_cap_info; ++ u8 mac_ht_param_info; ++ u8 mrvl_sta; ++ struct add_ht_info add_ht_info; ++ __le32 tx_bf_capabilities; /* EXBF_SUPPORT */ ++ __le32 vht_max_rx_mcs; ++ __le32 vht_cap; ++ /* 0:20Mhz, 1:40Mhz, 2:80Mhz, 3:160 or 80+80Mhz */ ++ u8 vht_rx_channel_width; ++} __packed; ++ ++struct hostcmd_cmd_set_new_stn { ++ struct hostcmd_header cmd_hdr; ++ __le16 aid; ++ u8 mac_addr[ETH_ALEN]; ++ __le16 stn_id; ++ __le16 action; ++ __le16 if_type; ++ struct peer_info peer_info; ++ /* UAPSD_SUPPORT */ ++ u8 qos_info; ++ u8 is_qos_sta; ++ __le32 fw_sta_ptr; ++} __packed; ++ ++struct retry_cnt_qos { ++ u8 retry_cfg_enable; ++ u8 retry_cnt_BK; ++ u8 retry_cnt_BE; ++ u8 retry_cnt_VI; ++ u8 retry_cnt_VO; ++} __packed; ++ ++struct peer_info_sc4 { ++ __le32 legacy_rate_bitmap; ++ u8 ht_rates[4]; ++ __le16 cap_info; ++ __le16 ht_cap_info; ++ u8 mac_ht_param_info; ++ u8 mrvl_sta; ++ struct add_ht_info add_ht_info; ++ __le32 tx_bf_capabilities; /* EXBF_SUPPORT */ ++ __le32 vht_max_rx_mcs; ++ __le32 vht_cap; ++ /* 0:20Mhz, 1:40Mhz, 2:80Mhz, 3:160 or 80+80Mhz */ ++ u8 vht_rx_channel_width; ++ struct retry_cnt_qos retry_cnt_qos; ++ u8 assoc_rssi; ++} __packed; ++ ++struct hostcmd_cmd_set_new_stn_sc4 { ++ struct hostcmd_header cmd_hdr; ++ __le16 aid; ++ u8 mac_addr[ETH_ALEN]; ++ __le16 stn_id; ++ __le16 action; ++ __le16 reserved; ++ struct peer_info_sc4 peer_info; ++ /* UAPSD_SUPPORT */ ++ u8 qos_info; ++ u8 is_qos_sta; ++ __le32 fw_sta_ptr; ++ __le32 wds; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_APMODE */ ++struct hostcmd_cmd_set_apmode { ++ struct hostcmd_header cmd_hdr; ++ u8 apmode; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_SWITCH_CHANNEL */ ++struct hostcmd_cmd_set_switch_channel { ++ struct hostcmd_header cmd_hdr; ++ __le32 next_11h_chnl; ++ __le32 mode; ++ __le32 init_count; ++ __le32 chnl_flags; ++ __le32 next_ht_extchnl_offset; ++ __le32 dfs_test_mode; ++} __packed; ++ ++/* HOSTCMD_CMD_UPDATE_ENCRYPTION */ ++struct hostcmd_cmd_update_encryption { ++ struct hostcmd_header cmd_hdr; ++ /* Action type - see encr_action_type */ ++ __le32 action_type; /* encr_action_type */ ++ /* size of the data buffer attached. */ ++ __le32 data_length; ++ u8 mac_addr[ETH_ALEN]; ++ u8 action_data[1]; ++} __packed; ++ ++struct wep_type_key { ++ /* WEP key material (max 128bit) */ ++ u8 key_material[MAX_ENCR_KEY_LENGTH]; ++} __packed; ++ ++struct encr_tkip_seqcnt { ++ __le16 low; ++ __le32 high; ++} __packed; ++ ++struct tkip_type_key { ++ /* TKIP Key material. Key type (group or pairwise key) is ++ * determined by flags ++ */ ++ /* in KEY_PARAM_SET structure. */ ++ u8 key_material[MAX_ENCR_KEY_LENGTH]; ++ /* MIC keys */ ++ u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; ++ u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; ++ struct encr_tkip_seqcnt tkip_rsc; ++ struct encr_tkip_seqcnt tkip_tsc; ++} __packed; ++ ++struct aes_type_key { ++ /* AES Key material */ ++ u8 key_material[MAX_ENCR_KEY_LENGTH]; ++} __packed; ++ ++union mwl_key_type { ++ struct wep_type_key wep_key; ++ struct tkip_type_key tkip_key; ++ struct aes_type_key aes_key; ++} __packed; ++ ++struct key_param_set { ++ /* Total length of this structure (Key is variable size array) */ ++ __le16 length; ++ /* Key type - WEP, TKIP or AES-CCMP. */ ++ /* See definitions above */ ++ __le16 key_type_id; ++ /* key flags (ENCR_KEY_FLAG_XXX_ */ ++ __le32 key_info; ++ /* For WEP only - actual key index */ ++ __le32 key_index; ++ /* Size of the key */ ++ __le16 key_len; ++ /* Key material (variable size array) */ ++ union mwl_key_type key; ++ u8 mac_addr[ETH_ALEN]; ++} __packed; ++ ++struct hostcmd_cmd_set_key { ++ struct hostcmd_header cmd_hdr; ++ /* Action type - see encr_action_type */ ++ __le32 action_type; /* encr_action_type */ ++ /* size of the data buffer attached. */ ++ __le32 data_length; ++ /* data buffer - maps to one KEY_PARAM_SET structure */ ++ struct key_param_set key_param; ++} __packed; ++ ++/* HOSTCMD_CMD_BASTREAM */ ++#define BA_TYPE_MASK 0x00000001 ++#define BA_DIRECTION_MASK 0x0000000e ++#define BA_DIRECTION_SHIFT 1 ++ ++#define BA_TYPE_MASK_NDP 0x00000003 ++#define BA_DIRECTION_MASK_NDP 0x0000001c ++#define BA_DIRECTION_SHIFT_NDP 2 ++ ++struct ba_context { ++ __le32 context; ++} __packed; ++ ++/* parameters for block ack creation */ ++struct create_ba_params { ++ /* BA Creation flags - see above */ ++ __le32 flags; ++ /* idle threshold */ ++ __le32 idle_thrs; ++ /* block ack transmit threshold (after how many pkts should we ++ * send BAR?) ++ */ ++ __le32 bar_thrs; ++ /* receiver window size */ ++ __le32 window_size; ++ /* MAC Address of the BA partner */ ++ u8 peer_mac_addr[ETH_ALEN]; ++ /* Dialog Token */ ++ u8 dialog_token; ++ /* TID for the traffic stream in this BA */ ++ u8 tid; ++ /* shared memory queue ID (not sure if this is required) */ ++ u8 queue_id; ++ u8 param_info; ++ /* returned by firmware - firmware context pointer. */ ++ /* this context pointer will be passed to firmware for all ++ * future commands. ++ */ ++ struct ba_context fw_ba_context; ++ u8 reset_seq_no; /** 0 or 1**/ ++ __le16 current_seq; ++ __le32 vht_rx_factor; ++ /* This is for virtual station in Sta proxy mode for V6FW */ ++ u8 sta_src_mac_addr[ETH_ALEN]; ++} __packed; ++ ++/* new transmit sequence number information */ ++struct ba_update_seq_num { ++ /* BA flags - see above */ ++ __le32 flags; ++ /* returned by firmware in the create ba stream response */ ++ struct ba_context fw_ba_context; ++ /* new sequence number for this block ack stream */ ++ __le16 ba_seq_num; ++} __packed; ++ ++struct ba_stream_context { ++ /* BA Stream flags */ ++ __le32 flags; ++ /* returned by firmware in the create ba stream response */ ++ struct ba_context fw_ba_context; ++ u8 tid; ++ u8 peer_mac_addr[ETH_ALEN]; ++} __packed; ++ ++union ba_info { ++ /* information required to create BA Stream... */ ++ struct create_ba_params create_params; ++ /* update starting/new sequence number etc. */ ++ struct ba_update_seq_num updt_seq_num; ++ /* destroy an existing stream... */ ++ struct ba_stream_context destroy_params; ++ /* destroy an existing stream... */ ++ struct ba_stream_context flush_params; ++} __packed; ++ ++struct hostcmd_cmd_bastream { ++ struct hostcmd_header cmd_hdr; ++ __le32 action_type; ++ union ba_info ba_info; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_SPECTRUM_MGMT */ ++struct hostcmd_cmd_set_spectrum_mgmt { ++ struct hostcmd_header cmd_hdr; ++ __le32 spectrum_mgmt; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_POWER_CONSTRAINT */ ++struct hostcmd_cmd_set_power_constraint { ++ struct hostcmd_header cmd_hdr; ++ __le32 power_constraint; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_COUNTRY_CODE */ ++struct domain_chnl_entry { ++ u8 first_chnl_num; ++ u8 chnl_num; ++ u8 max_transmit_pw; ++} __packed; ++ ++struct domain_country_info { ++ u8 country_string[3]; ++ u8 g_chnl_len; ++ struct domain_chnl_entry domain_entry_g[1]; ++ u8 a_chnl_len; ++ struct domain_chnl_entry domain_entry_a[20]; ++} __packed; ++ ++struct hostcmd_cmd_set_country_code { ++ struct hostcmd_header cmd_hdr; ++ __le32 action ; /* 0 -> unset, 1 ->set */ ++ struct domain_country_info domain_info; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL */ ++struct hostcmd_cmd_set_optimization_level { ++ struct hostcmd_header cmd_hdr; ++ u8 opt_level; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_WSC_IE */ ++struct hostcmd_cmd_set_wsc_ie { ++ struct hostcmd_header cmd_hdr; ++ __le16 ie_type; /* 0 -- beacon. or 1 -- probe response. */ ++ __le16 len; ++ u8 data[WSC_IE_MAX_LENGTH]; ++} __packed; ++ ++/* HOSTCMD_CMD_GET_RATETABLE */ ++struct hostcmd_cmd_get_ratetable { ++ struct hostcmd_header cmd_hdr; ++ u8 addr[ETH_ALEN]; ++ u8 type; /* 0: SU, 1: MU */ ++ /* multiply 2 because 2 DWORD in rate info */ ++ __le32 sorted_rates_idx_map[2 * SYSADPT_MAX_RATE_ADAPT_RATES]; ++} __packed; ++ ++/* HOSTCMD_CMD_GET_SEQNO */ ++struct hostcmd_cmd_get_seqno { ++ struct hostcmd_header cmd_hdr; ++ u8 mac_addr[ETH_ALEN]; ++ u8 tid; ++ __le16 seq_no; ++ u8 reserved; ++} __packed; ++ ++/* HOSTCMD_CMD_DWDS_ENABLE */ ++struct hostcmd_cmd_dwds_enable { ++ struct hostcmd_header cmd_hdr; ++ __le32 enable; /* 0 -- Disable. or 1 -- Enable. */ ++} __packed; ++ ++/* HOSTCMD_CMD_FW_FLUSH_TIMER */ ++struct hostcmd_cmd_fw_flush_timer { ++ struct hostcmd_header cmd_hdr; ++ /* 0 -- Disable. > 0 -- holds time value in usecs. */ ++ __le32 value; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_CDD */ ++struct hostcmd_cmd_set_cdd { ++ struct hostcmd_header cmd_hdr; ++ __le32 enable; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_BFTYPE */ ++struct hostcmd_cmd_set_bftype { ++ struct hostcmd_header cmd_hdr; ++ __le32 action; ++ __le32 mode; ++} __packed; ++ ++/* HOSTCMD_CMD_GET_TEMP */ ++struct hostcmd_cmd_get_temp { ++ struct hostcmd_header cmd_hdr; ++ __le32 celcius; ++ __le32 raw_data; ++} __packed; ++ ++/* HOSTCMD_CMD_LED_CTRL */ ++struct hostcmd_cmd_led_ctrl { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; /* 0: GET, 1: SET (only SET is supported) */ ++ u8 led_enable; /* 0: Disable, 1: Enable */ ++ u8 led_control; /* 0: HW 1: SW (only SW is supported) */ ++ u8 led_blink_rate; /* 1: Slow, 2: Medium, 3: Fast blink */ ++ u8 reserved; ++} __packed; ++ ++/* HOSTCMD_CMD_GET_FW_REGION_CODE */ ++struct hostcmd_cmd_get_fw_region_code { ++ struct hostcmd_header cmd_hdr; ++ __le32 status; /* 0 = Found, 1 = Error */ ++ __le32 fw_region_code; ++} __packed; ++ ++/* HOSTCMD_CMD_GET_DEVICE_PWR_TBL */ ++#define HAL_TRPC_ID_MAX 16 ++ ++struct channel_power_tbl { ++ u8 channel; ++ u8 tx_pwr[HAL_TRPC_ID_MAX]; ++ u8 dfs_capable; ++ u8 ax_ant; ++ u8 cdd; ++} __packed; ++ ++struct hostcmd_cmd_get_device_pwr_tbl { ++ struct hostcmd_header cmd_hdr; ++ __le16 status; /* 0 = Found, 1 = Error */ ++ u8 region_code; ++ u8 number_of_channels; ++ __le32 current_channel_index; ++ /* Only for 1 channel, so, 1 channel at a time */ ++ struct channel_power_tbl channel_pwr_tbl; ++} __packed; ++ ++/* HOSTCMD_CMD_SET_RATE_DROP */ ++struct hostcmd_cmd_set_rate_drop { ++ struct hostcmd_header cmd_hdr; ++ __le32 enable; ++ __le32 rate_index; ++ __le32 sta_index; ++} __packed; ++ ++/* HOSTCMD_CMD_NEWDP_DMATHREAD_START */ ++struct hostcmd_cmd_newdp_dmathread_start { ++ struct hostcmd_header cmd_hdr; ++} __packed; ++ ++/* HOSTCMD_CMD_GET_FW_REGION_CODE_SC4 */ ++struct hostcmd_cmd_get_fw_region_code_sc4 { ++ struct hostcmd_header cmd_hdr; ++ __le32 status; /* 0 = Found, 1 = Error */ ++ __le32 fw_region_code; ++} __packed; ++ ++/* HOSTCMD_CMD_GET_DEVICE_PWR_TBL_SC4 */ ++#define HAL_TRPC_ID_MAX_SC4 32 ++#define MAX_GROUP_PER_CHANNEL_5G 39 ++#define MAX_GROUP_PER_CHANNEL_2G 21 ++#define MAX(a, b) (((a) > (b)) ? (a) : (b)) ++#define MAX_GROUP_PER_CHANNEL_RATE \ ++ MAX(MAX_GROUP_PER_CHANNEL_5G, MAX_GROUP_PER_CHANNEL_2G) ++ ++struct channel_power_tbl_sc4 { ++ u8 channel; ++ u8 grp_pwr[MAX_GROUP_PER_CHANNEL_RATE]; ++ u8 tx_pwr[HAL_TRPC_ID_MAX_SC4]; ++ u8 dfs_capable; ++ u8 ax_ant; ++ u8 cdd; ++ u8 rsvd; ++} __packed; ++ ++struct hostcmd_cmd_get_device_pwr_tbl_sc4 { ++ struct hostcmd_header cmd_hdr; ++ __le16 status; /* 0 = Found, 1 = Error */ ++ u8 region_code; ++ u8 number_of_channels; ++ __le32 current_channel_index; ++ /* Only for 1 channel, so, 1 channel at a time */ ++ struct channel_power_tbl_sc4 channel_pwr_tbl; ++} __packed; ++ ++/* HOSTCMD_CMD_QUIET_MODE */ ++struct hostcmd_cmd_quiet_mode { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ __le32 enable; ++ __le32 period; ++ __le32 duration; ++ __le32 next_offset; ++} __packed; ++ ++/* HOSTCMD_CMD_CORE_DUMP_DIAG_MODE */ ++struct hostcmd_cmd_core_dump_diag_mode { ++ struct hostcmd_header cmd_hdr; ++ __le16 status; ++} __packed; ++ ++/* HOSTCMD_CMD_GET_FW_CORE_DUMP */ ++#define MAX_CORE_REGIONS 20 ++#define MAX_CORE_SYMBOLS 30 ++#define MVL_COREDUMP_DIAG_MODE 0x00000001 ++#define MVL_COREDUMP_INCL_EXT 0x00000002 ++#define MAX_CORE_DUMP_BUFFER 2048 ++ ++struct core_region { ++ __le32 address; ++ __le32 length; ++} __packed; ++ ++struct core_symbol { ++ u8 name[16]; ++ __le32 address; ++ __le32 length; ++ __le16 entries; ++} __packed; ++ ++struct coredump { ++ u8 version_major; ++ u8 version_minor; ++ u8 version_patch; ++ u8 hdr_version; ++ u8 num_regions; ++ u8 num_symbols; ++ u8 fill[2]; ++ struct core_region region[MAX_CORE_REGIONS]; ++ struct core_symbol symbol[MAX_CORE_SYMBOLS]; ++ __le32 fill_end[40]; ++} __packed; ++ ++struct coredump_cmd { ++ __le32 context; ++ __le32 buffer; ++ __le32 buffer_len; ++ __le16 size_kb; ++ __le16 flags; ++} __packed; ++ ++struct debug_mem_cmd { ++ __le32 set; ++ __le32 type; ++ __le32 addr; ++ __le32 val; ++} __packed; ++ ++struct hostcmd_cmd_get_fw_core_dump { ++ struct hostcmd_header cmd_hdr; ++ union { ++ struct coredump_cmd coredump; ++ struct debug_mem_cmd debug_mem; ++ } cmd_data; ++ /*Buffer where F/W Copies the Core Dump*/ ++ char buffer[MAX_CORE_DUMP_BUFFER]; ++} __packed; ++ ++struct hostcmd_cmd_get_fw_core_dump_ { ++ struct hostcmd_header cmd_hdr; ++ union { ++ struct coredump_cmd coredump; ++ struct debug_mem_cmd debug_mem; ++ } cmd_data; ++} __packed; ++ ++/* HOSTCMD_CMD_802_11_SLOT_TIME */ ++struct hostcmd_cmd_802_11_slot_time { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ /* 0:long slot; 1:short slot */ ++ __le16 short_slot; ++} __packed; ++ ++/* HOSTCMD_CMD_EDMAC_CTRL */ ++struct hostcmd_cmd_edmac_ctrl { ++ struct hostcmd_header cmd_hdr; ++ __le16 action; ++ __le16 ed_ctrl_2g; ++ __le16 ed_offset_2g; ++ __le16 ed_ctrl_5g; ++ __le16 ed_offset_5g; ++ __le16 ed_bitmap_txq_lock; ++} __packed; ++ ++/* HOSTCMD_CMD_TXPWRLMT_CFG */ ++#define TXPWRLMT_CFG_VERSION_INFO_LEN 0x4 ++#define TXPWRLMT_CFG_MAX_SUBBAND_INFO 0x5 ++#define TXPWRLMT_CFG_SIG_LEN 0x4 ++#define TXPWRLMT_CFG_SIGNATURE 0xA1240E01 ++ ++struct hostcmd_cmd_txpwrlmt_cfg { ++ struct hostcmd_header cmd_hdr; ++ /* Action */ ++ __le16 action; ++ /*Sub band id*/ ++ u8 subband_id; ++ /* Cfg Complete Info*/ ++ u8 cfgComplete; ++ /* Data length */ ++ __le16 data_len; ++ /*number of entries*/ ++ __le16 num_entries; ++ /* Data */ ++ u8 data[1]; ++} __packed; ++ ++struct mwl_txpwrlmt_cfg_entry_hdr { ++ /* subband id */ ++ __le16 id; ++ /* length */ ++ __le16 len; ++ /* number of entries */ ++ __le16 num_entries; ++} __packed; ++ ++/* HOSTCMD_CMD_MCAST_CTS */ ++struct hostcmd_cmd_mcast_cts { ++ struct hostcmd_header cmd_hdr; ++ u8 enable; /* 1:enable, 0:disable */ ++} __packed; ++ ++#endif /* _HOSTCMD_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/dev.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/dev.h +new file mode 100644 +index 000000000000..2c26bff80683 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/dev.h +@@ -0,0 +1,1032 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines device related information. */ ++ ++#ifndef _DEV_H_ ++#define _DEV_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PCIE_DRV_NAME KBUILD_MODNAME ++#define PCIE_DRV_VERSION "10.3.8.0-20181210" ++ ++#define PCIE_MIN_BYTES_HEADROOM 64 ++#define PCIE_MIN_TX_HEADROOM_KF2 96 ++#define PCIE_NUM_OF_DESC_DATA SYSADPT_TOTAL_TX_QUEUES ++#define PCIE_AMPDU_QUEUES 4 ++#define PCIE_MAX_NUM_TX_DESC 256 ++#define PCIE_TX_QUEUE_LIMIT (3 * PCIE_MAX_NUM_TX_DESC) ++#define PCIE_TX_WAKE_Q_THRESHOLD (2 * PCIE_MAX_NUM_TX_DESC) ++#define PCIE_DELAY_FREE_Q_LIMIT PCIE_MAX_NUM_TX_DESC ++#define PCIE_MAX_NUM_RX_DESC 256 ++#define PCIE_RECEIVE_LIMIT 64 ++ ++enum { ++ IEEE_TYPE_MANAGEMENT = 0, ++ IEEE_TYPE_CONTROL, ++ IEEE_TYPE_DATA ++}; ++ ++#define MAC_REG_ADDR(offset) (offset) ++#define MAC_REG_ADDR_PCI(offset) ((pcie_priv->iobase1 + 0xA000) + offset) ++ ++#define MCU_CCA_CNT MAC_REG_ADDR(0x06A0) ++#define MCU_TXPE_CNT MAC_REG_ADDR(0x06A4) ++#define MCU_LAST_READ MAC_REG_ADDR(0x06A8) ++ ++/* Map to 0x80000000 (Bus control) on BAR0 */ ++#define MACREG_REG_H2A_INTERRUPT_EVENTS 0x00000C18 /* (From host to ARM) */ ++#define MACREG_REG_H2A_INTERRUPT_CAUSE 0x00000C1C /* (From host to ARM) */ ++#define MACREG_REG_H2A_INTERRUPT_MASK 0x00000C20 /* (From host to ARM) */ ++#define MACREG_REG_H2A_INTERRUPT_CLEAR_SEL 0x00000C24 /* (From host to ARM) */ ++#define MACREG_REG_H2A_INTERRUPT_STATUS_MASK 0x00000C28 /* (From host to ARM) */ ++ ++#define MACREG_REG_A2H_INTERRUPT_EVENTS 0x00000C2C /* (From ARM to host) */ ++#define MACREG_REG_A2H_INTERRUPT_CAUSE 0x00000C30 /* (From ARM to host) */ ++#define MACREG_REG_A2H_INTERRUPT_MASK 0x00000C34 /* (From ARM to host) */ ++#define MACREG_REG_A2H_INTERRUPT_CLEAR_SEL 0x00000C38 /* (From ARM to host) */ ++#define MACREG_REG_A2H_INTERRUPT_STATUS_MASK 0x00000C3C /* (From ARM to host) */ ++ ++/* Map to 0x80000000 on BAR1 */ ++#define MACREG_REG_GEN_PTR 0x00000C10 ++#define MACREG_REG_INT_CODE 0x00000C14 ++ ++/* Bit definition for MACREG_REG_A2H_INTERRUPT_CAUSE (A2HRIC) */ ++#define MACREG_A2HRIC_BIT_TX_DONE BIT(0) ++#define MACREG_A2HRIC_BIT_RX_RDY BIT(1) ++#define MACREG_A2HRIC_BIT_OPC_DONE BIT(2) ++#define MACREG_A2HRIC_BIT_MAC_EVENT BIT(3) ++#define MACREG_A2HRIC_BIT_RX_PROBLEM BIT(4) ++#define MACREG_A2HRIC_BIT_RADIO_OFF BIT(5) ++#define MACREG_A2HRIC_BIT_RADIO_ON BIT(6) ++#define MACREG_A2HRIC_BIT_RADAR_DETECT BIT(7) ++#define MACREG_A2HRIC_BIT_ICV_ERROR BIT(8) ++#define MACREG_A2HRIC_BIT_WEAKIV_ERROR BIT(9) ++#define MACREG_A2HRIC_BIT_QUE_EMPTY BIT(10) ++#define MACREG_A2HRIC_BIT_QUE_FULL BIT(11) ++#define MACREG_A2HRIC_BIT_CHAN_SWITCH BIT(12) ++#define MACREG_A2HRIC_BIT_TX_WATCHDOG BIT(13) ++#define MACREG_A2HRIC_BA_WATCHDOG BIT(14) ++/* 15 taken by ISR_TXACK */ ++#define MACREG_A2HRIC_BIT_SSU_DONE BIT(16) ++#define MACREG_A2HRIC_CONSEC_TXFAIL BIT(17) ++ ++#define ISR_SRC_BITS (MACREG_A2HRIC_BIT_RX_RDY | \ ++ MACREG_A2HRIC_BIT_TX_DONE | \ ++ MACREG_A2HRIC_BIT_OPC_DONE | \ ++ MACREG_A2HRIC_BIT_MAC_EVENT | \ ++ MACREG_A2HRIC_BIT_WEAKIV_ERROR | \ ++ MACREG_A2HRIC_BIT_ICV_ERROR | \ ++ MACREG_A2HRIC_BIT_SSU_DONE | \ ++ MACREG_A2HRIC_BIT_RADAR_DETECT | \ ++ MACREG_A2HRIC_BIT_CHAN_SWITCH | \ ++ MACREG_A2HRIC_BIT_TX_WATCHDOG | \ ++ MACREG_A2HRIC_BIT_QUE_EMPTY | \ ++ MACREG_A2HRIC_BA_WATCHDOG | \ ++ MACREG_A2HRIC_CONSEC_TXFAIL) ++ ++#define MACREG_A2HRIC_BIT_MASK ISR_SRC_BITS ++ ++/* Bit definition for MACREG_REG_H2A_INTERRUPT_CAUSE (H2ARIC) */ ++#define MACREG_H2ARIC_BIT_PPA_READY BIT(0) ++#define MACREG_H2ARIC_BIT_DOOR_BELL BIT(1) ++#define MACREG_H2ARIC_BIT_PS BIT(2) ++#define MACREG_H2ARIC_BIT_PSPOLL BIT(3) ++#define ISR_RESET BIT(15) ++#define ISR_RESET_AP33 BIT(26) ++ ++/* Data descriptor related constants */ ++#define EAGLE_RXD_CTRL_DRIVER_OWN 0x00 ++#define EAGLE_RXD_CTRL_DMA_OWN 0x80 ++ ++#define EAGLE_RXD_STATUS_OK 0x01 ++ ++#define EAGLE_TXD_STATUS_IDLE 0x00000000 ++#define EAGLE_TXD_STATUS_OK 0x00000001 ++#define EAGLE_TXD_STATUS_FW_OWNED 0x80000000 ++ ++struct pcie_tx_desc { ++ u8 data_rate; ++ u8 tx_priority; ++ __le16 qos_ctrl; ++ __le32 pkt_ptr; ++ __le16 pkt_len; ++ u8 dest_addr[ETH_ALEN]; ++ __le32 pphys_next; ++ __le32 sap_pkt_info; ++ __le32 rate_info; ++ u8 type; ++ u8 xmit_control; /* bit 0: use rateinfo, bit 1: disable ampdu */ ++ __le16 reserved; ++ __le32 tcpack_sn; ++ __le32 tcpack_src_dst; ++ __le32 reserved1; ++ __le32 reserved2; ++ u8 reserved3[2]; ++ u8 packet_info; ++ u8 packet_id; ++ __le16 packet_len_and_retry; ++ __le16 packet_rate_info; ++ __le32 flags; ++ __le32 status; ++} __packed; ++ ++struct pcie_tx_hndl { ++ struct sk_buff *psk_buff; ++ struct pcie_tx_desc *pdesc; ++ struct pcie_tx_hndl *pnext; ++}; ++ ++/* Receive rate information constants */ ++#define RX_RATE_INFO_FORMAT_11A 0 ++#define RX_RATE_INFO_FORMAT_11B 1 ++#define RX_RATE_INFO_FORMAT_11N 2 ++#define RX_RATE_INFO_FORMAT_11AC 4 ++ ++#define RX_RATE_INFO_HT20 0 ++#define RX_RATE_INFO_HT40 1 ++#define RX_RATE_INFO_HT80 2 ++#define RX_RATE_INFO_HT160 3 ++ ++#define RX_RATE_INFO_LONG_INTERVAL 0 ++#define RX_RATE_INFO_SHORT_INTERVAL 1 ++ ++#define MWL_RX_RATE_FORMAT_MASK 0x0007 ++#define MWL_RX_RATE_NSS_MASK 0x0018 ++#define MWL_RX_RATE_NSS_SHIFT 3 ++#define MWL_RX_RATE_BW_MASK 0x0060 ++#define MWL_RX_RATE_BW_SHIFT 5 ++#define MWL_RX_RATE_GI_MASK 0x0080 ++#define MWL_RX_RATE_GI_SHIFT 7 ++#define MWL_RX_RATE_RT_MASK 0xFF00 ++#define MWL_RX_RATE_RT_SHIFT 8 ++ ++struct pcie_rx_desc { ++ __le16 pkt_len; /* total length of received data */ ++ __le16 rate; /* receive rate information */ ++ __le32 pphys_buff_data; /* physical address of payload data */ ++ __le32 pphys_next; /* physical address of next RX desc */ ++ __le16 qos_ctrl; /* received QosCtrl field variable */ ++ __le16 ht_sig2; /* like name states */ ++ __le32 hw_rssi_info; ++ __le32 hw_noise_floor_info; ++ u8 noise_floor; ++ u8 reserved[3]; ++ u8 rssi; /* received signal strengt indication */ ++ u8 status; /* status field containing USED bit */ ++ u8 channel; /* channel this pkt was received on */ ++ u8 rx_control; /* the control element of the desc */ ++ __le32 reserved1[3]; ++} __packed; ++ ++struct pcie_rx_hndl { ++ struct sk_buff *psk_buff; /* associated sk_buff for Linux */ ++ struct pcie_rx_desc *pdesc; ++ struct pcie_rx_hndl *pnext; ++}; ++ ++struct pcie_desc_data { ++ dma_addr_t pphys_tx_ring; /* ptr to first TX desc (phys.) */ ++ struct pcie_tx_desc *ptx_ring; /* ptr to first TX desc (virt.) */ ++ struct pcie_tx_hndl *tx_hndl; ++ struct pcie_tx_hndl *pnext_tx_hndl;/* next TX handle that can be used */ ++ struct pcie_tx_hndl *pstale_tx_hndl;/* the staled TX handle */ ++ dma_addr_t pphys_rx_ring; /* ptr to first RX desc (phys.) */ ++ struct pcie_rx_desc *prx_ring; /* ptr to first RX desc (virt.) */ ++ struct pcie_rx_hndl *rx_hndl; ++ struct pcie_rx_hndl *pnext_rx_hndl;/* next RX handle that can be used */ ++ u32 wcb_base; /* FW base offset for registers */ ++ u32 rx_desc_write; /* FW descriptor write position */ ++ u32 rx_desc_read; /* FW descriptor read position */ ++ u32 rx_buf_size; /* length of the RX buffers */ ++}; ++ ++/* DMA header used by firmware and hardware. */ ++struct pcie_dma_data { ++ __le16 fwlen; ++ struct ieee80211_hdr wh; ++ char data[0]; ++} __packed; ++ ++/* New Data Path */ ++#define MACREG_REG_SCRATCH3 0x00000C44 ++#define MACREG_REG_TXSENDHEAD 0x00000CD0 ++#define MACREG_REG_TXSEDNTAIL 0x00000CD4 ++#define MACREG_REG_TXDONEHEAD 0x00000CD8 ++#define MACREG_REG_TXDONETAIL 0x00000CDC ++#define MACREG_REG_RXDESCHEAD 0x00000CE0 ++#define MACREG_REG_RXDESCTAIL 0x00000CE4 ++#define MACREG_REG_RXDONEHEAD 0x00000CE8 ++#define MACREG_REG_RXDONETAIL 0x00000CEC ++#define MACREG_REG_ACNTHEAD 0x00000CF0 ++#define MACREG_REG_ACNTTAIL 0x00000CF4 ++ ++/* Buff removed from Tx Send Ring */ ++#define MACREG_A2HRIC_TX_DESC_TAIL_RDY (1<<9) ++/* Buff added to Tx Done Ring */ ++#define MACREG_A2HRIC_TX_DONE_HEAD_RDY (1<<10) ++/* Records added to Accounting Ring */ ++#define MACREG_A2HRIC_ACNT_HEAD_RDY (1<<12) ++/* Buff removed from Rx Desc Ring */ ++#define MACREG_A2HRIC_RX_DESC_TAIL_RDY (1<<17) ++/* Buff added to Rx Done Ring */ ++#define MACREG_A2HRIC_RX_DONE_HEAD_RDY (1<<18) ++#define MACREG_A2HRIC_NEWDP_DFS (1<<19) ++#define MACREG_A2HRIC_NEWDP_CHANNEL_SWITCH (1<<20) ++ ++#define ISR_SRC_BITS_NDP ((MACREG_A2HRIC_ACNT_HEAD_RDY) | \ ++ (MACREG_A2HRIC_RX_DONE_HEAD_RDY) | \ ++ (MACREG_A2HRIC_NEWDP_DFS) | \ ++ (MACREG_A2HRIC_NEWDP_CHANNEL_SWITCH)) ++ ++#define MACREG_A2HRIC_BIT_MASK_NDP ISR_SRC_BITS_NDP ++ ++#define MIN_BYTES_RX_HEADROOM (64 + 2) ++#define AMPDU_QUEUES_NDP (SYSADPT_MAX_STA_SC4 * \ ++ SYSADPT_MAX_TID) ++#define MAX_NUM_TX_DESC 1024 ++#define MAX_NUM_RX_DESC (1024 * 16) ++#define MAX_TX_RING_SEND_SIZE (4 * MAX_NUM_TX_DESC) ++#define MAX_TX_RING_DONE_SIZE MAX_NUM_TX_DESC ++#define MAX_RX_RING_SEND_SIZE MAX_NUM_RX_DESC ++#define MAX_RX_RING_DONE_SIZE MAX_NUM_RX_DESC ++#define DEFAULT_ACNT_RING_SIZE 0x10000 ++#define MAX_AGGR_SIZE 1900 ++#define TX_QUEUE_LIMIT MAX_NUM_TX_DESC ++#define TX_WAKE_Q_THRESHOLD (MAX_NUM_TX_DESC - 256) ++ ++/* RateCode usage notes: ++ * * General ++ * * No error checking is provided on RateCodes, so usage of invalid values ++ * or rates not supported by HW can result in undefined operation. ++ * * Some values are not allowed by Std, but are included to sanitize the ++ * table; ++ * * MaxPwr should only be used for rates that can be sent using Max Power, ++ * such as for TxEVM limits or regulatory. It is only valid for Host ++ * Generated frames, and not for DRA, etc. ++ * * VHT ++ * * Need to reconsile MU. ++ * * HT ++ * * MCS and SS are made to mimic 11ac, so MCS=mcs[2:0] and SS=mcs[4:3]; ++ * * MCS32 is selected by providing MCS=10; ++ * * Legacy ++ * * MCS0..7 = 6/9/12/18/24/36/48/54; ++ * * MCS8..15 = 1S/1L/2S/2L/5.5S/5.5L/11S/11L; ++ * * BW is used to request legacy duplicate modes; ++ */ ++#define RATECODE_DEFAULT 0xFFFF /* Don't override the Rate */ ++#define RATECODE_TYPE_MASK 0xC000 /* Mask to extract Type */ ++#define RATECODE_TYPE_SHIFT 14 /* Shift to extract Type */ ++#define RATECODE_TYPE_VHT 0x8000 /* Use VHT rates */ ++#define RATECODE_TYPE_HT 0x4000 /* Use HT rates */ ++#define RATECODE_TYPE_LEGACY 0x0000 /* Use Legacy (a/b/g) rates */ ++#define RATECODE_MAXPWR 0x2000 /* Send at Max Power / Off Channel */ ++#define RATECODE_RSVD 0x1000 /* Unused */ ++#define RATECODE_STBC 0x0800 /* Use Space Time Block Codes */ ++#define RATECODE_BFMR 0x0400 /* Use Beamforming */ ++#define RATECODE_SS_MASK 0x0300 /* Mask to extract nSS-1 */ ++#define RATECODE_SS_SHIFT 8 /* Shift to extract nSS-1 */ ++#define RATECODE_MCS_MASK 0x00F0 /* Mask to extract MCS rate */ ++#define RATECODE_MCS_SHIFT 4 /* Shift to extract MCS rate */ ++#define RATECODE_BW_MASK 0x000C /* Mask to extract Channel BW */ ++#define RATECODE_BW_SHIFT 2 /* Shift to extract Channel BW */ ++#define RATECODE_BW_160MHZ 0x000C /* Send 160M wide packet (or 80+80) */ ++#define RATECODE_BW_80MHZ 0x0008 /* Send 80M wide packet */ ++#define RATECODE_BW_40MHZ 0x0004 /* Send 40M wide packet */ ++#define RATECODE_BW_20MHZ 0x0000 /* Send 20M wide packet */ ++#define RATECODE_LDPC 0x0002 /* Use Low Density Parity Codes */ ++#define RATECODE_SGI 0x0001 /* Use Short Guard Interval */ ++ ++#define TXRING_CTRL_LEN_SHIFT 0 /* PCIe Payload size (Starts w/ SNAP) */ ++#define TXRING_CTRL_LEN_MASK 0x3FFF /* PCIe Payload size (Starts w/ SNAP) */ ++#define TXRING_CTRL_QID_SHIFT 14 /* Queue ID (STA*UP, Mcast, MC2UC, etc)*/ ++#define TXRING_CTRL_QID_MASK 0xFFF /* Queue ID (STA*UP, Mcast, MC2UC, etc)*/ ++#define TXRING_CTRL_TAG_SHIFT 26 /* Tags for special Processing */ ++#define TXRING_CTRL_TAG_MASK 0x3F /* Tags for special Processing */ ++#define TXRING_CTRL_TAG_MGMT 0x01 /* Has Host generated dot11 Header */ ++#define TXRING_CTRL_TAG_EAP 0x02 /* Tag for EAPOL frames */ ++#define TXRING_CTRL_TAG_TCP_ACK 0x4 ++#define TXRING_CTRL_TAG_RSVD 0x3C /* Unused */ ++ ++struct tx_info { /* Tx INFO used by MAC HW */ ++ __le32 reserved0[10]; ++ __le32 rate_info; ++ __le32 reserved1[14]; ++} __packed; ++ ++struct pcie_tx_desc_ndp { ++ union { /* Union for Tx DA/SA or Mgmt Overrides */ ++ struct { /* Fields for Data frames */ ++ u8 da[ETH_ALEN]; /* L2 Destination Address */ ++ u8 sa[ETH_ALEN]; /* L2 Source Address */ ++ }; ++ struct { /* Fields when marked as Mgmt */ ++ __le16 rate_code; /* Rate Code: Table + Index */ ++ u8 max_retry; ++ u8 pad[5]; /* Unused */ ++ __le32 call_back; /* Used for Packet returned to FW */ ++ }; ++ } u; ++ __le32 ctrl; /* Bit fields (TXRING_CTRL_*) */ ++ __le32 data; /* PCIe Payload Pointer (Starts w/ SNAP) */ ++ __le32 user; /* Value returned to Host when done */ ++ __le32 tcp_dst_src; ++ __le32 tcp_sn; ++} __packed; ++ ++struct tx_ring_done { ++ __le32 user; ++} __packed; ++ ++#define RXRING_CTRL_CASE_SHIFT 0 /* What is in the buffer(RXRING_CASE_*) */ ++#define RXRING_CTRL_CASE_MASK 0x1F /* What is in the buffer(RXRING_CASE_*) */ ++#define RXRING_CTRL_STA_SHIFT 5 /* STA information (or Mcast group) */ ++#define RXRING_CTRL_STA_MASK 0x1FF /* STA information (or Mcast group) */ ++#define RXRING_CTRL_STA_UNKNOWN 0x1FF /* STA Idx for packets from Unknown STA */ ++#define RXRING_CTRL_STA_FROMDS 0x1FE /* STA Idx for packets from DS */ ++#define RXRING_CTRL_TID_SHIFT 14 /* TID/UP for QoS Data frames */ ++#define RXRING_CTRL_TID_MASK 0xF /* TID/UP for QoS Data frames */ ++#define RXRING_CTRL_KEY_SHIFT 18 /* Key Type used (KEY_TYPE_*) */ ++#define RXRING_CTRL_KEY_MASK 0xF /* Key Type used (KEY_TYPE_*) */ ++#define RXRING_CTRL_TRUNC (1UL<<31) /* Packet Truncated */ ++ ++/* Rx Buffer Formats ++ * Each Case listed above will indicate the format used, and each format will ++ * carry their length in the packet buffer. Should the packet be too big for ++ * the buffer, it will be truncated, but the full length will still be ++ * indicated. Currently only a single, fixed size Rx Pool is envisioned. ++ * ++ * Fmt0 is used for Slow path, when some processing of dot11 headers may still ++ * be required, or for promiscuous mode captures. It is in the HW RxINFO ++ * (rx_info_t) format including dot11_t followed by Payload. The Length field in ++ * the dot11_t is updated to only include Payload bytes, and is in Little Endian ++ * format. If the frame is too big, it is truncated to the buffer size, and ++ * promiscuous packets may also be configured for truncation to reduce load. The ++ * mark field is replaced with software status, and the RSSI will be updated to ++ * apply Rx calibration. ++ * ++ * Fmt1 is used for fast path Data packets in the run state, where all rx ++ * processing of dot11 headers is performed from radio FW. It has an AMSDU ++ * centric format of DA/SA/Len followed by SNAP, with the Length in Big Endian ++ * Format. In most cases conversion to Ethernet format is accomplished by ++ * copying 12 bytes to drop 8 bytes in the middle. ++ * ++ * Fmt2 is used for fast path AMSDU packets that are malformed. They just ++ * contain the dot11 header (dot11_t) containing the residual Len (Little ++ * Endian) after any valid MSDU have been extracted. The header is followed by ++ * the first invalid MSDU which will be truncated to 64 bytes. ++ */ ++enum { /* What is in Rx Buffer and why it was delivered */ ++ /* Data for Assoc Clients in Run State on Channel [Fmt1] */ ++ RXRING_CASE_FAST_DATA, ++ RXRING_CASE_FAST_BAD_AMSDU, /* Fast Data with bad AMSDU Header [Fmt2] */ ++ /* Data for Assoc Clients using unconfigured queue [Fmt0] */ ++ RXRING_CASE_SLOW_NOQUEUE, ++ /* Data for Assoc Clients not matching Run State [Fmt0] */ ++ RXRING_CASE_SLOW_NORUN, ++ /* Data for filtered Multicast groups [Fmt0] */ ++ RXRING_CASE_SLOW_MCAST, ++ RXRING_CASE_SLOW_BAD_STA, /* Data for Unassoc Clients [Fmt0] */ ++ RXRING_CASE_SLOW_BAD_MIC, /* Decrypt failure [Fmt0] */ ++ RXRING_CASE_SLOW_BAD_PN, /* Decrypt PN replay [Fmt0] */ ++ RXRING_CASE_SLOW_MGMT, /* Mgmt traffic to this AP or Bcast [Fmt0]*/ ++ RXRING_CASE_SLOW_PROMISC, /* Packets captured promiscuously [Fmt0] */ ++ RXRING_CASE_SLOW_DEL_DONE, /* Client has been deleted [N/A] */ ++ RXRING_CASE_DROP, /* Buffer returned to Host [N/A] */ ++}; ++ ++enum { /* Type of Key */ ++ KEY_TYPE_NONE, /* Bypass (never stored in real keys) */ ++ KEY_TYPE_WEP40, /* WEP with 40 bit key + 24 bit IV = 64 */ ++ KEY_TYPE_WEP104, /* WEP with 104 bit key + 24 bit IV = 128 */ ++ KEY_TYPE_TKIP, /* TKIP */ ++ KEY_TYPE_CCMP128, /* CCMP with 128 bit Key */ ++ KEY_TYPE_CCMP256, /* CCMP with 256 bit Key + 16 byte MIC */ ++ KEY_TYPE_WAPI, /* WAPI */ ++ KEY_TYPE_UNKNOWN, /* Not known what key was used (Rx Only) */ ++ KEY_TYPE_GCMP128, /* GCMP with 128 bit Key */ ++ KEY_TYPE_GCMP256, /* GCMP with 256 bit Key + 16 byte MIC */ ++}; ++ ++#define RXINFO_RSSI_X_SHIFT 24 ++#define RXINFO_RSSI_X_MASK 0xFF ++#define RXINFO_HT_SIG1_SHIFT 0 ++#define RXINFO_HT_SIG1_MASK 0xFFFFFF ++#define RXINFO_HT_SIG2_SHIFT 0 ++#define RXINFO_HT_SIG2_MASK 0x3FFFF ++#define RXINFO_RATE_SHIFT 24 ++#define RXINFO_RATE_MASK 0xFF ++#define RXINFO_NF_A_SHIFT 12 ++#define RXINFO_NF_A_MASK 0xFFF ++#define RXINFO_NF_B_SHIFT 0 ++#define RXINFO_NF_B_MASK 0xFFF ++#define RXINFO_NF_C_SHIFT 12 ++#define RXINFO_NF_C_MASK 0xFFF ++#define RXINFO_NF_D_SHIFT 0 ++#define RXINFO_NF_D_MASK 0xFFF ++#define RXINFO_PARAM_SHIFT 0 ++#define RXINFO_PARAM_MASK 0xFFFFFF ++ ++struct rx_info { /* HW Rx buffer */ ++ __le32 reserved0[2]; ++ __le32 rssi_x; ++ __le32 reserved1[2]; ++ __le32 ht_sig1; ++ __le32 ht_sig2_rate; ++ __le32 reserved2[6]; ++ __le32 nf_a_b; ++ __le32 nf_c_d; ++ __le32 reserved3[6]; ++ __le32 param; ++ __le32 reserved4[2]; ++ __le32 hdr[0]; /* Len from HW includes rx_info w/ hdr */ ++} __packed; ++ ++struct pcie_rx_desc_ndp { /* ToNIC Rx Empty Buffer Ring Entry */ ++ __le32 data; /* PCIe Payload Pointer */ ++ __le32 user; /* Value returned to Host when done */ ++} __packed; ++ ++struct rx_ring_done { /* FromNIC Rx Done Ring Entry */ ++ __le32 user; /* Value returned to Host when done */ ++ __le32 tsf; /* Rx Radio Timestamp from MAC */ ++ __le32 ctrl; /* Bit fields (RXRING_CTRL_*) */ ++} __packed; ++ ++struct pcie_desc_data_ndp { ++ dma_addr_t pphys_tx_ring; /* ptr to first TX desc (phys.) */ ++ struct pcie_tx_desc_ndp *ptx_ring;/* ptr to first TX desc (virt.) */ ++ dma_addr_t pphys_rx_ring; /* ptr to first RX desc (phys.) */ ++ struct pcie_rx_desc_ndp *prx_ring;/* ptr to first RX desc (virt.) */ ++ u32 wcb_base; /* FW base offset for registers */ ++ u32 rx_buf_size; /* length of the RX buffers */ ++ u32 tx_sent_tail; /* index to the TX desc FW used */ ++ u32 tx_sent_head; /* index to next TX desc to be used*/ ++ u32 tx_done_tail; /* index to Tx Done queue tail */ ++ /* keept the skb owned by fw */ ++ dma_addr_t pphys_tx_buflist[MAX_TX_RING_SEND_SIZE]; ++ struct sk_buff *tx_vbuflist[MAX_TX_RING_SEND_SIZE]; ++ u32 tx_vbuflist_idx; /* idx to empty slot in tx_vbuflist*/ ++ struct sk_buff *rx_vbuflist[MAX_NUM_RX_DESC]; ++ struct tx_ring_done *ptx_ring_done; ++ dma_addr_t pphys_tx_ring_done; /* ptr to first TX done desc (phys.) */ ++ struct rx_ring_done *prx_ring_done; ++ dma_addr_t pphys_rx_ring_done; /* ptr to first RX done desc (phys.) */ ++ dma_addr_t pphys_acnt_ring; /* ptr to first account record (phys.)*/ ++ u8 *pacnt_ring; /* ptr to first accounting record */ ++ u32 tx_desc_busy_cnt; ++ u8 *pacnt_buf; ++ u32 acnt_ring_size; ++}; ++ ++struct ndp_rx_counter { ++ u32 fast_data_cnt; ++ u32 fast_bad_amsdu_cnt; ++ u32 slow_noqueue_cnt; ++ u32 slow_norun_cnt; ++ u32 slow_mcast_cnt; ++ u32 slow_bad_sta_cnt; ++ u32 slow_bad_mic_cnt; ++ u32 slow_bad_pn_cnt; ++ u32 slow_mgmt_cnt; ++ u32 slow_promisc_cnt; ++ u32 drop_cnt; ++ u32 offch_promisc_cnt; ++ u32 mu_pkt_cnt; ++}; ++ ++/* KF2 - 88W8997 */ ++#define PCIE_MAX_TXRX_BD 0x20 ++/* PCIE read data pointer for queue 0 and 1 */ ++#define PCIE_RD_DATA_PTR_Q0_Q1 0xC1A4 /* 0x8000C1A4 */ ++/* PCIE read data pointer for queue 2 and 3 */ ++#define PCIE_RD_DATA_PTR_Q2_Q3 0xC1A8 /* 0x8000C1A8 */ ++/* PCIE write data pointer for queue 0 and 1 */ ++#define PCIE_WR_DATA_PTR_Q0_Q1 0xC174 /* 0x8000C174 */ ++/* PCIE write data pointer for queue 2 and 3 */ ++#define PCIE_WR_DATA_PTR_Q2_Q3 0xC178 /* 0x8000C178 */ ++ ++/* TX buffer description read pointer */ ++#define REG_TXBD_RDPTR PCIE_RD_DATA_PTR_Q0_Q1 ++/* TX buffer description write pointer */ ++#define REG_TXBD_WRPTR PCIE_WR_DATA_PTR_Q0_Q1 ++ ++#define PCIE_TX_START_PTR 16 ++ ++#define PCIE_TXBD_MASK 0x0FFF0000 ++#define PCIE_TXBD_WRAP_MASK 0x1FFF0000 ++ ++#define PCIE_BD_FLAG_RX_ROLLOVER_IND BIT(12) ++#define PCIE_BD_FLAG_TX_START_PTR BIT(16) ++#define PCIE_BD_FLAG_TX_ROLLOVER_IND BIT(28) ++#define PCIE_BD_FLAG_TX2_START_PTR BIT(0) ++#define PCIE_BD_FLAG_TX2_ROLLOVER_IND BIT(12) ++ ++#define PCIE_BD_FLAG_FIRST_DESC BIT(0) ++#define PCIE_BD_FLAG_LAST_DESC BIT(1) ++ ++#define PCIE_TX_WCB_FLAGS_DONT_ENCRYPT 0x00000001 ++#define PCIE_TX_WCB_FLAGS_NO_CCK_RATE 0x00000002 ++ ++#define PCIE_TXBD_NOT_FULL(wrptr, rdptr) \ ++ (((wrptr & PCIE_TXBD_MASK) != (rdptr & PCIE_TXBD_MASK)) \ ++ || ((wrptr & PCIE_BD_FLAG_TX_ROLLOVER_IND) == \ ++ (rdptr & PCIE_BD_FLAG_TX_ROLLOVER_IND))) ++ ++struct pcie_data_buf { ++ /* Buffer descriptor flags */ ++ __le16 flags; ++ /* Offset of fragment/pkt to start of ip header */ ++ __le16 offset; ++ /* Fragment length of the buffer */ ++ __le16 frag_len; ++ /* Length of the buffer */ ++ __le16 len; ++ /* Physical address of the buffer */ ++ __le64 paddr; ++ /* Reserved */ ++ __le32 reserved; ++} __packed; ++ ++struct pcie_pfu_dma_data { ++ struct pcie_tx_desc tx_desc; ++ struct pcie_dma_data dma_data; ++} __packed; ++ ++struct pcie_priv { ++ struct mwl_priv *mwl_priv; ++ struct pci_dev *pdev; ++ void __iomem *iobase0; /* MEM Base Address Register 0 */ ++ void __iomem *iobase1; /* MEM Base Address Register 1 */ ++ u32 next_bar_num; ++ ++ struct sk_buff_head txq[PCIE_NUM_OF_DESC_DATA]; ++ ++ spinlock_t int_mask_lock ____cacheline_aligned_in_smp; ++ struct tasklet_struct tx_task; ++ struct tasklet_struct tx_done_task; ++ struct tasklet_struct rx_task; ++ struct tasklet_struct qe_task; ++ unsigned int tx_head_room; ++ int txq_limit; ++ int txq_wake_threshold; ++ bool is_tx_schedule; ++ bool is_tx_done_schedule; ++ int recv_limit; ++ bool is_rx_schedule; ++ bool is_qe_schedule; ++ u32 qe_trig_num; ++ unsigned long qe_trig_time; ++ ++ /* various descriptor data */ ++ /* for tx descriptor data */ ++ spinlock_t tx_desc_lock ____cacheline_aligned_in_smp; ++ struct pcie_desc_data desc_data[PCIE_NUM_OF_DESC_DATA]; ++ int delay_q_idx; ++ struct sk_buff *delay_q[PCIE_DELAY_FREE_Q_LIMIT]; ++ /* number of descriptors owned by fw at any one time */ ++ int fw_desc_cnt[PCIE_NUM_OF_DESC_DATA]; ++ ++ /* new data path */ ++ struct pcie_desc_data_ndp desc_data_ndp; ++ int tx_done_cnt; ++ struct ieee80211_sta *sta_link[SYSADPT_MAX_STA_SC4 + 1]; ++ struct sk_buff_head rx_skb_trace; ++ struct ndp_rx_counter rx_cnts; ++ u32 rx_skb_unlink_err; ++ u32 signature_err; ++ u32 recheck_rxringdone; ++ u32 acnt_busy; ++ u32 acnt_wrap; ++ u32 acnt_drop; ++ ++ /* KF2 - 88W8997 */ ++ struct firmware *cal_data; ++ /* Write pointer for TXBD ring */ ++ u32 txbd_wrptr; ++ /* Shadow copy of TXBD read pointer */ ++ u32 txbd_rdptr; ++ /* TXBD ring size */ ++ u32 txbd_ring_size; ++ /* Virtual base address of txbd_ring */ ++ u8 *txbd_ring_vbase; ++ /* Physical base address of txbd_ring */ ++ dma_addr_t txbd_ring_pbase; ++ /* Ring of buffer descriptors for TX */ ++ struct pcie_data_buf *txbd_ring[PCIE_MAX_TXRX_BD]; ++ struct sk_buff *tx_buf_list[PCIE_MAX_TXRX_BD]; ++}; ++ ++enum { /* Definition of accounting record codes */ ++ ACNT_CODE_BUSY = 0, /* Marked busy until filled in */ ++ ACNT_CODE_WRAP, /* Used to pad when wrapping */ ++ ACNT_CODE_DROP, /* Count of dropped records */ ++ ACNT_CODE_TX_ENQUEUE, /* TXINFO when added to TCQ (acnt_tx_s) */ ++ ACNT_CODE_RX_PPDU, /* RXINFO for each PPDu (acnt_rx_s) */ ++ ACNT_CODE_TX_FLUSH, /* Flush Tx Queue */ ++ ACNT_CODE_RX_RESET, /* Channel Change / Rx Reset */ ++ ACNT_CODE_TX_RESET, /* TCQ reset */ ++ ACNT_CODE_QUOTE_LEVEL,/* Quota Level changes */ ++ ACNT_CODE_TX_DONE, /* Tx status when done */ ++ ACNT_CODE_RA_STATS, /* rateinfo PER (acnt_ra_s) */ ++ ACNT_CODE_BA_STATS, /* BA stats (acnt_ba_s) */ ++ ACNT_CODE_BF_MIMO_CTRL,/* BF Mimo Ctrl Field Log (acnt_bf_mimo_ctrl_s)*/ ++}; ++ ++struct acnt_s { /* Baseline Accounting Record format */ ++ __le16 code; /* Unique code for each type */ ++ u8 len; /* Length in DWORDS, including header */ ++ u8 pad; /* Alignment for generic, but specific can reuse*/ ++ __le32 tsf; /* Timestamp for Entry (when len>1) */ ++} __packed; ++ ++struct acnt_tx_s { /* Accounting Record For Tx (at Enqueue time) */ ++ __le16 code; /* Unique code for each type */ ++ u8 len; /* Length in DWORDS, including header */ ++ u8 tcq; /* Which TCQ was used */ ++ __le32 tsf; /* Timestamp for Entry (when len>1) */ ++ __le64 bitmap; /* Map of SeqNr when AMPDU */ ++ __le16 air_time; /* Air Time used by PPDU */ ++ __le16 npkts; /* Number of Descriptors sent (AMPDU&AMSDU) */ ++ __le16 qid; /* Transmit Queue ID */ ++ __le16 latency; /* Latency of oldest frame in AMPDU (128us) */ ++ __le16 rate1; /* Rate Code for sending data */ ++ __le16 rate2; /* Rate Code for sending RTS/CTS protection */ ++ u8 rate_tbl_index; /* Rate table index for this TxInfo rate */ ++ u8 type; /* SU:0 or MU:1 */ ++ u8 pad[1]; /* Unused */ ++ u8 retries; /* Number of retries of oldest frame in AMPDU */ ++ __le32 tx_cnt; /* No. of pkt sent */ ++ struct tx_info tx_info;/* Transmit parameters used for 1st MPDU/AMPDU */ ++ struct pcie_dma_data hdr;/* Dot11 header used for 1st MPDU in AMPDU */ ++ u8 payload[0]; /* Variable Payload by use case */ ++} __packed; ++ ++struct acnt_rx_s { /* Accounting Record for Rx PPDU */ ++ __le16 code; /* Unique code for each type */ ++ u8 len; /* Length in DWORDS, including header */ ++ u8 flags; /* Flags (ACNTRX_*) */ ++ __le32 tsf; /* Timestamp for Entry (when len>1) */ ++ __le64 bitmap; /* Map of SeqNr when AMPDU */ ++ __le16 air_time; /* Air Time used by PPDU (no CSMA overhead) */ ++ __le16 rate; /* Rate Code for receiving data */ ++ struct rx_info rx_info;/* Receive parameters from 1st valid MPDU/AMPDU*/ ++} __packed; ++ ++struct acnt_ra_s { /* Accounting Record w/ rateinfo PER */ ++ __le16 code; /* Unique code for each type */ ++ u8 len; /* Length in DWORDS, including header */ ++ u8 per; /* PER for this rateinfo */ ++ __le32 tsf; /* Timestamp for Entry (when len>1) */ ++ __le16 stn_id; /* sta index this rateinfo is tied to */ ++ u8 type; /* SU:0 or MU:1 */ ++ u8 rate_tbl_index; /* ratetbl index */ ++ __le32 rate_info; /* rateinfo for this ratetbl index */ ++ __le32 tx_attempt_cnt;/* Total tx pkt during rate adapt interval */ ++} __packed; ++ ++struct acnt_ba_s { /* Accounting Record w/ rateinfo PER */ ++ __le16 code; /* Unique code for each type */ ++ u8 len; /* Length in DWORDS, including header */ ++ u8 ba_hole; /* Total missing pkt in a BA */ ++ __le32 tsf; /* Timestamp for Entry (when len>1) */ ++ __le16 stnid; /* sta index for this BA */ ++ u8 no_ba; /* No BA received */ ++ u8 ba_expected; /* Total expected pkt to be BA'd */ ++ u8 type; /* SU:0 or MU:1 */ ++ u8 pad[3]; /* Unused */ ++} __packed; ++ ++struct acnt_bf_mimo_ctrl_s {/* Accounting Record w/ BF MIMO Control Field Data*/ ++ __le16 code; /* Unique code for each type */ ++ u8 len; /* Length in DWORDS, including header */ ++ u8 type; /* SU:0, MU:1 */ ++ __le32 tsf; /* Timestamp for Entry (when len>1) */ ++ u8 rec_mac[6]; /* Received Packet Source MAC Address */ ++ __le16 pad; /* Padding */ ++ __le32 mimo_ctrl; /* BF MIMO Control Field Data */ ++ __le64 comp_bf_rep; /* First 8 bytes of Compressed BF Report */ ++} __packed; ++ ++static inline void pcie_tx_add_dma_header(struct mwl_priv *priv, ++ struct sk_buff *skb, ++ int head_pad, ++ int tail_pad) ++{ ++ struct ieee80211_hdr *wh; ++ int dma_hdrlen; ++ int hdrlen; ++ int reqd_hdrlen; ++ int needed_room; ++ struct pcie_dma_data *dma_data; ++ ++ dma_hdrlen = (priv->chip_type == MWL8997) ? ++ sizeof(struct pcie_pfu_dma_data) : ++ sizeof(struct pcie_dma_data); ++ ++ /* Add a firmware DMA header; the firmware requires that we ++ * present a 2-byte payload length followed by a 4-address ++ * header (without QoS field), followed (optionally) by any ++ * WEP/ExtIV header (but only filled in for CCMP). ++ */ ++ wh = (struct ieee80211_hdr *)skb->data; ++ ++ hdrlen = ieee80211_hdrlen(wh->frame_control); ++ ++ reqd_hdrlen = dma_hdrlen + head_pad; ++ ++ if (hdrlen != reqd_hdrlen) { ++ needed_room = reqd_hdrlen - hdrlen; ++ if (skb_headroom(skb) < needed_room) { ++ wiphy_debug(priv->hw->wiphy, "headroom is short: %d %d", ++ skb_headroom(skb), needed_room); ++ skb_cow(skb, needed_room); ++ } ++ skb_push(skb, needed_room); ++ } ++ ++ if (ieee80211_is_data_qos(wh->frame_control)) ++ hdrlen -= IEEE80211_QOS_CTL_LEN; ++ ++ if (priv->chip_type == MWL8997) ++ dma_data = &((struct pcie_pfu_dma_data *)skb->data)->dma_data; ++ else ++ dma_data = (struct pcie_dma_data *)skb->data; ++ ++ if (wh != &dma_data->wh) ++ memmove(&dma_data->wh, wh, hdrlen); ++ ++ if (hdrlen != sizeof(dma_data->wh)) ++ memset(((void *)&dma_data->wh) + hdrlen, 0, ++ sizeof(dma_data->wh) - hdrlen); ++ ++ /* Firmware length is the length of the fully formed "802.11 ++ * payload". That is, everything except for the 802.11 header. ++ * This includes all crypto material including the MIC. ++ */ ++ dma_data->fwlen = ++ cpu_to_le16(skb->len - dma_hdrlen + tail_pad); ++} ++ ++static inline void pcie_tx_encapsulate_frame(struct mwl_priv *priv, ++ struct sk_buff *skb, ++ struct ieee80211_key_conf *k_conf, ++ bool *ccmp) ++{ ++ int head_pad = 0; ++ int data_pad = 0; ++ ++ /* Make sure the packet header is in the DMA header format (4-address ++ * without QoS), and add head & tail padding when HW crypto is enabled. ++ * ++ * We have the following trailer padding requirements: ++ * - WEP: 4 trailer bytes (ICV) ++ * - TKIP: 12 trailer bytes (8 MIC + 4 ICV) ++ * - CCMP: 8 trailer bytes (MIC) ++ */ ++ ++ if (k_conf) { ++ head_pad = k_conf->iv_len; ++ ++ switch (k_conf->cipher) { ++ case WLAN_CIPHER_SUITE_WEP40: ++ case WLAN_CIPHER_SUITE_WEP104: ++ data_pad = 4; ++ break; ++ case WLAN_CIPHER_SUITE_TKIP: ++ data_pad = 12; ++ break; ++ case WLAN_CIPHER_SUITE_CCMP: ++ data_pad = 8; ++ if (ccmp) ++ *ccmp = true; ++ break; ++ } ++ } ++ ++ pcie_tx_add_dma_header(priv, skb, head_pad, data_pad); ++} ++ ++static inline void pcie_tx_prepare_info(struct mwl_priv *priv, u32 rate, ++ struct ieee80211_tx_info *info) ++{ ++ u32 format, bandwidth, short_gi, rate_id; ++ ++ ieee80211_tx_info_clear_status(info); ++ ++ info->status.rates[0].idx = -1; ++ info->status.rates[0].count = 0; ++ info->status.rates[0].flags = 0; ++ info->flags &= ~IEEE80211_TX_CTL_AMPDU; ++ info->flags |= IEEE80211_TX_STAT_ACK; ++ ++ if (rate) { ++ /* Prepare rate information */ ++ format = rate & MWL_TX_RATE_FORMAT_MASK; ++ bandwidth = ++ (rate & MWL_TX_RATE_BANDWIDTH_MASK) >> ++ MWL_TX_RATE_BANDWIDTH_SHIFT; ++ short_gi = (rate & MWL_TX_RATE_SHORTGI_MASK) >> ++ MWL_TX_RATE_SHORTGI_SHIFT; ++ rate_id = (rate & MWL_TX_RATE_RATEIDMCS_MASK) >> ++ MWL_TX_RATE_RATEIDMCS_SHIFT; ++ ++ info->status.rates[0].idx = rate_id; ++ if (format == TX_RATE_FORMAT_LEGACY) { ++ if (priv->hw->conf.chandef.chan->hw_value > ++ BAND_24_CHANNEL_NUM) { ++ info->status.rates[0].idx -= 5; ++ } ++ } ++ if (format == TX_RATE_FORMAT_11N) ++ info->status.rates[0].flags |= ++ IEEE80211_TX_RC_MCS; ++ if (format == TX_RATE_FORMAT_11AC) ++ info->status.rates[0].flags |= ++ IEEE80211_TX_RC_VHT_MCS; ++ if (bandwidth == TX_RATE_BANDWIDTH_40) ++ info->status.rates[0].flags |= ++ IEEE80211_TX_RC_40_MHZ_WIDTH; ++ if (bandwidth == TX_RATE_BANDWIDTH_80) ++ info->status.rates[0].flags |= ++ IEEE80211_TX_RC_80_MHZ_WIDTH; ++ if (bandwidth == TX_RATE_BANDWIDTH_160) ++ info->status.rates[0].flags |= ++ IEEE80211_TX_RC_160_MHZ_WIDTH; ++ if (short_gi == TX_RATE_INFO_SHORT_GI) ++ info->status.rates[0].flags |= ++ IEEE80211_TX_RC_SHORT_GI; ++ info->status.rates[0].count = 1; ++ info->status.rates[1].idx = -1; ++ } ++} ++ ++static inline void pcie_tx_count_packet(struct ieee80211_sta *sta, u8 tid) ++{ ++ struct mwl_sta *sta_info; ++ struct mwl_tx_info *tx_stats; ++ ++ if (WARN_ON(tid >= SYSADPT_MAX_TID)) ++ return; ++ ++ sta_info = mwl_dev_get_sta(sta); ++ ++ tx_stats = &sta_info->tx_stats[tid]; ++ ++ if (tx_stats->start_time == 0) ++ tx_stats->start_time = jiffies; ++ ++ /* reset the packet count after each second elapses. If the number of ++ * packets ever exceeds the ampdu_min_traffic threshold, we will allow ++ * an ampdu stream to be started. ++ */ ++ if (jiffies - tx_stats->start_time > HZ) { ++ tx_stats->pkts = 0; ++ tx_stats->start_time = jiffies; ++ } else { ++ tx_stats->pkts++; ++ } ++} ++ ++static inline void pcie_rx_prepare_status(struct mwl_priv *priv, u16 format, ++ u16 nss, u16 bw, u16 gi, u16 rate, ++ struct ieee80211_rx_status *status) ++{ ++#ifdef RX_ENC_FLAG_STBC_SHIFT ++ switch (format) { ++ case RX_RATE_INFO_FORMAT_11N: ++ status->encoding = RX_ENC_HT; ++ status->bw = RATE_INFO_BW_20; ++ if (bw == RX_RATE_INFO_HT40) ++ status->bw = RATE_INFO_BW_40; ++ if (gi == RX_RATE_INFO_SHORT_INTERVAL) ++ status->enc_flags |= RX_ENC_FLAG_SHORT_GI; ++ break; ++ case RX_RATE_INFO_FORMAT_11AC: ++ status->encoding = RX_ENC_VHT; ++ status->bw = RATE_INFO_BW_20; ++ if (bw == RX_RATE_INFO_HT40) ++ status->bw = RATE_INFO_BW_40; ++ if (bw == RX_RATE_INFO_HT80) ++ status->bw = RATE_INFO_BW_80; ++ if (bw == RX_RATE_INFO_HT160) ++ status->bw = RATE_INFO_BW_160; ++ if (gi == RX_RATE_INFO_SHORT_INTERVAL) ++ status->enc_flags |= RX_ENC_FLAG_SHORT_GI; ++ status->nss = (nss + 1); ++ break; ++ } ++#else ++ switch (format) { ++ case RX_RATE_INFO_FORMAT_11N: ++ status->flag |= RX_FLAG_HT; ++ if (bw == RX_RATE_INFO_HT40) ++ status->flag |= RX_FLAG_40MHZ; ++ if (gi == RX_RATE_INFO_SHORT_INTERVAL) ++ status->flag |= RX_FLAG_SHORT_GI; ++ break; ++ case RX_RATE_INFO_FORMAT_11AC: ++ status->flag |= RX_FLAG_VHT; ++ if (bw == RX_RATE_INFO_HT40) ++ status->flag |= RX_FLAG_40MHZ; ++ if (bw == RX_RATE_INFO_HT80) ++ status->vht_flag |= RX_VHT_FLAG_80MHZ; ++ if (bw == RX_RATE_INFO_HT160) ++ status->vht_flag |= RX_VHT_FLAG_160MHZ; ++ if (gi == RX_RATE_INFO_SHORT_INTERVAL) ++ status->flag |= RX_FLAG_SHORT_GI; ++ status->vht_nss = (nss + 1); ++ break; ++ } ++#endif ++ status->rate_idx = rate; ++ ++ if (priv->hw->conf.chandef.chan->hw_value > ++ BAND_24_CHANNEL_NUM) { ++ status->band = NL80211_BAND_5GHZ; ++#ifdef RX_ENC_FLAG_STBC_SHIFT ++ if ((!(status->encoding == RX_ENC_HT)) && ++ (!(status->encoding == RX_ENC_VHT))) { ++#else ++ if ((!(status->flag & RX_FLAG_HT)) && ++ (!(status->flag & RX_FLAG_VHT))) { ++#endif ++ status->rate_idx -= 5; ++ if (status->rate_idx >= BAND_50_RATE_NUM) ++ status->rate_idx = BAND_50_RATE_NUM - 1; ++ } ++ } else { ++ status->band = NL80211_BAND_2GHZ; ++#ifdef RX_ENC_FLAG_STBC_SHIFT ++ if ((!(status->encoding == RX_ENC_HT)) && ++ (!(status->encoding == RX_ENC_VHT))) { ++#else ++ if ((!(status->flag & RX_FLAG_HT)) && ++ (!(status->flag & RX_FLAG_VHT))) { ++#endif ++ if (status->rate_idx >= BAND_24_RATE_NUM) ++ status->rate_idx = BAND_24_RATE_NUM - 1; ++ } ++ } ++} ++ ++static inline void pcie_rx_remove_dma_header(struct sk_buff *skb, __le16 qos) ++{ ++ struct pcie_dma_data *dma_data; ++ int hdrlen; ++ ++ dma_data = (struct pcie_dma_data *)skb->data; ++ hdrlen = ieee80211_hdrlen(dma_data->wh.frame_control); ++ ++ if (hdrlen != sizeof(dma_data->wh)) { ++ if (ieee80211_is_data_qos(dma_data->wh.frame_control)) { ++ memmove(dma_data->data - hdrlen, ++ &dma_data->wh, hdrlen - 2); ++ *((__le16 *)(dma_data->data - 2)) = qos; ++ } else { ++ memmove(dma_data->data - hdrlen, &dma_data->wh, hdrlen); ++ } ++ } ++ ++ if (hdrlen != sizeof(*dma_data)) ++ skb_pull(skb, sizeof(*dma_data) - hdrlen); ++} ++ ++static inline void pcie_mask_int(struct pcie_priv *pcie_priv, ++ u32 mask_bit, bool set) ++{ ++ unsigned long flags; ++ void __iomem *int_status_mask; ++ u32 status; ++ ++ spin_lock_irqsave(&pcie_priv->int_mask_lock, flags); ++ int_status_mask = pcie_priv->iobase1 + ++ MACREG_REG_A2H_INTERRUPT_STATUS_MASK; ++ status = readl(int_status_mask); ++ if (set) ++ writel((status | mask_bit), int_status_mask); ++ else ++ writel((status & ~mask_bit), int_status_mask); ++ spin_unlock_irqrestore(&pcie_priv->int_mask_lock, flags); ++} ++ ++#endif /* _DEV_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.c +new file mode 100644 +index 000000000000..939ed54133c7 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.c +@@ -0,0 +1,274 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements firmware download related ++ * functions. ++ */ ++ ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "hif/fwcmd.h" ++#include "hif/pcie/dev.h" ++#include "hif/pcie/sc4_ddr.h" ++#include "hif/pcie/fwdl.h" ++ ++#define FW_DOWNLOAD_BLOCK_SIZE 256 ++#define FW_CHECK_MSECS 3 ++ ++#define FW_MAX_NUM_CHECKS 0xffff ++ ++static void pcie_trigger_pcicmd_bootcode(struct pcie_priv *pcie_priv) ++{ ++ writel(pcie_priv->mwl_priv->pphys_cmd_buf, ++ pcie_priv->iobase1 + MACREG_REG_GEN_PTR); ++ writel(0x00, pcie_priv->iobase1 + MACREG_REG_INT_CODE); ++ writel(MACREG_H2ARIC_BIT_DOOR_BELL, ++ pcie_priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS); ++} ++ ++static bool pcie_download_ddr_init(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = (struct pcie_priv *)priv->hif.priv; ++ u32 size_ddr_init = sizeof(sc4_ddr_init); ++ u8 *p = (u8 *)&sc4_ddr_init[0]; ++ u32 curr_iteration = 0; ++ u32 size_ddr_init_downloaded = 0; ++ u32 int_code = 0; ++ u32 len = 0; ++ ++ /* download ddr init code */ ++ wiphy_debug(priv->hw->wiphy, "ddr init: download start\n"); ++ ++ while (size_ddr_init_downloaded < size_ddr_init) { ++ len = readl(pcie_priv->iobase1 + 0xc40); ++ ++ if (!len) ++ break; ++ ++ /* this copies the next chunk of fw binary to be delivered */ ++ memcpy((char *)&priv->pcmd_buf[0], p, len); ++ /* this is arbitrary per your platform; we use 0xffff */ ++ curr_iteration = (FW_MAX_NUM_CHECKS * 500); ++ /* this function writes pdata to c10, then write 2 to c18 */ ++ pcie_trigger_pcicmd_bootcode(pcie_priv); ++ ++ /* NOTE: the following back to back checks on C1C is time ++ * sensitive, hence may need to be tweaked dependent on host ++ * processor. Time for SC2 to go from the write of event 2 to ++ * C1C == 2 is ~1300 nSec. Hence the checkings on host has to ++ * consider how efficient your code can be to meet this timing, ++ * or you can alternatively tweak this routines to fit your ++ * platform ++ */ ++ do { ++ int_code = readl(pcie_priv->iobase1 + 0xc1c); ++ if (int_code != 0) ++ break; ++ cond_resched(); ++ curr_iteration--; ++ } while (curr_iteration); ++ ++ do { ++ int_code = readl(pcie_priv->iobase1 + 0xc1c); ++ if ((int_code & MACREG_H2ARIC_BIT_DOOR_BELL) != ++ MACREG_H2ARIC_BIT_DOOR_BELL) ++ break; ++ cond_resched(); ++ curr_iteration--; ++ } while (curr_iteration); ++ ++ if (curr_iteration == 0) { ++ /* This limited loop check allows you to exit gracefully ++ * without locking up your entire system just because fw ++ * download failed ++ */ ++ wiphy_err(priv->hw->wiphy, ++ "Exhausted curr_iteration during download\n"); ++ return false; ++ } ++ ++ p += len; ++ size_ddr_init_downloaded += len; ++ } ++ ++ wiphy_debug(priv->hw->wiphy, "ddr init: download complete\n"); ++ ++ return true; ++} ++ ++void pcie_reset(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ u32 regval; ++ ++ regval = readl(pcie_priv->iobase1 + MACREG_REG_INT_CODE); ++ if (regval == 0xffffffff) { ++ wiphy_err(priv->hw->wiphy, "adapter does not exist\n"); ++ return; ++ } ++ ++ writel(ISR_RESET, pcie_priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS); ++} ++ ++int pcie_download_firmware(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ const struct firmware *fw = priv->fw_ucode; ++ u32 curr_iteration = 0; ++ u32 size_fw_downloaded = 0; ++ u32 int_code = 0; ++ u32 len = 0; ++ u32 fwreadysignature = HOSTCMD_SOFTAP_FWRDY_SIGNATURE; ++ ++ pcie_reset(hw); ++ ++ /* FW before jumping to boot rom, it will enable PCIe transaction retry, ++ * wait for boot code to stop it. ++ */ ++ usleep_range(FW_CHECK_MSECS * 1000, FW_CHECK_MSECS * 2000); ++ ++ if (priv->chip_type == MWL8964) { ++ writel(MACREG_A2HRIC_BIT_MASK_NDP, ++ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CLEAR_SEL); ++ } else { ++ writel(MACREG_A2HRIC_BIT_MASK, ++ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CLEAR_SEL); ++ } ++ writel(0x00, pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); ++ writel(0x00, pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); ++ if (priv->chip_type == MWL8964) { ++ writel(MACREG_A2HRIC_BIT_MASK_NDP, ++ pcie_priv->iobase1 + ++ MACREG_REG_A2H_INTERRUPT_STATUS_MASK); ++ } else { ++ writel(MACREG_A2HRIC_BIT_MASK, ++ pcie_priv->iobase1 + ++ MACREG_REG_A2H_INTERRUPT_STATUS_MASK); ++ } ++ ++ /* this routine interacts with SC2 bootrom to download firmware binary ++ * to the device. After DMA'd to SC2, the firmware could be deflated to ++ * reside on its respective blocks such as ITCM, DTCM, SQRAM, ++ * (or even DDR, AFTER DDR is init'd before fw download ++ */ ++ wiphy_debug(hw->wiphy, "fw download start\n"); ++ ++ if (priv->chip_type != MWL8997) ++ /* Disable PFU before FWDL */ ++ writel(0x100, pcie_priv->iobase1 + 0xE0E4); ++ ++ /* make sure SCRATCH2 C40 is clear, in case we are too quick */ ++ while (readl(pcie_priv->iobase1 + 0xc40) == 0) ++ cond_resched(); ++ ++ if (priv->chip_type == MWL8964) { ++ if (!pcie_download_ddr_init(priv)) { ++ wiphy_err(hw->wiphy, ++ "ddr init: code download failed\n"); ++ goto err_download; ++ } ++ } ++ ++ while (size_fw_downloaded < fw->size) { ++ len = readl(pcie_priv->iobase1 + 0xc40); ++ ++ if (!len) ++ break; ++ ++ /* this copies the next chunk of fw binary to be delivered */ ++ memcpy((char *)&priv->pcmd_buf[0], ++ (fw->data + size_fw_downloaded), len); ++ ++ /* this function writes pdata to c10, then write 2 to c18 */ ++ pcie_trigger_pcicmd_bootcode(pcie_priv); ++ ++ /* this is arbitrary per your platform; we use 0xffff */ ++ curr_iteration = FW_MAX_NUM_CHECKS; ++ ++ /* NOTE: the following back to back checks on C1C is time ++ * sensitive, hence may need to be tweaked dependent on host ++ * processor. Time for SC2 to go from the write of event 2 to ++ * C1C == 2 is ~1300 nSec. Hence the checkings on host has to ++ * consider how efficient your code can be to meet this timing, ++ * or you can alternatively tweak this routines to fit your ++ * platform ++ */ ++ do { ++ int_code = readl(pcie_priv->iobase1 + 0xc1c); ++ if ((int_code & MACREG_H2ARIC_BIT_DOOR_BELL) != ++ MACREG_H2ARIC_BIT_DOOR_BELL) ++ break; ++ cond_resched(); ++ curr_iteration--; ++ } while (curr_iteration); ++ ++ if (curr_iteration == 0) { ++ /* This limited loop check allows you to exit gracefully ++ * without locking up your entire system just because fw ++ * download failed ++ */ ++ wiphy_err(hw->wiphy, ++ "Exhausted curr_iteration for fw download\n"); ++ goto err_download; ++ } ++ ++ size_fw_downloaded += len; ++ } ++ ++ wiphy_debug(hw->wiphy, ++ "FwSize = %d downloaded Size = %d curr_iteration %d\n", ++ (int)fw->size, size_fw_downloaded, curr_iteration); ++ ++ /* Now firware is downloaded successfully, so this part is to check ++ * whether fw can properly execute to an extent that write back ++ * signature to indicate its readiness to the host. NOTE: if your ++ * downloaded fw crashes, this signature checking will fail. This ++ * part is similar as SC1 ++ */ ++ *((u32 *)&priv->pcmd_buf[1]) = 0; ++ pcie_trigger_pcicmd_bootcode(pcie_priv); ++ curr_iteration = FW_MAX_NUM_CHECKS; ++ do { ++ curr_iteration--; ++ writel(HOSTCMD_SOFTAP_MODE, ++ pcie_priv->iobase1 + MACREG_REG_GEN_PTR); ++ usleep_range(FW_CHECK_MSECS * 1000, FW_CHECK_MSECS * 2000); ++ int_code = readl(pcie_priv->iobase1 + MACREG_REG_INT_CODE); ++ if (!(curr_iteration % 0xff) && (int_code != 0)) ++ wiphy_err(hw->wiphy, "%x;", int_code); ++ } while ((curr_iteration) && ++ (int_code != fwreadysignature)); ++ ++ if (curr_iteration == 0) { ++ wiphy_err(hw->wiphy, ++ "Exhausted curr_iteration for fw signature\n"); ++ goto err_download; ++ } ++ ++ wiphy_debug(hw->wiphy, "fw download complete\n"); ++ writel(0x00, pcie_priv->iobase1 + MACREG_REG_INT_CODE); ++ ++ return 0; ++ ++err_download: ++ ++ pcie_reset(hw); ++ ++ return -EIO; ++} +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.h +new file mode 100644 +index 000000000000..36a3311aa678 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.h +@@ -0,0 +1,24 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines firmware download related functions. */ ++ ++#ifndef _FWDL_H_ ++#define _FWDL_H_ ++ ++void pcie_reset(struct ieee80211_hw *hw); ++int pcie_download_firmware(struct ieee80211_hw *hw); ++ ++#endif /* _FWDL_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/pcie.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/pcie.c +new file mode 100644 +index 000000000000..da55913c0570 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/pcie.c +@@ -0,0 +1,1645 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements functions needed for PCIe module. */ ++ ++#include ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "utils.h" ++#include "vendor_cmd.h" ++#include "hif/fwcmd.h" ++#include "hif/pcie/dev.h" ++#include "hif/pcie/fwdl.h" ++#include "hif/pcie/tx.h" ++#include "hif/pcie/rx.h" ++#include "hif/pcie/tx_ndp.h" ++#include "hif/pcie/rx_ndp.h" ++ ++#define PCIE_DRV_DESC "Marvell Mac80211 Wireless PCIE Network Driver" ++#define PCIE_DEV_NAME "Marvell 802.11ac PCIE Adapter" ++ ++#define MAX_WAIT_FW_COMPLETE_ITERATIONS 2000 ++#define CHECK_BA_TRAFFIC_TIME 300 /* msec */ ++#define CHECK_TX_DONE_TIME 50 /* msec */ ++ ++static struct pci_device_id pcie_id_tbl[] = { ++ { PCI_VDEVICE(MARVELL, 0x2a55), .driver_data = MWL8864, }, ++ { PCI_VDEVICE(MARVELL, 0x2b38), .driver_data = MWL8897, }, ++ { PCI_VDEVICE(MARVELL, 0x2b40), .driver_data = MWL8964, }, ++ { PCI_VDEVICE(MARVELL_EXT, 0x2b42), .driver_data = MWL8997, }, ++ { }, ++}; ++ ++static struct mwl_chip_info pcie_chip_tbl[] = { ++ [MWL8864] = { ++ .part_name = "88W8864", ++ .fw_image = "mwlwifi/88W8864.bin", ++ .cal_file = NULL, ++ .txpwrlmt_file = NULL, ++ .antenna_tx = ANTENNA_TX_4_AUTO, ++ .antenna_rx = ANTENNA_RX_4_AUTO, ++ }, ++ [MWL8897] = { ++ .part_name = "88W8897", ++ .fw_image = "mwlwifi/88W8897.bin", ++ .cal_file = NULL, ++ .txpwrlmt_file = NULL, ++ .antenna_tx = ANTENNA_TX_2, ++ .antenna_rx = ANTENNA_RX_2, ++ }, ++ [MWL8964] = { ++ .part_name = "88W8964", ++ .fw_image = "mwlwifi/88W8964.bin", ++ .cal_file = NULL, ++ .txpwrlmt_file = NULL, ++ .antenna_tx = ANTENNA_TX_4_AUTO, ++ .antenna_rx = ANTENNA_RX_4_AUTO, ++ }, ++ [MWL8997] = { ++ .part_name = "88W8997", ++ .fw_image = "mwlwifi/88W8997.bin", ++ .cal_file = "mwlwifi/WlanCalData_ext.conf", ++ .txpwrlmt_file = "mwlwifi/txpwrlmt_cfg.conf", ++ .antenna_tx = ANTENNA_TX_2, ++ .antenna_rx = ANTENNA_RX_2, ++ }, ++}; ++ ++static int pcie_alloc_resource(struct pcie_priv *pcie_priv) ++{ ++ struct pci_dev *pdev = pcie_priv->pdev; ++ struct device *dev = &pdev->dev; ++ void __iomem *addr; ++ ++ pcie_priv->next_bar_num = 1; /* 32-bit */ ++ if (pci_resource_flags(pdev, 0) & 0x04) ++ pcie_priv->next_bar_num = 2; /* 64-bit */ ++ ++ addr = devm_ioremap_resource(dev, &pdev->resource[0]); ++ if (IS_ERR(addr)) { ++ pr_err("%s: cannot reserve PCI memory region 0\n", ++ PCIE_DRV_NAME); ++ goto err; ++ } ++ pcie_priv->iobase0 = addr; ++ pr_debug("iobase0 = %p\n", pcie_priv->iobase0); ++ ++ addr = devm_ioremap_resource(dev, ++ &pdev->resource[pcie_priv->next_bar_num]); ++ if (IS_ERR(addr)) { ++ pr_err("%s: cannot reserve PCI memory region 1\n", ++ PCIE_DRV_NAME); ++ goto err; ++ } ++ pcie_priv->iobase1 = addr; ++ pr_debug("iobase1 = %p\n", pcie_priv->iobase1); ++ ++ return 0; ++ ++err: ++ pr_err("pci alloc fail\n"); ++ ++ return -EIO; ++} ++ ++static u32 pcie_read_mac_reg(struct pcie_priv *pcie_priv, u32 offset) ++{ ++ struct mwl_priv *priv = pcie_priv->mwl_priv; ++ ++ if (priv->chip_type == MWL8964) { ++ u32 *addr_val = kmalloc(64 * sizeof(u32), GFP_ATOMIC); ++ u32 val; ++ ++ if (addr_val) { ++ mwl_fwcmd_get_addr_value(priv->hw, ++ 0x8000a000 + offset, 4, ++ addr_val, 0); ++ val = addr_val[0]; ++ kfree(addr_val); ++ return val; ++ } ++ return 0; ++ } else ++ return le32_to_cpu(*(__le32 *) ++ (MAC_REG_ADDR_PCI(offset))); ++} ++ ++static bool pcie_chk_adapter(struct pcie_priv *pcie_priv) ++{ ++ struct mwl_priv *priv = pcie_priv->mwl_priv; ++ u32 regval; ++ ++ regval = readl(pcie_priv->iobase1 + MACREG_REG_INT_CODE); ++ ++ if (regval == 0xffffffff) { ++ wiphy_err(priv->hw->wiphy, "adapter does not exist\n"); ++ return false; ++ } ++ ++ if (priv->cmd_timeout) ++ wiphy_debug(priv->hw->wiphy, "MACREG_REG_INT_CODE: 0x%04x\n", ++ regval); ++ ++ return true; ++} ++ ++static void pcie_send_cmd(struct pcie_priv *pcie_priv) ++{ ++ writel(pcie_priv->mwl_priv->pphys_cmd_buf, ++ pcie_priv->iobase1 + MACREG_REG_GEN_PTR); ++ writel(MACREG_H2ARIC_BIT_DOOR_BELL, ++ pcie_priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS); ++} ++ ++static int pcie_wait_complete(struct mwl_priv *priv, unsigned short cmd) ++{ ++ unsigned int curr_iteration = MAX_WAIT_FW_COMPLETE_ITERATIONS; ++ unsigned short int_code = 0; ++ ++ do { ++ int_code = le16_to_cpu(*((__le16 *)&priv->pcmd_buf[0])); ++ usleep_range(1000, 2000); ++ } while ((int_code != cmd) && (--curr_iteration) && !priv->rmmod); ++ ++ if (curr_iteration == 0) { ++ wiphy_err(priv->hw->wiphy, "cmd 0x%04x=%s timed out\n", ++ cmd, mwl_fwcmd_get_cmd_string(cmd)); ++ wiphy_err(priv->hw->wiphy, "return code: 0x%04x\n", int_code); ++ return -EIO; ++ } ++ ++ if (priv->chip_type != MWL8997) ++ usleep_range(3000, 5000); ++ ++ return 0; ++} ++ ++static int pcie_init(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ const struct hostcmd_get_hw_spec *get_hw_spec; ++ struct hostcmd_set_hw_spec set_hw_spec; ++ int rc, i; ++ ++ spin_lock_init(&pcie_priv->int_mask_lock); ++ tasklet_init(&pcie_priv->tx_task, ++ (void *)pcie_tx_skbs, (unsigned long)hw); ++ tasklet_disable(&pcie_priv->tx_task); ++ tasklet_init(&pcie_priv->tx_done_task, ++ (void *)pcie_tx_done, (unsigned long)hw); ++ tasklet_disable(&pcie_priv->tx_done_task); ++ spin_lock_init(&pcie_priv->tx_desc_lock); ++ tasklet_init(&pcie_priv->rx_task, ++ (void *)pcie_rx_recv, (unsigned long)hw); ++ tasklet_disable(&pcie_priv->rx_task); ++ tasklet_init(&pcie_priv->qe_task, ++ (void *)pcie_tx_flush_amsdu, (unsigned long)hw); ++ tasklet_disable(&pcie_priv->qe_task); ++ pcie_priv->txq_limit = PCIE_TX_QUEUE_LIMIT; ++ pcie_priv->txq_wake_threshold = PCIE_TX_WAKE_Q_THRESHOLD; ++ pcie_priv->is_tx_done_schedule = false; ++ pcie_priv->recv_limit = PCIE_RECEIVE_LIMIT; ++ pcie_priv->is_rx_schedule = false; ++ pcie_priv->is_qe_schedule = false; ++ pcie_priv->qe_trig_num = 0; ++ pcie_priv->qe_trig_time = jiffies; ++ ++ rc = pcie_tx_init(hw); ++ if (rc) { ++ wiphy_err(hw->wiphy, "%s: fail to initialize TX\n", ++ PCIE_DRV_NAME); ++ goto err_mwl_tx_init; ++ } ++ ++ rc = pcie_rx_init(hw); ++ if (rc) { ++ wiphy_err(hw->wiphy, "%s: fail to initialize RX\n", ++ PCIE_DRV_NAME); ++ goto err_mwl_rx_init; ++ } ++ ++ /* get and prepare HW specifications */ ++ get_hw_spec = mwl_fwcmd_get_hw_specs(hw); ++ if (!get_hw_spec) { ++ wiphy_err(hw->wiphy, "%s: fail to get HW specifications\n", ++ PCIE_DRV_NAME); ++ goto err_get_hw_specs; ++ } ++ ether_addr_copy(&priv->hw_data.mac_addr[0], ++ get_hw_spec->permanent_addr); ++ pcie_priv->desc_data[0].wcb_base = ++ le32_to_cpu(get_hw_spec->wcb_base0) & 0x0000ffff; ++ for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++) ++ pcie_priv->desc_data[i].wcb_base = ++ le32_to_cpu(get_hw_spec->wcb_base[i - 1]) & 0x0000ffff; ++ pcie_priv->desc_data[0].rx_desc_read = ++ le32_to_cpu(get_hw_spec->rxpd_rd_ptr) & 0x0000ffff; ++ pcie_priv->desc_data[0].rx_desc_write = ++ le32_to_cpu(get_hw_spec->rxpd_wr_ptr) & 0x0000ffff; ++ priv->hw_data.fw_release_num = le32_to_cpu(get_hw_spec->fw_release_num); ++ priv->hw_data.hw_version = get_hw_spec->version; ++ if (priv->chip_type != MWL8997) { ++ writel(pcie_priv->desc_data[0].pphys_tx_ring, ++ pcie_priv->iobase0 + pcie_priv->desc_data[0].wcb_base); ++ for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++) ++ writel(pcie_priv->desc_data[i].pphys_tx_ring, ++ pcie_priv->iobase0 + ++ pcie_priv->desc_data[i].wcb_base); ++ } ++ writel(pcie_priv->desc_data[0].pphys_rx_ring, ++ pcie_priv->iobase0 + pcie_priv->desc_data[0].rx_desc_read); ++ writel(pcie_priv->desc_data[0].pphys_rx_ring, ++ pcie_priv->iobase0 + pcie_priv->desc_data[0].rx_desc_write); ++ ++ /* prepare and set HW specifications */ ++ memset(&set_hw_spec, 0, sizeof(set_hw_spec)); ++ if (priv->chip_type == MWL8997) { ++ set_hw_spec.wcb_base[0] = ++ cpu_to_le32(pcie_priv->txbd_ring_pbase); ++ set_hw_spec.tx_wcb_num_per_queue = ++ cpu_to_le32(PCIE_MAX_TXRX_BD); ++ set_hw_spec.num_tx_queues = cpu_to_le32(1); ++ set_hw_spec.features |= HW_SET_PARMS_FEATURES_HOST_PROBE_RESP; ++ } else { ++ set_hw_spec.wcb_base[0] = ++ cpu_to_le32(pcie_priv->desc_data[0].pphys_tx_ring); ++ for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++) ++ set_hw_spec.wcb_base[i] = cpu_to_le32( ++ pcie_priv->desc_data[i].pphys_tx_ring); ++ set_hw_spec.tx_wcb_num_per_queue = ++ cpu_to_le32(PCIE_MAX_NUM_TX_DESC); ++ set_hw_spec.num_tx_queues = cpu_to_le32(PCIE_NUM_OF_DESC_DATA); ++ } ++ set_hw_spec.total_rx_wcb = cpu_to_le32(PCIE_MAX_NUM_RX_DESC); ++ set_hw_spec.rxpd_wr_ptr = ++ cpu_to_le32(pcie_priv->desc_data[0].pphys_rx_ring); ++ rc = mwl_fwcmd_set_hw_specs(hw, &set_hw_spec); ++ if (rc) { ++ wiphy_err(hw->wiphy, "%s: fail to set HW specifications\n", ++ PCIE_DRV_NAME); ++ goto err_set_hw_specs; ++ } ++ ++ return rc; ++ ++err_set_hw_specs: ++err_get_hw_specs: ++ ++ pcie_rx_deinit(hw); ++ ++err_mwl_rx_init: ++ ++ pcie_tx_deinit(hw); ++ ++err_mwl_tx_init: ++ ++ wiphy_err(hw->wiphy, "%s: init fail\n", PCIE_DRV_NAME); ++ ++ return rc; ++} ++ ++static void pcie_deinit(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ pcie_rx_deinit(hw); ++ pcie_tx_deinit(hw); ++ tasklet_kill(&pcie_priv->qe_task); ++ tasklet_kill(&pcie_priv->rx_task); ++ tasklet_kill(&pcie_priv->tx_done_task); ++ tasklet_kill(&pcie_priv->tx_task); ++ pcie_reset(hw); ++} ++ ++static int pcie_get_info(struct ieee80211_hw *hw, char *buf, size_t size) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ char *p = buf; ++ int len = 0; ++ ++ len += scnprintf(p + len, size - len, "iobase0: %p\n", ++ pcie_priv->iobase0); ++ len += scnprintf(p + len, size - len, "iobase1: %p\n", ++ pcie_priv->iobase1); ++ len += scnprintf(p + len, size - len, ++ "tx limit: %d\n", pcie_priv->txq_limit); ++ len += scnprintf(p + len, size - len, ++ "rx limit: %d\n", pcie_priv->recv_limit); ++ len += scnprintf(p + len, size - len, ++ "qe trigger number: %d\n", pcie_priv->qe_trig_num); ++ return len; ++} ++ ++static void pcie_enable_data_tasks(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ tasklet_enable(&pcie_priv->tx_task); ++ tasklet_enable(&pcie_priv->tx_done_task); ++ tasklet_enable(&pcie_priv->rx_task); ++ tasklet_enable(&pcie_priv->qe_task); ++} ++ ++static void pcie_disable_data_tasks(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ tasklet_disable(&pcie_priv->tx_task); ++ tasklet_disable(&pcie_priv->tx_done_task); ++ tasklet_disable(&pcie_priv->rx_task); ++ tasklet_disable(&pcie_priv->qe_task); ++} ++ ++static int pcie_exec_cmd(struct ieee80211_hw *hw, unsigned short cmd) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ bool busy = false; ++ ++ might_sleep(); ++ ++ if (!pcie_chk_adapter(pcie_priv)) { ++ wiphy_err(priv->hw->wiphy, "adapter does not exist\n"); ++ priv->in_send_cmd = false; ++ return -EIO; ++ } ++ ++ if (!priv->in_send_cmd && !priv->rmmod) { ++ priv->in_send_cmd = true; ++ if (priv->dump_hostcmd) ++ wiphy_debug(priv->hw->wiphy, "send cmd 0x%04x=%s\n", ++ cmd, mwl_fwcmd_get_cmd_string(cmd)); ++ pcie_send_cmd(pcie_priv); ++ if (pcie_wait_complete(priv, 0x8000 | cmd)) { ++ wiphy_err(priv->hw->wiphy, "timeout: 0x%04x\n", cmd); ++ priv->in_send_cmd = false; ++ priv->cmd_timeout = true; ++ if (priv->heartbeat) ++ vendor_cmd_basic_event( ++ hw->wiphy, ++ MWL_VENDOR_EVENT_CMD_TIMEOUT); ++ return -EIO; ++ } ++ } else { ++ wiphy_warn(priv->hw->wiphy, ++ "previous command is running or module removed\n"); ++ busy = true; ++ } ++ ++ if (!busy) ++ priv->in_send_cmd = false; ++ ++ return 0; ++} ++ ++static int pcie_get_irq_num(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ return pcie_priv->pdev->irq; ++} ++ ++static irqreturn_t pcie_isr(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ u32 int_status; ++ ++ int_status = readl(pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); ++ ++ if (int_status == 0x00000000) ++ return IRQ_NONE; ++ ++ if (int_status == 0xffffffff) { ++ wiphy_warn(hw->wiphy, "card unplugged?\n"); ++ } else { ++ writel(~int_status, ++ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); ++ ++ if (int_status & MACREG_A2HRIC_BIT_TX_DONE) { ++ if (!pcie_priv->is_tx_done_schedule) { ++ pcie_mask_int(pcie_priv, ++ MACREG_A2HRIC_BIT_TX_DONE, false); ++ tasklet_schedule(&pcie_priv->tx_done_task); ++ pcie_priv->is_tx_done_schedule = true; ++ } ++ } ++ ++ if (int_status & MACREG_A2HRIC_BIT_RX_RDY) { ++ if (!pcie_priv->is_rx_schedule) { ++ pcie_mask_int(pcie_priv, ++ MACREG_A2HRIC_BIT_RX_RDY, false); ++ tasklet_schedule(&pcie_priv->rx_task); ++ pcie_priv->is_rx_schedule = true; ++ } ++ } ++ ++ if (int_status & MACREG_A2HRIC_BIT_RADAR_DETECT) { ++ wiphy_info(hw->wiphy, "radar detected by firmware\n"); ++ ieee80211_radar_detected(hw); ++ } ++ ++ if (int_status & MACREG_A2HRIC_BIT_QUE_EMPTY) { ++ if (!pcie_priv->is_qe_schedule) { ++ if (time_after(jiffies, ++ (pcie_priv->qe_trig_time + 1))) { ++ pcie_mask_int(pcie_priv, ++ MACREG_A2HRIC_BIT_QUE_EMPTY, ++ false); ++ tasklet_schedule(&pcie_priv->qe_task); ++ pcie_priv->qe_trig_num++; ++ pcie_priv->is_qe_schedule = true; ++ pcie_priv->qe_trig_time = jiffies; ++ } ++ } ++ } ++ ++ if (int_status & MACREG_A2HRIC_BIT_CHAN_SWITCH) ++ ieee80211_queue_work(hw, &priv->chnl_switch_handle); ++ ++ if (int_status & MACREG_A2HRIC_BA_WATCHDOG) ++ ieee80211_queue_work(hw, &priv->watchdog_ba_handle); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void pcie_irq_enable(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ if (pcie_chk_adapter(pcie_priv)) { ++ writel(0x00, ++ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); ++ writel(MACREG_A2HRIC_BIT_MASK, ++ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); ++ } ++} ++ ++static void pcie_irq_disable(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ if (pcie_chk_adapter(pcie_priv)) ++ writel(0x00, ++ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); ++} ++ ++static void pcie_timer_routine(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ static int cnt; ++ struct mwl_ampdu_stream *stream; ++ struct mwl_sta *sta_info; ++ struct mwl_tx_info *tx_stats; ++ struct mwl_ampdu_stream *rm_stream = NULL; ++ u32 rm_pkts = 0; ++ bool ba_full = true; ++ int i; ++ ++ if ((++cnt * SYSADPT_TIMER_WAKEUP_TIME) < CHECK_BA_TRAFFIC_TIME) ++ return; ++ cnt = 0; ++ spin_lock_bh(&priv->stream_lock); ++ for (i = 0; i < priv->ampdu_num; i++) { ++ stream = &priv->ampdu[i]; ++ ++ if (stream->state == AMPDU_STREAM_ACTIVE) { ++ sta_info = mwl_dev_get_sta(stream->sta); ++ tx_stats = &sta_info->tx_stats[stream->tid]; ++ ++ if ((jiffies - tx_stats->start_time > HZ) && ++ (tx_stats->pkts < SYSADPT_AMPDU_PACKET_THRESHOLD)) { ++ if (rm_pkts) { ++ if (tx_stats->pkts < rm_pkts) { ++ rm_stream = stream; ++ rm_pkts = tx_stats->pkts; ++ } ++ } else { ++ rm_stream = stream; ++ rm_pkts = tx_stats->pkts; ++ } ++ } ++ ++ if (jiffies - tx_stats->start_time > HZ) { ++ tx_stats->pkts = 0; ++ tx_stats->start_time = jiffies; ++ } ++ } else ++ ba_full = false; ++ } ++ if (ba_full && rm_stream) { ++ ieee80211_stop_tx_ba_session(rm_stream->sta, ++ rm_stream->tid); ++ wiphy_debug(hw->wiphy, "Stop BA %pM\n", rm_stream->sta->addr); ++ } ++ spin_unlock_bh(&priv->stream_lock); ++} ++ ++static void pcie_tx_return_pkts(struct ieee80211_hw *hw) ++{ ++ pcie_tx_done((unsigned long)hw); ++} ++ ++static struct device_node *pcie_get_device_node(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct device_node *dev_node; ++ ++ dev_node = pci_bus_to_OF_node(pcie_priv->pdev->bus); ++ wiphy_info(priv->hw->wiphy, "device node: %s\n", dev_node->full_name); ++ ++ return dev_node; ++} ++ ++static void pcie_get_survey(struct ieee80211_hw *hw, ++ struct mwl_survey_info *survey_info) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ survey_info->filled = SURVEY_INFO_TIME | ++ SURVEY_INFO_TIME_BUSY | ++ SURVEY_INFO_TIME_TX | ++ SURVEY_INFO_NOISE_DBM; ++ survey_info->time_period += pcie_read_mac_reg(pcie_priv, MCU_LAST_READ); ++ survey_info->time_busy += pcie_read_mac_reg(pcie_priv, MCU_CCA_CNT); ++ survey_info->time_tx += pcie_read_mac_reg(pcie_priv, MCU_TXPE_CNT); ++ survey_info->noise = priv->noise; ++} ++ ++static int pcie_reg_access(struct ieee80211_hw *hw, bool write) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ u8 set; ++ u32 *addr_val; ++ int ret = 0; ++ ++ set = write ? WL_SET : WL_GET; ++ ++ switch (priv->reg_type) { ++ case MWL_ACCESS_RF: ++ ret = mwl_fwcmd_reg_rf(hw, set, priv->reg_offset, ++ &priv->reg_value); ++ break; ++ case MWL_ACCESS_BBP: ++ ret = mwl_fwcmd_reg_bb(hw, set, priv->reg_offset, ++ &priv->reg_value); ++ break; ++ case MWL_ACCESS_CAU: ++ ret = mwl_fwcmd_reg_cau(hw, set, priv->reg_offset, ++ &priv->reg_value); ++ break; ++ case MWL_ACCESS_ADDR0: ++ if (set == WL_GET) ++ priv->reg_value = ++ readl(pcie_priv->iobase0 + priv->reg_offset); ++ else ++ writel(priv->reg_value, ++ pcie_priv->iobase0 + priv->reg_offset); ++ break; ++ case MWL_ACCESS_ADDR1: ++ if (set == WL_GET) ++ priv->reg_value = ++ readl(pcie_priv->iobase1 + priv->reg_offset); ++ else ++ writel(priv->reg_value, ++ pcie_priv->iobase1 + priv->reg_offset); ++ break; ++ case MWL_ACCESS_ADDR: ++ addr_val = kzalloc(64 * sizeof(u32), GFP_KERNEL); ++ if (addr_val) { ++ addr_val[0] = priv->reg_value; ++ ret = mwl_fwcmd_get_addr_value(hw, priv->reg_offset, ++ 4, addr_val, set); ++ if ((!ret) && (set == WL_GET)) ++ priv->reg_value = addr_val[0]; ++ kfree(addr_val); ++ } else { ++ ret = -ENOMEM; ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static struct mwl_hif_ops pcie_hif_ops = { ++ .driver_name = PCIE_DRV_NAME, ++ .driver_version = PCIE_DRV_VERSION, ++ .tx_head_room = PCIE_MIN_BYTES_HEADROOM, ++ .ampdu_num = PCIE_AMPDU_QUEUES, ++ .reset = pcie_reset, ++ .init = pcie_init, ++ .deinit = pcie_deinit, ++ .get_info = pcie_get_info, ++ .enable_data_tasks = pcie_enable_data_tasks, ++ .disable_data_tasks = pcie_disable_data_tasks, ++ .exec_cmd = pcie_exec_cmd, ++ .get_irq_num = pcie_get_irq_num, ++ .irq_handler = pcie_isr, ++ .irq_enable = pcie_irq_enable, ++ .irq_disable = pcie_irq_disable, ++ .download_firmware = pcie_download_firmware, ++ .timer_routine = pcie_timer_routine, ++ .tx_xmit = pcie_tx_xmit, ++ .tx_del_pkts_via_vif = pcie_tx_del_pkts_via_vif, ++ .tx_del_pkts_via_sta = pcie_tx_del_pkts_via_sta, ++ .tx_del_ampdu_pkts = pcie_tx_del_ampdu_pkts, ++ .tx_del_sta_amsdu_pkts = pcie_tx_del_sta_amsdu_pkts, ++ .tx_return_pkts = pcie_tx_return_pkts, ++ .get_device_node = pcie_get_device_node, ++ .get_survey = pcie_get_survey, ++ .reg_access = pcie_reg_access, ++}; ++ ++static int pcie_init_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ const struct hostcmd_get_hw_spec *get_hw_spec; ++ struct hostcmd_set_hw_spec set_hw_spec; ++ int rc; ++ ++ spin_lock_init(&pcie_priv->int_mask_lock); ++ tasklet_init(&pcie_priv->tx_task, ++ (void *)pcie_tx_skbs_ndp, (unsigned long)hw); ++ tasklet_disable(&pcie_priv->tx_task); ++ spin_lock_init(&pcie_priv->tx_desc_lock); ++ tasklet_init(&pcie_priv->rx_task, ++ (void *)pcie_rx_recv_ndp, (unsigned long)hw); ++ tasklet_disable(&pcie_priv->rx_task); ++ pcie_priv->txq_limit = TX_QUEUE_LIMIT; ++ pcie_priv->txq_wake_threshold = TX_WAKE_Q_THRESHOLD; ++ pcie_priv->is_tx_schedule = false; ++ pcie_priv->recv_limit = MAX_NUM_RX_DESC; ++ pcie_priv->is_rx_schedule = false; ++ ++ rc = pcie_tx_init_ndp(hw); ++ if (rc) { ++ wiphy_err(hw->wiphy, "%s: fail to initialize TX\n", ++ PCIE_DRV_NAME); ++ goto err_mwl_tx_init; ++ } ++ ++ rc = pcie_rx_init_ndp(hw); ++ if (rc) { ++ wiphy_err(hw->wiphy, "%s: fail to initialize RX\n", ++ PCIE_DRV_NAME); ++ goto err_mwl_rx_init; ++ } ++ ++ /* get and prepare HW specifications */ ++ get_hw_spec = mwl_fwcmd_get_hw_specs(hw); ++ if (!get_hw_spec) { ++ wiphy_err(hw->wiphy, "%s: fail to get HW specifications\n", ++ PCIE_DRV_NAME); ++ goto err_get_hw_specs; ++ } ++ ether_addr_copy(&priv->hw_data.mac_addr[0], ++ get_hw_spec->permanent_addr); ++ priv->hw_data.fw_release_num = le32_to_cpu(get_hw_spec->fw_release_num); ++ priv->hw_data.hw_version = get_hw_spec->version; ++ ++ /* prepare and set HW specifications */ ++ memset(&set_hw_spec, 0, sizeof(set_hw_spec)); ++ set_hw_spec.wcb_base[0] = ++ cpu_to_le32(pcie_priv->desc_data_ndp.pphys_tx_ring); ++ set_hw_spec.wcb_base[1] = ++ cpu_to_le32(pcie_priv->desc_data_ndp.pphys_tx_ring_done); ++ set_hw_spec.wcb_base[2] = ++ cpu_to_le32(pcie_priv->desc_data_ndp.pphys_rx_ring); ++ set_hw_spec.wcb_base[3] = ++ cpu_to_le32(pcie_priv->desc_data_ndp.pphys_rx_ring_done); ++ set_hw_spec.acnt_base_addr = ++ cpu_to_le32(pcie_priv->desc_data_ndp.pphys_acnt_ring); ++ set_hw_spec.acnt_buf_size = ++ cpu_to_le32(pcie_priv->desc_data_ndp.acnt_ring_size); ++ rc = mwl_fwcmd_set_hw_specs(hw, &set_hw_spec); ++ if (rc) { ++ wiphy_err(hw->wiphy, "%s: fail to set HW specifications\n", ++ PCIE_DRV_NAME); ++ goto err_set_hw_specs; ++ } ++ ++ return rc; ++ ++err_set_hw_specs: ++err_get_hw_specs: ++ ++ pcie_rx_deinit_ndp(hw); ++ ++err_mwl_rx_init: ++ ++ pcie_tx_deinit_ndp(hw); ++ ++err_mwl_tx_init: ++ ++ wiphy_err(hw->wiphy, "%s: init fail\n", PCIE_DRV_NAME); ++ ++ return rc; ++} ++ ++static void pcie_deinit_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ pcie_rx_deinit_ndp(hw); ++ pcie_tx_deinit_ndp(hw); ++ tasklet_kill(&pcie_priv->rx_task); ++ tasklet_kill(&pcie_priv->tx_task); ++ pcie_reset(hw); ++} ++ ++static int pcie_get_info_ndp(struct ieee80211_hw *hw, char *buf, size_t size) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ char *p = buf; ++ int len = 0; ++ ++ len += scnprintf(p + len, size - len, "iobase0: %p\n", ++ pcie_priv->iobase0); ++ len += scnprintf(p + len, size - len, "iobase1: %p\n", ++ pcie_priv->iobase1); ++ len += scnprintf(p + len, size - len, ++ "tx limit: %d\n", pcie_priv->txq_limit); ++ len += scnprintf(p + len, size - len, ++ "rx limit: %d\n", pcie_priv->recv_limit); ++ return len; ++} ++ ++static int pcie_get_tx_status_ndp(struct ieee80211_hw *hw, char *buf, ++ size_t size) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ char *p = buf; ++ int len = 0; ++ ++ len += scnprintf(p + len, size - len, "tx_done_cnt: %d\n", ++ pcie_priv->tx_done_cnt); ++ len += scnprintf(p + len, size - len, "tx_desc_busy_cnt: %d\n", ++ pcie_priv->desc_data_ndp.tx_desc_busy_cnt); ++ len += scnprintf(p + len, size - len, "tx_sent_head: %d\n", ++ pcie_priv->desc_data_ndp.tx_sent_head); ++ len += scnprintf(p + len, size - len, "tx_sent_tail: %d\n", ++ pcie_priv->desc_data_ndp.tx_sent_tail); ++ len += scnprintf(p + len, size - len, "tx_done_head: %d\n", ++ readl(pcie_priv->iobase1 + MACREG_REG_TXDONEHEAD)); ++ len += scnprintf(p + len, size - len, "tx_done_tail: %d\n", ++ pcie_priv->desc_data_ndp.tx_done_tail); ++ len += scnprintf(p + len, size - len, "tx_vbuflist_idx: %d\n", ++ pcie_priv->desc_data_ndp.tx_vbuflist_idx); ++ return len; ++} ++ ++static int pcie_get_rx_status_ndp(struct ieee80211_hw *hw, char *buf, ++ size_t size) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ char *p = buf; ++ int len = 0; ++ ++ len += scnprintf(p + len, size - len, "rx_done_head: %d\n", ++ readl(pcie_priv->iobase1 + MACREG_REG_RXDONEHEAD)); ++ len += scnprintf(p + len, size - len, "rx_done_tail: %d\n", ++ readl(pcie_priv->iobase1 + MACREG_REG_RXDONETAIL)); ++ len += scnprintf(p + len, size - len, "rx_desc_head: %d\n", ++ readl(pcie_priv->iobase1 + MACREG_REG_RXDESCHEAD)); ++ len += scnprintf(p + len, size - len, "rx_skb_trace: %d\n", ++ skb_queue_len(&pcie_priv->rx_skb_trace)); ++ len += scnprintf(p + len, size - len, "rx_skb_unlink_err: %d\n", ++ pcie_priv->rx_skb_unlink_err); ++ len += scnprintf(p + len, size - len, "signature_err: %d\n", ++ pcie_priv->signature_err); ++ len += scnprintf(p + len, size - len, "recheck_rxringdone: %d\n", ++ pcie_priv->recheck_rxringdone); ++ len += scnprintf(p + len, size - len, "fast_data_cnt: %d\n", ++ pcie_priv->rx_cnts.fast_data_cnt); ++ len += scnprintf(p + len, size - len, "fast_bad_amsdu_cnt: %d\n", ++ pcie_priv->rx_cnts.fast_bad_amsdu_cnt); ++ len += scnprintf(p + len, size - len, "slow_noqueue_cnt: %d\n", ++ pcie_priv->rx_cnts.slow_noqueue_cnt); ++ len += scnprintf(p + len, size - len, "slow_norun_cnt: %d\n", ++ pcie_priv->rx_cnts.slow_norun_cnt); ++ len += scnprintf(p + len, size - len, "slow_mcast_cnt: %d\n", ++ pcie_priv->rx_cnts.slow_mcast_cnt); ++ len += scnprintf(p + len, size - len, "slow_bad_sta_cnt: %d\n", ++ pcie_priv->rx_cnts.slow_bad_sta_cnt); ++ len += scnprintf(p + len, size - len, "slow_bad_mic_cnt: %d\n", ++ pcie_priv->rx_cnts.slow_bad_mic_cnt); ++ len += scnprintf(p + len, size - len, "slow_bad_pn_cnt: %d\n", ++ pcie_priv->rx_cnts.slow_bad_pn_cnt); ++ len += scnprintf(p + len, size - len, "slow_mgmt_cnt: %d\n", ++ pcie_priv->rx_cnts.slow_mgmt_cnt); ++ len += scnprintf(p + len, size - len, "slow_promisc_cnt: %d\n", ++ pcie_priv->rx_cnts.slow_promisc_cnt); ++ len += scnprintf(p + len, size - len, "drop_cnt: %d\n", ++ pcie_priv->rx_cnts.drop_cnt); ++ len += scnprintf(p + len, size - len, "offch_promisc_cnt: %d\n", ++ pcie_priv->rx_cnts.offch_promisc_cnt); ++ len += scnprintf(p + len, size - len, "mu_pkt_cnt: %d\n", ++ pcie_priv->rx_cnts.mu_pkt_cnt); ++ return len; ++} ++ ++static void pcie_enable_data_tasks_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ tasklet_enable(&pcie_priv->tx_task); ++ tasklet_enable(&pcie_priv->rx_task); ++} ++ ++static void pcie_disable_data_tasks_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ tasklet_disable(&pcie_priv->tx_task); ++ tasklet_disable(&pcie_priv->rx_task); ++} ++ ++static irqreturn_t pcie_isr_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ u32 int_status; ++ ++ int_status = readl(pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); ++ ++ if (int_status == 0x00000000) ++ return IRQ_NONE; ++ ++ if (int_status == 0xffffffff) { ++ wiphy_warn(hw->wiphy, "card unplugged?\n"); ++ } else { ++ writel(~int_status, ++ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); ++ ++ if (int_status & MACREG_A2HRIC_ACNT_HEAD_RDY) ++ ieee80211_queue_work(hw, &priv->account_handle); ++ ++ if (int_status & MACREG_A2HRIC_RX_DONE_HEAD_RDY) { ++ if (!pcie_priv->is_rx_schedule) { ++ pcie_mask_int(pcie_priv, ++ MACREG_A2HRIC_RX_DONE_HEAD_RDY, ++ false); ++ tasklet_schedule(&pcie_priv->rx_task); ++ pcie_priv->is_rx_schedule = true; ++ } ++ } ++ ++ if (int_status & MACREG_A2HRIC_NEWDP_DFS) { ++ wiphy_info(hw->wiphy, "radar detected by firmware\n"); ++ ieee80211_radar_detected(hw); ++ } ++ ++ if (int_status & MACREG_A2HRIC_NEWDP_CHANNEL_SWITCH) ++ ieee80211_queue_work(hw, &priv->chnl_switch_handle); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void pcie_irq_enable_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ if (pcie_chk_adapter(pcie_priv)) { ++ writel(0x00, ++ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); ++ writel(MACREG_A2HRIC_BIT_MASK_NDP, ++ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); ++ } ++} ++ ++static void pcie_timer_routine_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int num = SYSADPT_TX_WMM_QUEUES; ++ static int cnt; ++ ++ if (!pcie_priv->is_tx_schedule) { ++ while (num--) { ++ if (skb_queue_len(&pcie_priv->txq[num]) > 0) { ++ tasklet_schedule(&pcie_priv->tx_task); ++ pcie_priv->is_tx_schedule = true; ++ break; ++ } ++ } ++ } ++ ++ if ((++cnt * SYSADPT_TIMER_WAKEUP_TIME) >= CHECK_TX_DONE_TIME) { ++ pcie_tx_done_ndp(hw); ++ cnt = 0; ++ } ++} ++ ++static void pcie_tx_return_pkts_ndp(struct ieee80211_hw *hw) ++{ ++ pcie_tx_done_ndp(hw); ++} ++ ++static void pcie_set_sta_id(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta, ++ bool sta_mode, bool set) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct mwl_sta *sta_info; ++ u16 stnid; ++ ++ sta_info = mwl_dev_get_sta(sta); ++ stnid = sta_mode ? 0 : sta_info->stnid; ++ pcie_priv->sta_link[stnid] = set ? sta : NULL; ++} ++ ++static void pcie_tx_account(struct mwl_priv *priv, ++ struct mwl_sta *sta_info, ++ struct acnt_tx_s *acnt_tx) ++{ ++ u32 rate_info, tx_cnt; ++ u8 index, type, rate_ac, format, bw, gi, mcs, nss; ++ u16 ratemask; ++ u8 i, found; ++ struct mwl_tx_hist *tx_hist; ++ struct mwl_tx_hist_data *tx_hist_data; ++ ++ rate_info = le32_to_cpu(acnt_tx->tx_info.rate_info); ++ tx_cnt = le32_to_cpu(acnt_tx->tx_cnt); ++ index = acnt_tx->rate_tbl_index; ++ type = acnt_tx->type; ++ ++ if (!rate_info) ++ return; ++ sta_info->tx_rate_info = rate_info; ++ ++ tx_hist = &sta_info->tx_hist; ++ if (!tx_hist || (type >= SU_MU_TYPE_CNT)) ++ return; ++ ++ format = rate_info & MWL_TX_RATE_FORMAT_MASK; ++ bw = (rate_info & MWL_TX_RATE_BANDWIDTH_MASK) >> ++ MWL_TX_RATE_BANDWIDTH_SHIFT; ++ gi = (rate_info & MWL_TX_RATE_SHORTGI_MASK) >> ++ MWL_TX_RATE_SHORTGI_SHIFT; ++ mcs = (rate_info & MWL_TX_RATE_RATEIDMCS_MASK) >> ++ MWL_TX_RATE_RATEIDMCS_SHIFT; ++ ++ tx_hist->cur_rate_info[type] = rate_info; ++ ++ /* Rate table index is valid */ ++ if (index != 0xff) { ++ if (type == MU_MIMO) { ++ rate_ac = mcs & 0xf; ++ nss = mcs >> 4; ++ if (nss < (QS_NUM_SUPPORTED_11AC_NSS - 1)) { ++ tx_hist_data = ++ &tx_hist->mu_rate[nss][bw][gi][rate_ac]; ++ tx_hist_data->rateinfo = rate_info; ++ tx_hist_data->cnt++; ++ tx_hist->total_tx_cnt[type] += tx_cnt; ++ } ++ } else { ++ /* If legacy, skip legacy preamble bit 15 */ ++ if (format == TX_RATE_FORMAT_LEGACY) ++ ratemask = 0xfff; ++ else ++ ratemask = 0xffff; ++ tx_hist_data = &tx_hist->su_rate[0]; ++ if ((tx_hist_data[index].rateinfo & ratemask) == ++ (rate_info & ratemask)) { ++ tx_hist_data[index].cnt++; ++ tx_hist->total_tx_cnt[type] += tx_cnt; ++ } ++ } ++ } else { ++ if (type == MU_MIMO) { ++ rate_ac = mcs & 0xf; ++ nss = mcs >> 4; ++ if (nss < (QS_NUM_SUPPORTED_11AC_NSS - 1)) { ++ tx_hist_data = ++ &tx_hist->mu_rate[nss][bw][gi][rate_ac]; ++ tx_hist_data->rateinfo = rate_info; ++ tx_hist_data->cnt++; ++ tx_hist->total_tx_cnt[type] += tx_cnt; ++ } ++ } else { ++ /* If legacy, skip legacy preamble bit 15 */ ++ if (format == TX_RATE_FORMAT_LEGACY) ++ ratemask = 0xfff; ++ else ++ ratemask = 0xffff; ++ tx_hist_data = &tx_hist->custom_rate[0]; ++ /* Go through non rate table buffer to see if any has ++ * been used. If all used up, recycle by using index 0 ++ */ ++ for (i = 0; i < TX_RATE_HISTO_CUSTOM_CNT; i++) { ++ if (!tx_hist_data[i].rateinfo || ++ ((tx_hist_data[i].rateinfo & ratemask) == ++ (rate_info & ratemask))) { ++ found = 1; ++ break; ++ } ++ } ++ if (found) ++ index = i; ++ else ++ index = 0; /* reuse index 0 buffer */ ++ tx_hist_data[index].rateinfo = rate_info; ++ tx_hist_data[index].cnt++; ++ tx_hist->total_tx_cnt[type] += tx_cnt; ++ } ++ } ++} ++ ++static void pcie_rx_account(struct mwl_priv *priv, ++ struct mwl_sta *sta_info, ++ struct acnt_rx_s *acnt_rx) ++{ ++ u32 sig1, sig2, rate, param; ++ u16 format, nss, bw, gi, rate_mcs; ++ ++ sig1 = (le32_to_cpu(acnt_rx->rx_info.ht_sig1) >> ++ RXINFO_HT_SIG1_SHIFT) & RXINFO_HT_SIG1_MASK; ++ sig2 = (le32_to_cpu(acnt_rx->rx_info.ht_sig2_rate) >> ++ RXINFO_HT_SIG2_SHIFT) & RXINFO_HT_SIG2_MASK; ++ rate = (le32_to_cpu(acnt_rx->rx_info.ht_sig2_rate) >> ++ RXINFO_RATE_SHIFT) & RXINFO_RATE_MASK; ++ param = (le32_to_cpu(acnt_rx->rx_info.param) >> ++ RXINFO_PARAM_SHIFT) & RXINFO_PARAM_MASK; ++ ++ format = (param >> 3) & 0x7; ++ nss = 0; ++ bw = RX_RATE_INFO_HT20; ++ switch (format) { ++ case RX_RATE_INFO_FORMAT_11A: ++ rate_mcs = rate & 0xF; ++ if (rate_mcs == 10) ++ rate_mcs = 7; /* 12 Mbps */ ++ else ++ rate_mcs = utils_get_rate_id(rate_mcs); ++ gi = RX_RATE_INFO_SHORT_INTERVAL; ++ if ((rate_mcs == 5) || (rate_mcs == 7) || (rate_mcs == 9)) ++ return; ++ break; ++ case RX_RATE_INFO_FORMAT_11B: ++ rate_mcs = utils_get_rate_id(rate & 0xF); ++ gi = RX_RATE_INFO_LONG_INTERVAL; ++ if ((rate_mcs == 0) || (rate_mcs == 1)) ++ return; ++ break; ++ case RX_RATE_INFO_FORMAT_11N: ++ if ((sig1 & 0x3f) >= 16) ++ return; ++ bw = (sig1 >> 7) & 0x1; ++ gi = (sig2 >> 7) & 0x1; ++ rate_mcs = sig1 & 0x3F; ++ if (rate_mcs > 76) ++ return; ++ break; ++ case RX_RATE_INFO_FORMAT_11AC: ++ if (((sig2 >> 4) & 0xf) >= 10) ++ return; ++ nss = (sig1 >> 10) & 0x3; ++ if (!nss) ++ return; ++ bw = sig1 & 0x3; ++ gi = sig2 & 0x1; ++ rate_mcs = (sig2 >> 4) & 0xF; ++ if (rate_mcs > 9) ++ return; ++ break; ++ default: ++ return; ++ } ++ ++ sta_info->rx_format = format; ++ sta_info->rx_nss = nss; ++ sta_info->rx_bw = bw; ++ sta_info->rx_gi = gi; ++ sta_info->rx_rate_mcs = rate_mcs; ++ sta_info->rx_signal = ((le32_to_cpu(acnt_rx->rx_info.rssi_x) >> ++ RXINFO_RSSI_X_SHIFT) & RXINFO_RSSI_X_MASK); ++} ++ ++static void pcie_tx_per(struct mwl_priv *priv, struct mwl_sta *sta_info, ++ struct acnt_ra_s *acnt_ra) ++{ ++ u32 rate_info; ++ u8 index, per, type, rate_ac, per_index, format, bw, gi, mcs, nss; ++ u16 ratemask; ++ u8 i, found; ++ struct mwl_tx_hist *tx_hist; ++ struct mwl_tx_hist_data *tx_hist_data; ++ ++ rate_info = le32_to_cpu(acnt_ra->rate_info); ++ index = acnt_ra->rate_tbl_index; ++ per = acnt_ra->per; ++ type = acnt_ra->type; ++ ++ tx_hist = &sta_info->tx_hist; ++ ++ if (!tx_hist || !rate_info || (type >= SU_MU_TYPE_CNT)) ++ return; ++ ++ if ((type == SU_MIMO) && (index >= MAX_SUPPORTED_RATES) && ++ (index != 0xFF)) ++ return; ++ ++ if (per >= TX_HISTO_PER_THRES[3]) ++ per_index = 4; ++ else if (per >= TX_HISTO_PER_THRES[2]) ++ per_index = 3; ++ else if (per >= TX_HISTO_PER_THRES[1]) ++ per_index = 2; ++ else if (per >= TX_HISTO_PER_THRES[0]) ++ per_index = 1; ++ else ++ per_index = 0; ++ ++ format = rate_info & MWL_TX_RATE_FORMAT_MASK; ++ bw = (rate_info & MWL_TX_RATE_BANDWIDTH_MASK) >> ++ MWL_TX_RATE_BANDWIDTH_SHIFT; ++ gi = (rate_info & MWL_TX_RATE_SHORTGI_MASK) >> ++ MWL_TX_RATE_SHORTGI_SHIFT; ++ mcs = (rate_info & MWL_TX_RATE_RATEIDMCS_MASK) >> ++ MWL_TX_RATE_RATEIDMCS_SHIFT; ++ ++ /* Rate table index is valid */ ++ if (index != 0xff) { ++ if (type == MU_MIMO) { ++ rate_ac = mcs & 0xf; ++ nss = mcs >> 4; ++ if (nss < (QS_NUM_SUPPORTED_11AC_NSS - 1)) { ++ tx_hist_data = ++ &tx_hist->mu_rate[nss][bw][gi][rate_ac]; ++ tx_hist_data->rateinfo = rate_info; ++ tx_hist_data->per[per_index]++; ++ } ++ } else { ++ /* If legacy, skip legacy preamble bit 15 */ ++ if (format == TX_RATE_FORMAT_LEGACY) ++ ratemask = 0xfff; ++ else ++ ratemask = 0xffff; ++ tx_hist_data = &tx_hist->su_rate[0]; ++ if ((tx_hist_data[index].rateinfo & ratemask) == ++ (rate_info & ratemask)) ++ tx_hist_data[index].per[per_index]++; ++ } ++ } else { ++ if (type == MU_MIMO) { ++ rate_ac = mcs & 0xf; ++ nss = mcs >> 4; ++ if (nss < (QS_NUM_SUPPORTED_11AC_NSS - 1)) { ++ tx_hist_data = ++ &tx_hist->mu_rate[nss][bw][gi][rate_ac]; ++ tx_hist_data->rateinfo = rate_info; ++ tx_hist_data->per[per_index]++; ++ } ++ } else { ++ /* If legacy, skip legacy preamble bit 15 */ ++ if (format == TX_RATE_FORMAT_LEGACY) ++ ratemask = 0xfff; ++ else ++ ratemask = 0xffff; ++ tx_hist_data = &tx_hist->custom_rate[0]; ++ /* Go through non rate table buffer to see if any has ++ * been used. If all used up, recycle by using index 0 ++ */ ++ for (i = 0; i < TX_RATE_HISTO_CUSTOM_CNT; i++) { ++ if (!tx_hist_data[i].rateinfo || ++ ((tx_hist_data[i].rateinfo & ratemask) == ++ (rate_info & ratemask))) { ++ found = 1; ++ break; ++ } ++ } ++ if (found) ++ index = i; ++ else ++ index = 0; /* reuse index 0 buffer */ ++ tx_hist_data[index].rateinfo = rate_info; ++ tx_hist_data[index].per[per_index]++; ++ } ++ } ++} ++ ++static void pcie_ba_account(struct mwl_priv *priv, ++ struct mwl_sta *sta_info, ++ struct acnt_ba_s *acnt_ba) ++{ ++ struct mwl_tx_ba_hist *ba_hist = &sta_info->ba_hist; ++ ++ if (sta_info->stnid != le16_to_cpu(acnt_ba->stnid)) ++ return; ++ ++ if (ba_hist->enable && ba_hist->ba_stats && ++ (ba_hist->index < ACNT_BA_SIZE)) { ++ ba_hist->type = acnt_ba->type; ++ ba_hist->ba_stats[ba_hist->index].ba_hole = acnt_ba->ba_hole; ++ ba_hist->ba_stats[ba_hist->index].ba_expected = ++ acnt_ba->ba_expected; ++ ba_hist->ba_stats[ba_hist->index].no_ba = acnt_ba->no_ba; ++ ba_hist->index++; ++ if (ba_hist->index == ACNT_BA_SIZE) ++ wiphy_info(priv->hw->wiphy, ++ "Aid:%d BA histo collection done\n", ++ priv->ba_aid); ++ } ++} ++ ++static void pcie_bf_mimo_ctrl_decode(struct mwl_priv *priv, ++ struct acnt_bf_mimo_ctrl_s *bf_mimo_ctrl) ++{ ++ struct file *fp_data = NULL; ++ const char filename[] = "/tmp/BF_MIMO_Ctrl_Field_Output.txt"; ++ char str_buf[256]; ++ char *buf = &str_buf[0]; ++ mm_segment_t oldfs; ++ ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ buf += sprintf(buf, "\nMAC: %pM\n", bf_mimo_ctrl->rec_mac); ++ buf += sprintf(buf, "SU_0_MU_1: %d\n", bf_mimo_ctrl->type); ++ buf += sprintf(buf, "MIMO_Ctrl_Field: 0x%x\n", ++ le32_to_cpu(bf_mimo_ctrl->mimo_ctrl)); ++ buf += sprintf(buf, "Comp_BF_Report_First_8Bytes: 0x%llx\n", ++ le64_to_cpu(bf_mimo_ctrl->comp_bf_rep)); ++ ++ fp_data = filp_open(filename, O_RDWR | O_CREAT | O_TRUNC, 0); ++ ++ if (!IS_ERR(fp_data)) { ++ __kernel_write(fp_data, str_buf, strlen(str_buf), ++ &fp_data->f_pos); ++ filp_close(fp_data, current->files); ++ } else { ++ wiphy_err(priv->hw->wiphy, "Error opening %s! %x\n", ++ filename, (unsigned int)fp_data); ++ } ++ ++ set_fs(oldfs); ++} ++ ++static void pcie_process_account(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ u32 acnt_head, acnt_tail; ++ u32 read_size; ++ u8 *acnt_recds; ++ u8 *pstart, *pend; ++ struct acnt_s *acnt; ++ struct acnt_tx_s *acnt_tx; ++ struct acnt_rx_s *acnt_rx; ++ struct acnt_ra_s *acnt_ra; ++ struct acnt_ba_s *acnt_ba; ++ struct acnt_bf_mimo_ctrl_s *acnt_bf_mimo_ctrl; ++ struct pcie_dma_data *dma_data; ++ struct mwl_sta *sta_info; ++ u16 nf_a, nf_b, nf_c, nf_d; ++ u16 stnid; ++ u8 type; ++ ++ acnt_head = readl(pcie_priv->iobase1 + MACREG_REG_ACNTHEAD); ++ acnt_tail = readl(pcie_priv->iobase1 + MACREG_REG_ACNTTAIL); ++ ++ if (acnt_tail == acnt_head) ++ return; ++ ++ if (acnt_tail > acnt_head) { ++ read_size = desc->acnt_ring_size - acnt_tail + acnt_head; ++ if (read_size > desc->acnt_ring_size) { ++ wiphy_err(hw->wiphy, ++ "account size overflow (%d %d %d)\n", ++ acnt_head, acnt_tail, read_size); ++ goto process_next; ++ } ++ memset(desc->pacnt_buf, 0, desc->acnt_ring_size); ++ memcpy(desc->pacnt_buf, desc->pacnt_ring + acnt_tail, ++ desc->acnt_ring_size - acnt_tail); ++ memcpy(desc->pacnt_buf + desc->acnt_ring_size - acnt_tail, ++ desc->pacnt_ring, acnt_head); ++ acnt_recds = desc->pacnt_buf; ++ } else { ++ read_size = acnt_head - acnt_tail; ++ if (read_size > desc->acnt_ring_size) { ++ wiphy_err(hw->wiphy, ++ "account size overflow (%d %d %d)\n", ++ acnt_head, acnt_tail, read_size); ++ goto process_next; ++ } ++ acnt_recds = desc->pacnt_ring + acnt_tail; ++ } ++ ++ pstart = acnt_recds; ++ pend = pstart + read_size; ++ while (pstart < pend) { ++ acnt = (struct acnt_s *)pstart; ++ ++ switch (le16_to_cpu(acnt->code)) { ++ case ACNT_CODE_BUSY: ++ pcie_priv->acnt_busy++; ++ break; ++ case ACNT_CODE_WRAP: ++ pcie_priv->acnt_wrap++; ++ break; ++ case ACNT_CODE_DROP: ++ pcie_priv->acnt_drop++; ++ break; ++ case ACNT_CODE_TX_ENQUEUE: ++ acnt_tx = (struct acnt_tx_s *)pstart; ++ sta_info = utils_find_sta(priv, acnt_tx->hdr.wh.addr1); ++ if (sta_info) { ++ spin_lock_bh(&priv->sta_lock); ++ pcie_tx_account(priv, sta_info, acnt_tx); ++ spin_unlock_bh(&priv->sta_lock); ++ } ++ break; ++ case ACNT_CODE_RX_PPDU: ++ acnt_rx = (struct acnt_rx_s *)pstart; ++ nf_a = (le32_to_cpu(acnt_rx->rx_info.nf_a_b) >> ++ RXINFO_NF_A_SHIFT) & RXINFO_NF_A_MASK; ++ nf_b = (le32_to_cpu(acnt_rx->rx_info.nf_a_b) >> ++ RXINFO_NF_B_SHIFT) & RXINFO_NF_B_MASK; ++ nf_c = (le32_to_cpu(acnt_rx->rx_info.nf_c_d) >> ++ RXINFO_NF_C_SHIFT) & RXINFO_NF_C_MASK; ++ nf_d = (le32_to_cpu(acnt_rx->rx_info.nf_c_d) >> ++ RXINFO_NF_D_SHIFT) & RXINFO_NF_D_MASK; ++ if ((nf_a >= 2048) && (nf_b >= 2048) && ++ (nf_c >= 2048) && (nf_d >= 2048)) { ++ nf_a = ((4096 - nf_a) >> 4); ++ nf_b = ((4096 - nf_b) >> 4); ++ nf_c = ((4096 - nf_c) >> 4); ++ nf_d = ((4096 - nf_d) >> 4); ++ priv->noise = ++ -((nf_a + nf_b + nf_c + nf_d) / 4); ++ } ++ dma_data = (struct pcie_dma_data *) ++ &acnt_rx->rx_info.hdr[0]; ++ sta_info = utils_find_sta(priv, dma_data->wh.addr2); ++ if (sta_info) { ++ spin_lock_bh(&priv->sta_lock); ++ pcie_rx_account(priv, sta_info, acnt_rx); ++ spin_unlock_bh(&priv->sta_lock); ++ } ++ break; ++ case ACNT_CODE_RA_STATS: ++ acnt_ra = (struct acnt_ra_s *)pstart; ++ stnid = le16_to_cpu(acnt_ra->stn_id); ++ if ((stnid > 0) && (stnid <= priv->stnid_num)) { ++ type = acnt_ra->type; ++ if (type < 2) { ++ if (acnt_ra->tx_attempt_cnt >= 250) ++ priv->ra_tx_attempt[type][5]++; ++ else if (acnt_ra->tx_attempt_cnt >= 100) ++ priv->ra_tx_attempt[type][4]++; ++ else if (acnt_ra->tx_attempt_cnt >= 50) ++ priv->ra_tx_attempt[type][3]++; ++ else if (acnt_ra->tx_attempt_cnt >= 15) ++ priv->ra_tx_attempt[type][2]++; ++ else if (acnt_ra->tx_attempt_cnt >= 4) ++ priv->ra_tx_attempt[type][1]++; ++ else ++ priv->ra_tx_attempt[type][0]++; ++ } ++ sta_info = utils_find_sta_by_id(priv, stnid); ++ if (sta_info) { ++ spin_lock_bh(&priv->sta_lock); ++ pcie_tx_per(priv, sta_info, acnt_ra); ++ spin_unlock_bh(&priv->sta_lock); ++ } ++ } ++ break; ++ case ACNT_CODE_BA_STATS: ++ acnt_ba = (struct acnt_ba_s *)pstart; ++ if (priv->ba_aid) { ++ sta_info = utils_find_sta_by_aid(priv, ++ priv->ba_aid); ++ if (sta_info) { ++ spin_lock_bh(&priv->sta_lock); ++ pcie_ba_account(priv, sta_info, ++ acnt_ba); ++ spin_unlock_bh(&priv->sta_lock); ++ } ++ } ++ break; ++ case ACNT_CODE_BF_MIMO_CTRL: ++ acnt_bf_mimo_ctrl = ++ (struct acnt_bf_mimo_ctrl_s *)pstart; ++ pcie_bf_mimo_ctrl_decode(priv, acnt_bf_mimo_ctrl); ++ break; ++ default: ++ break; ++ } ++ ++ if (acnt->len) ++ pstart += acnt->len * 4; ++ else ++ goto process_next; ++ } ++process_next: ++ acnt_tail = acnt_head; ++ writel(acnt_tail, pcie_priv->iobase1 + MACREG_REG_ACNTTAIL); ++} ++ ++static int pcie_mcast_cts(struct ieee80211_hw *hw, bool enable) ++{ ++ return mwl_fwcmd_mcast_cts(hw, enable ? 1 : 0); ++} ++ ++static const struct mwl_hif_ops pcie_hif_ops_ndp = { ++ .driver_name = PCIE_DRV_NAME, ++ .driver_version = PCIE_DRV_VERSION, ++ .tx_head_room = PCIE_MIN_BYTES_HEADROOM, ++ .ampdu_num = AMPDU_QUEUES_NDP, ++ .reset = pcie_reset, ++ .init = pcie_init_ndp, ++ .deinit = pcie_deinit_ndp, ++ .get_info = pcie_get_info_ndp, ++ .get_tx_status = pcie_get_tx_status_ndp, ++ .get_rx_status = pcie_get_rx_status_ndp, ++ .enable_data_tasks = pcie_enable_data_tasks_ndp, ++ .disable_data_tasks = pcie_disable_data_tasks_ndp, ++ .exec_cmd = pcie_exec_cmd, ++ .get_irq_num = pcie_get_irq_num, ++ .irq_handler = pcie_isr_ndp, ++ .irq_enable = pcie_irq_enable_ndp, ++ .irq_disable = pcie_irq_disable, ++ .download_firmware = pcie_download_firmware, ++ .timer_routine = pcie_timer_routine_ndp, ++ .tx_xmit = pcie_tx_xmit_ndp, ++ .tx_return_pkts = pcie_tx_return_pkts_ndp, ++ .get_device_node = pcie_get_device_node, ++ .get_survey = pcie_get_survey, ++ .reg_access = pcie_reg_access, ++ .set_sta_id = pcie_set_sta_id, ++ .process_account = pcie_process_account, ++ .mcast_cts = pcie_mcast_cts, ++}; ++ ++static int pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ static bool printed_version; ++ struct ieee80211_hw *hw; ++ struct mwl_priv *priv; ++ struct pcie_priv *pcie_priv; ++ const struct mwl_hif_ops *hif_ops; ++ int rc = 0; ++ ++ if (id->driver_data >= MWLUNKNOWN) ++ return -ENODEV; ++ ++ if (!printed_version) { ++ pr_info("<<%s version %s>>\n", ++ PCIE_DRV_DESC, PCIE_DRV_VERSION); ++ printed_version = true; ++ } ++ ++ rc = pci_enable_device(pdev); ++ if (rc) { ++ pr_err("%s: cannot enable new PCI device\n", ++ PCIE_DRV_NAME); ++ return rc; ++ } ++ ++ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (rc) { ++ pr_err("%s: 32-bit PCI DMA not supported\n", ++ PCIE_DRV_NAME); ++ goto err_pci_disable_device; ++ } ++ ++ pci_set_master(pdev); ++ ++ if (id->driver_data == MWL8964) ++ hif_ops = &pcie_hif_ops_ndp; ++ else ++ hif_ops = &pcie_hif_ops; ++ hw = mwl_alloc_hw(MWL_BUS_PCIE, id->driver_data, &pdev->dev, ++ hif_ops, sizeof(*pcie_priv)); ++ if (!hw) { ++ pr_err("%s: mwlwifi hw alloc failed\n", ++ PCIE_DRV_NAME); ++ rc = -ENOMEM; ++ goto err_pci_disable_device; ++ } ++ ++ pci_set_drvdata(pdev, hw); ++ ++ priv = hw->priv; ++ priv->antenna_tx = pcie_chip_tbl[priv->chip_type].antenna_tx; ++ priv->antenna_rx = pcie_chip_tbl[priv->chip_type].antenna_rx; ++ pcie_priv = priv->hif.priv; ++ pcie_priv->mwl_priv = priv; ++ pcie_priv->pdev = pdev; ++ if (id->driver_data != MWL8964) { ++ pcie_priv->tx_head_room = PCIE_MIN_BYTES_HEADROOM; ++ if (id->driver_data == MWL8997) { ++ if (NET_SKB_PAD < PCIE_MIN_TX_HEADROOM_KF2) { ++ pcie_priv->tx_head_room = ++ PCIE_MIN_TX_HEADROOM_KF2; ++ pcie_hif_ops.tx_head_room = ++ PCIE_MIN_TX_HEADROOM_KF2; ++ } ++ } ++ } ++ ++ rc = pcie_alloc_resource(pcie_priv); ++ if (rc) ++ goto err_alloc_pci_resource; ++ ++ rc = mwl_init_hw(hw, pcie_chip_tbl[priv->chip_type].fw_image, ++ pcie_chip_tbl[priv->chip_type].cal_file, ++ pcie_chip_tbl[priv->chip_type].txpwrlmt_file); ++ if (rc) ++ goto err_wl_init; ++ ++ vendor_cmd_basic_event(hw->wiphy, MWL_VENDOR_EVENT_DRIVER_READY); ++ ++ return rc; ++ ++err_wl_init: ++ ++ pcie_reset(hw); ++ ++err_alloc_pci_resource: ++ ++ pci_set_drvdata(pdev, NULL); ++ mwl_free_hw(hw); ++ ++err_pci_disable_device: ++ ++ pci_disable_device(pdev); ++ ++ return rc; ++} ++ ++static void pcie_remove(struct pci_dev *pdev) ++{ ++ struct ieee80211_hw *hw = pci_get_drvdata(pdev); ++ struct mwl_priv *priv = hw->priv; ++ ++ priv->rmmod = true; ++ while (priv->in_send_cmd) ++ usleep_range(1000, 2000); ++ vendor_cmd_basic_event(hw->wiphy, MWL_VENDOR_EVENT_DRIVER_START_REMOVE); ++ mwl_deinit_hw(hw); ++ pci_set_drvdata(pdev, NULL); ++ mwl_free_hw(hw); ++ pci_disable_device(pdev); ++} ++ ++static struct pci_driver mwl_pcie_driver = { ++ .name = PCIE_DRV_NAME, ++ .id_table = pcie_id_tbl, ++ .probe = pcie_probe, ++ .remove = pcie_remove ++}; ++ ++module_pci_driver(mwl_pcie_driver); ++ ++MODULE_DESCRIPTION(PCIE_DRV_DESC); ++MODULE_VERSION(PCIE_DRV_VERSION); ++MODULE_AUTHOR("Marvell Semiconductor, Inc."); ++MODULE_LICENSE("GPL v2"); ++MODULE_SUPPORTED_DEVICE(PCIE_DEV_NAME); ++MODULE_DEVICE_TABLE(pci, pcie_id_tbl); +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.c +new file mode 100644 +index 000000000000..25076c6d66df +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.c +@@ -0,0 +1,540 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements receive related functions. */ ++ ++#include ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "utils.h" ++#include "hif/pcie/dev.h" ++#include "hif/pcie/rx.h" ++ ++#define MAX_NUM_RX_RING_BYTES (PCIE_MAX_NUM_RX_DESC * \ ++ sizeof(struct pcie_rx_desc)) ++ ++#define MAX_NUM_RX_HNDL_BYTES (PCIE_MAX_NUM_RX_DESC * \ ++ sizeof(struct pcie_rx_hndl)) ++ ++#define DECRYPT_ERR_MASK 0x80 ++#define GENERAL_DECRYPT_ERR 0xFF ++#define TKIP_DECRYPT_MIC_ERR 0x02 ++#define WEP_DECRYPT_ICV_ERR 0x04 ++#define TKIP_DECRYPT_ICV_ERR 0x08 ++ ++#define W836X_RSSI_OFFSET 8 ++ ++static int pcie_rx_ring_alloc(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data *desc; ++ ++ desc = &pcie_priv->desc_data[0]; ++ ++ desc->prx_ring = (struct pcie_rx_desc *) ++ dma_alloc_coherent(priv->dev, ++ MAX_NUM_RX_RING_BYTES, ++ &desc->pphys_rx_ring, ++ GFP_KERNEL); ++ ++ if (!desc->prx_ring) { ++ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); ++ return -ENOMEM; ++ } ++ ++ memset(desc->prx_ring, 0x00, MAX_NUM_RX_RING_BYTES); ++ ++ desc->rx_hndl = kzalloc(MAX_NUM_RX_HNDL_BYTES, GFP_KERNEL); ++ ++ if (!desc->rx_hndl) { ++ dma_free_coherent(priv->dev, ++ MAX_NUM_RX_RING_BYTES, ++ desc->prx_ring, ++ desc->pphys_rx_ring); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int pcie_rx_ring_init(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data *desc; ++ int i; ++ struct pcie_rx_hndl *rx_hndl; ++ dma_addr_t dma; ++ u32 val; ++ ++ desc = &pcie_priv->desc_data[0]; ++ ++ if (desc->prx_ring) { ++ desc->rx_buf_size = SYSADPT_MAX_AGGR_SIZE; ++ ++ for (i = 0; i < PCIE_MAX_NUM_RX_DESC; i++) { ++ rx_hndl = &desc->rx_hndl[i]; ++ rx_hndl->psk_buff = ++ dev_alloc_skb(desc->rx_buf_size); ++ ++ if (!rx_hndl->psk_buff) { ++ wiphy_err(priv->hw->wiphy, ++ "rxdesc %i: no skbuff available\n", ++ i); ++ return -ENOMEM; ++ } ++ ++ skb_reserve(rx_hndl->psk_buff, ++ PCIE_MIN_BYTES_HEADROOM); ++ desc->prx_ring[i].rx_control = ++ EAGLE_RXD_CTRL_DRIVER_OWN; ++ desc->prx_ring[i].status = EAGLE_RXD_STATUS_OK; ++ desc->prx_ring[i].qos_ctrl = 0x0000; ++ desc->prx_ring[i].channel = 0x00; ++ desc->prx_ring[i].rssi = 0x00; ++ desc->prx_ring[i].pkt_len = ++ cpu_to_le16(SYSADPT_MAX_AGGR_SIZE); ++ dma = pci_map_single(pcie_priv->pdev, ++ rx_hndl->psk_buff->data, ++ desc->rx_buf_size, ++ PCI_DMA_FROMDEVICE); ++ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { ++ wiphy_err(priv->hw->wiphy, ++ "failed to map pci memory!\n"); ++ return -ENOMEM; ++ } ++ desc->prx_ring[i].pphys_buff_data = cpu_to_le32(dma); ++ val = (u32)desc->pphys_rx_ring + ++ ((i + 1) * sizeof(struct pcie_rx_desc)); ++ desc->prx_ring[i].pphys_next = cpu_to_le32(val); ++ rx_hndl->pdesc = &desc->prx_ring[i]; ++ if (i < (PCIE_MAX_NUM_RX_DESC - 1)) ++ rx_hndl->pnext = &desc->rx_hndl[i + 1]; ++ } ++ desc->prx_ring[PCIE_MAX_NUM_RX_DESC - 1].pphys_next = ++ cpu_to_le32((u32)desc->pphys_rx_ring); ++ desc->rx_hndl[PCIE_MAX_NUM_RX_DESC - 1].pnext = ++ &desc->rx_hndl[0]; ++ desc->pnext_rx_hndl = &desc->rx_hndl[0]; ++ ++ return 0; ++ } ++ ++ wiphy_err(priv->hw->wiphy, "no valid RX mem\n"); ++ ++ return -ENOMEM; ++} ++ ++static void pcie_rx_ring_cleanup(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data *desc; ++ int i; ++ struct pcie_rx_hndl *rx_hndl; ++ ++ desc = &pcie_priv->desc_data[0]; ++ ++ if (desc->prx_ring) { ++ for (i = 0; i < PCIE_MAX_NUM_RX_DESC; i++) { ++ rx_hndl = &desc->rx_hndl[i]; ++ if (!rx_hndl->psk_buff) ++ continue; ++ ++ pci_unmap_single(pcie_priv->pdev, ++ le32_to_cpu ++ (rx_hndl->pdesc->pphys_buff_data), ++ desc->rx_buf_size, ++ PCI_DMA_FROMDEVICE); ++ ++ dev_kfree_skb_any(rx_hndl->psk_buff); ++ ++ wiphy_debug(priv->hw->wiphy, ++ "unmapped+free'd %i 0x%p 0x%x %i\n", ++ i, rx_hndl->psk_buff->data, ++ le32_to_cpu( ++ rx_hndl->pdesc->pphys_buff_data), ++ desc->rx_buf_size); ++ ++ rx_hndl->psk_buff = NULL; ++ } ++ } ++} ++ ++static void pcie_rx_ring_free(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data *desc; ++ ++ desc = &pcie_priv->desc_data[0]; ++ ++ if (desc->prx_ring) { ++ pcie_rx_ring_cleanup(priv); ++ ++ dma_free_coherent(priv->dev, ++ MAX_NUM_RX_RING_BYTES, ++ desc->prx_ring, ++ desc->pphys_rx_ring); ++ ++ desc->prx_ring = NULL; ++ } ++ ++ kfree(desc->rx_hndl); ++ ++ desc->pnext_rx_hndl = NULL; ++} ++ ++static inline void pcie_rx_status(struct mwl_priv *priv, ++ struct pcie_rx_desc *pdesc, ++ struct ieee80211_rx_status *status) ++{ ++ u16 rx_rate; ++ ++ memset(status, 0, sizeof(*status)); ++ ++ if (priv->chip_type == MWL8997) ++ status->signal = (s8)pdesc->rssi; ++ else ++ status->signal = -(pdesc->rssi + W836X_RSSI_OFFSET); ++ ++ rx_rate = le16_to_cpu(pdesc->rate); ++ pcie_rx_prepare_status(priv, ++ rx_rate & MWL_RX_RATE_FORMAT_MASK, ++ (rx_rate & MWL_RX_RATE_NSS_MASK) >> ++ MWL_RX_RATE_NSS_SHIFT, ++ (rx_rate & MWL_RX_RATE_BW_MASK) >> ++ MWL_RX_RATE_BW_SHIFT, ++ (rx_rate & MWL_RX_RATE_GI_MASK) >> ++ MWL_RX_RATE_GI_SHIFT, ++ (rx_rate & MWL_RX_RATE_RT_MASK) >> ++ MWL_RX_RATE_RT_SHIFT, ++ status); ++ ++ status->freq = ieee80211_channel_to_frequency(pdesc->channel, ++ status->band); ++ ++ /* check if status has a specific error bit (bit 7) set or indicates ++ * a general decrypt error ++ */ ++ if ((pdesc->status == GENERAL_DECRYPT_ERR) || ++ (pdesc->status & DECRYPT_ERR_MASK)) { ++ /* check if status is not equal to 0xFF ++ * the 0xFF check is for backward compatibility ++ */ ++ if (pdesc->status != GENERAL_DECRYPT_ERR) { ++ if (((pdesc->status & (~DECRYPT_ERR_MASK)) & ++ TKIP_DECRYPT_MIC_ERR) && !((pdesc->status & ++ (WEP_DECRYPT_ICV_ERR | TKIP_DECRYPT_ICV_ERR)))) { ++ status->flag |= RX_FLAG_MMIC_ERROR; ++ } ++ } ++ } ++} ++ ++static inline bool pcie_rx_process_mesh_amsdu(struct mwl_priv *priv, ++ struct sk_buff *skb, ++ struct ieee80211_rx_status *status) ++{ ++ struct ieee80211_hdr *wh; ++ struct mwl_sta *sta_info; ++ struct ieee80211_sta *sta; ++ u8 *qc; ++ int wh_len; ++ int len; ++ u8 pad; ++ u8 *data; ++ u16 frame_len; ++ struct sk_buff *newskb; ++ ++ wh = (struct ieee80211_hdr *)skb->data; ++ ++ spin_lock_bh(&priv->sta_lock); ++ list_for_each_entry(sta_info, &priv->sta_list, list) { ++ sta = container_of((void *)sta_info, struct ieee80211_sta, ++ drv_priv[0]); ++ if (ether_addr_equal(sta->addr, wh->addr2)) { ++ if (!sta_info->is_mesh_node) { ++ spin_unlock_bh(&priv->sta_lock); ++ return false; ++ } ++ } ++ } ++ spin_unlock_bh(&priv->sta_lock); ++ ++ qc = ieee80211_get_qos_ctl(wh); ++ *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; ++ ++ wh_len = ieee80211_hdrlen(wh->frame_control); ++ len = wh_len; ++ data = skb->data; ++ ++ while (len < skb->len) { ++ frame_len = *(u8 *)(data + len + ETH_HLEN - 1) | ++ (*(u8 *)(data + len + ETH_HLEN - 2) << 8); ++ ++ if ((len + ETH_HLEN + frame_len) > skb->len) ++ break; ++ ++ newskb = dev_alloc_skb(wh_len + frame_len); ++ if (!newskb) ++ break; ++ ++ ether_addr_copy(wh->addr3, data + len); ++ ether_addr_copy(wh->addr4, data + len + ETH_ALEN); ++ memcpy(newskb->data, wh, wh_len); ++ memcpy(newskb->data + wh_len, data + len + ETH_HLEN, frame_len); ++ skb_put(newskb, wh_len + frame_len); ++ ++ pad = ((ETH_HLEN + frame_len) % 4) ? ++ (4 - (ETH_HLEN + frame_len) % 4) : 0; ++ len += (ETH_HLEN + frame_len + pad); ++ if (len < skb->len) ++ status->flag |= RX_FLAG_AMSDU_MORE; ++ else ++ status->flag &= ~RX_FLAG_AMSDU_MORE; ++ memcpy(IEEE80211_SKB_RXCB(newskb), status, sizeof(*status)); ++ ieee80211_rx(priv->hw, newskb); ++ } ++ ++ dev_kfree_skb_any(skb); ++ ++ return true; ++} ++ ++static inline int pcie_rx_refill(struct mwl_priv *priv, ++ struct pcie_rx_hndl *rx_hndl) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data *desc; ++ dma_addr_t dma; ++ ++ desc = &pcie_priv->desc_data[0]; ++ ++ rx_hndl->psk_buff = dev_alloc_skb(desc->rx_buf_size); ++ ++ if (!rx_hndl->psk_buff) ++ return -ENOMEM; ++ ++ skb_reserve(rx_hndl->psk_buff, PCIE_MIN_BYTES_HEADROOM); ++ ++ rx_hndl->pdesc->status = EAGLE_RXD_STATUS_OK; ++ rx_hndl->pdesc->qos_ctrl = 0x0000; ++ rx_hndl->pdesc->channel = 0x00; ++ rx_hndl->pdesc->rssi = 0x00; ++ rx_hndl->pdesc->pkt_len = cpu_to_le16(desc->rx_buf_size); ++ ++ dma = pci_map_single(pcie_priv->pdev, ++ rx_hndl->psk_buff->data, ++ desc->rx_buf_size, ++ PCI_DMA_FROMDEVICE); ++ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { ++ dev_kfree_skb_any(rx_hndl->psk_buff); ++ wiphy_err(priv->hw->wiphy, ++ "failed to map pci memory!\n"); ++ return -ENOMEM; ++ } ++ ++ rx_hndl->pdesc->pphys_buff_data = cpu_to_le32(dma); ++ ++ return 0; ++} ++ ++int pcie_rx_init(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ int rc; ++ ++ rc = pcie_rx_ring_alloc(priv); ++ if (rc) { ++ wiphy_err(hw->wiphy, "allocating RX ring failed\n"); ++ return rc; ++ } ++ ++ rc = pcie_rx_ring_init(priv); ++ if (rc) { ++ pcie_rx_ring_free(priv); ++ wiphy_err(hw->wiphy, ++ "initializing RX ring failed\n"); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++void pcie_rx_deinit(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ pcie_rx_ring_cleanup(priv); ++ pcie_rx_ring_free(priv); ++} ++ ++void pcie_rx_recv(unsigned long data) ++{ ++ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data *desc; ++ struct pcie_rx_hndl *curr_hndl; ++ int work_done = 0; ++ struct sk_buff *prx_skb = NULL; ++ int pkt_len; ++ struct ieee80211_rx_status *status; ++ struct mwl_vif *mwl_vif = NULL; ++ struct ieee80211_hdr *wh; ++ ++ desc = &pcie_priv->desc_data[0]; ++ curr_hndl = desc->pnext_rx_hndl; ++ ++ if (!curr_hndl) { ++ pcie_mask_int(pcie_priv, MACREG_A2HRIC_BIT_RX_RDY, true); ++ pcie_priv->is_rx_schedule = false; ++ wiphy_warn(hw->wiphy, "busy or no receiving packets\n"); ++ return; ++ } ++ ++ while ((curr_hndl->pdesc->rx_control == EAGLE_RXD_CTRL_DMA_OWN) && ++ (work_done < pcie_priv->recv_limit)) { ++ prx_skb = curr_hndl->psk_buff; ++ if (!prx_skb) ++ goto out; ++ pci_unmap_single(pcie_priv->pdev, ++ le32_to_cpu(curr_hndl->pdesc->pphys_buff_data), ++ desc->rx_buf_size, ++ PCI_DMA_FROMDEVICE); ++ pkt_len = le16_to_cpu(curr_hndl->pdesc->pkt_len); ++ ++ if (skb_tailroom(prx_skb) < pkt_len) { ++ dev_kfree_skb_any(prx_skb); ++ goto out; ++ } ++ ++ if (curr_hndl->pdesc->channel != ++ hw->conf.chandef.chan->hw_value) { ++ dev_kfree_skb_any(prx_skb); ++ goto out; ++ } ++ ++ status = IEEE80211_SKB_RXCB(prx_skb); ++ pcie_rx_status(priv, curr_hndl->pdesc, status); ++ ++ if (priv->chip_type == MWL8997) { ++ priv->noise = (s8)curr_hndl->pdesc->noise_floor; ++ if (priv->noise > 0) ++ priv->noise = -priv->noise; ++ } else ++ priv->noise = -curr_hndl->pdesc->noise_floor; ++ ++ wh = &((struct pcie_dma_data *)prx_skb->data)->wh; ++ ++ if (ieee80211_has_protected(wh->frame_control)) { ++ /* Check if hw crypto has been enabled for ++ * this bss. If yes, set the status flags ++ * accordingly ++ */ ++ if (ieee80211_has_tods(wh->frame_control)) { ++ mwl_vif = utils_find_vif_bss(priv, wh->addr1); ++ if (!mwl_vif && ++ ieee80211_has_a4(wh->frame_control)) ++ mwl_vif = ++ utils_find_vif_bss(priv, ++ wh->addr2); ++ } else { ++ mwl_vif = utils_find_vif_bss(priv, wh->addr2); ++ } ++ ++ if ((mwl_vif && mwl_vif->is_hw_crypto_enabled) || ++ is_multicast_ether_addr(wh->addr1) || ++ (ieee80211_is_mgmt(wh->frame_control) && ++ !is_multicast_ether_addr(wh->addr1))) { ++ /* When MMIC ERROR is encountered ++ * by the firmware, payload is ++ * dropped and only 32 bytes of ++ * mwlwifi Firmware header is sent ++ * to the host. ++ * ++ * We need to add four bytes of ++ * key information. In it ++ * MAC80211 expects keyidx set to ++ * 0 for triggering Counter ++ * Measure of MMIC failure. ++ */ ++ if (status->flag & RX_FLAG_MMIC_ERROR) { ++ struct pcie_dma_data *dma_data; ++ ++ dma_data = (struct pcie_dma_data *) ++ prx_skb->data; ++ memset((void *)&dma_data->data, 0, 4); ++ pkt_len += 4; ++ } ++ ++ if (!ieee80211_is_auth(wh->frame_control)) { ++ if (priv->chip_type != MWL8997) ++ status->flag |= ++ RX_FLAG_IV_STRIPPED | ++ RX_FLAG_DECRYPTED | ++ RX_FLAG_MMIC_STRIPPED; ++ else ++ status->flag |= ++ RX_FLAG_DECRYPTED | ++ RX_FLAG_MMIC_STRIPPED; ++ } ++ } ++ } ++ ++ skb_put(prx_skb, pkt_len); ++ pcie_rx_remove_dma_header(prx_skb, curr_hndl->pdesc->qos_ctrl); ++ ++ wh = (struct ieee80211_hdr *)prx_skb->data; ++ ++ if (ieee80211_is_data_qos(wh->frame_control)) { ++ const u8 eapol[] = {0x88, 0x8e}; ++ u8 *qc = ieee80211_get_qos_ctl(wh); ++ u8 *data; ++ ++ data = prx_skb->data + ++ ieee80211_hdrlen(wh->frame_control) + 6; ++ ++ if (!memcmp(data, eapol, sizeof(eapol))) ++ *qc |= 7; ++ } ++ ++ if (ieee80211_is_data_qos(wh->frame_control) && ++ ieee80211_has_a4(wh->frame_control)) { ++ u8 *qc = ieee80211_get_qos_ctl(wh); ++ ++ if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT) ++ if (pcie_rx_process_mesh_amsdu(priv, prx_skb, ++ status)) ++ goto out; ++ } ++ ++ if (ieee80211_is_probe_req(wh->frame_control) && ++ priv->dump_probe) ++ wiphy_info(hw->wiphy, "Probe Req: %pM\n", wh->addr2); ++ ++ ieee80211_rx(hw, prx_skb); ++out: ++ pcie_rx_refill(priv, curr_hndl); ++ curr_hndl->pdesc->rx_control = EAGLE_RXD_CTRL_DRIVER_OWN; ++ curr_hndl->pdesc->qos_ctrl = 0; ++ curr_hndl = curr_hndl->pnext; ++ work_done++; ++ } ++ ++ desc->pnext_rx_hndl = curr_hndl; ++ pcie_mask_int(pcie_priv, MACREG_A2HRIC_BIT_RX_RDY, true); ++ pcie_priv->is_rx_schedule = false; ++} +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.h +new file mode 100644 +index 000000000000..d2b580fceb0a +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines receive related functions. */ ++ ++#ifndef _RX_H_ ++#define _RX_H_ ++ ++int pcie_rx_init(struct ieee80211_hw *hw); ++void pcie_rx_deinit(struct ieee80211_hw *hw); ++void pcie_rx_recv(unsigned long data); ++ ++#endif /* _RX_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.c +new file mode 100644 +index 000000000000..d1ede588b4c1 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.c +@@ -0,0 +1,612 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements receive related functions for new data ++ * path. ++ */ ++ ++#include ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "utils.h" ++#include "hif/pcie/dev.h" ++#include "hif/pcie/rx_ndp.h" ++ ++#define MAX_NUM_RX_RING_BYTES (MAX_NUM_RX_DESC * \ ++ sizeof(struct pcie_rx_desc_ndp)) ++#define MAX_NUM_RX_RING_DONE_BYTES (MAX_NUM_RX_DESC * \ ++ sizeof(struct rx_ring_done)) ++ ++static int pcie_rx_ring_alloc_ndp(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ ++ desc->prx_ring = (struct pcie_rx_desc_ndp *) ++ dma_alloc_coherent(priv->dev, ++ MAX_NUM_RX_RING_BYTES, ++ &desc->pphys_rx_ring, ++ GFP_KERNEL); ++ if (!desc->prx_ring) ++ goto err_no_mem; ++ memset(desc->prx_ring, 0x00, MAX_NUM_RX_RING_BYTES); ++ ++ desc->prx_ring_done = (struct rx_ring_done *) ++ dma_alloc_coherent(priv->dev, ++ MAX_NUM_RX_RING_DONE_BYTES, ++ &desc->pphys_rx_ring_done, ++ GFP_KERNEL); ++ if (!desc->prx_ring_done) ++ goto err_no_mem; ++ memset(desc->prx_ring_done, 0x00, MAX_NUM_RX_RING_DONE_BYTES); ++ return 0; ++ ++err_no_mem: ++ ++ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); ++ return -ENOMEM; ++} ++ ++static int pcie_rx_ring_init_ndp(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ int i; ++ struct sk_buff *psk_buff; ++ dma_addr_t dma; ++ ++ skb_queue_head_init(&pcie_priv->rx_skb_trace); ++ if (desc->prx_ring) { ++ desc->rx_buf_size = MAX_AGGR_SIZE; ++ ++ for (i = 0; i < MAX_NUM_RX_DESC; i++) { ++ psk_buff = __alloc_skb(desc->rx_buf_size + NET_SKB_PAD, ++ GFP_ATOMIC, SKB_ALLOC_RX, ++ NUMA_NO_NODE); ++ skb_reserve(psk_buff, NET_SKB_PAD); ++ if (!psk_buff) { ++ wiphy_err(priv->hw->wiphy, ++ "rxdesc %i: no skbuff available\n", ++ i); ++ return -ENOMEM; ++ } ++ skb_reserve(psk_buff, MIN_BYTES_RX_HEADROOM); ++ ++ dma = pci_map_single(pcie_priv->pdev, ++ psk_buff->data, ++ desc->rx_buf_size, ++ PCI_DMA_FROMDEVICE); ++ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { ++ wiphy_err(priv->hw->wiphy, ++ "failed to map pci memory!\n"); ++ return -ENOMEM; ++ } ++ ++ desc->rx_vbuflist[i] = psk_buff; ++ desc->prx_ring[i].user = cpu_to_le32(i); ++ desc->prx_ring[i].data = cpu_to_le32(dma); ++ *((u32 *)&psk_buff->cb[16]) = 0xdeadbeef; ++ skb_queue_tail(&pcie_priv->rx_skb_trace, psk_buff); ++ } ++ ++ writel(1023, pcie_priv->iobase1 + MACREG_REG_RXDESCHEAD); ++ return 0; ++ } ++ ++ wiphy_err(priv->hw->wiphy, "no valid RX mem\n"); ++ return -ENOMEM; ++} ++ ++static void pcie_rx_ring_cleanup_ndp(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ int i; ++ ++ if (desc->prx_ring) { ++ for (i = 0; i < MAX_NUM_RX_DESC; i++) { ++ if (desc->rx_vbuflist[i]) { ++ pci_unmap_single(pcie_priv->pdev, ++ le32_to_cpu( ++ desc->prx_ring[i].data), ++ desc->rx_buf_size, ++ PCI_DMA_FROMDEVICE); ++ desc->rx_vbuflist[i] = NULL; ++ } ++ } ++ skb_queue_purge(&pcie_priv->rx_skb_trace); ++ } ++} ++ ++static void pcie_rx_ring_free_ndp(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ ++ if (desc->prx_ring) { ++ pcie_rx_ring_cleanup_ndp(priv); ++ dma_free_coherent(priv->dev, ++ MAX_NUM_RX_RING_BYTES, ++ desc->prx_ring, ++ desc->pphys_rx_ring); ++ desc->prx_ring = NULL; ++ } ++ ++ if (desc->prx_ring_done) { ++ dma_free_coherent(priv->dev, ++ MAX_NUM_RX_RING_DONE_BYTES, ++ desc->prx_ring_done, ++ desc->pphys_rx_ring_done); ++ desc->prx_ring_done = NULL; ++ } ++} ++ ++static inline void pcie_rx_update_ndp_cnts(struct mwl_priv *priv, u32 ctrl) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ ++ switch (ctrl) { ++ case RXRING_CASE_DROP: ++ pcie_priv->rx_cnts.drop_cnt++; ++ break; ++ case RXRING_CASE_FAST_BAD_AMSDU: ++ pcie_priv->rx_cnts.fast_bad_amsdu_cnt++; ++ break; ++ case RXRING_CASE_FAST_DATA: ++ pcie_priv->rx_cnts.fast_data_cnt++; ++ break; ++ case RXRING_CASE_SLOW_BAD_MIC: ++ pcie_priv->rx_cnts.slow_bad_mic_cnt++; ++ break; ++ case RXRING_CASE_SLOW_BAD_PN: ++ pcie_priv->rx_cnts.slow_bad_pn_cnt++; ++ break; ++ case RXRING_CASE_SLOW_BAD_STA: ++ pcie_priv->rx_cnts.slow_bad_sta_cnt++; ++ break; ++ case RXRING_CASE_SLOW_MCAST: ++ pcie_priv->rx_cnts.slow_mcast_cnt++; ++ break; ++ case RXRING_CASE_SLOW_MGMT: ++ pcie_priv->rx_cnts.slow_mgmt_cnt++; ++ break; ++ case RXRING_CASE_SLOW_NOQUEUE: ++ pcie_priv->rx_cnts.slow_noqueue_cnt++; ++ break; ++ case RXRING_CASE_SLOW_NORUN: ++ pcie_priv->rx_cnts.slow_norun_cnt++; ++ break; ++ case RXRING_CASE_SLOW_PROMISC: ++ pcie_priv->rx_cnts.slow_promisc_cnt++; ++ break; ++ } ++} ++ ++static void pcie_rx_status_ndp(struct mwl_priv *priv, ++ struct mwl_sta *sta_info, ++ struct ieee80211_rx_status *status) ++{ ++ memset(status, 0, sizeof(*status)); ++ pcie_rx_prepare_status(priv, ++ sta_info->rx_format, ++ sta_info->rx_nss, ++ sta_info->rx_bw, ++ sta_info->rx_gi, ++ sta_info->rx_rate_mcs, ++ status); ++ status->signal = -sta_info->rx_signal; ++ status->band = priv->hw->conf.chandef.chan->band; ++ status->freq = ieee80211_channel_to_frequency( ++ priv->hw->conf.chandef.chan->hw_value, status->band); ++} ++ ++static inline void pcie_rx_process_fast_data(struct mwl_priv *priv, ++ struct sk_buff *skb, ++ u16 stnid) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct ieee80211_sta *sta; ++ struct mwl_sta *sta_info; ++ struct mwl_vif *mwl_vif; ++ struct ieee80211_hdr hdr; ++ u16 hdrlen, ethertype; ++ __le16 fc; ++ struct ieee80211_rx_status *status; ++ ++ if (stnid == RXRING_CTRL_STA_FROMDS) ++ stnid = 0; ++ ++ if (stnid > SYSADPT_MAX_STA_SC4) ++ goto drop_packet; ++ ++ sta = pcie_priv->sta_link[stnid]; ++ if (!sta) ++ goto drop_packet; ++ ++ sta_info = mwl_dev_get_sta(sta); ++ mwl_vif = sta_info->mwl_vif; ++ if (!mwl_vif) ++ goto drop_packet; ++ ++ ethertype = (skb->data[20] << 8) | skb->data[21]; ++ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); ++ ++ memset(&hdr, 0, sizeof(hdr)); ++ switch (mwl_vif->type) { ++ case NL80211_IFTYPE_AP: ++ if (sta_info->wds) { ++ fc |= (cpu_to_le16(IEEE80211_FCTL_TODS) | ++ cpu_to_le16(IEEE80211_FCTL_FROMDS)); ++ /* RA TA DA SA */ ++ ether_addr_copy(hdr.addr1, mwl_vif->bssid); ++ ether_addr_copy(hdr.addr2, sta->addr); ++ ether_addr_copy(hdr.addr3, skb->data); ++ ether_addr_copy(hdr.addr4, skb->data + ETH_ALEN); ++ hdrlen = 30; ++ } else { ++ fc |= cpu_to_le16(IEEE80211_FCTL_TODS); ++ /* BSSID SA DA */ ++ ether_addr_copy(hdr.addr1, mwl_vif->bssid); ++ ether_addr_copy(hdr.addr2, skb->data + ETH_ALEN); ++ ether_addr_copy(hdr.addr3, skb->data); ++ hdrlen = 24; ++ } ++ break; ++ case NL80211_IFTYPE_STATION: ++ if (sta_info->wds) { ++ fc |= (cpu_to_le16(IEEE80211_FCTL_TODS) | ++ cpu_to_le16(IEEE80211_FCTL_FROMDS)); ++ /* RA TA DA SA */ ++ ether_addr_copy(hdr.addr1, mwl_vif->sta_mac); ++ ether_addr_copy(hdr.addr2, mwl_vif->bssid); ++ ether_addr_copy(hdr.addr3, skb->data); ++ ether_addr_copy(hdr.addr4, skb->data + ETH_ALEN); ++ hdrlen = 30; ++ } else { ++ fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); ++ /* DA BSSID SA */ ++ ether_addr_copy(hdr.addr1, skb->data); ++ ether_addr_copy(hdr.addr2, mwl_vif->bssid); ++ ether_addr_copy(hdr.addr3, skb->data + ETH_ALEN); ++ hdrlen = 24; ++ } ++ break; ++ default: ++ goto drop_packet; ++ } ++ ++ if (sta->wme) { ++ fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); ++ hdrlen += 2; ++ } ++ ++ status = IEEE80211_SKB_RXCB(skb); ++ pcie_rx_status_ndp(priv, sta_info, status); ++ if (mwl_vif->is_hw_crypto_enabled) { ++ fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); ++ status->flag |= RX_FLAG_IV_STRIPPED | ++ RX_FLAG_DECRYPTED | ++ RX_FLAG_MMIC_STRIPPED; ++ } ++ ++ hdr.frame_control = fc; ++ hdr.duration_id = 0; ++ ++ skb_pull(skb, ETH_HLEN); ++ ++ if (ieee80211_is_data_qos(fc)) { ++ __le16 *qos_control; ++ ++ qos_control = (__le16 *)skb_push(skb, 2); ++ memcpy(skb_push(skb, hdrlen - 2), &hdr, hdrlen - 2); ++ if (ethertype == ETH_P_PAE) ++ *qos_control = cpu_to_le16( ++ IEEE80211_QOS_CTL_ACK_POLICY_NOACK | 7); ++ else ++ *qos_control = cpu_to_le16( ++ IEEE80211_QOS_CTL_ACK_POLICY_NOACK); ++ } else ++ memcpy(skb_push(skb, hdrlen), &hdr, hdrlen); ++ ++ status->flag |= RX_FLAG_DUP_VALIDATED; ++ ieee80211_rx(priv->hw, skb); ++ ++ return; ++drop_packet: ++ ++ dev_kfree_skb_any(skb); ++} ++ ++static inline void pcie_rx_process_slow_data(struct mwl_priv *priv, ++ struct sk_buff *skb, ++ bool bad_mic, u8 signal) ++{ ++ struct ieee80211_rx_status *status; ++ struct ieee80211_hdr *wh; ++ struct mwl_vif *mwl_vif = NULL; ++ ++ pcie_rx_remove_dma_header(skb, 0); ++ status = IEEE80211_SKB_RXCB(skb); ++ memset(status, 0, sizeof(*status)); ++ status->signal = -signal; ++ status->band = priv->hw->conf.chandef.chan->band; ++ status->freq = ieee80211_channel_to_frequency( ++ priv->hw->conf.chandef.chan->hw_value, status->band); ++ ++ if (bad_mic) ++ status->flag |= RX_FLAG_MMIC_ERROR; ++ else { ++ wh = (struct ieee80211_hdr *)skb->data; ++ ++ if (ieee80211_has_protected(wh->frame_control)) { ++ if (ieee80211_has_tods(wh->frame_control)) { ++ mwl_vif = utils_find_vif_bss(priv, wh->addr1); ++ if (!mwl_vif && ++ ieee80211_has_a4(wh->frame_control)) ++ mwl_vif = ++ utils_find_vif_bss(priv, ++ wh->addr2); ++ } else { ++ mwl_vif = utils_find_vif_bss(priv, wh->addr2); ++ } ++ ++ if ((mwl_vif && mwl_vif->is_hw_crypto_enabled) || ++ is_multicast_ether_addr(wh->addr1) || ++ (ieee80211_is_mgmt(wh->frame_control) && ++ !is_multicast_ether_addr(wh->addr1))) { ++ if (!ieee80211_is_auth(wh->frame_control)) ++ status->flag |= RX_FLAG_IV_STRIPPED | ++ RX_FLAG_DECRYPTED | ++ RX_FLAG_MMIC_STRIPPED; ++ } ++ } ++ ++ if (ieee80211_has_a4(wh->frame_control) && !priv->wds_check) { ++ ether_addr_copy(priv->wds_check_sta, wh->addr2); ++ ieee80211_queue_work(priv->hw, &priv->wds_check_handle); ++ priv->wds_check = true; ++ } ++ } ++ ++ status->flag |= RX_FLAG_DUP_VALIDATED; ++ ieee80211_rx(priv->hw, skb); ++} ++ ++static inline int pcie_rx_refill_ndp(struct mwl_priv *priv, u32 buf_idx) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ struct sk_buff *psk_buff; ++ dma_addr_t dma; ++ ++ psk_buff = __alloc_skb(desc->rx_buf_size + NET_SKB_PAD, GFP_ATOMIC, ++ SKB_ALLOC_RX, NUMA_NO_NODE); ++ skb_reserve(psk_buff, NET_SKB_PAD); ++ if (!psk_buff) ++ return -ENOMEM; ++ skb_reserve(psk_buff, MIN_BYTES_RX_HEADROOM); ++ ++ dma = pci_map_single(pcie_priv->pdev, ++ psk_buff->data, ++ desc->rx_buf_size, ++ PCI_DMA_FROMDEVICE); ++ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { ++ wiphy_err(priv->hw->wiphy, ++ "refill: failed to map pci memory!\n"); ++ return -ENOMEM; ++ } ++ ++ desc->rx_vbuflist[buf_idx] = psk_buff; ++ desc->prx_ring[buf_idx].data = cpu_to_le32(dma); ++ *((u32 *)&psk_buff->cb[16]) = 0xdeadbeef; ++ skb_queue_tail(&pcie_priv->rx_skb_trace, psk_buff); ++ ++ return 0; ++} ++ ++int pcie_rx_init_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ int rc; ++ ++ rc = pcie_rx_ring_alloc_ndp(priv); ++ if (rc) { ++ pcie_rx_ring_free_ndp(priv); ++ wiphy_err(hw->wiphy, "allocating RX ring failed\n"); ++ return rc; ++ } ++ ++ rc = pcie_rx_ring_init_ndp(priv); ++ if (rc) { ++ pcie_rx_ring_free_ndp(priv); ++ wiphy_err(hw->wiphy, ++ "initializing RX ring failed\n"); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++void pcie_rx_deinit_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ pcie_rx_ring_cleanup_ndp(priv); ++ pcie_rx_ring_free_ndp(priv); ++} ++ ++void pcie_rx_recv_ndp(unsigned long data) ++{ ++ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ struct rx_ring_done *prx_ring_done; ++ struct pcie_rx_desc_ndp *prx_desc; ++ u32 rx_done_head; ++ u32 rx_done_tail; ++ u32 rx_desc_head; ++ struct sk_buff *psk_buff; ++ u32 buf_idx; ++ u32 rx_cnt; ++ u32 ctrl, ctrl_case; ++ bool bad_mic; ++ u16 stnid; ++ u16 pktlen; ++ struct rx_info *rx_info; ++ struct pcie_dma_data *dma_data; ++ u8 signal; ++ ++ rx_done_head = readl(pcie_priv->iobase1 + MACREG_REG_RXDONEHEAD); ++ rx_done_tail = readl(pcie_priv->iobase1 + MACREG_REG_RXDONETAIL); ++ rx_desc_head = readl(pcie_priv->iobase1 + MACREG_REG_RXDESCHEAD); ++ rx_cnt = 0; ++ ++ while ((rx_done_tail != rx_done_head) && ++ (rx_cnt < pcie_priv->recv_limit)) { ++recheck: ++ prx_ring_done = &desc->prx_ring_done[rx_done_tail]; ++ wmb(); /*Data Memory Barrier*/ ++ if (le32_to_cpu(prx_ring_done->user) == 0xdeadbeef) { ++ pcie_priv->recheck_rxringdone++; ++ udelay(1); ++ goto recheck; ++ } ++ buf_idx = le32_to_cpu(prx_ring_done->user) & 0x3fff; ++ prx_ring_done->user = cpu_to_le32(0xdeadbeef); ++ rx_done_tail++; ++ prx_desc = &desc->prx_ring[buf_idx]; ++ if (!prx_desc->data) ++ wiphy_err(hw->wiphy, "RX desc data is NULL\n"); ++ psk_buff = desc->rx_vbuflist[buf_idx]; ++ if (!psk_buff) { ++ wiphy_err(hw->wiphy, "RX socket buffer is NULL\n"); ++ goto out; ++ } ++ if (*((u32 *)&psk_buff->cb[16]) != 0xdeadbeef) { ++ pcie_priv->signature_err++; ++ break; ++ } ++ if (psk_buff->next && psk_buff->prev) { ++ skb_unlink(psk_buff, &pcie_priv->rx_skb_trace); ++ *((u32 *)&psk_buff->cb[16]) = 0xbeefdead; ++ } else { ++ pcie_priv->rx_skb_unlink_err++; ++ break; ++ } ++ ++ pci_unmap_single(pcie_priv->pdev, ++ le32_to_cpu(prx_desc->data), ++ desc->rx_buf_size, ++ PCI_DMA_FROMDEVICE); ++ ++ bad_mic = false; ++ ctrl = le32_to_cpu(prx_ring_done->ctrl); ++ ctrl_case = ctrl & RXRING_CTRL_CASE_MASK; ++ stnid = (ctrl >> RXRING_CTRL_STA_SHIFT) & RXRING_CTRL_STA_MASK; ++ pcie_rx_update_ndp_cnts(priv, ctrl_case); ++ ++ switch (ctrl_case) { ++ case RXRING_CASE_FAST_DATA: ++ if (stnid == RXRING_CTRL_STA_UNKNOWN) { ++ dev_kfree_skb_any(psk_buff); ++ break; ++ } ++ pktlen = psk_buff->data[12] << 8 | psk_buff->data[13]; ++ pktlen += ETH_HLEN; ++ ++ if (skb_tailroom(psk_buff) >= pktlen) { ++ skb_put(psk_buff, pktlen); ++ pcie_rx_process_fast_data(priv, psk_buff, ++ stnid); ++ } else { ++ wiphy_err(hw->wiphy, ++ "fast: space %d(%d) is not enough\n", ++ skb_tailroom(psk_buff), pktlen); ++ dev_kfree_skb_any(psk_buff); ++ } ++ break; ++ case RXRING_CASE_FAST_BAD_AMSDU: ++ case RXRING_CASE_SLOW_BAD_STA: ++ case RXRING_CASE_SLOW_DEL_DONE: ++ case RXRING_CASE_DROP: ++ case RXRING_CASE_SLOW_BAD_PN: ++ if (ctrl_case == RXRING_CASE_SLOW_DEL_DONE) { ++ wiphy_debug(hw->wiphy, ++ "staid %d deleted\n", ++ stnid); ++ utils_free_stnid(priv, stnid); ++ } ++ dev_kfree_skb_any(psk_buff); ++ break; ++ case RXRING_CASE_SLOW_BAD_MIC: ++ bad_mic = true; ++ case RXRING_CASE_SLOW_NOQUEUE: ++ case RXRING_CASE_SLOW_NORUN: ++ case RXRING_CASE_SLOW_MGMT: ++ case RXRING_CASE_SLOW_MCAST: ++ case RXRING_CASE_SLOW_PROMISC: ++ rx_info = (struct rx_info *)psk_buff->data; ++ dma_data = (struct pcie_dma_data *)&rx_info->hdr[0]; ++ pktlen = le16_to_cpu(dma_data->fwlen); ++ pktlen += sizeof(*rx_info); ++ pktlen += sizeof(struct pcie_dma_data); ++ if (bad_mic) { ++ memset((void *)&dma_data->data, 0, 4); ++ pktlen += 4; ++ } ++ if (skb_tailroom(psk_buff) >= pktlen) { ++ skb_put(psk_buff, pktlen); ++ skb_pull(psk_buff, sizeof(*rx_info)); ++ signal = ((le32_to_cpu(rx_info->rssi_x) >> ++ RXINFO_RSSI_X_SHIFT) & ++ RXINFO_RSSI_X_MASK); ++ pcie_rx_process_slow_data(priv, psk_buff, ++ bad_mic, signal); ++ } else { ++ wiphy_err(hw->wiphy, ++ "slow: space %d(%d) is not enough\n", ++ skb_tailroom(psk_buff), pktlen); ++ dev_kfree_skb_any(psk_buff); ++ } ++ break; ++ default: ++ wiphy_err(hw->wiphy, "unknown control case: %d\n", ++ ctrl_case); ++ dev_kfree_skb_any(psk_buff); ++ break; ++ } ++out: ++ pcie_rx_refill_ndp(priv, buf_idx); ++ ++ if (rx_done_tail >= MAX_RX_RING_DONE_SIZE) ++ rx_done_tail = 0; ++ ++ rx_done_head = ++ readl(pcie_priv->iobase1 + MACREG_REG_RXDONEHEAD); ++ rx_cnt++; ++ } ++ ++ rx_desc_head += rx_cnt; ++ if (rx_desc_head >= MAX_RX_RING_SEND_SIZE) ++ rx_desc_head = rx_desc_head - MAX_RX_RING_SEND_SIZE; ++ writel(rx_done_tail, pcie_priv->iobase1 + MACREG_REG_RXDONETAIL); ++ writel(rx_desc_head, pcie_priv->iobase1 + MACREG_REG_RXDESCHEAD); ++ ++ pcie_mask_int(pcie_priv, MACREG_A2HRIC_RX_DONE_HEAD_RDY, true); ++ pcie_priv->is_rx_schedule = false; ++} +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.h +new file mode 100644 +index 000000000000..7e83cedf4351 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.h +@@ -0,0 +1,26 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines receive related functions for new data path. ++ */ ++ ++#ifndef _RX_NDP_H_ ++#define _RX_NDP_H_ ++ ++int pcie_rx_init_ndp(struct ieee80211_hw *hw); ++void pcie_rx_deinit_ndp(struct ieee80211_hw *hw); ++void pcie_rx_recv_ndp(unsigned long data); ++ ++#endif /* _RX_NDP_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/sc4_ddr.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/sc4_ddr.h +new file mode 100644 +index 000000000000..2da0257accba +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/sc4_ddr.h +@@ -0,0 +1,965 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++static unsigned char sc4_ddr_init[] = { ++0x05, ++0x00, ++0x00, ++0x00, ++0x00, ++0x00, ++0x00, ++0x00, ++0xa4, ++0x03, ++0x00, ++0x00, ++0x2a, ++0xbe, ++0xad, ++0x7e, ++0x00, ++0x00, ++0x00, ++0xa8, ++0x01, ++0x00, ++0x00, ++0x00, ++0x00, ++0x00, ++0x00, ++0x00, ++0x5c, ++0x48, ++0x5b, ++0x49, ++0x04, ++0x00, ++0x00, ++0x00, ++0x10, ++0xb5, ++0xc0, ++0xf8, ++0x08, ++0x00, ++0x00, ++0x00, ++0xe0, ++0x12, ++0xef, ++0x21, ++0x0c, ++0x00, ++0x00, ++0x00, ++0xc0, ++0xf8, ++0x20, ++0x13, ++0x10, ++0x00, ++0x00, ++0x00, ++0x02, ++0x21, ++0xc0, ++0xf8, ++0x14, ++0x00, ++0x00, ++0x00, ++0x24, ++0x13, ++0x02, ++0x01, ++0x18, ++0x00, ++0x00, ++0x00, ++0x57, ++0x49, ++0x0a, ++0x60, ++0x1c, ++0x00, ++0x00, ++0x00, ++0x00, ++0x21, ++0x56, ++0x4b, ++0x20, ++0x00, ++0x00, ++0x00, ++0xc0, ++0x3b, ++0x19, ++0x60, ++0x24, ++0x00, ++0x00, ++0x00, ++0x54, ++0x4b, ++0x1b, ++0x1d, ++0x28, ++0x00, ++0x00, ++0x00, ++0x1a, ++0x60, ++0x53, ++0x4b, ++0x2c, ++0x00, ++0x00, ++0x00, ++0xbc, ++0x3b, ++0x19, ++0x60, ++0x30, ++0x00, ++0x00, ++0x00, ++0x51, ++0x4b, ++0x08, ++0x33, ++0x34, ++0x00, ++0x00, ++0x00, ++0x1a, ++0x60, ++0x50, ++0x4b, ++0x38, ++0x00, ++0x00, ++0x00, ++0xb8, ++0x3b, ++0x19, ++0x60, ++0x3c, ++0x00, ++0x00, ++0x00, ++0x4e, ++0x4b, ++0x0c, ++0x33, ++0x40, ++0x00, ++0x00, ++0x00, ++0x1a, ++0x60, ++0x4d, ++0x4b, ++0x44, ++0x00, ++0x00, ++0x00, ++0xb4, ++0x3b, ++0x19, ++0x60, ++0x48, ++0x00, ++0x00, ++0x00, ++0x4b, ++0x4b, ++0x10, ++0x33, ++0x4c, ++0x00, ++0x00, ++0x00, ++0x1a, ++0x60, ++0x4a, ++0x4b, ++0x50, ++0x00, ++0x00, ++0x00, ++0xb0, ++0x3b, ++0x19, ++0x60, ++0x54, ++0x00, ++0x00, ++0x00, ++0x48, ++0x4b, ++0x80, ++0x33, ++0x58, ++0x00, ++0x00, ++0x00, ++0x1a, ++0x60, ++0x47, ++0x4b, ++0x5c, ++0x00, ++0x00, ++0x00, ++0x40, ++0x3b, ++0x19, ++0x60, ++0x60, ++0x00, ++0x00, ++0x00, ++0x47, ++0x4c, ++0x46, ++0x4b, ++0x64, ++0x00, ++0x00, ++0x00, ++0x23, ++0x60, ++0x24, ++0x1d, ++0x68, ++0x00, ++0x00, ++0x00, ++0x23, ++0x60, ++0x24, ++0x1d, ++0x6c, ++0x00, ++0x00, ++0x00, ++0x23, ++0x60, ++0x44, ++0x4b, ++0x70, ++0x00, ++0x00, ++0x00, ++0x40, ++0x33, ++0x19, ++0x60, ++0x74, ++0x00, ++0x00, ++0x00, ++0x1b, ++0x1d, ++0x19, ++0x60, ++0x78, ++0x00, ++0x00, ++0x00, ++0x1b, ++0x1d, ++0x19, ++0x60, ++0x7c, ++0x00, ++0x00, ++0x00, ++0x41, ++0x4b, ++0xc0, ++0xf8, ++0x80, ++0x00, ++0x00, ++0x00, ++0xe0, ++0x31, ++0x41, ++0x4b, ++0x84, ++0x00, ++0x00, ++0x00, ++0xc0, ++0xf8, ++0xf0, ++0x31, ++0x88, ++0x00, ++0x00, ++0x00, ++0x03, ++0x04, ++0xc0, ++0xf8, ++0x8c, ++0x00, ++0x00, ++0x00, ++0xf0, ++0x32, ++0x40, ++0xf2, ++0x90, ++0x00, ++0x00, ++0x00, ++0x55, ++0x13, ++0xc0, ++0xf8, ++0x94, ++0x00, ++0x00, ++0x00, ++0x60, ++0x33, ++0x3d, ++0x4b, ++0x98, ++0x00, ++0x00, ++0x00, ++0xc0, ++0xf8, ++0x64, ++0x33, ++0x9c, ++0x00, ++0x00, ++0x00, ++0x13, ++0x1d, ++0xc0, ++0xf8, ++0xa0, ++0x00, ++0x00, ++0x00, ++0x68, ++0x33, ++0x3b, ++0x4b, ++0xa4, ++0x00, ++0x00, ++0x00, ++0xc0, ++0xf8, ++0x6c, ++0x33, ++0xa8, ++0x00, ++0x00, ++0x00, ++0x3a, ++0x4b, ++0xc0, ++0xf8, ++0xac, ++0x00, ++0x00, ++0x00, ++0x70, ++0x33, ++0x3a, ++0x4b, ++0xb0, ++0x00, ++0x00, ++0x00, ++0xc0, ++0xf8, ++0x74, ++0x33, ++0xb4, ++0x00, ++0x00, ++0x00, ++0x39, ++0x4b, ++0xc0, ++0xf8, ++0xb8, ++0x00, ++0x00, ++0x00, ++0x78, ++0x33, ++0xc4, ++0x23, ++0xbc, ++0x00, ++0x00, ++0x00, ++0xc0, ++0xf8, ++0x7c, ++0x33, ++0xc0, ++0x00, ++0x00, ++0x00, ++0x37, ++0x4b, ++0xc0, ++0xf8, ++0xc4, ++0x00, ++0x00, ++0x00, ++0x80, ++0x33, ++0x37, ++0x4b, ++0xc8, ++0x00, ++0x00, ++0x00, ++0xc0, ++0xf8, ++0x84, ++0x33, ++0xcc, ++0x00, ++0x00, ++0x00, ++0x36, ++0x4b, ++0xc0, ++0xf8, ++0xd0, ++0x00, ++0x00, ++0x00, ++0x88, ++0x33, ++0x42, ++0xf2, ++0xd4, ++0x00, ++0x00, ++0x00, ++0x44, ++0x23, ++0xc0, ++0xf8, ++0xd8, ++0x00, ++0x00, ++0x00, ++0x8c, ++0x33, ++0xc0, ++0xf8, ++0xdc, ++0x00, ++0x00, ++0x00, ++0x90, ++0x13, ++0x4f, ++0xf4, ++0xe0, ++0x00, ++0x00, ++0x00, ++0x60, ++0x43, ++0xc0, ++0xf8, ++0xe4, ++0x00, ++0x00, ++0x00, ++0xa0, ++0x32, ++0xc0, ++0xf8, ++0xe8, ++0x00, ++0x00, ++0x00, ++0xa4, ++0x22, ++0xc0, ++0xf8, ++0xec, ++0x00, ++0x00, ++0x00, ++0xa8, ++0x12, ++0x44, ++0xf2, ++0xf0, ++0x00, ++0x00, ++0x00, ++0x40, ++0x02, ++0x2e, ++0x4b, ++0xf4, ++0x00, ++0x00, ++0x00, ++0x1a, ++0x60, ++0x1b, ++0x1d, ++0xf8, ++0x00, ++0x00, ++0x00, ++0x2d, ++0x4a, ++0x1a, ++0x60, ++0xfc, ++0x00, ++0x00, ++0x00, ++0x1b, ++0x1d, ++0x2d, ++0x4a, ++0x00, ++0x01, ++0x00, ++0x00, ++0x1a, ++0x60, ++0x1b, ++0x1d, ++0x04, ++0x01, ++0x00, ++0x00, ++0x2c, ++0x4a, ++0x1a, ++0x60, ++0x08, ++0x01, ++0x00, ++0x00, ++0x1b, ++0x1d, ++0x2c, ++0x4a, ++0x0c, ++0x01, ++0x00, ++0x00, ++0x1a, ++0x60, ++0x1b, ++0x1d, ++0x10, ++0x01, ++0x00, ++0x00, ++0x4f, ++0xf4, ++0x60, ++0x12, ++0x14, ++0x01, ++0x00, ++0x00, ++0x1a, ++0x60, ++0x25, ++0x4a, ++0x18, ++0x01, ++0x00, ++0x00, ++0x28, ++0x32, ++0x11, ++0x60, ++0x1c, ++0x01, ++0x00, ++0x00, ++0x23, ++0x4a, ++0x30, ++0x32, ++0x20, ++0x01, ++0x00, ++0x00, ++0x11, ++0x60, ++0x12, ++0x1d, ++0x24, ++0x01, ++0x00, ++0x00, ++0x11, ++0x60, ++0x03, ++0x22, ++0x28, ++0x01, ++0x00, ++0x00, ++0x20, ++0x4b, ++0x38, ++0x33, ++0x2c, ++0x01, ++0x00, ++0x00, ++0x1a, ++0x60, ++0x20, ++0x22, ++0x30, ++0x01, ++0x00, ++0x00, ++0x1b, ++0x1d, ++0x1a, ++0x60, ++0x34, ++0x01, ++0x00, ++0x00, ++0x1b, ++0x1d, ++0x4f, ++0xf0, ++0x38, ++0x01, ++0x00, ++0x00, ++0x04, ++0x22, ++0x1a, ++0x60, ++0x3c, ++0x01, ++0x00, ++0x00, ++0x03, ++0x06, ++0x1b, ++0x4a, ++0x40, ++0x01, ++0x00, ++0x00, ++0x20, ++0x32, ++0x13, ++0x60, ++0x44, ++0x01, ++0x00, ++0x00, ++0x43, ++0x06, ++0x13, ++0x60, ++0x48, ++0x01, ++0x00, ++0x00, ++0x83, ++0x06, ++0x13, ++0x60, ++0x4c, ++0x01, ++0x00, ++0x00, ++0x0c, ++0x4b, ++0x1c, ++0x4a, ++0x50, ++0x01, ++0x00, ++0x00, ++0x70, ++0x33, ++0x1a, ++0x60, ++0x54, ++0x01, ++0x00, ++0x00, ++0x41, ++0x62, ++0x4f, ++0xf4, ++0x58, ++0x01, ++0x00, ++0x00, ++0x7f, ++0x22, ++0x82, ++0x62, ++0x5c, ++0x01, ++0x00, ++0x00, ++0x19, ++0x4a, ++0x82, ++0x66, ++0x60, ++0x01, ++0x00, ++0x00, ++0xc1, ++0x66, ++0x40, ++0xf2, ++0x64, ++0x01, ++0x00, ++0x00, ++0x63, ++0x42, ++0x42, ++0x63, ++0x68, ++0x01, ++0x00, ++0x00, ++0x01, ++0x64, ++0x17, ++0x49, ++0x6c, ++0x01, ++0x00, ++0x00, ++0x01, ++0x60, ++0x10, ++0xbd, ++0x70, ++0x01, ++0x00, ++0x00, ++0x22, ++0x76, ++0x30, ++0x00, ++0x74, ++0x01, ++0x00, ++0x00, ++0x20, ++0x00, ++0x00, ++0xf0, ++0x78, ++0x01, ++0x00, ++0x00, ++0x40, ++0x06, ++0x00, ++0xf0, ++0x7c, ++0x01, ++0x00, ++0x00, ++0x04, ++0x00, ++0x15, ++0x15, ++0x80, ++0x01, ++0x00, ++0x00, ++0x00, ++0x05, ++0x00, ++0xf0, ++0x84, ++0x01, ++0x00, ++0x00, ++0x01, ++0x00, ++0x0d, ++0x00, ++0x88, ++0x01, ++0x00, ++0x00, ++0x32, ++0x05, ++0x00, ++0x04, ++0x8c, ++0x01, ++0x00, ++0x00, ++0xa9, ++0x02, ++0xb8, ++0x00, ++0x90, ++0x01, ++0x00, ++0x00, ++0x00, ++0x01, ++0x40, ++0x00, ++0x94, ++0x01, ++0x00, ++0x00, ++0xeb, ++0x06, ++0x77, ++0x00, ++0x98, ++0x01, ++0x00, ++0x00, ++0x00, ++0x52, ++0x7b, ++0x50, ++0x9c, ++0x01, ++0x00, ++0x00, ++0x0b, ++0x06, ++0x04, ++0x10, ++0xa0, ++0x01, ++0x00, ++0x00, ++0x10, ++0x07, ++0x17, ++0x13, ++0xa4, ++0x01, ++0x00, ++0x00, ++0x07, ++0x74, ++0x70, ++0x00, ++0xa8, ++0x01, ++0x00, ++0x00, ++0x40, ++0x00, ++0x70, ++0x50, ++0xac, ++0x01, ++0x00, ++0x00, ++0x00, ++0x04, ++0x00, ++0xf0, ++0xb0, ++0x01, ++0x00, ++0x00, ++0x79, ++0x07, ++0x70, ++0x17, ++0xb4, ++0x01, ++0x00, ++0x00, ++0x70, ++0x07, ++0xf0, ++0x0f, ++0xb8, ++0x01, ++0x00, ++0x00, ++0x77, ++0xfc, ++0x03, ++0x3f, ++0xbc, ++0x01, ++0x00, ++0x00, ++0x00, ++0x31, ++0x10, ++0x00, ++0xc0, ++0x01, ++0x00, ++0x00, ++0x01, ++0x00, ++0x00, ++0xc0, ++0xc4, ++0x01, ++0x00, ++0x00, ++0x66, ++0x66, ++0x66, ++0x00, ++0xc8, ++0x01, ++0x00, ++0x00, ++0x01, ++0x00, ++0x00, ++0x11, ++0x37, ++0x3e, ++0xfc, ++0xdc, ++}; +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.c +new file mode 100644 +index 000000000000..dd77589ef5a6 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.c +@@ -0,0 +1,1396 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements transmit related functions. */ ++ ++#include ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "utils.h" ++#include "hif/fwcmd.h" ++#include "hif/pcie/dev.h" ++#include "hif/pcie/tx.h" ++ ++#define MAX_NUM_TX_RING_BYTES (PCIE_MAX_NUM_TX_DESC * \ ++ sizeof(struct pcie_tx_desc)) ++ ++#define MAX_NUM_TX_HNDL_BYTES (PCIE_MAX_NUM_TX_DESC * \ ++ sizeof(struct pcie_tx_hndl)) ++ ++#define TOTAL_HW_QUEUES (SYSADPT_TX_WMM_QUEUES + \ ++ PCIE_AMPDU_QUEUES) ++ ++#define EAGLE_TXD_XMITCTRL_USE_MC_RATE 0x8 /* Use multicast data rate */ ++ ++#define MWL_QOS_ACK_POLICY_MASK 0x0060 ++#define MWL_QOS_ACK_POLICY_NORMAL 0x0000 ++#define MWL_QOS_ACK_POLICY_BLOCKACK 0x0060 ++ ++#define EXT_IV 0x20 ++#define INCREASE_IV(iv16, iv32) \ ++{ \ ++ (iv16)++; \ ++ if ((iv16) == 0) \ ++ (iv32)++; \ ++} ++ ++/* Transmission information to transmit a socket buffer. */ ++struct pcie_tx_ctrl { ++ void *vif; ++ void *sta; ++ void *k_conf; ++ void *amsdu_pkts; ++ u8 tx_priority; ++ u8 type; ++ u16 qos_ctrl; ++ u8 xmit_control; ++}; ++ ++struct ccmp_hdr { ++ __le16 iv16; ++ u8 rsvd; ++ u8 key_id; ++ __le32 iv32; ++} __packed; ++ ++static int pcie_tx_ring_alloc(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data *desc; ++ int num; ++ u8 *mem; ++ ++ desc = &pcie_priv->desc_data[0]; ++ ++ mem = dma_alloc_coherent(priv->dev, ++ MAX_NUM_TX_RING_BYTES * ++ PCIE_NUM_OF_DESC_DATA, ++ &desc->pphys_tx_ring, ++ GFP_KERNEL); ++ ++ if (!mem) { ++ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); ++ return -ENOMEM; ++ } ++ ++ for (num = 0; num < PCIE_NUM_OF_DESC_DATA; num++) { ++ desc = &pcie_priv->desc_data[num]; ++ ++ desc->ptx_ring = (struct pcie_tx_desc *) ++ (mem + num * MAX_NUM_TX_RING_BYTES); ++ ++ desc->pphys_tx_ring = (dma_addr_t) ++ ((u32)pcie_priv->desc_data[0].pphys_tx_ring + ++ num * MAX_NUM_TX_RING_BYTES); ++ ++ memset(desc->ptx_ring, 0x00, ++ MAX_NUM_TX_RING_BYTES); ++ } ++ ++ mem = kzalloc(MAX_NUM_TX_HNDL_BYTES * PCIE_NUM_OF_DESC_DATA, ++ GFP_KERNEL); ++ ++ if (!mem) { ++ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); ++ dma_free_coherent(priv->dev, ++ MAX_NUM_TX_RING_BYTES * ++ PCIE_NUM_OF_DESC_DATA, ++ pcie_priv->desc_data[0].ptx_ring, ++ pcie_priv->desc_data[0].pphys_tx_ring); ++ return -ENOMEM; ++ } ++ ++ for (num = 0; num < PCIE_NUM_OF_DESC_DATA; num++) { ++ desc = &pcie_priv->desc_data[num]; ++ ++ desc->tx_hndl = (struct pcie_tx_hndl *) ++ (mem + num * MAX_NUM_TX_HNDL_BYTES); ++ } ++ ++ return 0; ++} ++ ++static int pcie_txbd_ring_create(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int num; ++ u8 *mem; ++ ++ /* driver maintaines the write pointer and firmware maintaines the read ++ * pointer. ++ */ ++ pcie_priv->txbd_wrptr = 0; ++ pcie_priv->txbd_rdptr = 0; ++ ++ /* allocate shared memory for the BD ring and divide the same in to ++ * several descriptors ++ */ ++ pcie_priv->txbd_ring_size = ++ sizeof(struct pcie_data_buf) * PCIE_MAX_TXRX_BD; ++ wiphy_info(priv->hw->wiphy, "TX ring: allocating %d bytes\n", ++ pcie_priv->txbd_ring_size); ++ ++ mem = dma_alloc_coherent(priv->dev, ++ pcie_priv->txbd_ring_size, ++ &pcie_priv->txbd_ring_pbase, ++ GFP_KERNEL); ++ ++ if (!mem) { ++ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); ++ return -ENOMEM; ++ } ++ pcie_priv->txbd_ring_vbase = mem; ++ wiphy_info(priv->hw->wiphy, ++ "TX ring: - base: %p, pbase: 0x%x, len: %d\n", ++ pcie_priv->txbd_ring_vbase, ++ pcie_priv->txbd_ring_pbase, ++ pcie_priv->txbd_ring_size); ++ ++ for (num = 0; num < PCIE_MAX_TXRX_BD; num++) { ++ pcie_priv->txbd_ring[num] = ++ (struct pcie_data_buf *)(pcie_priv->txbd_ring_vbase + ++ (sizeof(struct pcie_data_buf) * num)); ++ pcie_priv->txbd_ring[num]->flags = 0; ++ pcie_priv->txbd_ring[num]->offset = 0; ++ pcie_priv->txbd_ring[num]->frag_len = 0; ++ pcie_priv->txbd_ring[num]->len = 0; ++ pcie_priv->txbd_ring[num]->paddr = 0; ++ pcie_priv->tx_buf_list[num] = NULL; ++ } ++ ++ return 0; ++} ++ ++static int pcie_tx_ring_init(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int num, i; ++ struct pcie_desc_data *desc; ++ ++ for (num = 0; num < PCIE_NUM_OF_DESC_DATA; num++) { ++ skb_queue_head_init(&pcie_priv->txq[num]); ++ pcie_priv->fw_desc_cnt[num] = 0; ++ ++ if (priv->chip_type == MWL8997) ++ continue; ++ ++ desc = &pcie_priv->desc_data[num]; ++ ++ if (desc->ptx_ring) { ++ for (i = 0; i < PCIE_MAX_NUM_TX_DESC; i++) { ++ desc->ptx_ring[i].status = ++ cpu_to_le32(EAGLE_TXD_STATUS_IDLE); ++ desc->ptx_ring[i].pphys_next = ++ cpu_to_le32((u32)desc->pphys_tx_ring + ++ ((i + 1) * ++ sizeof(struct pcie_tx_desc))); ++ desc->tx_hndl[i].pdesc = ++ &desc->ptx_ring[i]; ++ if (i < PCIE_MAX_NUM_TX_DESC - 1) ++ desc->tx_hndl[i].pnext = ++ &desc->tx_hndl[i + 1]; ++ } ++ desc->ptx_ring[PCIE_MAX_NUM_TX_DESC - 1].pphys_next = ++ cpu_to_le32((u32)desc->pphys_tx_ring); ++ desc->tx_hndl[PCIE_MAX_NUM_TX_DESC - 1].pnext = ++ &desc->tx_hndl[0]; ++ ++ desc->pstale_tx_hndl = &desc->tx_hndl[0]; ++ desc->pnext_tx_hndl = &desc->tx_hndl[0]; ++ } else { ++ wiphy_err(priv->hw->wiphy, "no valid TX mem\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ return 0; ++} ++ ++static void pcie_tx_ring_cleanup(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int cleaned_tx_desc = 0; ++ int num, i; ++ struct pcie_desc_data *desc; ++ ++ for (num = 0; num < PCIE_NUM_OF_DESC_DATA; num++) { ++ skb_queue_purge(&pcie_priv->txq[num]); ++ pcie_priv->fw_desc_cnt[num] = 0; ++ ++ if (priv->chip_type == MWL8997) ++ continue; ++ ++ desc = &pcie_priv->desc_data[num]; ++ ++ if (desc->ptx_ring) { ++ for (i = 0; i < PCIE_MAX_NUM_TX_DESC; i++) { ++ if (!desc->tx_hndl[i].psk_buff) ++ continue; ++ ++ wiphy_debug(priv->hw->wiphy, ++ "unmapped and free'd %i %p %x\n", ++ i, ++ desc->tx_hndl[i].psk_buff->data, ++ le32_to_cpu( ++ desc->ptx_ring[i].pkt_ptr)); ++ pci_unmap_single(pcie_priv->pdev, ++ le32_to_cpu( ++ desc->ptx_ring[i].pkt_ptr), ++ desc->tx_hndl[i].psk_buff->len, ++ PCI_DMA_TODEVICE); ++ dev_kfree_skb_any(desc->tx_hndl[i].psk_buff); ++ desc->ptx_ring[i].status = ++ cpu_to_le32(EAGLE_TXD_STATUS_IDLE); ++ desc->ptx_ring[i].pkt_ptr = 0; ++ desc->ptx_ring[i].pkt_len = 0; ++ desc->tx_hndl[i].psk_buff = NULL; ++ cleaned_tx_desc++; ++ } ++ } ++ } ++ ++ wiphy_info(priv->hw->wiphy, "cleaned %i TX descr\n", cleaned_tx_desc); ++} ++ ++static void pcie_tx_ring_free(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int num; ++ ++ if (pcie_priv->desc_data[0].ptx_ring) { ++ dma_free_coherent(priv->dev, ++ MAX_NUM_TX_RING_BYTES * ++ PCIE_NUM_OF_DESC_DATA, ++ pcie_priv->desc_data[0].ptx_ring, ++ pcie_priv->desc_data[0].pphys_tx_ring); ++ } ++ ++ for (num = 0; num < PCIE_NUM_OF_DESC_DATA; num++) { ++ if (pcie_priv->desc_data[num].ptx_ring) ++ pcie_priv->desc_data[num].ptx_ring = NULL; ++ pcie_priv->desc_data[num].pstale_tx_hndl = NULL; ++ pcie_priv->desc_data[num].pnext_tx_hndl = NULL; ++ } ++ ++ kfree(pcie_priv->desc_data[0].tx_hndl); ++} ++ ++static void pcie_txbd_ring_delete(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct sk_buff *skb; ++ struct pcie_tx_desc *tx_desc; ++ int num; ++ ++ if (pcie_priv->txbd_ring_vbase) { ++ dma_free_coherent(priv->dev, ++ pcie_priv->txbd_ring_size, ++ pcie_priv->txbd_ring_vbase, ++ pcie_priv->txbd_ring_pbase); ++ } ++ ++ for (num = 0; num < PCIE_MAX_TXRX_BD; num++) { ++ pcie_priv->txbd_ring[num] = NULL; ++ if (pcie_priv->tx_buf_list[num]) { ++ skb = pcie_priv->tx_buf_list[num]; ++ tx_desc = (struct pcie_tx_desc *)skb->data; ++ ++ pci_unmap_single(pcie_priv->pdev, ++ le32_to_cpu(tx_desc->pkt_ptr), ++ skb->len, ++ PCI_DMA_TODEVICE); ++ dev_kfree_skb_any(skb); ++ } ++ pcie_priv->tx_buf_list[num] = NULL; ++ } ++ ++ pcie_priv->txbd_wrptr = 0; ++ pcie_priv->txbd_rdptr = 0; ++ pcie_priv->txbd_ring_size = 0; ++ pcie_priv->txbd_ring_vbase = NULL; ++ pcie_priv->txbd_ring_pbase = 0; ++} ++ ++static inline void pcie_tx_add_ccmp_hdr(u8 *pccmp_hdr, ++ u8 key_id, u16 iv16, u32 iv32) ++{ ++ struct ccmp_hdr *ccmp_h = (struct ccmp_hdr *)pccmp_hdr; ++ ++ ccmp_h->iv16 = cpu_to_le16(iv16); ++ ccmp_h->rsvd = 0; ++ ccmp_h->key_id = EXT_IV | (key_id << 6); ++ ccmp_h->iv32 = cpu_to_le32(iv32); ++} ++ ++static inline bool pcie_tx_available(struct mwl_priv *priv, int desc_num) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_tx_hndl *tx_hndl; ++ ++ if (priv->chip_type == MWL8997) ++ return PCIE_TXBD_NOT_FULL(pcie_priv->txbd_wrptr, ++ pcie_priv->txbd_rdptr); ++ ++ tx_hndl = pcie_priv->desc_data[desc_num].pnext_tx_hndl; ++ ++ if (!tx_hndl->pdesc) ++ return false; ++ ++ if (tx_hndl->pdesc->status != EAGLE_TXD_STATUS_IDLE) { ++ /* Interrupt F/W anyway */ ++ if (tx_hndl->pdesc->status & ++ cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED)) ++ writel(MACREG_H2ARIC_BIT_PPA_READY, ++ pcie_priv->iobase1 + ++ MACREG_REG_H2A_INTERRUPT_EVENTS); ++ return false; ++ } ++ ++ return true; ++} ++ ++static inline void pcie_tx_skb(struct mwl_priv *priv, int desc_num, ++ struct sk_buff *tx_skb) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct ieee80211_tx_info *tx_info; ++ struct pcie_tx_ctrl *tx_ctrl; ++ struct pcie_tx_hndl *tx_hndl = NULL; ++ struct pcie_tx_desc *tx_desc; ++ struct ieee80211_sta *sta; ++ struct ieee80211_vif *vif; ++ struct mwl_vif *mwl_vif; ++ struct ieee80211_key_conf *k_conf; ++ bool ccmp = false; ++ struct pcie_pfu_dma_data *pfu_dma_data; ++ struct pcie_dma_data *dma_data; ++ struct ieee80211_hdr *wh; ++ dma_addr_t dma; ++ ++ if (WARN_ON(!tx_skb)) ++ return; ++ ++ tx_info = IEEE80211_SKB_CB(tx_skb); ++ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; ++ sta = (struct ieee80211_sta *)tx_ctrl->sta; ++ vif = (struct ieee80211_vif *)tx_ctrl->vif; ++ mwl_vif = mwl_dev_get_vif(vif); ++ k_conf = (struct ieee80211_key_conf *)tx_ctrl->k_conf; ++ ++ pcie_tx_encapsulate_frame(priv, tx_skb, k_conf, &ccmp); ++ ++ if (priv->chip_type == MWL8997) { ++ pfu_dma_data = (struct pcie_pfu_dma_data *)tx_skb->data; ++ tx_desc = &pfu_dma_data->tx_desc; ++ dma_data = &pfu_dma_data->dma_data; ++ } else { ++ tx_hndl = pcie_priv->desc_data[desc_num].pnext_tx_hndl; ++ tx_hndl->psk_buff = tx_skb; ++ tx_desc = tx_hndl->pdesc; ++ dma_data = (struct pcie_dma_data *)tx_skb->data; ++ } ++ wh = &dma_data->wh; ++ ++ if (ieee80211_is_probe_resp(wh->frame_control) && ++ priv->dump_probe) ++ wiphy_info(priv->hw->wiphy, ++ "Probe Resp: %pM\n", wh->addr1); ++ ++ if (ieee80211_is_data(wh->frame_control) || ++ (ieee80211_is_mgmt(wh->frame_control) && ++ ieee80211_has_protected(wh->frame_control) && ++ !is_multicast_ether_addr(wh->addr1))) { ++ if (is_multicast_ether_addr(wh->addr1)) { ++ if (ccmp) { ++ pcie_tx_add_ccmp_hdr(dma_data->data, ++ mwl_vif->keyidx, ++ mwl_vif->iv16, ++ mwl_vif->iv32); ++ INCREASE_IV(mwl_vif->iv16, mwl_vif->iv32); ++ } ++ } else { ++ if (ccmp) { ++ if (vif->type == NL80211_IFTYPE_STATION) { ++ pcie_tx_add_ccmp_hdr(dma_data->data, ++ mwl_vif->keyidx, ++ mwl_vif->iv16, ++ mwl_vif->iv32); ++ INCREASE_IV(mwl_vif->iv16, ++ mwl_vif->iv32); ++ } else { ++ struct mwl_sta *sta_info; ++ ++ sta_info = mwl_dev_get_sta(sta); ++ ++ pcie_tx_add_ccmp_hdr(dma_data->data, ++ 0, ++ sta_info->iv16, ++ sta_info->iv32); ++ INCREASE_IV(sta_info->iv16, ++ sta_info->iv32); ++ } ++ } ++ } ++ } ++ ++ if (tx_info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT) ++ tx_desc->flags |= PCIE_TX_WCB_FLAGS_DONT_ENCRYPT; ++ if (tx_info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) ++ tx_desc->flags |= PCIE_TX_WCB_FLAGS_NO_CCK_RATE; ++ tx_desc->tx_priority = tx_ctrl->tx_priority; ++ tx_desc->qos_ctrl = cpu_to_le16(tx_ctrl->qos_ctrl); ++ tx_desc->pkt_len = cpu_to_le16(tx_skb->len); ++ tx_desc->packet_info = 0; ++ tx_desc->data_rate = 0; ++ tx_desc->type = tx_ctrl->type; ++ tx_desc->xmit_control = tx_ctrl->xmit_control; ++ tx_desc->sap_pkt_info = 0; ++ dma = pci_map_single(pcie_priv->pdev, tx_skb->data, ++ tx_skb->len, PCI_DMA_TODEVICE); ++ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { ++ dev_kfree_skb_any(tx_skb); ++ wiphy_err(priv->hw->wiphy, ++ "failed to map pci memory!\n"); ++ return; ++ } ++ if (priv->chip_type == MWL8997) ++ tx_desc->pkt_ptr = cpu_to_le32(sizeof(struct pcie_tx_desc)); ++ else ++ tx_desc->pkt_ptr = cpu_to_le32(dma); ++ tx_desc->status = cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED); ++ /* make sure all the memory transactions done by cpu were completed */ ++ wmb(); /*Data Memory Barrier*/ ++ ++ if (priv->chip_type == MWL8997) { ++ u32 wrindx; ++ struct pcie_data_buf *data_buf; ++ const u32 num_tx_buffs = PCIE_MAX_TXRX_BD << PCIE_TX_START_PTR; ++ ++ wrindx = (pcie_priv->txbd_wrptr & PCIE_TXBD_MASK) >> ++ PCIE_TX_START_PTR; ++ pcie_priv->tx_buf_list[wrindx] = tx_skb; ++ data_buf = pcie_priv->txbd_ring[wrindx]; ++ data_buf->paddr = cpu_to_le64(dma); ++ data_buf->len = cpu_to_le16(tx_skb->len); ++ data_buf->flags = cpu_to_le16(PCIE_BD_FLAG_FIRST_DESC | ++ PCIE_BD_FLAG_LAST_DESC); ++ data_buf->frag_len = cpu_to_le16(tx_skb->len); ++ data_buf->offset = 0; ++ pcie_priv->txbd_wrptr += PCIE_BD_FLAG_TX_START_PTR; ++ ++ if ((pcie_priv->txbd_wrptr & PCIE_TXBD_MASK) == num_tx_buffs) ++ pcie_priv->txbd_wrptr = ((pcie_priv->txbd_wrptr & ++ PCIE_BD_FLAG_TX_ROLLOVER_IND) ^ ++ PCIE_BD_FLAG_TX_ROLLOVER_IND); ++ ++ /* Write the TX ring write pointer in to REG_TXBD_WRPTR */ ++ writel(pcie_priv->txbd_wrptr, ++ pcie_priv->iobase1 + REG_TXBD_WRPTR); ++ } else { ++ writel(MACREG_H2ARIC_BIT_PPA_READY, ++ pcie_priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS); ++ pcie_priv->desc_data[desc_num].pnext_tx_hndl = tx_hndl->pnext; ++ pcie_priv->fw_desc_cnt[desc_num]++; ++ } ++} ++ ++static inline ++struct sk_buff *pcie_tx_do_amsdu(struct mwl_priv *priv, ++ int desc_num, ++ struct sk_buff *tx_skb, ++ struct ieee80211_tx_info *tx_info) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct ieee80211_sta *sta; ++ struct mwl_sta *sta_info; ++ struct pcie_tx_ctrl *tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; ++ struct ieee80211_tx_info *amsdu_info; ++ struct sk_buff_head *amsdu_pkts; ++ struct mwl_amsdu_frag *amsdu; ++ int amsdu_allow_size; ++ struct ieee80211_hdr *wh; ++ int wh_len; ++ u16 len; ++ u8 *data; ++ ++ sta = (struct ieee80211_sta *)tx_ctrl->sta; ++ sta_info = mwl_dev_get_sta(sta); ++ ++ if (!sta_info->is_amsdu_allowed) ++ return tx_skb; ++ ++ wh = (struct ieee80211_hdr *)tx_skb->data; ++ if (sta_info->is_mesh_node && is_multicast_ether_addr(wh->addr3)) ++ return tx_skb; ++ ++ if (ieee80211_is_qos_nullfunc(wh->frame_control)) ++ return tx_skb; ++ ++ if (sta_info->amsdu_ctrl.cap == MWL_AMSDU_SIZE_4K) ++ amsdu_allow_size = SYSADPT_AMSDU_4K_MAX_SIZE; ++ else if (sta_info->amsdu_ctrl.cap == MWL_AMSDU_SIZE_8K) ++ amsdu_allow_size = SYSADPT_AMSDU_8K_MAX_SIZE; ++ else ++ return tx_skb; ++ ++ spin_lock_bh(&sta_info->amsdu_lock); ++ amsdu = &sta_info->amsdu_ctrl.frag[desc_num]; ++ ++ if ((tx_skb->len > SYSADPT_AMSDU_ALLOW_SIZE) || ++ utils_is_non_amsdu_packet(tx_skb->data, true)) { ++ if (amsdu->num) { ++ pcie_tx_skb(priv, desc_num, amsdu->skb); ++ amsdu->num = 0; ++ amsdu->cur_pos = NULL; ++ } ++ spin_unlock_bh(&sta_info->amsdu_lock); ++ return tx_skb; ++ } ++ ++ /* potential amsdu size, should add amsdu header 14 bytes + ++ * maximum padding 3. ++ */ ++ wh_len = ieee80211_hdrlen(wh->frame_control); ++ len = tx_skb->len - wh_len + 17; ++ ++ if (amsdu->num) { ++ if ((amsdu->skb->len + len) > amsdu_allow_size) { ++ pcie_tx_skb(priv, desc_num, amsdu->skb); ++ amsdu->num = 0; ++ amsdu->cur_pos = NULL; ++ } ++ } ++ ++ amsdu->jiffies = jiffies; ++ len = tx_skb->len - wh_len; ++ ++ if (amsdu->num == 0) { ++ struct sk_buff *newskb; ++ int headroom; ++ ++ amsdu_pkts = (struct sk_buff_head *) ++ kmalloc(sizeof(*amsdu_pkts), GFP_ATOMIC); ++ if (!amsdu_pkts) { ++ spin_unlock_bh(&sta_info->amsdu_lock); ++ return tx_skb; ++ } ++ newskb = dev_alloc_skb(amsdu_allow_size + ++ pcie_priv->tx_head_room); ++ if (!newskb) { ++ spin_unlock_bh(&sta_info->amsdu_lock); ++ kfree(amsdu_pkts); ++ return tx_skb; ++ } ++ ++ headroom = skb_headroom(newskb); ++ if (headroom < pcie_priv->tx_head_room) ++ skb_reserve(newskb, ++ (pcie_priv->tx_head_room - headroom)); ++ ++ data = newskb->data; ++ memcpy(data, tx_skb->data, wh_len); ++ if (sta_info->is_mesh_node) { ++ ether_addr_copy(data + wh_len, wh->addr3); ++ ether_addr_copy(data + wh_len + ETH_ALEN, wh->addr4); ++ } else { ++ ether_addr_copy(data + wh_len, ++ ieee80211_get_DA(wh)); ++ ether_addr_copy(data + wh_len + ETH_ALEN, ++ ieee80211_get_SA(wh)); ++ } ++ *(u8 *)(data + wh_len + ETH_HLEN - 1) = len & 0xff; ++ *(u8 *)(data + wh_len + ETH_HLEN - 2) = (len >> 8) & 0xff; ++ memcpy(data + wh_len + ETH_HLEN, tx_skb->data + wh_len, len); ++ ++ skb_put(newskb, tx_skb->len + ETH_HLEN); ++ tx_ctrl->qos_ctrl |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; ++ amsdu_info = IEEE80211_SKB_CB(newskb); ++ memcpy(amsdu_info, tx_info, sizeof(*tx_info)); ++ skb_queue_head_init(amsdu_pkts); ++ ((struct pcie_tx_ctrl *)&amsdu_info->status)->amsdu_pkts = ++ (void *)amsdu_pkts; ++ amsdu->skb = newskb; ++ } else { ++ amsdu->cur_pos += amsdu->pad; ++ data = amsdu->cur_pos; ++ ++ if (sta_info->is_mesh_node) { ++ ether_addr_copy(data, wh->addr3); ++ ether_addr_copy(data + ETH_ALEN, wh->addr4); ++ } else { ++ ether_addr_copy(data, ieee80211_get_DA(wh)); ++ ether_addr_copy(data + ETH_ALEN, ieee80211_get_SA(wh)); ++ } ++ *(u8 *)(data + ETH_HLEN - 1) = len & 0xff; ++ *(u8 *)(data + ETH_HLEN - 2) = (len >> 8) & 0xff; ++ memcpy(data + ETH_HLEN, tx_skb->data + wh_len, len); ++ ++ skb_put(amsdu->skb, len + ETH_HLEN + amsdu->pad); ++ amsdu_info = IEEE80211_SKB_CB(amsdu->skb); ++ amsdu_pkts = (struct sk_buff_head *) ++ ((struct pcie_tx_ctrl *) ++ &amsdu_info->status)->amsdu_pkts; ++ } ++ ++ amsdu->num++; ++ amsdu->pad = ((len + ETH_HLEN) % 4) ? (4 - (len + ETH_HLEN) % 4) : 0; ++ amsdu->cur_pos = amsdu->skb->data + amsdu->skb->len; ++ skb_queue_tail(amsdu_pkts, tx_skb); ++ ++ if (amsdu->num > SYSADPT_AMSDU_PACKET_THRESHOLD) { ++ amsdu->num = 0; ++ amsdu->cur_pos = NULL; ++ spin_unlock_bh(&sta_info->amsdu_lock); ++ return amsdu->skb; ++ } ++ ++ spin_unlock_bh(&sta_info->amsdu_lock); ++ return NULL; ++} ++ ++static inline void pcie_tx_ack_amsdu_pkts(struct ieee80211_hw *hw, u32 rate, ++ struct sk_buff_head *amsdu_pkts) ++{ ++ struct sk_buff *amsdu_pkt; ++ struct ieee80211_tx_info *info; ++ ++ while (skb_queue_len(amsdu_pkts) > 0) { ++ amsdu_pkt = skb_dequeue(amsdu_pkts); ++ info = IEEE80211_SKB_CB(amsdu_pkt); ++ pcie_tx_prepare_info(hw->priv, rate, info); ++ ieee80211_tx_status(hw, amsdu_pkt); ++ } ++ ++ kfree(amsdu_pkts); ++} ++ ++static void pcie_pfu_tx_done(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ u32 wrdoneidx, rdptr; ++ const u32 num_tx_buffs = PCIE_MAX_TXRX_BD << PCIE_TX_START_PTR; ++ struct pcie_data_buf *data_buf; ++ struct sk_buff *done_skb; ++ struct pcie_pfu_dma_data *pfu_dma; ++ struct pcie_tx_desc *tx_desc; ++ struct pcie_dma_data *dma_data; ++ struct ieee80211_hdr *wh; ++ struct ieee80211_tx_info *info; ++ struct pcie_tx_ctrl *tx_ctrl; ++ struct ieee80211_sta *sta; ++ struct mwl_sta *sta_info; ++ u32 rate = 0; ++ struct sk_buff_head *amsdu_pkts; ++ int hdrlen; ++ ++ spin_lock_bh(&pcie_priv->tx_desc_lock); ++ /* Read the TX ring read pointer set by firmware */ ++ rdptr = readl(pcie_priv->iobase1 + REG_TXBD_RDPTR); ++ /* free from previous txbd_rdptr to current txbd_rdptr */ ++ while (((pcie_priv->txbd_rdptr & PCIE_TXBD_MASK) != ++ (rdptr & PCIE_TXBD_MASK)) || ++ ((pcie_priv->txbd_rdptr & PCIE_BD_FLAG_TX_ROLLOVER_IND) != ++ (rdptr & PCIE_BD_FLAG_TX_ROLLOVER_IND))) { ++ wrdoneidx = pcie_priv->txbd_rdptr & PCIE_TXBD_MASK; ++ wrdoneidx >>= PCIE_TX_START_PTR; ++ ++ data_buf = pcie_priv->txbd_ring[wrdoneidx]; ++ done_skb = pcie_priv->tx_buf_list[wrdoneidx]; ++ if (done_skb) { ++ pfu_dma = (struct pcie_pfu_dma_data *)done_skb->data; ++ tx_desc = &pfu_dma->tx_desc; ++ dma_data = &pfu_dma->dma_data; ++ pci_unmap_single(pcie_priv->pdev, ++ le32_to_cpu(data_buf->paddr), ++ le16_to_cpu(data_buf->len), ++ PCI_DMA_TODEVICE); ++ tx_desc->pkt_ptr = 0; ++ tx_desc->pkt_len = 0; ++ tx_desc->status = cpu_to_le32(EAGLE_TXD_STATUS_IDLE); ++ wmb(); /* memory barrier */ ++ ++ wh = &dma_data->wh; ++ if (ieee80211_is_nullfunc(wh->frame_control) || ++ ieee80211_is_qos_nullfunc(wh->frame_control)) { ++ dev_kfree_skb_any(done_skb); ++ done_skb = NULL; ++ goto next; ++ } ++ ++ info = IEEE80211_SKB_CB(done_skb); ++ tx_ctrl = (struct pcie_tx_ctrl *)&info->status; ++ sta = (struct ieee80211_sta *)tx_ctrl->sta; ++ if (sta) { ++ sta_info = mwl_dev_get_sta(sta); ++ rate = sta_info->tx_rate_info; ++ } ++ ++ if (ieee80211_is_data(wh->frame_control) || ++ ieee80211_is_data_qos(wh->frame_control)) { ++ amsdu_pkts = (struct sk_buff_head *) ++ tx_ctrl->amsdu_pkts; ++ if (amsdu_pkts) { ++ pcie_tx_ack_amsdu_pkts(priv->hw, rate, ++ amsdu_pkts); ++ dev_kfree_skb_any(done_skb); ++ done_skb = NULL; ++ } else { ++ pcie_tx_prepare_info(priv, rate, info); ++ } ++ } else { ++ pcie_tx_prepare_info(priv, 0, info); ++ } ++ ++ if (done_skb) { ++ /* Remove H/W dma header */ ++ hdrlen = ieee80211_hdrlen( ++ dma_data->wh.frame_control); ++ memmove(dma_data->data - hdrlen, ++ &dma_data->wh, hdrlen); ++ skb_pull(done_skb, sizeof(*pfu_dma) - hdrlen); ++ ieee80211_tx_status(priv->hw, done_skb); ++ } ++ } ++next: ++ memset(data_buf, 0, sizeof(*data_buf)); ++ pcie_priv->tx_buf_list[wrdoneidx] = NULL; ++ ++ pcie_priv->txbd_rdptr += PCIE_BD_FLAG_TX_START_PTR; ++ if ((pcie_priv->txbd_rdptr & PCIE_TXBD_MASK) == num_tx_buffs) ++ pcie_priv->txbd_rdptr = ((pcie_priv->txbd_rdptr & ++ PCIE_BD_FLAG_TX_ROLLOVER_IND) ^ ++ PCIE_BD_FLAG_TX_ROLLOVER_IND); ++ } ++ spin_unlock_bh(&pcie_priv->tx_desc_lock); ++ ++ if (pcie_priv->is_tx_done_schedule) { ++ pcie_mask_int(pcie_priv, MACREG_A2HRIC_BIT_TX_DONE, true); ++ tasklet_schedule(&pcie_priv->tx_task); ++ pcie_priv->is_tx_done_schedule = false; ++ } ++} ++ ++static void pcie_non_pfu_tx_done(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int num; ++ struct pcie_desc_data *desc; ++ struct pcie_tx_hndl *tx_hndl; ++ struct pcie_tx_desc *tx_desc; ++ struct sk_buff *done_skb; ++ int idx; ++ u32 rate; ++ struct pcie_dma_data *dma_data; ++ struct ieee80211_hdr *wh; ++ struct ieee80211_tx_info *info; ++ struct pcie_tx_ctrl *tx_ctrl; ++ struct sk_buff_head *amsdu_pkts; ++ int hdrlen; ++ ++ spin_lock_bh(&pcie_priv->tx_desc_lock); ++ for (num = 0; num < SYSADPT_TX_WMM_QUEUES; num++) { ++ desc = &pcie_priv->desc_data[num]; ++ tx_hndl = desc->pstale_tx_hndl; ++ tx_desc = tx_hndl->pdesc; ++ ++ if ((tx_desc->status & ++ cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED)) && ++ (tx_hndl->pnext->pdesc->status & ++ cpu_to_le32(EAGLE_TXD_STATUS_OK))) ++ tx_desc->status = cpu_to_le32(EAGLE_TXD_STATUS_OK); ++ ++ while (tx_hndl && ++ (tx_desc->status & cpu_to_le32(EAGLE_TXD_STATUS_OK)) && ++ (!(tx_desc->status & ++ cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED)))) { ++ pci_unmap_single(pcie_priv->pdev, ++ le32_to_cpu(tx_desc->pkt_ptr), ++ le16_to_cpu(tx_desc->pkt_len), ++ PCI_DMA_TODEVICE); ++ done_skb = tx_hndl->psk_buff; ++ rate = le32_to_cpu(tx_desc->rate_info); ++ tx_desc->pkt_ptr = 0; ++ tx_desc->pkt_len = 0; ++ tx_desc->status = ++ cpu_to_le32(EAGLE_TXD_STATUS_IDLE); ++ tx_hndl->psk_buff = NULL; ++ wmb(); /*Data Memory Barrier*/ ++ ++ skb_get(done_skb); ++ idx = pcie_priv->delay_q_idx; ++ if (pcie_priv->delay_q[idx]) ++ dev_kfree_skb_any(pcie_priv->delay_q[idx]); ++ pcie_priv->delay_q[idx] = done_skb; ++ idx++; ++ if (idx >= PCIE_DELAY_FREE_Q_LIMIT) ++ idx = 0; ++ pcie_priv->delay_q_idx = idx; ++ ++ dma_data = (struct pcie_dma_data *)done_skb->data; ++ wh = &dma_data->wh; ++ if (ieee80211_is_nullfunc(wh->frame_control) || ++ ieee80211_is_qos_nullfunc(wh->frame_control)) { ++ dev_kfree_skb_any(done_skb); ++ done_skb = NULL; ++ goto next; ++ } ++ ++ info = IEEE80211_SKB_CB(done_skb); ++ if (ieee80211_is_data(wh->frame_control) || ++ ieee80211_is_data_qos(wh->frame_control)) { ++ tx_ctrl = (struct pcie_tx_ctrl *)&info->status; ++ amsdu_pkts = (struct sk_buff_head *) ++ tx_ctrl->amsdu_pkts; ++ if (amsdu_pkts) { ++ pcie_tx_ack_amsdu_pkts(priv->hw, rate, ++ amsdu_pkts); ++ dev_kfree_skb_any(done_skb); ++ done_skb = NULL; ++ } else { ++ pcie_tx_prepare_info(priv, rate, info); ++ } ++ } else { ++ pcie_tx_prepare_info(priv, 0, info); ++ } ++ ++ if (done_skb) { ++ /* Remove H/W dma header */ ++ hdrlen = ieee80211_hdrlen( ++ dma_data->wh.frame_control); ++ memmove(dma_data->data - hdrlen, ++ &dma_data->wh, hdrlen); ++ skb_pull(done_skb, sizeof(*dma_data) - hdrlen); ++ ieee80211_tx_status(priv->hw, done_skb); ++ } ++next: ++ tx_hndl = tx_hndl->pnext; ++ tx_desc = tx_hndl->pdesc; ++ pcie_priv->fw_desc_cnt[num]--; ++ } ++ ++ desc->pstale_tx_hndl = tx_hndl; ++ } ++ spin_unlock_bh(&pcie_priv->tx_desc_lock); ++ ++ if (pcie_priv->is_tx_done_schedule) { ++ pcie_mask_int(pcie_priv, MACREG_A2HRIC_BIT_TX_DONE, true); ++ tasklet_schedule(&pcie_priv->tx_task); ++ pcie_priv->is_tx_done_schedule = false; ++ } ++} ++ ++int pcie_tx_init(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int rc; ++ int i; ++ ++ if (priv->chip_type == MWL8997) ++ rc = pcie_txbd_ring_create(priv); ++ else ++ rc = pcie_tx_ring_alloc(priv); ++ ++ if (rc) { ++ wiphy_err(hw->wiphy, "allocating TX ring failed\n"); ++ return rc; ++ } ++ ++ rc = pcie_tx_ring_init(priv); ++ if (rc) { ++ pcie_tx_ring_free(priv); ++ wiphy_err(hw->wiphy, "initializing TX ring failed\n"); ++ return rc; ++ } ++ ++ pcie_priv->delay_q_idx = 0; ++ for (i = 0; i < PCIE_DELAY_FREE_Q_LIMIT; i++) ++ pcie_priv->delay_q[i] = NULL; ++ ++ return 0; ++} ++ ++void pcie_tx_deinit(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int i; ++ ++ for (i = 0; i < PCIE_DELAY_FREE_Q_LIMIT; i++) ++ if (pcie_priv->delay_q[i]) ++ dev_kfree_skb_any(pcie_priv->delay_q[i]); ++ ++ pcie_tx_ring_cleanup(priv); ++ ++ if (priv->chip_type == MWL8997) ++ pcie_txbd_ring_delete(priv); ++ else ++ pcie_tx_ring_free(priv); ++} ++ ++void pcie_tx_skbs(unsigned long data) ++{ ++ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int num = SYSADPT_TX_WMM_QUEUES; ++ struct sk_buff *tx_skb; ++ ++ spin_lock_bh(&pcie_priv->tx_desc_lock); ++ while (num--) { ++ while (skb_queue_len(&pcie_priv->txq[num]) > 0) { ++ struct ieee80211_tx_info *tx_info; ++ struct pcie_tx_ctrl *tx_ctrl; ++ ++ if (!pcie_tx_available(priv, num)) ++ break; ++ ++ tx_skb = skb_dequeue(&pcie_priv->txq[num]); ++ if (!tx_skb) ++ continue; ++ tx_info = IEEE80211_SKB_CB(tx_skb); ++ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; ++ ++ if (tx_ctrl->tx_priority >= SYSADPT_TX_WMM_QUEUES) ++ tx_skb = pcie_tx_do_amsdu(priv, num, ++ tx_skb, tx_info); ++ ++ if (tx_skb) { ++ if (pcie_tx_available(priv, num)) ++ pcie_tx_skb(priv, num, tx_skb); ++ else ++ skb_queue_head(&pcie_priv->txq[num], ++ tx_skb); ++ } ++ } ++ ++ if (skb_queue_len(&pcie_priv->txq[num]) < ++ pcie_priv->txq_wake_threshold) { ++ int queue; ++ ++ queue = SYSADPT_TX_WMM_QUEUES - num - 1; ++ if (ieee80211_queue_stopped(hw, queue)) ++ ieee80211_wake_queue(hw, queue); ++ } ++ } ++ spin_unlock_bh(&pcie_priv->tx_desc_lock); ++} ++ ++void pcie_tx_flush_amsdu(unsigned long data) ++{ ++ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct mwl_sta *sta_info; ++ int i; ++ struct mwl_amsdu_frag *amsdu_frag; ++ ++ spin_lock(&priv->sta_lock); ++ list_for_each_entry(sta_info, &priv->sta_list, list) { ++ spin_lock(&pcie_priv->tx_desc_lock); ++ spin_lock(&sta_info->amsdu_lock); ++ for (i = 0; i < SYSADPT_TX_WMM_QUEUES; i++) { ++ amsdu_frag = &sta_info->amsdu_ctrl.frag[i]; ++ if (amsdu_frag->num) { ++ if (time_after(jiffies, ++ (amsdu_frag->jiffies + 1))) { ++ if (pcie_tx_available(priv, i)) { ++ pcie_tx_skb(priv, i, ++ amsdu_frag->skb); ++ amsdu_frag->num = 0; ++ amsdu_frag->cur_pos = NULL; ++ } ++ } ++ } ++ } ++ spin_unlock(&sta_info->amsdu_lock); ++ spin_unlock(&pcie_priv->tx_desc_lock); ++ } ++ spin_unlock(&priv->sta_lock); ++ ++ pcie_mask_int(pcie_priv, MACREG_A2HRIC_BIT_QUE_EMPTY, true); ++ pcie_priv->is_qe_schedule = false; ++} ++ ++void pcie_tx_done(unsigned long data) ++{ ++ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; ++ struct mwl_priv *priv = hw->priv; ++ ++ if (priv->chip_type == MWL8997) ++ pcie_pfu_tx_done(priv); ++ else ++ pcie_non_pfu_tx_done(priv); ++} ++ ++void pcie_tx_xmit(struct ieee80211_hw *hw, ++ struct ieee80211_tx_control *control, ++ struct sk_buff *skb) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int index; ++ struct ieee80211_sta *sta; ++ struct ieee80211_tx_info *tx_info; ++ struct mwl_vif *mwl_vif; ++ struct ieee80211_hdr *wh; ++ u8 xmitcontrol; ++ u16 qos; ++ int txpriority; ++ u8 tid = 0; ++ struct mwl_ampdu_stream *stream = NULL; ++ bool start_ba_session = false; ++ bool mgmtframe = false; ++ struct ieee80211_mgmt *mgmt; ++ bool eapol_frame = false; ++ struct pcie_tx_ctrl *tx_ctrl; ++ struct ieee80211_key_conf *k_conf = NULL; ++ int rc; ++ ++ index = skb_get_queue_mapping(skb); ++ sta = control->sta; ++ ++ wh = (struct ieee80211_hdr *)skb->data; ++ tx_info = IEEE80211_SKB_CB(skb); ++ mwl_vif = mwl_dev_get_vif(tx_info->control.vif); ++ ++ if (ieee80211_is_data_qos(wh->frame_control)) ++ qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); ++ else ++ qos = 0; ++ ++ if (ieee80211_is_mgmt(wh->frame_control)) { ++ mgmtframe = true; ++ mgmt = (struct ieee80211_mgmt *)skb->data; ++ } else { ++ u16 pkt_type; ++ struct mwl_sta *sta_info; ++ ++ pkt_type = be16_to_cpu(*((__be16 *) ++ &skb->data[ieee80211_hdrlen(wh->frame_control) + 6])); ++ if (pkt_type == ETH_P_PAE) { ++ index = IEEE80211_AC_VO; ++ eapol_frame = true; ++ } ++ if (sta) { ++ if (mwl_vif->is_hw_crypto_enabled) { ++ sta_info = mwl_dev_get_sta(sta); ++ if (!sta_info->is_key_set && !eapol_frame) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ } ++ } ++ } ++ ++ if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { ++ wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); ++ wh->seq_ctrl |= cpu_to_le16(mwl_vif->seqno); ++ mwl_vif->seqno += 0x10; ++ } ++ ++ /* Setup firmware control bit fields for each frame type. */ ++ xmitcontrol = 0; ++ ++ if (mgmtframe || ieee80211_is_ctl(wh->frame_control)) { ++ qos = 0; ++ } else if (ieee80211_is_data(wh->frame_control)) { ++ qos &= ~MWL_QOS_ACK_POLICY_MASK; ++ ++ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { ++ xmitcontrol &= 0xfb; ++ qos |= MWL_QOS_ACK_POLICY_BLOCKACK; ++ } else { ++ xmitcontrol |= 0x4; ++ qos |= MWL_QOS_ACK_POLICY_NORMAL; ++ } ++ ++ if (is_multicast_ether_addr(wh->addr1) || eapol_frame) ++ xmitcontrol |= EAGLE_TXD_XMITCTRL_USE_MC_RATE; ++ } ++ ++ k_conf = tx_info->control.hw_key; ++ ++ /* Queue ADDBA request in the respective data queue. While setting up ++ * the ampdu stream, mac80211 queues further packets for that ++ * particular ra/tid pair. However, packets piled up in the hardware ++ * for that ra/tid pair will still go out. ADDBA request and the ++ * related data packets going out from different queues asynchronously ++ * will cause a shift in the receiver window which might result in ++ * ampdu packets getting dropped at the receiver after the stream has ++ * been setup. ++ */ ++ if (mgmtframe) { ++ u16 capab; ++ ++ if (unlikely(ieee80211_is_action(wh->frame_control) && ++ mgmt->u.action.category == WLAN_CATEGORY_BACK && ++ mgmt->u.action.u.addba_req.action_code == ++ WLAN_ACTION_ADDBA_REQ)) { ++ capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); ++ tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; ++ index = utils_tid_to_ac(tid); ++ } ++ ++ if (unlikely(ieee80211_is_assoc_req(wh->frame_control))) ++ utils_add_basic_rates(hw->conf.chandef.chan->band, skb); ++ } ++ ++ index = SYSADPT_TX_WMM_QUEUES - index - 1; ++ txpriority = index; ++ ++ if (sta && sta->ht_cap.ht_supported && !eapol_frame && ++ ieee80211_is_data_qos(wh->frame_control)) { ++ tid = qos & 0xf; ++ pcie_tx_count_packet(sta, tid); ++ ++ spin_lock_bh(&priv->stream_lock); ++ stream = mwl_fwcmd_lookup_stream(hw, sta, tid); ++ ++ if (stream) { ++ if (stream->state == AMPDU_STREAM_ACTIVE) { ++ if (WARN_ON(!(qos & ++ MWL_QOS_ACK_POLICY_BLOCKACK))) { ++ spin_unlock_bh(&priv->stream_lock); ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ txpriority = ++ (SYSADPT_TX_WMM_QUEUES + stream->idx) % ++ TOTAL_HW_QUEUES; ++ } else if (stream->state == AMPDU_STREAM_NEW) { ++ /* We get here if the driver sends us packets ++ * after we've initiated a stream, but before ++ * our ampdu_action routine has been called ++ * with IEEE80211_AMPDU_TX_START to get the SSN ++ * for the ADDBA request. So this packet can ++ * go out with no risk of sequence number ++ * mismatch. No special handling is required. ++ */ ++ } else { ++ /* Drop packets that would go out after the ++ * ADDBA request was sent but before the ADDBA ++ * response is received. If we don't do this, ++ * the recipient would probably receive it ++ * after the ADDBA request with SSN 0. This ++ * will cause the recipient's BA receive window ++ * to shift, which would cause the subsequent ++ * packets in the BA stream to be discarded. ++ * mac80211 queues our packets for us in this ++ * case, so this is really just a safety check. ++ */ ++ wiphy_warn(hw->wiphy, ++ "can't send packet during ADDBA\n"); ++ spin_unlock_bh(&priv->stream_lock); ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ } else { ++ if (mwl_fwcmd_ampdu_allowed(sta, tid)) { ++ stream = mwl_fwcmd_add_stream(hw, sta, tid); ++ ++ if (stream) ++ start_ba_session = true; ++ } ++ } ++ ++ spin_unlock_bh(&priv->stream_lock); ++ } else { ++ qos &= ~MWL_QOS_ACK_POLICY_MASK; ++ qos |= MWL_QOS_ACK_POLICY_NORMAL; ++ } ++ ++ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; ++ tx_ctrl->vif = (void *)tx_info->control.vif; ++ tx_ctrl->sta = (void *)sta; ++ tx_ctrl->k_conf = (void *)k_conf; ++ tx_ctrl->amsdu_pkts = NULL; ++ tx_ctrl->tx_priority = txpriority; ++ tx_ctrl->type = (mgmtframe ? IEEE_TYPE_MANAGEMENT : IEEE_TYPE_DATA); ++ tx_ctrl->qos_ctrl = qos; ++ tx_ctrl->xmit_control = xmitcontrol; ++ ++ if (skb_queue_len(&pcie_priv->txq[index]) > pcie_priv->txq_limit) ++ ieee80211_stop_queue(hw, SYSADPT_TX_WMM_QUEUES - index - 1); ++ ++ skb_queue_tail(&pcie_priv->txq[index], skb); ++ ++ tasklet_schedule(&pcie_priv->tx_task); ++ ++ /* Initiate the ampdu session here */ ++ if (start_ba_session) { ++ spin_lock_bh(&priv->stream_lock); ++ rc = mwl_fwcmd_start_stream(hw, stream); ++ if (rc) ++ mwl_fwcmd_remove_stream(hw, stream); ++ else ++ wiphy_debug(hw->wiphy, "Mac80211 start BA %pM\n", ++ stream->sta->addr); ++ spin_unlock_bh(&priv->stream_lock); ++ } ++} ++ ++void pcie_tx_del_pkts_via_vif(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int num; ++ struct sk_buff *skb, *tmp; ++ struct ieee80211_tx_info *tx_info; ++ struct pcie_tx_ctrl *tx_ctrl; ++ struct sk_buff_head *amsdu_pkts; ++ unsigned long flags; ++ ++ for (num = 1; num < PCIE_NUM_OF_DESC_DATA; num++) { ++ spin_lock_irqsave(&pcie_priv->txq[num].lock, flags); ++ skb_queue_walk_safe(&pcie_priv->txq[num], skb, tmp) { ++ tx_info = IEEE80211_SKB_CB(skb); ++ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; ++ if (tx_ctrl->vif == vif) { ++ amsdu_pkts = (struct sk_buff_head *) ++ tx_ctrl->amsdu_pkts; ++ if (amsdu_pkts) { ++ skb_queue_purge(amsdu_pkts); ++ kfree(amsdu_pkts); ++ } ++ __skb_unlink(skb, &pcie_priv->txq[num]); ++ dev_kfree_skb_any(skb); ++ } ++ } ++ spin_unlock_irqrestore(&pcie_priv->txq[num].lock, flags); ++ } ++} ++ ++void pcie_tx_del_pkts_via_sta(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int num; ++ struct sk_buff *skb, *tmp; ++ struct ieee80211_tx_info *tx_info; ++ struct pcie_tx_ctrl *tx_ctrl; ++ struct sk_buff_head *amsdu_pkts; ++ unsigned long flags; ++ ++ for (num = 1; num < PCIE_NUM_OF_DESC_DATA; num++) { ++ spin_lock_irqsave(&pcie_priv->txq[num].lock, flags); ++ skb_queue_walk_safe(&pcie_priv->txq[num], skb, tmp) { ++ tx_info = IEEE80211_SKB_CB(skb); ++ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; ++ if (tx_ctrl->sta == sta) { ++ amsdu_pkts = (struct sk_buff_head *) ++ tx_ctrl->amsdu_pkts; ++ if (amsdu_pkts) { ++ skb_queue_purge(amsdu_pkts); ++ kfree(amsdu_pkts); ++ } ++ __skb_unlink(skb, &pcie_priv->txq[num]); ++ dev_kfree_skb_any(skb); ++ } ++ } ++ spin_unlock_irqrestore(&pcie_priv->txq[num].lock, flags); ++ } ++} ++ ++void pcie_tx_del_ampdu_pkts(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta, u8 tid) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); ++ int ac, desc_num; ++ struct mwl_amsdu_frag *amsdu_frag; ++ struct sk_buff *skb, *tmp; ++ struct ieee80211_tx_info *tx_info; ++ struct pcie_tx_ctrl *tx_ctrl; ++ struct sk_buff_head *amsdu_pkts; ++ unsigned long flags; ++ ++ ac = utils_tid_to_ac(tid); ++ desc_num = SYSADPT_TX_WMM_QUEUES - ac - 1; ++ spin_lock_irqsave(&pcie_priv->txq[desc_num].lock, flags); ++ skb_queue_walk_safe(&pcie_priv->txq[desc_num], skb, tmp) { ++ tx_info = IEEE80211_SKB_CB(skb); ++ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; ++ if (tx_ctrl->sta == sta) { ++ amsdu_pkts = (struct sk_buff_head *) ++ tx_ctrl->amsdu_pkts; ++ if (amsdu_pkts) { ++ skb_queue_purge(amsdu_pkts); ++ kfree(amsdu_pkts); ++ } ++ __skb_unlink(skb, &pcie_priv->txq[desc_num]); ++ dev_kfree_skb_any(skb); ++ } ++ } ++ spin_unlock_irqrestore(&pcie_priv->txq[desc_num].lock, flags); ++ ++ spin_lock_bh(&sta_info->amsdu_lock); ++ amsdu_frag = &sta_info->amsdu_ctrl.frag[desc_num]; ++ if (amsdu_frag->num) { ++ amsdu_frag->num = 0; ++ amsdu_frag->cur_pos = NULL; ++ if (amsdu_frag->skb) { ++ tx_info = IEEE80211_SKB_CB(amsdu_frag->skb); ++ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; ++ amsdu_pkts = (struct sk_buff_head *) ++ tx_ctrl->amsdu_pkts; ++ if (amsdu_pkts) { ++ skb_queue_purge(amsdu_pkts); ++ kfree(amsdu_pkts); ++ } ++ dev_kfree_skb_any(amsdu_frag->skb); ++ } ++ } ++ spin_unlock_bh(&sta_info->amsdu_lock); ++} ++ ++void pcie_tx_del_sta_amsdu_pkts(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta) ++{ ++ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); ++ int num; ++ struct mwl_amsdu_frag *amsdu_frag; ++ struct ieee80211_tx_info *tx_info; ++ struct pcie_tx_ctrl *tx_ctrl; ++ struct sk_buff_head *amsdu_pkts; ++ ++ spin_lock_bh(&sta_info->amsdu_lock); ++ for (num = 0; num < SYSADPT_TX_WMM_QUEUES; num++) { ++ amsdu_frag = &sta_info->amsdu_ctrl.frag[num]; ++ if (amsdu_frag->num) { ++ amsdu_frag->num = 0; ++ amsdu_frag->cur_pos = NULL; ++ if (amsdu_frag->skb) { ++ tx_info = IEEE80211_SKB_CB(amsdu_frag->skb); ++ tx_ctrl = (struct pcie_tx_ctrl *) ++ &tx_info->status; ++ amsdu_pkts = (struct sk_buff_head *) ++ tx_ctrl->amsdu_pkts; ++ if (amsdu_pkts) { ++ skb_queue_purge(amsdu_pkts); ++ kfree(amsdu_pkts); ++ } ++ dev_kfree_skb_any(amsdu_frag->skb); ++ } ++ } ++ } ++ spin_unlock_bh(&sta_info->amsdu_lock); ++} +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.h +new file mode 100644 +index 000000000000..c233ba1aaa9d +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.h +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines transmit related functions. */ ++ ++#ifndef _TX_H_ ++#define _TX_H_ ++ ++int pcie_tx_init(struct ieee80211_hw *hw); ++void pcie_tx_deinit(struct ieee80211_hw *hw); ++void pcie_tx_skbs(unsigned long data); ++void pcie_tx_done(unsigned long data); ++void pcie_tx_flush_amsdu(unsigned long data); ++void pcie_tx_xmit(struct ieee80211_hw *hw, ++ struct ieee80211_tx_control *control, ++ struct sk_buff *skb); ++void pcie_tx_del_pkts_via_vif(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif); ++void pcie_tx_del_pkts_via_sta(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta); ++void pcie_tx_del_ampdu_pkts(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta, u8 tid); ++void pcie_tx_del_sta_amsdu_pkts(struct ieee80211_hw *hw, ++ struct ieee80211_sta *sta); ++ ++#endif /* _TX_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.c +new file mode 100644 +index 000000000000..6758cde363c6 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.c +@@ -0,0 +1,693 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements transmit related functions for new data ++ * path. ++ */ ++ ++#include ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "utils.h" ++#include "hif/fwcmd.h" ++#include "hif/pcie/dev.h" ++#include "hif/pcie/tx_ndp.h" ++ ++#define MAX_NUM_TX_RING_BYTES (MAX_NUM_TX_DESC * \ ++ sizeof(struct pcie_tx_desc_ndp)) ++#define MAX_NUM_TX_RING_DONE_BYTES (MAX_NUM_TX_DESC * \ ++ sizeof(struct tx_ring_done)) ++#define QUEUE_STAOFFSET ((SYSADPT_NUM_OF_AP - 1) + \ ++ SYSADPT_NUM_OF_CLIENT) ++#define PROBE_RESPONSE_TXQNUM ((SYSADPT_MAX_STA_SC4 + SYSADPT_NUM_OF_AP + \ ++ SYSADPT_NUM_OF_CLIENT) * SYSADPT_MAX_TID) ++#define MGMT_TXQNUM ((PROBE_RESPONSE_TXQNUM + 1)) ++#define TXDONE_THRESHOLD 4 ++ ++#define TX_CTRL_TYPE_DATA BIT(0) ++#define TX_CTRL_EAPOL BIT(1) ++#define TX_CTRL_TCP_ACK BIT(2) ++ ++/* Transmission information to transmit a socket buffer. ++ */ ++struct pcie_tx_ctrl_ndp { ++ u16 tx_que_priority; ++ u8 hdrlen; ++ u8 flags; ++ u32 rate; ++ u32 tcp_dst_src; ++ u32 tcp_sn; ++} __packed; ++ ++static int pcie_tx_ring_alloc_ndp(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ u8 *mem; ++ ++ mem = dma_alloc_coherent(priv->dev, ++ MAX_NUM_TX_RING_BYTES, ++ &desc->pphys_tx_ring, ++ GFP_KERNEL); ++ if (!mem) ++ goto err_no_mem; ++ desc->ptx_ring = (struct pcie_tx_desc_ndp *)mem; ++ memset(desc->ptx_ring, 0x00, MAX_NUM_TX_RING_BYTES); ++ ++ mem = dma_alloc_coherent(priv->dev, ++ MAX_NUM_TX_RING_DONE_BYTES, ++ &desc->pphys_tx_ring_done, ++ GFP_KERNEL); ++ if (!mem) ++ goto err_no_mem; ++ desc->ptx_ring_done = (struct tx_ring_done *)mem; ++ memset(desc->ptx_ring_done, 0x00, MAX_NUM_TX_RING_DONE_BYTES); ++ ++ mem = dma_alloc_coherent(priv->dev, ++ DEFAULT_ACNT_RING_SIZE, ++ &desc->pphys_acnt_ring, ++ GFP_KERNEL); ++ if (!mem) ++ goto err_no_mem; ++ desc->pacnt_ring = (u8 *)mem; ++ memset(desc->pacnt_ring, 0x00, DEFAULT_ACNT_RING_SIZE); ++ ++ desc->pacnt_buf = kzalloc(DEFAULT_ACNT_RING_SIZE, GFP_KERNEL); ++ if (!desc->pacnt_buf) ++ goto err_no_mem; ++ desc->acnt_ring_size = DEFAULT_ACNT_RING_SIZE; ++ ++ return 0; ++ ++err_no_mem: ++ ++ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); ++ ++ return -ENOMEM; ++} ++ ++static int pcie_tx_ring_init_ndp(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ int i; ++ ++ for (i = 0; i < PCIE_NUM_OF_DESC_DATA; i++) ++ skb_queue_head_init(&pcie_priv->txq[i]); ++ ++ if (!desc->ptx_ring) { ++ for (i = 0; i < MAX_NUM_TX_DESC; i++) ++ desc->ptx_ring[i].user = cpu_to_le32(i); ++ desc->tx_desc_busy_cnt = 0; ++ } ++ ++ return 0; ++} ++ ++static void pcie_tx_ring_cleanup_ndp(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ struct sk_buff *tx_skb; ++ int i; ++ ++ for (i = 0; i < PCIE_NUM_OF_DESC_DATA; i++) ++ skb_queue_purge(&pcie_priv->txq[i]); ++ ++ for (i = 0; i < MAX_TX_RING_SEND_SIZE; i++) { ++ tx_skb = desc->tx_vbuflist[i]; ++ if (tx_skb) { ++ pci_unmap_single(pcie_priv->pdev, ++ desc->pphys_tx_buflist[i], ++ tx_skb->len, ++ PCI_DMA_TODEVICE); ++ dev_kfree_skb_any(tx_skb); ++ desc->pphys_tx_buflist[i] = 0; ++ desc->tx_vbuflist[i] = NULL; ++ } ++ } ++ desc->tx_sent_tail = 0; ++ desc->tx_sent_head = 0; ++ desc->tx_done_tail = 0; ++ desc->tx_vbuflist_idx = 0; ++ desc->tx_desc_busy_cnt = 0; ++} ++ ++static void pcie_tx_ring_free_ndp(struct mwl_priv *priv) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ ++ if (desc->ptx_ring) { ++ dma_free_coherent(priv->dev, ++ MAX_NUM_TX_RING_BYTES, ++ desc->ptx_ring, ++ desc->pphys_tx_ring); ++ desc->ptx_ring = NULL; ++ } ++ ++ if (desc->ptx_ring_done) { ++ dma_free_coherent(priv->dev, ++ MAX_NUM_TX_RING_DONE_BYTES, ++ desc->ptx_ring_done, ++ desc->pphys_tx_ring_done); ++ desc->prx_ring_done = NULL; ++ } ++ ++ if (desc->pacnt_ring) { ++ dma_free_coherent(priv->dev, ++ DEFAULT_ACNT_RING_SIZE, ++ desc->pacnt_ring, ++ desc->pphys_acnt_ring); ++ desc->pacnt_ring = NULL; ++ } ++ ++ kfree(desc->pacnt_buf); ++} ++ ++static inline u32 pcie_tx_set_skb(struct mwl_priv *priv, struct sk_buff *skb, ++ dma_addr_t dma) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ u32 index = desc->tx_vbuflist_idx; ++ ++ while (desc->tx_vbuflist[index]) ++ index = (index + 1) % MAX_TX_RING_SEND_SIZE; ++ ++ desc->tx_vbuflist_idx = (index + 1) % MAX_TX_RING_SEND_SIZE; ++ desc->pphys_tx_buflist[index] = dma; ++ desc->tx_vbuflist[index] = skb; ++ ++ return index; ++} ++ ++static inline int pcie_tx_skb_ndp(struct mwl_priv *priv, ++ struct sk_buff *tx_skb) ++{ ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ u32 tx_send_tail; ++ u32 tx_send_head_new; ++ struct ieee80211_tx_info *tx_info; ++ struct pcie_tx_ctrl_ndp *tx_ctrl; ++ struct pcie_tx_desc_ndp *pnext_tx_desc; ++ struct ieee80211_hdr *wh; ++ u32 ctrl = 0; ++ dma_addr_t dma; ++ ++ spin_lock_bh(&pcie_priv->tx_desc_lock); ++ ++ tx_send_tail = desc->tx_sent_tail; ++ tx_send_head_new = desc->tx_sent_head; ++ ++ if (((tx_send_head_new + 1) & (MAX_NUM_TX_DESC-1)) == tx_send_tail) { ++ /* Update the tx_send_tail */ ++ tx_send_tail = readl(pcie_priv->iobase1 + ++ MACREG_REG_TXSEDNTAIL); ++ desc->tx_sent_tail = tx_send_tail; ++ ++ if (((tx_send_head_new + 1) & (MAX_NUM_TX_DESC-1)) == ++ tx_send_tail) { ++ spin_unlock_bh(&pcie_priv->tx_desc_lock); ++ return -EAGAIN; ++ } ++ } ++ ++ tx_info = IEEE80211_SKB_CB(tx_skb); ++ tx_ctrl = (struct pcie_tx_ctrl_ndp *)tx_info->status.status_driver_data; ++ pnext_tx_desc = &desc->ptx_ring[tx_send_head_new]; ++ ++ if (tx_ctrl->flags & TX_CTRL_TYPE_DATA) { ++ wh = (struct ieee80211_hdr *)tx_skb->data; ++ ++ skb_pull(tx_skb, tx_ctrl->hdrlen); ++ ether_addr_copy(pnext_tx_desc->u.sa, ++ ieee80211_get_SA(wh)); ++ ether_addr_copy(pnext_tx_desc->u.da, ++ ieee80211_get_DA(wh)); ++ ++ if (tx_ctrl->flags & TX_CTRL_EAPOL) ++ ctrl = TXRING_CTRL_TAG_EAP << TXRING_CTRL_TAG_SHIFT; ++ if (tx_ctrl->flags & TX_CTRL_TCP_ACK) { ++ pnext_tx_desc->tcp_dst_src = ++ cpu_to_le32(tx_ctrl->tcp_dst_src); ++ pnext_tx_desc->tcp_sn = cpu_to_le32(tx_ctrl->tcp_sn); ++ ctrl = TXRING_CTRL_TAG_TCP_ACK << TXRING_CTRL_TAG_SHIFT; ++ } ++ ctrl |= (((tx_ctrl->tx_que_priority & TXRING_CTRL_QID_MASK) << ++ TXRING_CTRL_QID_SHIFT) | ++ ((tx_skb->len & TXRING_CTRL_LEN_MASK) << ++ TXRING_CTRL_LEN_SHIFT)); ++ } else { ++ /* Assigning rate code; use legacy 6mbps rate. */ ++ pnext_tx_desc->u.rate_code = cpu_to_le16(RATECODE_TYPE_LEGACY + ++ (0 << RATECODE_MCS_SHIFT) + RATECODE_BW_20MHZ); ++ pnext_tx_desc->u.max_retry = 5; ++ ++ ctrl = (((tx_ctrl->tx_que_priority & TXRING_CTRL_QID_MASK) << ++ TXRING_CTRL_QID_SHIFT) | ++ (((tx_skb->len - sizeof(struct pcie_dma_data)) & ++ TXRING_CTRL_LEN_MASK) << TXRING_CTRL_LEN_SHIFT) | ++ (TXRING_CTRL_TAG_MGMT << TXRING_CTRL_TAG_SHIFT)); ++ } ++ ++ dma = pci_map_single(pcie_priv->pdev, tx_skb->data, ++ tx_skb->len, PCI_DMA_TODEVICE); ++ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { ++ dev_kfree_skb_any(tx_skb); ++ wiphy_err(priv->hw->wiphy, ++ "failed to map pci memory!\n"); ++ spin_unlock_bh(&pcie_priv->tx_desc_lock); ++ return -EIO; ++ } ++ ++ pnext_tx_desc->data = cpu_to_le32(dma); ++ pnext_tx_desc->ctrl = cpu_to_le32(ctrl); ++ pnext_tx_desc->user = cpu_to_le32(pcie_tx_set_skb(priv, tx_skb, dma)); ++ ++ if ((tx_ctrl->flags & TX_CTRL_TYPE_DATA) && ++ (tx_ctrl->rate != 0)) { ++ skb_push(tx_skb, tx_ctrl->hdrlen); ++ skb_get(tx_skb); ++ pcie_tx_prepare_info(priv, tx_ctrl->rate, tx_info); ++ tx_ctrl->flags |= TX_CTRL_TYPE_DATA; ++ ieee80211_tx_status(priv->hw, tx_skb); ++ } ++ ++ if (++tx_send_head_new >= MAX_NUM_TX_DESC) ++ tx_send_head_new = 0; ++ desc->tx_sent_head = tx_send_head_new; ++ wmb(); /*Data Memory Barrier*/ ++ writel(tx_send_head_new, pcie_priv->iobase1 + MACREG_REG_TXSENDHEAD); ++ desc->tx_desc_busy_cnt++; ++ ++ spin_unlock_bh(&pcie_priv->tx_desc_lock); ++ ++ return 0; ++} ++ ++static inline void pcie_tx_check_tcp_ack(struct sk_buff *tx_skb, ++ struct pcie_tx_ctrl_ndp *tx_ctrl) ++{ ++ struct iphdr *iph; ++ struct tcphdr *tcph; ++ ++ if (tx_ctrl->flags & TX_CTRL_TYPE_DATA) { ++ iph = (struct iphdr *)(tx_skb->data + tx_ctrl->hdrlen + 8); ++ tcph = (struct tcphdr *)((u8 *)iph + (iph->ihl * 4)); ++ if ((iph->protocol == IPPROTO_TCP) && ++ (tx_skb->protocol == htons(ETH_P_IP))) { ++ if ((tcph->ack == 1) && (ntohs(iph->tot_len) == ++ (iph->ihl * 4 + tcph->doff * 4))) { ++ if (tcph->syn || tcph->fin) ++ return; ++ ++ tx_ctrl->flags |= TX_CTRL_TCP_ACK; ++ tx_ctrl->tcp_dst_src = ntohs(tcph->source) | ++ (ntohs(tcph->dest) << 16); ++ tx_ctrl->tcp_sn = ntohl(tcph->ack_seq); ++ } ++ } ++ } ++} ++ ++int pcie_tx_init_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct sk_buff skb; ++ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(&skb); ++ int rc; ++ ++ if (sizeof(struct pcie_tx_ctrl_ndp) > ++ sizeof(tx_info->status.status_driver_data)) { ++ wiphy_err(hw->wiphy, "driver data is not enough: %d (%d)\n", ++ sizeof(struct pcie_tx_ctrl_ndp), ++ sizeof(tx_info->status.status_driver_data)); ++ return -ENOMEM; ++ } ++ ++ rc = pcie_tx_ring_alloc_ndp(priv); ++ if (rc) { ++ pcie_tx_ring_free_ndp(priv); ++ wiphy_err(hw->wiphy, "allocating TX ring failed\n"); ++ return rc; ++ } ++ ++ rc = pcie_tx_ring_init_ndp(priv); ++ if (rc) { ++ pcie_tx_ring_free_ndp(priv); ++ wiphy_err(hw->wiphy, "initializing TX ring failed\n"); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++void pcie_tx_deinit_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ pcie_tx_ring_cleanup_ndp(priv); ++ pcie_tx_ring_free_ndp(priv); ++} ++ ++void pcie_tx_skbs_ndp(unsigned long data) ++{ ++ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ int num = SYSADPT_TX_WMM_QUEUES; ++ struct sk_buff *tx_skb; ++ int rc; ++ ++ while (num--) { ++ while (skb_queue_len(&pcie_priv->txq[num]) > 0) { ++ if (pcie_priv->desc_data_ndp.tx_desc_busy_cnt >= ++ (MAX_TX_RING_SEND_SIZE - 1)) { ++ pcie_tx_done_ndp(hw); ++ break; ++ } ++ ++ tx_skb = skb_dequeue(&pcie_priv->txq[num]); ++ ++ rc = pcie_tx_skb_ndp(priv, tx_skb); ++ if (rc) { ++ pcie_tx_done_ndp(hw); ++ if (rc == -EAGAIN) ++ skb_queue_head(&pcie_priv->txq[num], ++ tx_skb); ++ break; ++ } ++ ++ if (++pcie_priv->tx_done_cnt > TXDONE_THRESHOLD) { ++ pcie_tx_done_ndp(hw); ++ pcie_priv->tx_done_cnt = 0; ++ } ++ } ++ ++ if (skb_queue_len(&pcie_priv->txq[num]) < ++ pcie_priv->txq_wake_threshold) { ++ int queue; ++ ++ queue = SYSADPT_TX_WMM_QUEUES - num - 1; ++ if (ieee80211_queue_stopped(hw, queue)) ++ ieee80211_wake_queue(hw, queue); ++ } ++ } ++ ++ pcie_priv->is_tx_schedule = false; ++} ++ ++void pcie_tx_done_ndp(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; ++ u32 tx_done_head, tx_done_tail; ++ struct tx_ring_done *ptx_ring_done; ++ u32 index; ++ struct sk_buff *skb; ++ struct ieee80211_tx_info *tx_info; ++ struct pcie_tx_ctrl_ndp *tx_ctrl; ++ struct pcie_dma_data *dma_data; ++ u16 hdrlen; ++ ++ spin_lock_bh(&pcie_priv->tx_desc_lock); ++ ++ tx_done_head = readl(pcie_priv->iobase1 + ++ MACREG_REG_TXDONEHEAD); ++ tx_done_tail = desc->tx_done_tail & (MAX_TX_RING_DONE_SIZE - 1); ++ tx_done_head &= (MAX_TX_RING_DONE_SIZE - 1); ++ ++ while (tx_done_head != tx_done_tail) { ++ ptx_ring_done = &desc->ptx_ring_done[tx_done_tail]; ++ ++ index = le32_to_cpu(ptx_ring_done->user); ++ ptx_ring_done->user = 0; ++ if (index >= MAX_TX_RING_SEND_SIZE) { ++ wiphy_err(hw->wiphy, ++ "corruption for index of buffer\n"); ++ break; ++ } ++ skb = desc->tx_vbuflist[index]; ++ if (!skb) { ++ wiphy_err(hw->wiphy, ++ "buffer is NULL for tx done ring\n"); ++ break; ++ } ++ pci_unmap_single(pcie_priv->pdev, ++ desc->pphys_tx_buflist[index], ++ skb->len, ++ PCI_DMA_TODEVICE); ++ desc->pphys_tx_buflist[index] = 0; ++ desc->tx_vbuflist[index] = NULL; ++ ++ tx_info = IEEE80211_SKB_CB(skb); ++ tx_ctrl = (struct pcie_tx_ctrl_ndp *) ++ tx_info->status.status_driver_data; ++ ++ if (tx_ctrl->flags & TX_CTRL_TYPE_DATA) { ++ dev_kfree_skb_any(skb); ++ goto bypass_ack; ++ } else { ++ /* Remove H/W dma header */ ++ dma_data = (struct pcie_dma_data *)skb->data; ++ ++ if (ieee80211_is_assoc_resp( ++ dma_data->wh.frame_control) || ++ ieee80211_is_reassoc_resp( ++ dma_data->wh.frame_control)) { ++ dev_kfree_skb_any(skb); ++ goto bypass_ack; ++ } ++ hdrlen = ieee80211_hdrlen( ++ dma_data->wh.frame_control); ++ memmove(dma_data->data - hdrlen, ++ &dma_data->wh, hdrlen); ++ skb_pull(skb, sizeof(*dma_data) - hdrlen); ++ } ++ ++ pcie_tx_prepare_info(priv, 0, tx_info); ++ ieee80211_tx_status(hw, skb); ++ ++bypass_ack: ++ if (++tx_done_tail >= MAX_TX_RING_DONE_SIZE) ++ tx_done_tail = 0; ++ desc->tx_desc_busy_cnt--; ++ } ++ ++ writel(tx_done_tail, pcie_priv->iobase1 + ++ MACREG_REG_TXDONETAIL); ++ desc->tx_done_tail = tx_done_tail; ++ ++ spin_unlock_bh(&pcie_priv->tx_desc_lock); ++} ++ ++void pcie_tx_xmit_ndp(struct ieee80211_hw *hw, ++ struct ieee80211_tx_control *control, ++ struct sk_buff *skb) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct pcie_priv *pcie_priv = priv->hif.priv; ++ struct ieee80211_tx_info *tx_info; ++ struct ieee80211_key_conf *k_conf; ++ struct mwl_vif *mwl_vif; ++ int index; ++ struct ieee80211_sta *sta; ++ struct mwl_sta *sta_info; ++ struct ieee80211_hdr *wh; ++ u8 *da; ++ u16 qos; ++ u8 tid = 0; ++ struct mwl_ampdu_stream *stream = NULL; ++ u16 tx_que_priority; ++ bool mgmtframe = false; ++ struct ieee80211_mgmt *mgmt; ++ bool eapol_frame = false; ++ bool start_ba_session = false; ++ struct pcie_tx_ctrl_ndp *tx_ctrl; ++ ++ tx_info = IEEE80211_SKB_CB(skb); ++ k_conf = tx_info->control.hw_key; ++ mwl_vif = mwl_dev_get_vif(tx_info->control.vif); ++ index = skb_get_queue_mapping(skb); ++ sta = control->sta; ++ sta_info = sta ? mwl_dev_get_sta(sta) : NULL; ++ ++ wh = (struct ieee80211_hdr *)skb->data; ++ ++ if (ieee80211_is_data_qos(wh->frame_control)) ++ qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); ++ else ++ qos = 0xFFFF; ++ ++ if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { ++ index = IEEE80211_AC_VO; ++ eapol_frame = true; ++ } ++ ++ if (ieee80211_is_mgmt(wh->frame_control)) { ++ mgmtframe = true; ++ mgmt = (struct ieee80211_mgmt *)skb->data; ++ } ++ ++ if (mgmtframe) { ++ u16 capab; ++ ++ if (unlikely(ieee80211_is_action(wh->frame_control) && ++ mgmt->u.action.category == WLAN_CATEGORY_BACK && ++ mgmt->u.action.u.addba_req.action_code == ++ WLAN_ACTION_ADDBA_REQ)) { ++ capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); ++ tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; ++ index = utils_tid_to_ac(tid); ++ } ++ ++ if (unlikely(ieee80211_is_assoc_req(wh->frame_control))) ++ utils_add_basic_rates(hw->conf.chandef.chan->band, skb); ++ ++ if (ieee80211_is_probe_req(wh->frame_control) || ++ ieee80211_is_probe_resp(wh->frame_control)) ++ tx_que_priority = PROBE_RESPONSE_TXQNUM; ++ else { ++ if (( ++ (mwl_vif->macid == SYSADPT_NUM_OF_AP) && ++ (!ieee80211_has_protected(wh->frame_control) || ++ (ieee80211_has_protected(wh->frame_control) && ++ ieee80211_is_auth(wh->frame_control))) ++ ) || ++ !sta || ++ ieee80211_is_auth(wh->frame_control) || ++ ieee80211_is_assoc_req(wh->frame_control) || ++ ieee80211_is_assoc_resp(wh->frame_control)) ++ tx_que_priority = MGMT_TXQNUM; ++ else { ++ if (is_multicast_ether_addr(wh->addr1) && ++ (mwl_vif->macid != SYSADPT_NUM_OF_AP)) ++ tx_que_priority = mwl_vif->macid * ++ SYSADPT_MAX_TID; ++ else ++ tx_que_priority = SYSADPT_MAX_TID * ++ (sta_info->stnid + ++ QUEUE_STAOFFSET) + 6; ++ } ++ } ++ ++ if (ieee80211_is_assoc_resp(wh->frame_control) || ++ ieee80211_is_reassoc_resp(wh->frame_control)) { ++ struct sk_buff *ack_skb; ++ struct ieee80211_tx_info *ack_info; ++ ++ ack_skb = skb_copy(skb, GFP_ATOMIC); ++ ack_info = IEEE80211_SKB_CB(ack_skb); ++ pcie_tx_prepare_info(priv, 0, ack_info); ++ ieee80211_tx_status(hw, ack_skb); ++ } ++ ++ pcie_tx_encapsulate_frame(priv, skb, k_conf, NULL); ++ } else { ++ tid = qos & 0x7; ++ if (sta && sta->ht_cap.ht_supported && !eapol_frame && ++ qos != 0xFFFF) { ++ pcie_tx_count_packet(sta, tid); ++ spin_lock_bh(&priv->stream_lock); ++ stream = mwl_fwcmd_lookup_stream(hw, sta, tid); ++ if (!stream || ++ stream->state == AMPDU_STREAM_IN_PROGRESS) { ++ wiphy_warn(hw->wiphy, ++ "can't send packet during ADDBA\n"); ++ spin_unlock_bh(&priv->stream_lock); ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ if ((stream->state == AMPDU_NO_STREAM) && ++ mwl_fwcmd_ampdu_allowed(sta, tid)) { ++ stream = mwl_fwcmd_add_stream(hw, sta, tid); ++ if (stream) ++ start_ba_session = true; ++ } ++ spin_unlock_bh(&priv->stream_lock); ++ } ++ ++ da = ieee80211_get_DA(wh); ++ ++ if (is_multicast_ether_addr(da) ++ && (mwl_vif->macid != SYSADPT_NUM_OF_AP)) { ++ ++ tx_que_priority = mwl_vif->macid * SYSADPT_MAX_TID; ++ ++ if (da[ETH_ALEN - 1] == 0xff) ++ tx_que_priority += 7; ++ ++ if (ieee80211_has_a4(wh->frame_control)) { ++ if (sta && sta_info->wds) ++ tx_que_priority = SYSADPT_MAX_TID * ++ (sta_info->stnid + ++ QUEUE_STAOFFSET) + 6; ++ } ++ } else { ++ if (sta) { ++ if (!eapol_frame) ++ tx_que_priority = SYSADPT_MAX_TID * ++ (sta_info->stnid + ++ QUEUE_STAOFFSET) + ++ ((qos == 0xFFFF) ? 0 : tid); ++ else ++ tx_que_priority = SYSADPT_MAX_TID * ++ (sta_info->stnid + ++ QUEUE_STAOFFSET) + ++ ((qos == 0xFFFF) ? 0 : 6); ++ } else ++ tx_que_priority = 0; ++ } ++ } ++ ++ index = SYSADPT_TX_WMM_QUEUES - index - 1; ++ ++ tx_ctrl = (struct pcie_tx_ctrl_ndp *)tx_info->status.status_driver_data; ++ tx_ctrl->tx_que_priority = tx_que_priority; ++ tx_ctrl->hdrlen = ieee80211_hdrlen(wh->frame_control); ++ tx_ctrl->flags = 0; ++ if (!mgmtframe) ++ tx_ctrl->flags |= TX_CTRL_TYPE_DATA; ++ if (eapol_frame) ++ tx_ctrl->flags |= TX_CTRL_EAPOL; ++ tx_ctrl->rate = sta ? sta_info->tx_rate_info : 0; ++ if (ieee80211_is_nullfunc(wh->frame_control) || ++ ieee80211_is_qos_nullfunc(wh->frame_control)) ++ tx_ctrl->rate = 0; ++ pcie_tx_check_tcp_ack(skb, tx_ctrl); ++ ++ if (skb_queue_len(&pcie_priv->txq[index]) > pcie_priv->txq_limit) ++ ieee80211_stop_queue(hw, SYSADPT_TX_WMM_QUEUES - index - 1); ++ ++ skb_queue_tail(&pcie_priv->txq[index], skb); ++ ++ if (!pcie_priv->is_tx_schedule) { ++ tasklet_schedule(&pcie_priv->tx_task); ++ pcie_priv->is_tx_schedule = true; ++ } ++ ++ /* Initiate the ampdu session here */ ++ if (start_ba_session) { ++ spin_lock_bh(&priv->stream_lock); ++ if (mwl_fwcmd_start_stream(hw, stream)) ++ mwl_fwcmd_remove_stream(hw, stream); ++ spin_unlock_bh(&priv->stream_lock); ++ } ++} +diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.h +new file mode 100644 +index 000000000000..2ad5f381b9ee +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.h +@@ -0,0 +1,30 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines transmit related functions for new data path. ++ */ ++ ++#ifndef _TX_NDP_H_ ++#define _TX_NDP_H_ ++ ++int pcie_tx_init_ndp(struct ieee80211_hw *hw); ++void pcie_tx_deinit_ndp(struct ieee80211_hw *hw); ++void pcie_tx_skbs_ndp(unsigned long data); ++void pcie_tx_done_ndp(struct ieee80211_hw *hw); ++void pcie_tx_xmit_ndp(struct ieee80211_hw *hw, ++ struct ieee80211_tx_control *control, ++ struct sk_buff *skb); ++ ++#endif /* _TX_NDP_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/hostapd/700-interoperability-workaround-for-80+80-and-160-MHz-channels b/drivers/net/wireless/marvell/mwlwifi/hostapd/700-interoperability-workaround-for-80+80-and-160-MHz-channels +new file mode 100644 +index 000000000000..adadd2e4d8d4 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hostapd/700-interoperability-workaround-for-80+80-and-160-MHz-channels +@@ -0,0 +1,32 @@ ++diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c ++index 3236016..e923094 100644 ++--- a/src/ap/ieee802_11_vht.c +++++ b/src/ap/ieee802_11_vht.c ++@@ -82,6 +82,27 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) ++ ++ oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; ++ +++ if (hapd->iconf->vht_oper_chwidth == 2) { +++ /* +++ * Convert 160 MHz channel width to new style as interop +++ * workaround. +++ */ +++ oper->vht_op_info_chwidth = 1; +++ oper->vht_op_info_chan_center_freq_seg1_idx = +++ oper->vht_op_info_chan_center_freq_seg0_idx; +++ if (hapd->iconf->channel < +++ hapd->iconf->vht_oper_centr_freq_seg0_idx) +++ oper->vht_op_info_chan_center_freq_seg0_idx -= 8; +++ else +++ oper->vht_op_info_chan_center_freq_seg0_idx += 8; +++ } else if (hapd->iconf->vht_oper_chwidth == 3) { +++ /* +++ * Convert 80+80 MHz channel width to new style as interop +++ * workaround. +++ */ +++ oper->vht_op_info_chwidth = 1; +++ } +++ ++ /* VHT Basic MCS set comes from hw */ ++ /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ ++ oper->vht_basic_mcs_set = host_to_le16(0xfffc); +diff --git a/drivers/net/wireless/marvell/mwlwifi/hostapd/README b/drivers/net/wireless/marvell/mwlwifi/hostapd/README +new file mode 100644 +index 000000000000..a5fb2b68d3d3 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/hostapd/README +@@ -0,0 +1,26 @@ ++700-interoperability-workaround-for-80+80-and-160-MHz-channels: ++ ++patch for OpenWrt hostapd package 2016-01-15 for following commit ++(move it to package/network/services/hostapd/patches). ++ ++Note: After hostapd package 2016-06-15, this commit is already included. ++ ++commit 03a72eacda5d9a1837a74387081596a0d5466ec1 ++Author: Jouni Malinen ++Date: Thu Dec 17 18:39:19 2015 +0200 ++ ++ VHT: Add an interoperability workaround for 80+80 and 160 MHz channels ++ ++ Number of deployed 80 MHz capable VHT stations that do not support 80+80 ++ and 160 MHz bandwidths seem to misbehave when trying to connect to an AP ++ that advertises 80+80 or 160 MHz channel bandwidth in the VHT Operation ++ element. To avoid such issues with deployed devices, modify the design ++ based on newly proposed IEEE 802.11 standard changes. ++ ++ This allows poorly implemented VHT 80 MHz stations to connect with the ++ AP in 80 MHz mode. 80+80 and 160 MHz capable stations need to support ++ the new workaround mechanism to allow full bandwidth to be used. ++ However, there are more or less no impacted station with 80+80/160 ++ capability deployed. ++ ++ Signed-off-by: Jouni Malinen jouni@qca.qualcomm.com +diff --git a/drivers/net/wireless/marvell/mwlwifi/mac80211.c b/drivers/net/wireless/marvell/mwlwifi/mac80211.c +new file mode 100644 +index 000000000000..725dec0f604b +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/mac80211.c +@@ -0,0 +1,933 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements mac80211 related functions. */ ++ ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "utils.h" ++#include "hif/fwcmd.h" ++#include "hif/hif-ops.h" ++ ++#define MAX_AMPDU_ATTEMPTS 5 ++ ++static const struct ieee80211_rate mwl_rates_24[] = { ++ { .bitrate = 10, .hw_value = 2, }, ++ { .bitrate = 20, .hw_value = 4, }, ++ { .bitrate = 55, .hw_value = 11, }, ++ { .bitrate = 110, .hw_value = 22, }, ++ { .bitrate = 220, .hw_value = 44, }, ++ { .bitrate = 60, .hw_value = 12, }, ++ { .bitrate = 90, .hw_value = 18, }, ++ { .bitrate = 120, .hw_value = 24, }, ++ { .bitrate = 180, .hw_value = 36, }, ++ { .bitrate = 240, .hw_value = 48, }, ++ { .bitrate = 360, .hw_value = 72, }, ++ { .bitrate = 480, .hw_value = 96, }, ++ { .bitrate = 540, .hw_value = 108, }, ++}; ++ ++static const struct ieee80211_rate mwl_rates_50[] = { ++ { .bitrate = 60, .hw_value = 12, }, ++ { .bitrate = 90, .hw_value = 18, }, ++ { .bitrate = 120, .hw_value = 24, }, ++ { .bitrate = 180, .hw_value = 36, }, ++ { .bitrate = 240, .hw_value = 48, }, ++ { .bitrate = 360, .hw_value = 72, }, ++ { .bitrate = 480, .hw_value = 96, }, ++ { .bitrate = 540, .hw_value = 108, }, ++}; ++ ++static void mwl_get_rateinfo(struct mwl_priv *priv, u8 *addr, ++ struct mwl_sta *sta_info) ++{ ++ int table_size = (sizeof(__le32) * 2 * SYSADPT_MAX_RATE_ADAPT_RATES); ++ u8 *rate_table, *rate_idx; ++ u32 rate_info; ++ struct mwl_tx_hist_data *tx_hist_data; ++ int ret, idx; ++ ++ rate_table = kzalloc(table_size, GFP_KERNEL); ++ if (!rate_table) ++ return; ++ ++ ret = mwl_fwcmd_get_ratetable(priv->hw, addr, rate_table, ++ table_size, 0); ++ if (ret) { ++ kfree(rate_table); ++ return; ++ } ++ ++ idx = 0; ++ rate_idx = rate_table; ++ rate_info = le32_to_cpu(*(__le32 *)rate_idx); ++ tx_hist_data = &sta_info->tx_hist.su_rate[0]; ++ while (rate_info) { ++ if (idx < SYSADPT_MAX_RATE_ADAPT_RATES) ++ tx_hist_data[idx].rateinfo = rate_info; ++ idx++; ++ rate_idx += (2 * sizeof(__le32)); ++ rate_info = le32_to_cpu(*(__le32 *)rate_idx); ++ } ++ ++ kfree(rate_table); ++} ++ ++static void mwl_mac80211_tx(struct ieee80211_hw *hw, ++ struct ieee80211_tx_control *control, ++ struct sk_buff *skb) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if (!priv->radio_on) { ++ wiphy_warn(hw->wiphy, ++ "dropped TX frame since radio is disabled\n"); ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ mwl_hif_tx_xmit(hw, control, skb); ++} ++ ++static int mwl_mac80211_start(struct ieee80211_hw *hw) ++{ ++ struct mwl_priv *priv = hw->priv; ++ int rc; ++ ++ /* Enable TX and RX tasklets. */ ++ mwl_hif_enable_data_tasks(hw); ++ ++ /* Enable interrupts */ ++ mwl_hif_irq_enable(hw); ++ ++ rc = mwl_fwcmd_radio_enable(hw); ++ if (rc) ++ goto fwcmd_fail; ++ rc = mwl_fwcmd_set_rate_adapt_mode(hw, 0); ++ if (rc) ++ goto fwcmd_fail; ++ rc = mwl_fwcmd_set_wmm_mode(hw, true); ++ if (rc) ++ goto fwcmd_fail; ++ rc = mwl_fwcmd_ht_guard_interval(hw, GUARD_INTERVAL_AUTO); ++ if (rc) ++ goto fwcmd_fail; ++ rc = mwl_fwcmd_set_dwds_stamode(hw, true); ++ if (rc) ++ goto fwcmd_fail; ++ rc = mwl_fwcmd_set_fw_flush_timer(hw, SYSADPT_AMSDU_FLUSH_TIME); ++ if (rc) ++ goto fwcmd_fail; ++ rc = mwl_fwcmd_set_optimization_level(hw, 1); ++ if (rc) ++ goto fwcmd_fail; ++ if (priv->chip_type == MWL8997) { ++ rc = mwl_fwcmd_config_EDMACCtrl(hw, 0); ++ if (rc) ++ goto fwcmd_fail; ++ } ++ if (priv->chip_type == MWL8964) { ++ rc = mwl_fwcmd_newdp_dmathread_start(hw); ++ if (rc) ++ goto fwcmd_fail; ++ rc = mwl_fwcmd_set_bftype(hw, priv->bf_type); ++ if (rc) ++ goto fwcmd_fail; ++ } ++ ++ ieee80211_wake_queues(hw); ++ return 0; ++ ++fwcmd_fail: ++ mwl_hif_irq_disable(hw); ++ mwl_hif_disable_data_tasks(hw); ++ ++ return rc; ++} ++ ++static void mwl_mac80211_stop(struct ieee80211_hw *hw) ++{ ++ mwl_fwcmd_radio_disable(hw); ++ ++ ieee80211_stop_queues(hw); ++ ++ /* Disable interrupts */ ++ mwl_hif_irq_disable(hw); ++ ++ /* Disable TX and RX tasklets. */ ++ mwl_hif_disable_data_tasks(hw); ++ ++ /* Return all skbs to mac80211 */ ++ mwl_hif_tx_return_pkts(hw); ++} ++ ++static int mwl_mac80211_add_interface(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ u32 macids_supported; ++ int macid; ++ ++ switch (vif->type) { ++ case NL80211_IFTYPE_AP: ++ case NL80211_IFTYPE_MESH_POINT: ++ if (vif->type == NL80211_IFTYPE_MESH_POINT) ++ if (priv->chip_type != MWL8997) ++ return -EPERM; ++ macids_supported = priv->ap_macids_supported; ++ break; ++ case NL80211_IFTYPE_STATION: ++ macids_supported = priv->sta_macids_supported; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ macid = ffs(macids_supported & ~priv->macids_used); ++ ++ if (!macid) { ++ wiphy_warn(hw->wiphy, "no macid can be allocated\n"); ++ return -EBUSY; ++ } ++ macid--; ++ ++ /* Setup driver private area. */ ++ mwl_vif = mwl_dev_get_vif(vif); ++ memset(mwl_vif, 0, sizeof(*mwl_vif)); ++ mwl_vif->type = vif->type; ++ mwl_vif->macid = macid; ++ mwl_vif->seqno = 0; ++ mwl_vif->is_hw_crypto_enabled = false; ++ mwl_vif->beacon_info.valid = false; ++ mwl_vif->set_beacon = false; ++ mwl_vif->basic_rate_idx = 0; ++ mwl_vif->broadcast_ssid = 0xFF; ++ mwl_vif->iv16 = 1; ++ mwl_vif->iv32 = 0; ++ mwl_vif->keyidx = 0; ++ ++ switch (vif->type) { ++ case NL80211_IFTYPE_AP: ++ ether_addr_copy(mwl_vif->bssid, vif->addr); ++ mwl_fwcmd_set_new_stn_add_self(hw, vif); ++ if (priv->chip_type == MWL8964) { ++ /* allow firmware to really set channel */ ++ mwl_fwcmd_bss_start(hw, vif, true); ++ mwl_fwcmd_bss_start(hw, vif, false); ++ } ++ break; ++ case NL80211_IFTYPE_MESH_POINT: ++ ether_addr_copy(mwl_vif->bssid, vif->addr); ++ mwl_fwcmd_set_new_stn_add_self(hw, vif); ++ break; ++ case NL80211_IFTYPE_STATION: ++ ether_addr_copy(mwl_vif->sta_mac, vif->addr); ++ mwl_fwcmd_bss_start(hw, vif, true); ++ mwl_fwcmd_set_infra_mode(hw, vif); ++ mwl_fwcmd_set_mac_addr_client(hw, vif, vif->addr); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ priv->macids_used |= 1 << mwl_vif->macid; ++ spin_lock_bh(&priv->vif_lock); ++ list_add_tail(&mwl_vif->list, &priv->vif_list); ++ spin_unlock_bh(&priv->vif_lock); ++ ++ return 0; ++} ++ ++static void mwl_mac80211_remove_vif(struct mwl_priv *priv, ++ struct ieee80211_vif *vif) ++{ ++ struct mwl_vif *mwl_vif = mwl_dev_get_vif(vif); ++ ++ if (!priv->macids_used) ++ return; ++ ++ mwl_hif_tx_del_pkts_via_vif(priv->hw, vif); ++ ++ priv->macids_used &= ~(1 << mwl_vif->macid); ++ spin_lock_bh(&priv->vif_lock); ++ list_del(&mwl_vif->list); ++ spin_unlock_bh(&priv->vif_lock); ++} ++ ++static void mwl_mac80211_remove_interface(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ switch (vif->type) { ++ case NL80211_IFTYPE_AP: ++ case NL80211_IFTYPE_MESH_POINT: ++ mwl_fwcmd_set_new_stn_del(hw, vif, vif->addr); ++ break; ++ case NL80211_IFTYPE_STATION: ++ mwl_fwcmd_remove_mac_addr(hw, vif, vif->addr); ++ break; ++ default: ++ break; ++ } ++ ++ mwl_mac80211_remove_vif(priv, vif); ++} ++ ++static int mwl_mac80211_config(struct ieee80211_hw *hw, ++ u32 changed) ++{ ++ struct ieee80211_conf *conf = &hw->conf; ++ int rc; ++ ++ wiphy_debug(hw->wiphy, "change: 0x%x\n", changed); ++ ++ if (conf->flags & IEEE80211_CONF_IDLE) ++ rc = mwl_fwcmd_radio_disable(hw); ++ else ++ rc = mwl_fwcmd_radio_enable(hw); ++ ++ if (rc) ++ goto out; ++ ++ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { ++ int rate = 0; ++ ++ if (conf->chandef.chan->band == NL80211_BAND_2GHZ) { ++ mwl_fwcmd_set_apmode(hw, AP_MODE_2_4GHZ_11AC_MIXED); ++ mwl_fwcmd_set_linkadapt_cs_mode(hw, ++ LINK_CS_STATE_CONSERV); ++ rate = mwl_rates_24[0].hw_value; ++ } else if (conf->chandef.chan->band == NL80211_BAND_5GHZ) { ++ mwl_fwcmd_set_apmode(hw, AP_MODE_11AC); ++ mwl_fwcmd_set_linkadapt_cs_mode(hw, ++ LINK_CS_STATE_AUTO); ++ rate = mwl_rates_50[0].hw_value; ++ ++ if (conf->radar_enabled) ++ mwl_fwcmd_set_radar_detect(hw, MONITOR_START); ++ else ++ mwl_fwcmd_set_radar_detect(hw, ++ STOP_DETECT_RADAR); ++ } ++ ++ rc = mwl_fwcmd_set_rf_channel(hw, conf); ++ if (rc) ++ goto out; ++ rc = mwl_fwcmd_use_fixed_rate(hw, rate, rate); ++ if (rc) ++ goto out; ++ rc = mwl_fwcmd_max_tx_power(hw, conf, 0); ++ if (rc) ++ goto out; ++ rc = mwl_fwcmd_tx_power(hw, conf, 0); ++ if (rc) ++ goto out; ++ rc = mwl_fwcmd_set_cdd(hw); ++ } ++ ++out: ++ ++ return rc; ++} ++ ++static void mwl_mac80211_bss_info_changed_sta(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_bss_conf *info, ++ u32 changed) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ if ((changed & BSS_CHANGED_ERP_SLOT) && (priv->chip_type == MWL8997)) { ++ if (priv->use_short_slot != vif->bss_conf.use_short_slot) { ++ mwl_fwcmd_set_slot_time(hw, ++ vif->bss_conf.use_short_slot); ++ priv->use_short_slot = vif->bss_conf.use_short_slot; ++ } ++ } ++ ++ if (changed & BSS_CHANGED_ERP_PREAMBLE) { ++ if (priv->use_short_preamble != ++ vif->bss_conf.use_short_preamble) { ++ mwl_fwcmd_set_radio_preamble( ++ hw, vif->bss_conf.use_short_preamble); ++ priv->use_short_preamble = ++ vif->bss_conf.use_short_preamble; ++ } ++ } ++ ++ if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) ++ mwl_fwcmd_set_aid(hw, vif, (u8 *)vif->bss_conf.bssid, ++ vif->bss_conf.aid); ++} ++ ++static void mwl_mac80211_bss_info_changed_ap(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_bss_conf *info, ++ u32 changed) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_vif *mwl_vif; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ ++ if ((changed & BSS_CHANGED_ERP_SLOT) && (priv->chip_type == MWL8997)) { ++ if (priv->use_short_slot != vif->bss_conf.use_short_slot) { ++ mwl_fwcmd_set_slot_time(hw, ++ vif->bss_conf.use_short_slot); ++ priv->use_short_slot = vif->bss_conf.use_short_slot; ++ } ++ } ++ ++ if (changed & BSS_CHANGED_ERP_PREAMBLE) { ++ if (priv->use_short_preamble != ++ vif->bss_conf.use_short_preamble) { ++ mwl_fwcmd_set_radio_preamble( ++ hw, vif->bss_conf.use_short_preamble); ++ priv->use_short_preamble = ++ vif->bss_conf.use_short_preamble; ++ } ++ } ++ ++ if (changed & BSS_CHANGED_BASIC_RATES) { ++ int idx; ++ int rate; ++ ++ /* Use lowest supported basic rate for multicasts ++ * and management frames (such as probe responses -- ++ * beacons will always go out at 1 Mb/s). ++ */ ++ idx = ffs(vif->bss_conf.basic_rates); ++ if (idx) ++ idx--; ++ if (mwl_vif->basic_rate_idx != idx) { ++ if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) ++ rate = mwl_rates_24[idx].hw_value; ++ else ++ rate = mwl_rates_50[idx].hw_value; ++ ++ mwl_fwcmd_use_fixed_rate(hw, rate, rate); ++ mwl_vif->basic_rate_idx = idx; ++ } ++ } ++ ++ if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) { ++ struct sk_buff *skb; ++ ++ if ((info->ssid[0] != '\0') && ++ (info->ssid_len != 0) && ++ (!info->hidden_ssid)) { ++ if (mwl_vif->broadcast_ssid != true) { ++ mwl_fwcmd_broadcast_ssid_enable(hw, vif, true); ++ mwl_vif->broadcast_ssid = true; ++ } ++ } else { ++ if (mwl_vif->broadcast_ssid != false) { ++ mwl_fwcmd_broadcast_ssid_enable(hw, vif, false); ++ mwl_vif->broadcast_ssid = false; ++ } ++ } ++ ++ if (!mwl_vif->set_beacon) { ++ skb = ieee80211_beacon_get(hw, vif); ++ ++ if (skb) { ++ mwl_fwcmd_set_beacon(hw, vif, skb->data, skb->len); ++ dev_kfree_skb_any(skb); ++ } ++ mwl_vif->set_beacon = true; ++ } ++ } ++ ++ if (changed & BSS_CHANGED_BEACON_ENABLED) ++ mwl_fwcmd_bss_start(hw, vif, info->enable_beacon); ++} ++ ++static void mwl_mac80211_bss_info_changed(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_bss_conf *info, ++ u32 changed) ++{ ++ switch (vif->type) { ++ case NL80211_IFTYPE_AP: ++ case NL80211_IFTYPE_MESH_POINT: ++ mwl_mac80211_bss_info_changed_ap(hw, vif, info, changed); ++ break; ++ case NL80211_IFTYPE_STATION: ++ mwl_mac80211_bss_info_changed_sta(hw, vif, info, changed); ++ break; ++ default: ++ break; ++ } ++} ++ ++static void mwl_mac80211_configure_filter(struct ieee80211_hw *hw, ++ unsigned int changed_flags, ++ unsigned int *total_flags, ++ u64 multicast) ++{ ++ /* AP firmware doesn't allow fine-grained control over ++ * the receive filter. ++ */ ++ *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; ++} ++ ++static int mwl_mac80211_set_key(struct ieee80211_hw *hw, ++ enum set_key_cmd cmd_param, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta, ++ struct ieee80211_key_conf *key) ++{ ++ struct mwl_vif *mwl_vif; ++ struct mwl_sta *sta_info; ++ int rc = 0; ++ u8 encr_type; ++ u8 *addr; ++ ++ mwl_vif = mwl_dev_get_vif(vif); ++ addr = sta ? sta->addr : vif->addr; ++ ++ if (cmd_param == SET_KEY) { ++ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40) || ++ (key->cipher == WLAN_CIPHER_SUITE_WEP104)) { ++ encr_type = ENCR_TYPE_WEP; ++ } else if (key->cipher == WLAN_CIPHER_SUITE_CCMP) { ++ encr_type = ENCR_TYPE_AES; ++ if ((key->flags & IEEE80211_KEY_FLAG_PAIRWISE) == 0) { ++ if (vif->type != NL80211_IFTYPE_STATION) ++ mwl_vif->keyidx = key->keyidx; ++ } ++ } else if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { ++ encr_type = ENCR_TYPE_TKIP; ++ } else { ++ encr_type = ENCR_TYPE_DISABLE; ++ } ++ ++ rc = mwl_fwcmd_update_encryption_enable(hw, vif, addr, ++ encr_type); ++ if (rc) ++ goto out; ++ rc = mwl_fwcmd_encryption_set_key(hw, vif, addr, key); ++ if (rc) ++ goto out; ++ ++ mwl_vif->is_hw_crypto_enabled = true; ++ if (sta) { ++ sta_info = mwl_dev_get_sta(sta); ++ sta_info->is_key_set = true; ++ } ++ } else { ++ rc = mwl_fwcmd_encryption_remove_key(hw, vif, addr, key); ++ if (rc) ++ goto out; ++ } ++ ++out: ++ ++ return rc; ++} ++ ++static int mwl_mac80211_set_rts_threshold(struct ieee80211_hw *hw, ++ u32 value) ++{ ++ return mwl_fwcmd_set_rts_threshold(hw, value); ++} ++ ++static int mwl_mac80211_sta_add(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta) ++{ ++ struct mwl_priv *priv = hw->priv; ++ u16 stnid, sta_stnid = 0; ++ struct mwl_vif *mwl_vif; ++ struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif); ++ bool use_4addr = wdev->use_4addr; ++ struct mwl_sta *sta_info; ++ struct ieee80211_key_conf *key; ++ int rc; ++ int i; ++ ++ if (vif->type == NL80211_IFTYPE_STATION) ++ sta->aid = 1; ++ mwl_vif = mwl_dev_get_vif(vif); ++ stnid = utils_assign_stnid(priv, mwl_vif->macid, sta->aid); ++ if (!stnid) ++ return -EPERM; ++ if (vif->type == NL80211_IFTYPE_STATION) { ++ sta_stnid = utils_assign_stnid(priv, mwl_vif->macid, ++ sta->aid + 1); ++ if (!sta_stnid) { ++ utils_free_stnid(priv, stnid); ++ return -EPERM; ++ } ++ ether_addr_copy(mwl_vif->bssid, sta->addr); ++ } ++ sta_info = mwl_dev_get_sta(sta); ++ memset(sta_info, 0, sizeof(*sta_info)); ++ ++ if (vif->type == NL80211_IFTYPE_MESH_POINT) ++ sta_info->is_mesh_node = true; ++ ++ if (sta->ht_cap.ht_supported) { ++ sta_info->is_ampdu_allowed = true; ++ sta_info->is_amsdu_allowed = false; ++ if (sta->ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU) ++ sta_info->amsdu_ctrl.cap = MWL_AMSDU_SIZE_8K; ++ else ++ sta_info->amsdu_ctrl.cap = MWL_AMSDU_SIZE_4K; ++ if ((sta->tdls) && (!sta->wme)) ++ sta->wme = true; ++ } ++ sta_info->mwl_vif = mwl_vif; ++ sta_info->stnid = stnid; ++ if (vif->type == NL80211_IFTYPE_STATION) ++ sta_info->sta_stnid = sta_stnid; ++ sta_info->tx_rate_info = utils_get_init_tx_rate(priv, &hw->conf, sta); ++ sta_info->iv16 = 1; ++ sta_info->iv32 = 0; ++ spin_lock_init(&sta_info->amsdu_lock); ++ spin_lock_bh(&priv->sta_lock); ++ list_add_tail(&sta_info->list, &priv->sta_list); ++ spin_unlock_bh(&priv->sta_lock); ++ ++ if (vif->type == NL80211_IFTYPE_STATION) ++ mwl_fwcmd_set_new_stn_del(hw, vif, sta->addr); ++ ++ if (priv->chip_type == MWL8964) { ++ if (use_4addr) { ++ sta_info->wds = true; ++ rc = mwl_fwcmd_set_new_stn_add_sc4(hw, vif, sta, ++ WDS_MODE); ++ } else ++ rc = mwl_fwcmd_set_new_stn_add_sc4(hw, vif, sta, 0); ++ } else ++ rc = mwl_fwcmd_set_new_stn_add(hw, vif, sta); ++ ++ if ((vif->type == NL80211_IFTYPE_STATION) && !use_4addr) ++ mwl_hif_set_sta_id(hw, sta, true, true); ++ else ++ mwl_hif_set_sta_id(hw, sta, false, true); ++ ++ for (i = 0; i < NUM_WEP_KEYS; i++) { ++ key = (struct ieee80211_key_conf *)mwl_vif->wep_key_conf[i].key; ++ ++ if (mwl_vif->wep_key_conf[i].enabled) ++ mwl_mac80211_set_key(hw, SET_KEY, vif, sta, key); ++ } ++ ++ mwl_get_rateinfo(priv, sta->addr, sta_info); ++ ++ return rc; ++} ++ ++static int mwl_mac80211_sta_remove(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta) ++{ ++ struct mwl_priv *priv = hw->priv; ++ int rc; ++ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); ++ ++ mwl_hif_tx_del_sta_amsdu_pkts(hw, sta); ++ mwl_fwcmd_del_sta_streams(hw, sta); ++ mwl_hif_tx_del_pkts_via_sta(hw, sta); ++ ++ rc = mwl_fwcmd_set_new_stn_del(hw, vif, sta->addr); ++ ++ if (vif->type == NL80211_IFTYPE_STATION) ++ mwl_hif_set_sta_id(hw, sta, true, false); ++ else ++ mwl_hif_set_sta_id(hw, sta, false, false); ++ ++ if (priv->chip_type != MWL8964) ++ utils_free_stnid(priv, sta_info->stnid); ++ if (vif->type == NL80211_IFTYPE_STATION) ++ utils_free_stnid(priv, sta_info->sta_stnid); ++ ++ spin_lock_bh(&priv->sta_lock); ++ list_del(&sta_info->list); ++ spin_unlock_bh(&priv->sta_lock); ++ ++ return rc; ++} ++ ++static int mwl_mac80211_conf_tx(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ u16 queue, ++ const struct ieee80211_tx_queue_params *params) ++{ ++ struct mwl_priv *priv = hw->priv; ++ int rc = 0; ++ ++ if (WARN_ON(queue > SYSADPT_TX_WMM_QUEUES - 1)) ++ return -EINVAL; ++ ++ memcpy(&priv->wmm_params[queue], params, sizeof(*params)); ++ ++ if (!priv->wmm_enabled) { ++ rc = mwl_fwcmd_set_wmm_mode(hw, true); ++ priv->wmm_enabled = true; ++ } ++ ++ if (!rc) { ++ int q = SYSADPT_TX_WMM_QUEUES - 1 - queue; ++ ++ rc = mwl_fwcmd_set_edca_params(hw, q, ++ params->cw_min, params->cw_max, ++ params->aifs, params->txop); ++ } ++ ++ return rc; ++} ++ ++static int mwl_mac80211_get_stats(struct ieee80211_hw *hw, ++ struct ieee80211_low_level_stats *stats) ++{ ++ return mwl_fwcmd_get_stat(hw, stats); ++} ++ ++static int mwl_mac80211_get_survey(struct ieee80211_hw *hw, ++ int idx, ++ struct survey_info *survey) ++{ ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_survey_info *survey_info; ++ ++ if (priv->survey_info_idx) { ++ if (idx >= priv->survey_info_idx) { ++ priv->survey_info_idx = 0; ++ return -ENOENT; ++ } ++ survey_info = &priv->survey_info[idx]; ++ } else { ++ if (idx != 0) ++ return -ENOENT; ++ mwl_fwcmd_get_survey(hw, 0); ++ survey_info = &priv->cur_survey_info; ++ if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) ++ survey->filled |= SURVEY_INFO_IN_USE; ++ } ++ ++ survey->channel = &survey_info->channel; ++ survey->filled |= survey_info->filled; ++ survey->time = survey_info->time_period / 1000; ++ survey->time_busy = survey_info->time_busy / 1000; ++ survey->time_tx = survey_info->time_tx / 1000; ++ survey->noise = survey_info->noise; ++ ++ return 0; ++} ++ ++static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_ampdu_params *params) ++{ ++ int rc = 0; ++ struct mwl_priv *priv = hw->priv; ++ struct mwl_ampdu_stream *stream; ++ enum ieee80211_ampdu_mlme_action action = params->action; ++ struct ieee80211_sta *sta = params->sta; ++ u16 tid = params->tid; ++ u8 buf_size = params->buf_size; ++ u8 *addr = sta->addr; ++ struct mwl_sta *sta_info; ++ ++ sta_info = mwl_dev_get_sta(sta); ++ ++ spin_lock_bh(&priv->stream_lock); ++ ++ stream = mwl_fwcmd_lookup_stream(hw, sta, tid); ++ ++ switch (action) { ++ case IEEE80211_AMPDU_RX_START: ++ if (priv->chip_type == MWL8964) { ++ struct mwl_ampdu_stream tmp; ++ ++ tmp.sta = sta; ++ tmp.tid = tid; ++ spin_unlock_bh(&priv->stream_lock); ++ mwl_fwcmd_create_ba(hw, &tmp, vif, ++ BA_FLAG_DIRECTION_DOWN, ++ buf_size, params->ssn, ++ params->amsdu); ++ spin_lock_bh(&priv->stream_lock); ++ break; ++ } ++ case IEEE80211_AMPDU_RX_STOP: ++ if (priv->chip_type == MWL8964) { ++ struct mwl_ampdu_stream tmp; ++ ++ tmp.sta = sta; ++ tmp.tid = tid; ++ spin_unlock_bh(&priv->stream_lock); ++ mwl_fwcmd_destroy_ba(hw, &tmp, ++ BA_FLAG_DIRECTION_DOWN); ++ spin_lock_bh(&priv->stream_lock); ++ } ++ break; ++ case IEEE80211_AMPDU_TX_START: ++ if (!sta_info->is_ampdu_allowed) { ++ wiphy_warn(hw->wiphy, "ampdu not allowed\n"); ++ rc = -EPERM; ++ break; ++ } ++ ++ if (!stream) { ++ stream = mwl_fwcmd_add_stream(hw, sta, tid); ++ if (!stream) { ++ wiphy_warn(hw->wiphy, "no stream found\n"); ++ rc = -EPERM; ++ break; ++ } ++ } ++ ++ if (priv->chip_type != MWL8964) { ++ spin_unlock_bh(&priv->stream_lock); ++ rc = mwl_fwcmd_check_ba(hw, stream, vif, ++ BA_FLAG_DIRECTION_UP); ++ spin_lock_bh(&priv->stream_lock); ++ if (rc) { ++ mwl_fwcmd_remove_stream(hw, stream); ++ sta_info->check_ba_failed[tid]++; ++ break; ++ } ++ } ++ stream->state = AMPDU_STREAM_IN_PROGRESS; ++ spin_unlock_bh(&priv->stream_lock); ++ rc = mwl_fwcmd_get_seqno(hw, stream, ¶ms->ssn); ++ spin_lock_bh(&priv->stream_lock); ++ if (rc) ++ break; ++ ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); ++ break; ++ case IEEE80211_AMPDU_TX_STOP_CONT: ++ case IEEE80211_AMPDU_TX_STOP_FLUSH: ++ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ++ if (stream) { ++ if (stream->state == AMPDU_STREAM_ACTIVE) { ++ stream->state = AMPDU_STREAM_IN_PROGRESS; ++ mwl_hif_tx_del_ampdu_pkts(hw, sta, tid); ++ spin_unlock_bh(&priv->stream_lock); ++ mwl_fwcmd_destroy_ba(hw, stream, ++ BA_FLAG_DIRECTION_UP); ++ spin_lock_bh(&priv->stream_lock); ++ sta_info->is_amsdu_allowed = false; ++ } ++ ++ mwl_fwcmd_remove_stream(hw, stream); ++ ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); ++ } else { ++ rc = -EPERM; ++ } ++ break; ++ case IEEE80211_AMPDU_TX_OPERATIONAL: ++ if (stream) { ++ if (WARN_ON(stream->state != ++ AMPDU_STREAM_IN_PROGRESS)) { ++ rc = -EPERM; ++ break; ++ } ++ spin_unlock_bh(&priv->stream_lock); ++ rc = mwl_fwcmd_create_ba(hw, stream, vif, ++ BA_FLAG_DIRECTION_UP, ++ buf_size, params->ssn, ++ params->amsdu); ++ spin_lock_bh(&priv->stream_lock); ++ ++ if (!rc) { ++ stream->state = AMPDU_STREAM_ACTIVE; ++ sta_info->check_ba_failed[tid] = 0; ++ if (priv->tx_amsdu) ++ sta_info->is_amsdu_allowed = ++ params->amsdu; ++ else ++ sta_info->is_amsdu_allowed = false; ++ } else { ++ spin_unlock_bh(&priv->stream_lock); ++ mwl_fwcmd_destroy_ba(hw, stream, ++ BA_FLAG_DIRECTION_UP); ++ spin_lock_bh(&priv->stream_lock); ++ mwl_fwcmd_remove_stream(hw, stream); ++ wiphy_err(hw->wiphy, ++ "ampdu operation error code: %d\n", ++ rc); ++ } ++ } else { ++ rc = -EPERM; ++ } ++ break; ++ default: ++ rc = -ENOTSUPP; ++ break; ++ } ++ ++ spin_unlock_bh(&priv->stream_lock); ++ ++ return rc; ++} ++ ++static int mwl_mac80211_chnl_switch(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_channel_switch *ch_switch) ++{ ++ int rc = 0; ++ ++ rc = mwl_fwcmd_set_switch_channel(hw, ch_switch); ++ ++ return rc; ++} ++ ++static void mwl_mac80211_sw_scan_start(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ const u8 *mac_addr) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ priv->sw_scanning = true; ++ priv->survey_info_idx = 0; ++} ++ ++static void mwl_mac80211_sw_scan_complete(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ struct mwl_priv *priv = hw->priv; ++ ++ priv->sw_scanning = false; ++} ++ ++const struct ieee80211_ops mwl_mac80211_ops = { ++ .tx = mwl_mac80211_tx, ++ .start = mwl_mac80211_start, ++ .stop = mwl_mac80211_stop, ++ .add_interface = mwl_mac80211_add_interface, ++ .remove_interface = mwl_mac80211_remove_interface, ++ .config = mwl_mac80211_config, ++ .bss_info_changed = mwl_mac80211_bss_info_changed, ++ .configure_filter = mwl_mac80211_configure_filter, ++ .set_key = mwl_mac80211_set_key, ++ .set_rts_threshold = mwl_mac80211_set_rts_threshold, ++ .sta_add = mwl_mac80211_sta_add, ++ .sta_remove = mwl_mac80211_sta_remove, ++ .conf_tx = mwl_mac80211_conf_tx, ++ .get_stats = mwl_mac80211_get_stats, ++ .get_survey = mwl_mac80211_get_survey, ++ .ampdu_action = mwl_mac80211_ampdu_action, ++ .pre_channel_switch = mwl_mac80211_chnl_switch, ++ .sw_scan_start = mwl_mac80211_sw_scan_start, ++ .sw_scan_complete = mwl_mac80211_sw_scan_complete, ++}; +diff --git a/drivers/net/wireless/marvell/mwlwifi/mu_mimo.c b/drivers/net/wireless/marvell/mwlwifi/mu_mimo.c +new file mode 100644 +index 000000000000..74ab054f947e +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/mu_mimo.c +@@ -0,0 +1,21 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements MU-MIMO functions. */ ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "mu_mimo.h" ++ +diff --git a/drivers/net/wireless/marvell/mwlwifi/mu_mimo.h b/drivers/net/wireless/marvell/mwlwifi/mu_mimo.h +new file mode 100644 +index 000000000000..24179f404774 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/mu_mimo.h +@@ -0,0 +1,23 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines MU-MIMO functions. */ ++ ++#ifndef _MU_MIMO_H_ ++#define _MU_MIMO_H_ ++ ++ ++ ++#endif /* _MU_MIMO_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/sysadpt.h b/drivers/net/wireless/marvell/mwlwifi/sysadpt.h +new file mode 100644 +index 000000000000..1194e5271870 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/sysadpt.h +@@ -0,0 +1,86 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines system adaptation related information. */ ++ ++#ifndef _SYSADPT_H_ ++#define _SYSADPT_H_ ++ ++#define SYSADPT_MAX_STA 64 ++ ++#define SYSADPT_MAX_STA_SC4 300 ++ ++#define SYSADPT_MAX_NUM_CHANNELS 64 ++ ++#define SYSADPT_MAX_DATA_RATES_G 14 ++ ++#define SYSADPT_MAX_MCS_RATES 24 ++ ++#define SYSADPT_MAX_11AC_RATES 20 ++ ++#define SYSADPT_MAX_RATE_ADAPT_RATES (SYSADPT_MAX_DATA_RATES_G + \ ++ SYSADPT_MAX_MCS_RATES + \ ++ SYSADPT_MAX_11AC_RATES) ++ ++#define SYSADPT_TX_POWER_LEVEL_TOTAL 16 /* SC3 */ ++ ++#define SYSADPT_TX_GRP_PWR_LEVEL_TOTAL 28 /* KF2 */ ++ ++#define SYSADPT_TX_PWR_LEVEL_TOTAL_SC4 32 /* SC4 */ ++ ++#define SYSADPT_TX_WMM_QUEUES 4 ++ ++#define SYSADPT_NUM_OF_CLIENT 1 ++ ++#define SYSADPT_NUM_OF_AP 16 ++ ++#define SYSADPT_NUM_OF_MESH 1 ++ ++#define SYSADPT_TOTAL_TX_QUEUES (SYSADPT_TX_WMM_QUEUES + \ ++ SYSADPT_NUM_OF_AP) ++ ++#define SYSADPT_MAX_AGGR_SIZE 4096 ++ ++#define SYSADPT_AMPDU_PACKET_THRESHOLD 64 ++ ++#define SYSADPT_AMSDU_FW_MAX_SIZE 3300 ++ ++#define SYSADPT_AMSDU_4K_MAX_SIZE SYSADPT_AMSDU_FW_MAX_SIZE ++ ++#define SYSADPT_AMSDU_8K_MAX_SIZE SYSADPT_AMSDU_FW_MAX_SIZE ++ ++#define SYSADPT_AMSDU_ALLOW_SIZE 1600 ++ ++#define SYSADPT_AMSDU_FLUSH_TIME 500 ++ ++#define SYSADPT_AMSDU_PACKET_THRESHOLD 10 ++ ++#define SYSADPT_MAX_TID 8 ++ ++#define SYSADPT_QUIET_PERIOD_DEFAULT 100 ++ ++#define SYSADPT_QUIET_PERIOD_MIN 25 ++ ++#define SYSADPT_QUIET_START_OFFSET 10 ++ ++#define SYSADPT_THERMAL_THROTTLE_MAX 100 ++ ++#define SYSADPT_TIMER_WAKEUP_TIME 10 /* ms */ ++ ++#define SYSADPT_OTP_BUF_SIZE (256*8) /* 258 lines * 8 bytes */ ++ ++#define SYSADPT_TXPWRLMT_CFG_BUF_SIZE (3650) ++ ++#endif /* _SYSADPT_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/thermal.c b/drivers/net/wireless/marvell/mwlwifi/thermal.c +new file mode 100644 +index 000000000000..7c59def51e7f +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/thermal.c +@@ -0,0 +1,182 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements thermal framework related functions. */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "hif/fwcmd.h" ++#include "thermal.h" ++ ++static int ++mwl_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, ++ unsigned long *state) ++{ ++ *state = SYSADPT_THERMAL_THROTTLE_MAX; ++ ++ return 0; ++} ++ ++static int ++mwl_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, ++ unsigned long *state) ++{ ++ struct mwl_priv *priv = cdev->devdata; ++ ++ *state = priv->throttle_state; ++ ++ return 0; ++} ++ ++static int ++mwl_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, ++ unsigned long throttle_state) ++{ ++ struct mwl_priv *priv = cdev->devdata; ++ ++ if (throttle_state > SYSADPT_THERMAL_THROTTLE_MAX) { ++ wiphy_warn(priv->hw->wiphy, ++ "throttle state %ld is exceeding the limit %d\n", ++ throttle_state, SYSADPT_THERMAL_THROTTLE_MAX); ++ return -EINVAL; ++ } ++ priv->throttle_state = throttle_state; ++ mwl_thermal_set_throttling(priv); ++ ++ return 0; ++} ++ ++static struct thermal_cooling_device_ops mwl_thermal_ops = { ++ .get_max_state = mwl_thermal_get_max_throttle_state, ++ .get_cur_state = mwl_thermal_get_cur_throttle_state, ++ .set_cur_state = mwl_thermal_set_cur_throttle_state, ++}; ++ ++static ssize_t mwl_thermal_show_temp(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct mwl_priv *priv = dev_get_drvdata(dev); ++ int ret, temperature; ++ ++ ret = mwl_fwcmd_get_temp(priv->hw, &priv->temperature); ++ if (ret) { ++ wiphy_warn(priv->hw->wiphy, "failed: can't get temperature\n"); ++ goto out; ++ } ++ ++ temperature = priv->temperature; ++ ++ /* display in millidegree celcius */ ++ ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000); ++out: ++ return ret; ++} ++ ++static SENSOR_DEVICE_ATTR(temp1_input, 0444, mwl_thermal_show_temp, ++ NULL, 0); ++ ++static struct attribute *mwl_hwmon_attrs[] = { ++ &sensor_dev_attr_temp1_input.dev_attr.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(mwl_hwmon); ++ ++void mwl_thermal_set_throttling(struct mwl_priv *priv) ++{ ++ u32 period, duration, enabled; ++ int ret; ++ ++ period = priv->quiet_period; ++ duration = (period * priv->throttle_state) / 100; ++ enabled = duration ? 1 : 0; ++ ++ ret = mwl_fwcmd_quiet_mode(priv->hw, enabled, period, ++ duration, SYSADPT_QUIET_START_OFFSET); ++ if (ret) { ++ wiphy_warn(priv->hw->wiphy, ++ "failed: period %u duarion %u enabled %u ret %d\n", ++ period, duration, enabled, ret); ++ } ++} ++ ++int mwl_thermal_register(struct mwl_priv *priv) ++{ ++ struct thermal_cooling_device *cdev; ++ struct device *hwmon_dev; ++ int ret; ++ ++ if (priv->chip_type != MWL8897) ++ return 0; ++ ++ cdev = thermal_cooling_device_register("mwlwifi_thermal", priv, ++ &mwl_thermal_ops); ++ if (IS_ERR(cdev)) { ++ wiphy_err(priv->hw->wiphy, ++ "failed to setup thermal device result: %ld\n", ++ PTR_ERR(cdev)); ++ return -EINVAL; ++ } ++ ++ ret = sysfs_create_link(&priv->dev->kobj, &cdev->device.kobj, ++ "cooling_device"); ++ if (ret) { ++ wiphy_err(priv->hw->wiphy, ++ "failed to create cooling device symlink\n"); ++ goto err_cooling_destroy; ++ } ++ ++ priv->cdev = cdev; ++ priv->quiet_period = SYSADPT_QUIET_PERIOD_DEFAULT; ++ ++ if (!IS_ENABLED(CONFIG_HWMON)) ++ return 0; ++ ++ hwmon_dev = ++ devm_hwmon_device_register_with_groups(priv->dev, ++ "mwlwifi_hwmon", priv, ++ mwl_hwmon_groups); ++ if (IS_ERR(hwmon_dev)) { ++ wiphy_err(priv->hw->wiphy, ++ "failed to register hwmon device: %ld\n", ++ PTR_ERR(hwmon_dev)); ++ ret = -EINVAL; ++ goto err_remove_link; ++ } ++ ++ return 0; ++ ++err_remove_link: ++ sysfs_remove_link(&priv->dev->kobj, "cooling_device"); ++err_cooling_destroy: ++ thermal_cooling_device_unregister(cdev); ++ ++ return ret; ++} ++ ++void mwl_thermal_unregister(struct mwl_priv *priv) ++{ ++ if (priv->chip_type != MWL8897) ++ return; ++ ++ sysfs_remove_link(&priv->dev->kobj, "cooling_device"); ++ thermal_cooling_device_unregister(priv->cdev); ++} +diff --git a/drivers/net/wireless/marvell/mwlwifi/thermal.h b/drivers/net/wireless/marvell/mwlwifi/thermal.h +new file mode 100644 +index 000000000000..c7f0ad2b87eb +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/thermal.h +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines Linux thermal framework related functions. */ ++ ++#ifndef _THERMAL_H_ ++#define _THERMAL_H_ ++ ++#include ++ ++#if IS_ENABLED(CONFIG_THERMAL) ++int mwl_thermal_register(struct mwl_priv *priv); ++void mwl_thermal_unregister(struct mwl_priv *priv); ++void mwl_thermal_set_throttling(struct mwl_priv *priv); ++#else ++static inline int mwl_thermal_register(struct mwl_priv *priv) ++{ ++ return 0; ++} ++ ++static inline void mwl_thermal_unregister(struct mwl_priv *priv) ++{ ++} ++ ++static inline void mwl_thermal_set_throttling(struct mwl_priv *priv) ++{ ++} ++#endif ++ ++#endif /* _THERMAL_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/utils.c b/drivers/net/wireless/marvell/mwlwifi/utils.c +new file mode 100644 +index 000000000000..b73054a3f55e +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/utils.c +@@ -0,0 +1,576 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements common utility functions. */ ++ ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "utils.h" ++ ++static unsigned short phy_rate[][5] = { ++ {2, 13, 15, 27, 30}, /* 0 */ ++ {4, 26, 29, 54, 60}, /* 1 */ ++ {11, 39, 43, 81, 90}, /* 2 */ ++ {22, 52, 58, 108, 120}, /* 3 */ ++ {44, 78, 87, 162, 180}, /* 4 */ ++ {12, 104, 115, 216, 240}, /* 5 */ ++ {18, 117, 130, 243, 270}, /* 6 */ ++ {24, 130, 144, 270, 300}, /* 7 */ ++ {36, 26, 29, 54, 60}, /* 8 */ ++ {48, 52, 58, 108, 120}, /* 9 */ ++ {72, 78, 87, 162, 180}, /* 10 */ ++ {96, 104, 116, 216, 240}, /* 11 */ ++ {108, 156, 173, 324, 360}, /* 12 */ ++ {0, 208, 231, 432, 480}, /* 13 */ ++ {0, 234, 260, 486, 540}, /* 14 */ ++ {0, 260, 289, 540, 600}, /* 15 */ ++ {0, 39, 43, 81, 90}, /* 16 */ ++ {0, 78, 87, 162, 180}, /* 17 */ ++ {0, 117, 130, 243, 270}, /* 18 */ ++ {0, 156, 173, 324, 360}, /* 19 */ ++ {0, 234, 260, 486, 540}, /* 20 */ ++ {0, 312, 347, 648, 720}, /* 21 */ ++ {0, 351, 390, 729, 810}, /* 22 */ ++ {0, 390, 433, 810, 900}, /* 23 */ ++}; ++ ++/* 20Mhz: Nss1_LGI, Nss1_SGI, Nss2_LGI, Nss2_SGI, Nss3_LGI, Nss3_SGI */ ++static unsigned short phy_rate_11ac20M[][6] = { ++ {13, 15, 26, 29, 39, 44}, /* 0 */ ++ {26, 29, 52, 58, 78, 87}, /* 1 */ ++ {39, 44, 78, 87, 117, 130}, /* 2 */ ++ {52, 58, 104, 116, 156, 174}, /* 3 */ ++ {78, 87, 156, 174, 234, 260}, /* 4 */ ++ {104, 116, 208, 231, 312, 347}, /* 5 */ ++ {117, 130, 234, 260, 351, 390}, /* 6 */ ++ {130, 145, 260, 289, 390, 434}, /* 7 */ ++ {156, 174, 312, 347, 468, 520}, /* 8 */ ++ /* Nss 1 and Nss 2 mcs9 not valid */ ++ {2, 2, 2, 2, 520, 578}, /* 9 */ ++}; ++ ++/* 40Mhz: Nss1_LGI, Nss1_SGI, Nss2_LGI, Nss2_SGI, Nss3_LGI, Nss3_SGI */ ++static unsigned short phy_rate_11ac40M[][6] = { ++ {27, 30, 54, 60, 81, 90}, /* 0 */ ++ {54, 60, 108, 120, 162, 180}, /* 1 */ ++ {81, 90, 162, 180, 243, 270}, /* 2 */ ++ {108, 120, 216, 240, 324, 360}, /* 3 */ ++ {162, 180, 324, 360, 486, 540}, /* 4 */ ++ {216, 240, 432, 480, 648, 720}, /* 5 */ ++ {243, 270, 486, 540, 729, 810}, /* 6 */ ++ {270, 300, 540, 600, 810, 900}, /* 7 */ ++ {324, 360, 648, 720, 972, 1080}, /* 8 */ ++ {360, 400, 720, 800, 1080, 1200}, /* 9 */ ++}; ++ ++/* 80Mhz: Nss1_LGI, Nss1_SGI, Nss2_LGI, Nss2_SGI, Nss3_LGI, Nss3_SGI */ ++static unsigned short phy_rate_11ac80M[][6] = { ++ {59, 65, 117, 130, 175, 195}, /* 0 */ ++ {117, 130, 234, 260, 351, 390}, /* 1 */ ++ {175, 195, 351, 390, 527, 585}, /* 2 */ ++ {234, 260, 468, 520, 702, 780}, /* 3 */ ++ {351, 390, 702, 780, 1053, 1170}, /* 4 */ ++ {468, 520, 936, 1040, 1404, 1560}, /* 5 */ ++ {527, 585, 1053, 1170, 2, 2}, /* 6, Nss 3 mcs6 not valid */ ++ {585, 650, 1170, 1300, 1755, 1950}, /* 7 */ ++ {702, 780, 1404, 1560, 2106, 2340}, /* 8 */ ++ {780, 867, 1560, 1733, 2340, 2600}, /* 9 */ ++}; ++ ++/* 160Mhz: Nss1_LGI, Nss1_SGI, Nss2_LGI, Nss2_SGI, Nss3_LGI, Nss3_SGI */ ++static unsigned short phy_rate_11ac160M[][6] = { ++ {117, 130, 234, 260, 351, 390}, /* 0 */ ++ {234, 260, 468, 520, 702, 780}, /* 1 */ ++ {351, 390, 702, 780, 1053, 1170}, /* 2 */ ++ {468, 520, 936, 1040, 1404, 1560}, /* 3 */ ++ {702, 780, 1404, 1560, 2106, 2340}, /* 4 */ ++ {936, 1040, 1872, 2080, 2808, 3120}, /* 5 */ ++ {1053, 1170, 2106, 2340, 3159, 3510}, /* 6 */ ++ {1170, 1300, 2340, 2600, 3510, 3900}, /* 7 */ ++ {1404, 1560, 2808, 3120, 4212, 4680}, /* 8 */ ++ {1560, 1733, 2130, 3467, 4680, 5200}, /* 9 */ ++}; ++ ++int utils_get_phy_rate(u8 format, u8 bandwidth, u8 short_gi, u8 mcs_id) ++{ ++ u8 index = 0; ++ u8 nss_11ac = 0; ++ u8 rate_11ac = 0; ++ ++ if (format == TX_RATE_FORMAT_11N) { ++ index = (bandwidth << 1) | short_gi; ++ index++; ++ } else if (format == TX_RATE_FORMAT_11AC) { ++ rate_11ac = mcs_id & 0xf; /* 11ac, mcs_id[3:0]: rate */ ++ nss_11ac = mcs_id >> 4; /* 11ac, mcs_id[6:4]: nss code */ ++ index = (nss_11ac << 1) | short_gi; ++ } ++ ++ if (format != TX_RATE_FORMAT_11AC) ++ return (phy_rate[mcs_id][index] / 2); ++ ++ if (bandwidth == TX_RATE_BANDWIDTH_20) ++ return (phy_rate_11ac20M[rate_11ac][index] / 2); ++ else if (bandwidth == TX_RATE_BANDWIDTH_40) ++ return (phy_rate_11ac40M[rate_11ac][index] / 2); ++ else if (bandwidth == TX_RATE_BANDWIDTH_80) ++ return (phy_rate_11ac80M[rate_11ac][index] / 2); ++ else ++ return (phy_rate_11ac160M[rate_11ac][index] / 2); ++} ++ ++u8 utils_get_rate_id(u8 rate) ++{ ++ switch (rate) { ++ case 10: /* 1 Mbit/s or 12 Mbit/s */ ++ return 0; ++ case 20: /* 2 Mbit/s */ ++ return 1; ++ case 55: /* 5.5 Mbit/s */ ++ return 2; ++ case 110: /* 11 Mbit/s */ ++ return 3; ++ case 220: /* 22 Mbit/s */ ++ return 4; ++ case 0xb: /* 6 Mbit/s */ ++ return 5; ++ case 0xf: /* 9 Mbit/s */ ++ return 6; ++ case 0xe: /* 18 Mbit/s */ ++ return 8; ++ case 0x9: /* 24 Mbit/s */ ++ return 9; ++ case 0xd: /* 36 Mbit/s */ ++ return 10; ++ case 0x8: /* 48 Mbit/s */ ++ return 11; ++ case 0xc: /* 54 Mbit/s */ ++ return 12; ++ case 0x7: /* 72 Mbit/s */ ++ return 13; ++ } ++ ++ return 0; ++} ++ ++u32 utils_get_init_tx_rate(struct mwl_priv *priv, struct ieee80211_conf *conf, ++ struct ieee80211_sta *sta) ++{ ++ u32 tx_rate; ++ u16 format, nss, bw, rate_mcs; ++ ++ if (sta->vht_cap.vht_supported) ++ format = TX_RATE_FORMAT_11AC; ++ else if (sta->ht_cap.ht_supported) ++ format = TX_RATE_FORMAT_11N; ++ else ++ format = TX_RATE_FORMAT_LEGACY; ++ ++ switch (priv->antenna_tx) { ++ case ANTENNA_TX_1: ++ nss = 1; ++ break; ++ case ANTENNA_TX_2: ++ nss = 2; ++ break; ++ case ANTENNA_TX_3: ++ case ANTENNA_TX_4_AUTO: ++ nss = 3; ++ break; ++ default: ++ nss = sta->rx_nss; ++ break; ++ } ++ if (nss > sta->rx_nss) ++ nss = sta->rx_nss; ++ ++ switch (conf->chandef.width) { ++ case NL80211_CHAN_WIDTH_20_NOHT: ++ case NL80211_CHAN_WIDTH_20: ++ bw = TX_RATE_BANDWIDTH_20; ++ break; ++ case NL80211_CHAN_WIDTH_40: ++ bw = TX_RATE_BANDWIDTH_40; ++ break; ++ case NL80211_CHAN_WIDTH_80: ++ bw = TX_RATE_BANDWIDTH_80; ++ break; ++ case NL80211_CHAN_WIDTH_160: ++ bw = TX_RATE_BANDWIDTH_160; ++ break; ++ default: ++ bw = sta->bandwidth; ++ break; ++ } ++ if (bw > sta->bandwidth) ++ bw = sta->bandwidth; ++ ++ switch (format) { ++ case TX_RATE_FORMAT_LEGACY: ++ rate_mcs = 12; /* ignore 11b */ ++ break; ++ case TX_RATE_FORMAT_11N: ++ rate_mcs = (nss * 8) - 1; ++ break; ++ default: ++ rate_mcs = ((nss - 1) << 4) | 8; ++ break; ++ } ++ ++ tx_rate = (format | (bw << MWL_TX_RATE_BANDWIDTH_SHIFT) | ++ (TX_RATE_INFO_SHORT_GI << MWL_TX_RATE_SHORTGI_SHIFT) | ++ (rate_mcs << MWL_TX_RATE_RATEIDMCS_SHIFT)); ++ ++ return tx_rate; ++} ++ ++struct mwl_vif *utils_find_vif_bss(struct mwl_priv *priv, u8 *bssid) ++{ ++ struct mwl_vif *mwl_vif; ++ ++ spin_lock_bh(&priv->vif_lock); ++ list_for_each_entry(mwl_vif, &priv->vif_list, list) { ++ if (ether_addr_equal(bssid, mwl_vif->bssid)) { ++ spin_unlock_bh(&priv->vif_lock); ++ return mwl_vif; ++ } ++ } ++ spin_unlock_bh(&priv->vif_lock); ++ ++ return NULL; ++} ++ ++struct mwl_sta *utils_find_sta(struct mwl_priv *priv, u8 *addr) ++{ ++ struct mwl_sta *sta_info; ++ struct ieee80211_sta *sta; ++ ++ spin_lock_bh(&priv->sta_lock); ++ list_for_each_entry(sta_info, &priv->sta_list, list) { ++ sta = container_of((void *)sta_info, struct ieee80211_sta, ++ drv_priv); ++ if (ether_addr_equal(addr, sta->addr)) { ++ spin_unlock_bh(&priv->sta_lock); ++ return sta_info; ++ } ++ } ++ spin_unlock_bh(&priv->sta_lock); ++ ++ return NULL; ++} ++ ++struct mwl_sta *utils_find_sta_by_aid(struct mwl_priv *priv, u16 aid) ++{ ++ struct mwl_sta *sta_info; ++ struct ieee80211_sta *sta; ++ ++ spin_lock_bh(&priv->sta_lock); ++ list_for_each_entry(sta_info, &priv->sta_list, list) { ++ sta = container_of((void *)sta_info, struct ieee80211_sta, ++ drv_priv); ++ if (sta->aid == aid) { ++ spin_unlock_bh(&priv->sta_lock); ++ return sta_info; ++ } ++ } ++ spin_unlock_bh(&priv->sta_lock); ++ ++ return NULL; ++} ++ ++struct mwl_sta *utils_find_sta_by_id(struct mwl_priv *priv, u16 stnid) ++{ ++ struct mwl_sta *sta_info; ++ ++ spin_lock_bh(&priv->sta_lock); ++ list_for_each_entry(sta_info, &priv->sta_list, list) { ++ if (sta_info->stnid == stnid) { ++ spin_unlock_bh(&priv->sta_lock); ++ return sta_info; ++ } ++ } ++ spin_unlock_bh(&priv->sta_lock); ++ ++ return NULL; ++} ++ ++void utils_dump_data_info(const char *prefix_str, const void *buf, size_t len) ++{ ++ print_hex_dump(KERN_INFO, prefix_str, DUMP_PREFIX_OFFSET, ++ 16, 1, buf, len, true); ++} ++ ++void utils_dump_data_debug(const char *prefix_str, const void *buf, size_t len) ++{ ++ print_hex_dump(KERN_DEBUG, prefix_str, DUMP_PREFIX_OFFSET, ++ 16, 1, buf, len, true); ++} ++ ++bool utils_is_non_amsdu_packet(const void *packet, bool mac80211) ++{ ++ const u8 *data = packet; ++ struct ieee80211_hdr *wh; ++ __be16 *protocol; ++ struct iphdr *iph; ++ struct udphdr *udph; ++ ++ if (mac80211) { ++ /* mac80211 packet */ ++ wh = (struct ieee80211_hdr *)data; ++ data += ieee80211_hdrlen(wh->frame_control) + 6; ++ protocol = (__be16 *)data; ++ } else { ++ /* mac802.3 packet */ ++ data += (2 * ETH_ALEN); ++ protocol = (__be16 *)data; ++ } ++ ++ if (*protocol == cpu_to_be16(ETH_P_PAE)) ++ return true; ++ ++ if (*protocol == htons(ETH_P_ARP)) ++ return true; ++ ++ if (*protocol == htons(ETH_P_IP)) { ++ data += sizeof(__be16); ++ iph = (struct iphdr *)data; ++ if (iph->protocol == IPPROTO_ICMP) ++ return true; ++ if (iph->protocol == IPPROTO_UDP) { ++ data += (iph->ihl * 4); ++ udph = (struct udphdr *)data; ++ if (((udph->source == htons(68)) && ++ (udph->dest == htons(67))) || ++ ((udph->source == htons(67)) && ++ (udph->dest == htons(68)))) ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++bool utils_is_arp(const void *packet, bool mac80211, u16 *arp_op) ++{ ++ const u8 *data = packet; ++ struct ieee80211_hdr *wh; ++ __be16 *protocol; ++ struct arphdr *arph; ++ ++ if (mac80211) { ++ /* mac80211 packet */ ++ wh = (struct ieee80211_hdr *)data; ++ data += ieee80211_hdrlen(wh->frame_control) + 6; ++ protocol = (__be16 *)data; ++ } else { ++ /* mac802.3 packet */ ++ data += (2 * ETH_ALEN); ++ protocol = (__be16 *)data; ++ } ++ ++ if (*protocol == htons(ETH_P_ARP)) { ++ data += sizeof(__be16); ++ arph = (struct arphdr *)data; ++ *arp_op = ntohs(arph->ar_op); ++ return true; ++ } ++ ++ return false; ++} ++ ++bool utils_is_icmp_echo(const void *packet, bool mac80211, u8 *type) ++{ ++ const u8 *data = packet; ++ struct ieee80211_hdr *wh; ++ __be16 *protocol; ++ struct iphdr *iph; ++ struct icmphdr *icmph; ++ ++ if (mac80211) { ++ /* mac80211 packet */ ++ wh = (struct ieee80211_hdr *)data; ++ data += ieee80211_hdrlen(wh->frame_control) + 6; ++ protocol = (__be16 *)data; ++ } else { ++ /* mac802.3 packet */ ++ data += (2 * ETH_ALEN); ++ protocol = (__be16 *)data; ++ } ++ ++ if (*protocol == htons(ETH_P_IP)) { ++ data += sizeof(__be16); ++ iph = (struct iphdr *)data; ++ if (iph->protocol == IPPROTO_ICMP) { ++ data += (iph->ihl * 4); ++ icmph = (struct icmphdr *)data; ++ *type = icmph->type; ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++bool utils_is_dhcp(const void *packet, bool mac80211, u8 *op, u8 *dhcp_client) ++{ ++ const u8 *data = packet; ++ struct ieee80211_hdr *wh; ++ __be16 *protocol; ++ struct iphdr *iph; ++ struct udphdr *udph; ++ ++ if (mac80211) { ++ /* mac80211 packet */ ++ wh = (struct ieee80211_hdr *)data; ++ data += ieee80211_hdrlen(wh->frame_control) + 6; ++ protocol = (__be16 *)data; ++ } else { ++ /* mac802.3 packet */ ++ data += (2 * ETH_ALEN); ++ protocol = (__be16 *)data; ++ } ++ ++ if (*protocol == htons(ETH_P_IP)) { ++ data += sizeof(__be16); ++ iph = (struct iphdr *)data; ++ if (iph->protocol == IPPROTO_UDP) { ++ data += (iph->ihl * 4); ++ udph = (struct udphdr *)data; ++ if (((udph->source == htons(68)) && ++ (udph->dest == htons(67))) || ++ ((udph->source == htons(67)) && ++ (udph->dest == htons(68)))) { ++ data += sizeof(struct udphdr); ++ *op = *data; ++ ether_addr_copy(dhcp_client, data + 28); ++ return true; ++ } ++ } ++ } ++ ++ return false; ++} ++ ++void utils_dump_arp(const void *packet, bool mac80211, size_t len) ++{ ++ const u8 *data = packet; ++ struct ieee80211_hdr *wh; ++ __be16 *protocol; ++ struct arphdr *arph; ++ ++ if (mac80211) { ++ /* mac80211 packet */ ++ wh = (struct ieee80211_hdr *)data; ++ data += ieee80211_hdrlen(wh->frame_control) + 6; ++ protocol = (__be16 *)data; ++ } else { ++ /* mac802.3 packet */ ++ data += (2 * ETH_ALEN); ++ protocol = (__be16 *)data; ++ } ++ ++ if (*protocol == htons(ETH_P_ARP)) { ++ data += sizeof(__be16); ++ arph = (struct arphdr *)data; ++ if (arph->ar_op == htons(ARPOP_REQUEST)) ++ utils_dump_data_info("ARP REQUEST: ", packet, len); ++ else if (arph->ar_op == htons(ARPOP_REPLY)) ++ utils_dump_data_info("ARP REPLY: ", packet, len); ++ } ++} ++ ++void utils_dump_icmp_echo(const void *packet, bool mac80211, size_t len) ++{ ++ const u8 *data = packet; ++ struct ieee80211_hdr *wh; ++ __be16 *protocol; ++ struct iphdr *iph; ++ struct icmphdr *icmph; ++ ++ if (mac80211) { ++ /* mac80211 packet */ ++ wh = (struct ieee80211_hdr *)data; ++ data += ieee80211_hdrlen(wh->frame_control) + 6; ++ protocol = (__be16 *)data; ++ } else { ++ /* mac802.3 packet */ ++ data += (2 * ETH_ALEN); ++ protocol = (__be16 *)data; ++ } ++ ++ if (*protocol == htons(ETH_P_IP)) { ++ data += sizeof(__be16); ++ iph = (struct iphdr *)data; ++ if (iph->protocol == IPPROTO_ICMP) { ++ data += (iph->ihl * 4); ++ icmph = (struct icmphdr *)data; ++ if (icmph->type == ICMP_ECHO) ++ utils_dump_data_info("ECHO REQUEST: ", ++ packet, len); ++ else if (icmph->type == ICMP_ECHOREPLY) ++ utils_dump_data_info("ECHO REPLY: ", ++ packet, len); ++ } ++ } ++} ++ ++void utils_dump_dhcp(const void *packet, bool mac80211, size_t len) ++{ ++ const u8 *data = packet; ++ struct ieee80211_hdr *wh; ++ __be16 *protocol; ++ struct iphdr *iph; ++ struct udphdr *udph; ++ const char *dhcp_op[8] = { ++ "DHCPDISCOVER", ++ "DHCPOFFER", ++ "DHCPREQUEST", ++ "DHCPDECLINE", ++ "DHCPACK", ++ "DHCPNAK", ++ "DHCPRELEASE", ++ "DHCPINFORM" ++ }; ++ ++ if (mac80211) { ++ /* mac80211 packet */ ++ wh = (struct ieee80211_hdr *)data; ++ data += ieee80211_hdrlen(wh->frame_control) + 6; ++ protocol = (__be16 *)data; ++ } else { ++ /* mac802.3 packet */ ++ data += (2 * ETH_ALEN); ++ protocol = (__be16 *)data; ++ } ++ ++ if (*protocol == htons(ETH_P_IP)) { ++ data += sizeof(__be16); ++ iph = (struct iphdr *)data; ++ if (iph->protocol == IPPROTO_UDP) { ++ data += (iph->ihl * 4); ++ udph = (struct udphdr *)data; ++ if (((udph->source == htons(68)) && ++ (udph->dest == htons(67))) || ++ ((udph->source == htons(67)) && ++ (udph->dest == htons(68)))) { ++ data += sizeof(struct udphdr); ++ utils_dump_data_info(dhcp_op[*data - 1], ++ packet, len); ++ } ++ } ++ } ++} +diff --git a/drivers/net/wireless/marvell/mwlwifi/utils.h b/drivers/net/wireless/marvell/mwlwifi/utils.h +new file mode 100644 +index 000000000000..4a292e990412 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/utils.h +@@ -0,0 +1,158 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines common utility functions. */ ++ ++#ifndef _UTILS_H_ ++#define _UTILS_H_ ++ ++#include ++#include ++#include ++#include ++ ++/* DHCP message types */ ++#define DHCPDISCOVER 1 ++#define DHCPOFFER 2 ++#define DHCPREQUEST 3 ++#define DHCPDECLINE 4 ++#define DHCPACK 5 ++#define DHCPNAK 6 ++#define DHCPRELEASE 7 ++#define DHCPINFORM 8 ++ ++static inline int utils_tid_to_ac(u8 tid) ++{ ++ switch (tid) { ++ case 0: ++ case 3: ++ return IEEE80211_AC_BE; ++ case 1: ++ case 2: ++ return IEEE80211_AC_BK; ++ case 4: ++ case 5: ++ return IEEE80211_AC_VI; ++ case 6: ++ case 7: ++ return IEEE80211_AC_VO; ++ default: ++ break; ++ } ++ ++ return -1; ++} ++ ++static inline void utils_add_basic_rates(int band, struct sk_buff *skb) ++{ ++ struct ieee80211_mgmt *mgmt; ++ int len; ++ u8 *pos; ++ ++ mgmt = (struct ieee80211_mgmt *)skb->data; ++ len = skb->len - ieee80211_hdrlen(mgmt->frame_control); ++ len -= 4; ++ pos = (u8 *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, ++ mgmt->u.assoc_req.variable, ++ len); ++ if (pos) { ++ pos++; ++ len = *pos++; ++ while (len) { ++ if (band == NL80211_BAND_2GHZ) { ++ if ((*pos == 2) || (*pos == 4) || ++ (*pos == 11) || (*pos == 22)) ++ *pos |= 0x80; ++ } else { ++ if ((*pos == 12) || (*pos == 24) || ++ (*pos == 48)) ++ *pos |= 0x80; ++ } ++ pos++; ++ len--; ++ } ++ } ++} ++ ++static inline int utils_assign_stnid(struct mwl_priv *priv, int macid, u16 aid) ++{ ++ int stnid; ++ int i; ++ ++ spin_lock_bh(&priv->stnid_lock); ++ stnid = priv->available_stnid; ++ if (stnid >= priv->stnid_num) { ++ spin_unlock_bh(&priv->stnid_lock); ++ return 0; ++ } ++ priv->stnid[stnid].macid = macid; ++ priv->stnid[stnid].aid = aid; ++ stnid++; ++ for (i = stnid; i < priv->stnid_num; i++) { ++ if (!priv->stnid[i].aid) ++ break; ++ } ++ priv->available_stnid = i; ++ spin_unlock_bh(&priv->stnid_lock); ++ return stnid; ++} ++ ++static inline void utils_free_stnid(struct mwl_priv *priv, u16 stnid) ++{ ++ spin_lock_bh(&priv->stnid_lock); ++ if (stnid && (stnid <= priv->stnid_num)) { ++ stnid--; ++ priv->stnid[stnid].macid = 0; ++ priv->stnid[stnid].aid = 0; ++ if (priv->available_stnid > stnid) ++ priv->available_stnid = stnid; ++ } ++ spin_unlock_bh(&priv->stnid_lock); ++} ++ ++int utils_get_phy_rate(u8 format, u8 bandwidth, u8 short_gi, u8 mcs_id); ++ ++u8 utils_get_rate_id(u8 rate); ++ ++u32 utils_get_init_tx_rate(struct mwl_priv *priv, struct ieee80211_conf *conf, ++ struct ieee80211_sta *sta); ++ ++struct mwl_vif *utils_find_vif_bss(struct mwl_priv *priv, u8 *bssid); ++ ++struct mwl_sta *utils_find_sta(struct mwl_priv *priv, u8 *addr); ++ ++struct mwl_sta *utils_find_sta_by_aid(struct mwl_priv *priv, u16 aid); ++ ++struct mwl_sta *utils_find_sta_by_id(struct mwl_priv *priv, u16 stnid); ++ ++void utils_dump_data_info(const char *prefix_str, const void *buf, size_t len); ++ ++void utils_dump_data_debug(const char *prefix_str, const void *buf, size_t len); ++ ++bool utils_is_non_amsdu_packet(const void *packet, bool mac80211); ++ ++bool utils_is_arp(const void *packet, bool mac80211, u16 *arp_op); ++ ++bool utils_is_icmp_echo(const void *packet, bool mac80211, u8 *type); ++ ++bool utils_is_dhcp(const void *packet, bool mac80211, u8 *op, u8 *dhcp_client); ++ ++void utils_dump_arp(const void *packet, bool mac80211, size_t len); ++ ++void utils_dump_icmp_echo(const void *packet, bool mac80211, size_t len); ++ ++void utils_dump_dhcp(const void *packet, bool mac80211, size_t len); ++ ++#endif /* _UTILS_H_ */ +diff --git a/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.c b/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.c +new file mode 100644 +index 000000000000..3e26fc42c225 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.c +@@ -0,0 +1,136 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file implements vendor spcific functions. */ ++ ++#include ++#include ++#include ++ ++#include "sysadpt.h" ++#include "core.h" ++#include "utils.h" ++#include "hif/fwcmd.h" ++#include "vendor_cmd.h" ++ ++static const struct nla_policy mwl_vendor_attr_policy[NUM_MWL_VENDOR_ATTR] = { ++ [MWL_VENDOR_ATTR_BF_TYPE] = { .type = NLA_U8 }, ++}; ++ ++static int mwl_vendor_cmd_set_bf_type(struct wiphy *wiphy, ++ struct wireless_dev *wdev, ++ const void *data, int data_len) ++{ ++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); ++ struct mwl_priv *priv = hw->priv; ++ struct nlattr *tb[NUM_MWL_VENDOR_ATTR]; ++ int rc; ++ u8 val; ++ ++ if (priv->chip_type != MWL8964) ++ return -EPERM; ++ ++ rc = nla_parse(tb, MWL_VENDOR_ATTR_MAX, data, data_len, ++ mwl_vendor_attr_policy ++#if (defined(LINUX_BACKPORT) || (LINUX_VERSION_CODE >=KERNEL_VERSION(4,12,0))) ++ , NULL ++#endif ++ ); ++ if (rc) ++ return rc; ++ ++ if (!tb[MWL_VENDOR_ATTR_BF_TYPE]) ++ return -EINVAL; ++ ++ val = nla_get_u8(tb[MWL_VENDOR_ATTR_BF_TYPE]); ++ if ((val < TXBF_MODE_OFF) || (val > TXBF_MODE_BFMER_AUTO)) ++ return -EINVAL; ++ wiphy_debug(wiphy, "set bf_type: 0x%x\n", val); ++ ++ rc = mwl_fwcmd_set_bftype(hw, val); ++ if (!rc) ++ priv->bf_type = val; ++ ++ return rc; ++} ++ ++static int mwl_vendor_cmd_get_bf_type(struct wiphy *wiphy, ++ struct wireless_dev *wdev, ++ const void *data, int data_len) ++{ ++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); ++ struct mwl_priv *priv = hw->priv; ++ struct sk_buff *skb; ++ ++ if (priv->chip_type != MWL8964) ++ return -EPERM; ++ ++ skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 8); ++ if (!skb) ++ return -ENOMEM; ++ ++ nla_put_u8(skb, MWL_VENDOR_ATTR_BF_TYPE, priv->bf_type); ++ ++ return cfg80211_vendor_cmd_reply(skb); ++} ++ ++static const struct wiphy_vendor_command mwl_vendor_commands[] = { ++ { ++ .info = { .vendor_id = MRVL_OUI, ++ .subcmd = MWL_VENDOR_CMD_SET_BF_TYPE}, ++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV, ++ .doit = mwl_vendor_cmd_set_bf_type, ++ }, ++ { ++ .info = { .vendor_id = MRVL_OUI, ++ .subcmd = MWL_VENDOR_CMD_GET_BF_TYPE}, ++ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV, ++ .doit = mwl_vendor_cmd_get_bf_type, ++ } ++}; ++ ++static const struct nl80211_vendor_cmd_info mwl_vendor_events[] = { ++ { ++ .vendor_id = MRVL_OUI, ++ .subcmd = MWL_VENDOR_EVENT_DRIVER_READY, ++ }, ++ { ++ .vendor_id = MRVL_OUI, ++ .subcmd = MWL_VENDOR_EVENT_DRIVER_START_REMOVE, ++ }, ++ { ++ .vendor_id = MRVL_OUI, ++ .subcmd = MWL_VENDOR_EVENT_CMD_TIMEOUT, ++ } ++}; ++ ++void vendor_cmd_register(struct wiphy *wiphy) ++{ ++ wiphy->vendor_commands = mwl_vendor_commands; ++ wiphy->n_vendor_commands = ARRAY_SIZE(mwl_vendor_commands); ++ wiphy->vendor_events = mwl_vendor_events; ++ wiphy->n_vendor_events = ARRAY_SIZE(mwl_vendor_events); ++} ++ ++void vendor_cmd_basic_event(struct wiphy *wiphy, int event_idx) ++{ ++ struct sk_buff *skb; ++ ++ skb = cfg80211_vendor_event_alloc(wiphy, NULL, 0, ++ event_idx, GFP_KERNEL); ++ ++ if (skb) ++ cfg80211_vendor_event(skb, GFP_KERNEL); ++} +diff --git a/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.h b/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.h +new file mode 100644 +index 000000000000..b6fdf70c22fb +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.h +@@ -0,0 +1,60 @@ ++/* ++ * Copyright (C) 2006-2018, Marvell International Ltd. ++ * ++ * This software file (the "File") is distributed by Marvell International ++ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 ++ * (the "License"). You may use, redistribute and/or modify this File in ++ * accordance with the terms and conditions of the License, a copy of which ++ * is available by writing to the Free Software Foundation, Inc. ++ * ++ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ++ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about ++ * this warranty disclaimer. ++ */ ++ ++/* Description: This file defines vendor constants and register function. */ ++ ++#ifndef _VENDOR_CMD_H_ ++#define _VENDOR_CMD_H_ ++ ++#ifdef __KERNEL__ ++void vendor_cmd_register(struct wiphy *wiphy); ++void vendor_cmd_basic_event(struct wiphy *wiphy, int event_idx); ++#endif ++ ++#define MRVL_OUI 0x005043 ++ ++enum mwl_vendor_commands { ++ MWL_VENDOR_CMD_SET_BF_TYPE, ++ MWL_VENDOR_CMD_GET_BF_TYPE, ++ ++ /* add commands here, update the command in vendor_cmd.c */ ++ ++ __MWL_VENDOR_CMD_AFTER_LAST, ++ NUM_MWL_VENDOR_CMD = __MWL_VENDOR_CMD_AFTER_LAST, ++ MWL_VENDOR_CMD_MAX = __MWL_VENDOR_CMD_AFTER_LAST - 1 ++}; ++ ++enum mwl_vendor_attributes { ++ MWL_VENDOR_ATTR_NOT_USE, ++ MWL_VENDOR_ATTR_BF_TYPE, ++ ++ /* add attributes here, update the policy in vendor_cmd.c */ ++ ++ __MWL_VENDOR_ATTR_AFTER_LAST, ++ NUM_MWL_VENDOR_ATTR = __MWL_VENDOR_ATTR_AFTER_LAST, ++ MWL_VENDOR_ATTR_MAX = __MWL_VENDOR_ATTR_AFTER_LAST - 1 ++}; ++ ++enum mwl_vendor_events { ++ MWL_VENDOR_EVENT_DRIVER_READY, ++ MWL_VENDOR_EVENT_DRIVER_START_REMOVE, ++ MWL_VENDOR_EVENT_CMD_TIMEOUT, ++ ++ __MWL_VENDOR_EVENT_AFTER_LAST, ++ NUM_MWL_VENDOR_EVENT = __MWL_VENDOR_EVENT_AFTER_LAST, ++ MWL_VENDOR_EVENT_MAX = __MWL_VENDOR_EVENT_AFTER_LAST - 1 ++}; ++ ++#endif /* _VENDOR_CMD_H_ */ +-- +2.17.1 + diff --git a/linux_surface/patches/bridge-stp-helper.patch b/linux_surface/patches/bridge-stp-helper.patch new file mode 100644 index 0000000..70d0f94 --- /dev/null +++ b/linux_surface/patches/bridge-stp-helper.patch @@ -0,0 +1,13 @@ +diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h +index aea3d13..8fcbf81 100644 +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -39,7 +39,7 @@ + #define BR_GROUPFWD_8021AD 0xB801u + + /* Path to usermode spanning tree program */ +-#define BR_STP_PROG "/sbin/bridge-stp" ++#define BR_STP_PROG "/run/current-system/sw/bin/bridge-stp" + + typedef struct bridge_id bridge_id; + typedef struct mac_addr mac_addr; diff --git a/linux_surface/patches/config b/linux_surface/patches/config new file mode 100644 index 0000000..607cf7c --- /dev/null +++ b/linux_surface/patches/config @@ -0,0 +1,9569 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/x86 4.19.23 Kernel Configuration +# + +# +# Compiler: gcc (Ubuntu 8.2.0-7ubuntu1) 8.2.0 +# +CONFIG_CC_IS_GCC=y +CONFIG_GCC_VERSION=80200 +CONFIG_CLANG_VERSION=0 +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y +CONFIG_THREAD_INFO_IN_TASK=y + +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=32 +# CONFIG_COMPILE_TEST is not set +CONFIG_LOCALVERSION="-surface" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_BUILD_SALT="" +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_USELIB=y +CONFIG_AUDIT=y +CONFIG_HAVE_ARCH_AUDITSYSCALL=y +CONFIG_AUDITSYSCALL=y +CONFIG_AUDIT_WATCH=y +CONFIG_AUDIT_TREE=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_PENDING_IRQ=y +CONFIG_GENERIC_IRQ_MIGRATION=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_SIM=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR=y +CONFIG_GENERIC_IRQ_RESERVATION_MODE=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_SPARSE_IRQ=y +# CONFIG_GENERIC_IRQ_DEBUGFS is not set +CONFIG_CLOCKSOURCE_WATCHDOG=y +CONFIG_ARCH_CLOCKSOURCE_DATA=y +CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y +CONFIG_GENERIC_CMOS_UPDATE=y + +# +# Timers subsystem +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ_COMMON=y +# CONFIG_HZ_PERIODIC is not set +CONFIG_NO_HZ_IDLE=y +# CONFIG_NO_HZ_FULL is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set +# CONFIG_IRQ_TIME_ACCOUNTING is not set +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_CPU_ISOLATION=y + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_RCU_EXPERT is not set +CONFIG_SRCU=y +CONFIG_TREE_SRCU=y +CONFIG_TASKS_RCU=y +CONFIG_RCU_STALL_COMMON=y +CONFIG_RCU_NEED_SEGCBLIST=y +CONFIG_BUILD_BIN2C=y +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 +CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13 +CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y +CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y +CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y +CONFIG_ARCH_SUPPORTS_INT128=y +CONFIG_NUMA_BALANCING=y +CONFIG_NUMA_BALANCING_DEFAULT_ENABLED=y +CONFIG_CGROUPS=y +CONFIG_PAGE_COUNTER=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +# CONFIG_MEMCG_SWAP_ENABLED is not set +CONFIG_MEMCG_KMEM=y +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set +CONFIG_CGROUP_WRITEBACK=y +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_RDMA=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_BPF=y +# CONFIG_CGROUP_DEBUG is not set +CONFIG_SOCK_CGROUP_DATA=y +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +CONFIG_NET_NS=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_SCHED_AUTOGROUP=y +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_RD_XZ=y +CONFIG_RD_LZO=y +CONFIG_RD_LZ4=y +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_HAVE_UID16=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_HAVE_PCSPKR_PLATFORM=y +CONFIG_BPF=y +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_MULTIUSER=y +CONFIG_SGETMASK_SYSCALL=y +CONFIG_SYSFS_SYSCALL=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_FHANDLE=y +CONFIG_POSIX_TIMERS=y +CONFIG_PRINTK=y +CONFIG_PRINTK_NMI=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_PCSPKR_PLATFORM=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_FUTEX_PI=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_ADVISE_SYSCALLS=y +CONFIG_MEMBARRIER=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y +CONFIG_KALLSYMS_BASE_RELATIVE=y +CONFIG_BPF_SYSCALL=y +# CONFIG_BPF_JIT_ALWAYS_ON is not set +CONFIG_USERFAULTFD=y +CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y +CONFIG_RSEQ=y +# CONFIG_DEBUG_RSEQ is not set +# CONFIG_EMBEDDED is not set +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PC104=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +CONFIG_SLUB_MEMCG_SYSFS_ON=y +# CONFIG_COMPAT_BRK is not set +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +CONFIG_SLAB_MERGE_DEFAULT=y +CONFIG_SLAB_FREELIST_RANDOM=y +# CONFIG_SLAB_FREELIST_HARDENED is not set +CONFIG_SLUB_CPU_PARTIAL=y +CONFIG_SYSTEM_DATA_VERIFICATION=y +CONFIG_PROFILING=y +CONFIG_TRACEPOINTS=y +CONFIG_64BIT=y +CONFIG_X86_64=y +CONFIG_X86=y +CONFIG_INSTRUCTION_DECODER=y +CONFIG_OUTPUT_FORMAT="elf64-x86-64" +CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_MMU=y +CONFIG_ARCH_MMAP_RND_BITS_MIN=28 +CONFIG_ARCH_MMAP_RND_BITS_MAX=32 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16 +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_ARCH_MAY_HAVE_PC_FDC=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ARCH_HAS_CPU_RELAX=y +CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y +CONFIG_ARCH_HAS_FILTER_PGPROT=y +CONFIG_HAVE_SETUP_PER_CPU_AREA=y +CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y +CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +CONFIG_ZONE_DMA32=y +CONFIG_AUDIT_ARCH=y +CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y +CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y +CONFIG_HAVE_INTEL_TXT=y +CONFIG_X86_64_SMP=y +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_PGTABLE_LEVELS=4 +CONFIG_CC_HAS_SANE_STACKPROTECTOR=y + +# +# Processor type and features +# +CONFIG_ZONE_DMA=y +CONFIG_SMP=y +CONFIG_X86_FEATURE_NAMES=y +CONFIG_X86_X2APIC=y +CONFIG_X86_MPPARSE=y +# CONFIG_GOLDFISH is not set +CONFIG_RETPOLINE=y +# CONFIG_INTEL_RDT is not set +CONFIG_X86_EXTENDED_PLATFORM=y +CONFIG_X86_NUMACHIP=y +# CONFIG_X86_VSMP is not set +# CONFIG_X86_UV is not set +# CONFIG_X86_GOLDFISH is not set +# CONFIG_X86_INTEL_MID is not set +CONFIG_X86_INTEL_LPSS=y +CONFIG_X86_AMD_PLATFORM_DEVICE=y +CONFIG_IOSF_MBI=y +CONFIG_IOSF_MBI_DEBUG=y +CONFIG_X86_SUPPORTS_MEMORY_FAILURE=y +CONFIG_SCHED_OMIT_FRAME_POINTER=y +CONFIG_HYPERVISOR_GUEST=y +CONFIG_PARAVIRT=y +# CONFIG_PARAVIRT_DEBUG is not set +CONFIG_PARAVIRT_SPINLOCKS=y +# CONFIG_QUEUED_LOCK_STAT is not set +CONFIG_XEN=y +CONFIG_XEN_PV=y +CONFIG_XEN_PV_SMP=y +CONFIG_XEN_DOM0=y +CONFIG_XEN_PVHVM=y +CONFIG_XEN_PVHVM_SMP=y +CONFIG_XEN_512GB=y +CONFIG_XEN_SAVE_RESTORE=y +# CONFIG_XEN_DEBUG_FS is not set +CONFIG_XEN_PVH=y +CONFIG_KVM_GUEST=y +CONFIG_KVM_DEBUG_FS=y +# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set +CONFIG_PARAVIRT_CLOCK=y +# CONFIG_JAILHOUSE_GUEST is not set +CONFIG_NO_BOOTMEM=y +# CONFIG_MK8 is not set +# CONFIG_MPSC is not set +# CONFIG_MCORE2 is not set +# CONFIG_MATOM is not set +CONFIG_GENERIC_CPU=y +CONFIG_X86_INTERNODE_CACHE_SHIFT=6 +CONFIG_X86_L1_CACHE_SHIFT=6 +CONFIG_X86_TSC=y +CONFIG_X86_CMPXCHG64=y +CONFIG_X86_CMOV=y +CONFIG_X86_MINIMUM_CPU_FAMILY=64 +CONFIG_X86_DEBUGCTLMSR=y +CONFIG_PROCESSOR_SELECT=y +CONFIG_CPU_SUP_INTEL=y +CONFIG_CPU_SUP_AMD=y +CONFIG_CPU_SUP_CENTAUR=y +CONFIG_HPET_TIMER=y +CONFIG_HPET_EMULATE_RTC=y +CONFIG_DMI=y +CONFIG_GART_IOMMU=y +CONFIG_CALGARY_IOMMU=y +CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT=y +CONFIG_MAXSMP=y +CONFIG_NR_CPUS_RANGE_BEGIN=8192 +CONFIG_NR_CPUS_RANGE_END=8192 +CONFIG_NR_CPUS_DEFAULT=8192 +CONFIG_NR_CPUS=8192 +CONFIG_SCHED_SMT=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_MC_PRIO=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y +CONFIG_X86_MCE=y +CONFIG_X86_MCELOG_LEGACY=y +CONFIG_X86_MCE_INTEL=y +CONFIG_X86_MCE_AMD=y +CONFIG_X86_MCE_THRESHOLD=y +CONFIG_X86_MCE_INJECT=m +CONFIG_X86_THERMAL_VECTOR=y + +# +# Performance monitoring +# +CONFIG_PERF_EVENTS_INTEL_UNCORE=y +CONFIG_PERF_EVENTS_INTEL_RAPL=m +CONFIG_PERF_EVENTS_INTEL_CSTATE=m +# CONFIG_PERF_EVENTS_AMD_POWER is not set +CONFIG_X86_16BIT=y +CONFIG_X86_ESPFIX64=y +CONFIG_X86_VSYSCALL_EMULATION=y +CONFIG_I8K=m +CONFIG_MICROCODE=y +CONFIG_MICROCODE_INTEL=y +CONFIG_MICROCODE_AMD=y +CONFIG_MICROCODE_OLD_INTERFACE=y +CONFIG_X86_MSR=m +CONFIG_X86_CPUID=m +# CONFIG_X86_5LEVEL is not set +CONFIG_X86_DIRECT_GBPAGES=y +CONFIG_ARCH_HAS_MEM_ENCRYPT=y +# CONFIG_AMD_MEM_ENCRYPT is not set +CONFIG_NUMA=y +CONFIG_AMD_NUMA=y +CONFIG_X86_64_ACPI_NUMA=y +CONFIG_NODES_SPAN_OTHER_NODES=y +# CONFIG_NUMA_EMU is not set +CONFIG_NODES_SHIFT=10 +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SPARSEMEM_DEFAULT=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_MEMORY_PROBE=y +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_X86_PMEM_LEGACY_DEVICE=y +CONFIG_X86_PMEM_LEGACY=y +CONFIG_X86_CHECK_BIOS_CORRUPTION=y +CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK=y +CONFIG_X86_RESERVE_LOW=64 +CONFIG_MTRR=y +CONFIG_MTRR_SANITIZER=y +CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=1 +CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=1 +CONFIG_X86_PAT=y +CONFIG_ARCH_USES_PG_UNCACHED=y +CONFIG_ARCH_RANDOM=y +CONFIG_X86_SMAP=y +CONFIG_X86_INTEL_UMIP=y +CONFIG_X86_INTEL_MPX=y +CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS=y +CONFIG_EFI=y +CONFIG_EFI_STUB=y +CONFIG_EFI_MIXED=y +CONFIG_SECCOMP=y +# CONFIG_HZ_100 is not set +CONFIG_HZ_250=y +# CONFIG_HZ_300 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=250 +CONFIG_SCHED_HRTICK=y +CONFIG_KEXEC=y +CONFIG_KEXEC_FILE=y +CONFIG_ARCH_HAS_KEXEC_PURGATORY=y +CONFIG_KEXEC_VERIFY_SIG=y +CONFIG_KEXEC_BZIMAGE_VERIFY_SIG=y +CONFIG_CRASH_DUMP=y +CONFIG_KEXEC_JUMP=y +CONFIG_PHYSICAL_START=0x1000000 +CONFIG_RELOCATABLE=y +CONFIG_RANDOMIZE_BASE=y +CONFIG_X86_NEED_RELOCS=y +CONFIG_PHYSICAL_ALIGN=0x200000 +CONFIG_DYNAMIC_MEMORY_LAYOUT=y +CONFIG_RANDOMIZE_MEMORY=y +CONFIG_RANDOMIZE_MEMORY_PHYSICAL_PADDING=0xa +CONFIG_HOTPLUG_CPU=y +# CONFIG_BOOTPARAM_HOTPLUG_CPU0 is not set +# CONFIG_DEBUG_HOTPLUG_CPU0 is not set +# CONFIG_COMPAT_VDSO is not set +CONFIG_LEGACY_VSYSCALL_EMULATE=y +# CONFIG_LEGACY_VSYSCALL_NONE is not set +# CONFIG_CMDLINE_BOOL is not set +CONFIG_MODIFY_LDT_SYSCALL=y +CONFIG_HAVE_LIVEPATCH=y +CONFIG_LIVEPATCH=y +CONFIG_ARCH_HAS_ADD_PAGES=y +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y +CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y +CONFIG_USE_PERCPU_NUMA_NODE_ID=y +CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y +CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y +CONFIG_ARCH_ENABLE_THP_MIGRATION=y + +# +# Power management and ACPI options +# +CONFIG_ARCH_HIBERNATION_HEADER=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +# CONFIG_SUSPEND_SKIP_SYNC is not set +CONFIG_HIBERNATE_CALLBACKS=y +CONFIG_HIBERNATION=y +CONFIG_PM_STD_PARTITION="" +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +# CONFIG_PM_AUTOSLEEP is not set +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=100 +CONFIG_PM_WAKELOCKS_GC=y +CONFIG_PM=y +CONFIG_PM_DEBUG=y +CONFIG_PM_ADVANCED_DEBUG=y +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_PM_SLEEP_DEBUG=y +# CONFIG_DPM_WATCHDOG is not set +CONFIG_PM_TRACE=y +CONFIG_PM_TRACE_RTC=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +CONFIG_PM_GENERIC_DOMAINS_SLEEP=y +CONFIG_ARCH_SUPPORTS_ACPI=y +CONFIG_ACPI=y +CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y +CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y +CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y +# CONFIG_ACPI_DEBUGGER is not set +CONFIG_ACPI_SPCR_TABLE=y +CONFIG_ACPI_LPIT=y +CONFIG_ACPI_SLEEP=y +# CONFIG_ACPI_PROCFS_POWER is not set +CONFIG_ACPI_REV_OVERRIDE_POSSIBLE=y +CONFIG_ACPI_EC_DEBUGFS=m +CONFIG_ACPI_AC=y +CONFIG_ACPI_BATTERY=m +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_VIDEO=m +CONFIG_ACPI_FAN=y +# CONFIG_ACPI_TAD is not set +CONFIG_ACPI_DOCK=y +CONFIG_ACPI_CPU_FREQ_PSS=y +CONFIG_ACPI_PROCESSOR_CSTATE=y +CONFIG_ACPI_PROCESSOR_IDLE=y +CONFIG_ACPI_CPPC_LIB=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_IPMI=m +CONFIG_ACPI_HOTPLUG_CPU=y +CONFIG_ACPI_PROCESSOR_AGGREGATOR=m +CONFIG_ACPI_THERMAL=y +CONFIG_ACPI_NUMA=y +CONFIG_ACPI_CUSTOM_DSDT_FILE="" +CONFIG_ARCH_HAS_ACPI_TABLE_UPGRADE=y +CONFIG_ACPI_TABLE_UPGRADE=y +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_PCI_SLOT=y +CONFIG_ACPI_CONTAINER=y +CONFIG_ACPI_HOTPLUG_MEMORY=y +CONFIG_ACPI_HOTPLUG_IOAPIC=y +CONFIG_ACPI_SBS=m +CONFIG_ACPI_HED=y +# CONFIG_ACPI_CUSTOM_METHOD is not set +CONFIG_ACPI_BGRT=y +# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set +CONFIG_ACPI_NFIT=m +CONFIG_HAVE_ACPI_APEI=y +CONFIG_HAVE_ACPI_APEI_NMI=y +CONFIG_ACPI_APEI=y +CONFIG_ACPI_APEI_GHES=y +CONFIG_ACPI_APEI_MEMORY_FAILURE=y +CONFIG_ACPI_APEI_EINJ=m +# CONFIG_ACPI_APEI_ERST_DEBUG is not set +CONFIG_DPTF_POWER=m +CONFIG_ACPI_WATCHDOG=y +CONFIG_ACPI_EXTLOG=m +# CONFIG_PMIC_OPREGION is not set +CONFIG_ACPI_CONFIGFS=m +CONFIG_X86_PM_TIMER=y +CONFIG_SFI=y + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y + +# +# CPU frequency scaling drivers +# +CONFIG_X86_INTEL_PSTATE=y +CONFIG_X86_PCC_CPUFREQ=y +CONFIG_X86_ACPI_CPUFREQ=y +CONFIG_X86_ACPI_CPUFREQ_CPB=y +CONFIG_X86_POWERNOW_K8=y +CONFIG_X86_AMD_FREQ_SENSITIVITY=m +CONFIG_X86_SPEEDSTEP_CENTRINO=y +CONFIG_X86_P4_CLOCKMOD=m + +# +# shared options +# +CONFIG_X86_SPEEDSTEP_LIB=m + +# +# CPU Idle +# +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_INTEL_IDLE=y + +# +# Bus options (PCI etc.) +# +CONFIG_PCI=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_XEN=y +CONFIG_PCI_DOMAINS=y +CONFIG_MMCONF_FAM10H=y +# CONFIG_PCI_CNB20LE_QUIRK is not set +# CONFIG_PCIEPORTBUS is not set +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PCI_QUIRKS=y +# CONFIG_PCI_DEBUG is not set +CONFIG_PCI_REALLOC_ENABLE_AUTO=y +CONFIG_PCI_STUB=m +# CONFIG_PCI_PF_STUB is not set +CONFIG_XEN_PCIDEV_FRONTEND=m +CONFIG_PCI_ATS=y +CONFIG_PCI_LOCKLESS_CONFIG=y +CONFIG_PCI_IOV=y +CONFIG_PCI_PRI=y +CONFIG_PCI_PASID=y +CONFIG_PCI_LABEL=y +CONFIG_PCI_HYPERV=m +CONFIG_HOTPLUG_PCI=y +CONFIG_HOTPLUG_PCI_ACPI=y +CONFIG_HOTPLUG_PCI_ACPI_IBM=m +CONFIG_HOTPLUG_PCI_CPCI=y +CONFIG_HOTPLUG_PCI_CPCI_ZT5550=m +CONFIG_HOTPLUG_PCI_CPCI_GENERIC=m +# CONFIG_HOTPLUG_PCI_SHPC is not set + +# +# PCI controller drivers +# + +# +# Cadence PCIe controllers support +# +CONFIG_VMD=m + +# +# DesignWare PCI Core Support +# +# CONFIG_PCIE_DW_PLAT_HOST is not set +# CONFIG_PCIE_DW_PLAT_EP is not set + +# +# PCI Endpoint +# +CONFIG_PCI_ENDPOINT=y +CONFIG_PCI_ENDPOINT_CONFIGFS=y +# CONFIG_PCI_EPF_TEST is not set + +# +# PCI switch controller drivers +# +CONFIG_PCI_SW_SWITCHTEC=m +CONFIG_ISA_BUS=y +CONFIG_ISA_DMA_API=y +CONFIG_AMD_NB=y +CONFIG_PCCARD=m +CONFIG_PCMCIA=m +CONFIG_PCMCIA_LOAD_CIS=y +CONFIG_CARDBUS=y + +# +# PC-card bridges +# +CONFIG_YENTA=m +CONFIG_YENTA_O2=y +CONFIG_YENTA_RICOH=y +CONFIG_YENTA_TI=y +CONFIG_YENTA_ENE_TUNE=y +CONFIG_YENTA_TOSHIBA=y +CONFIG_PD6729=m +CONFIG_I82092=m +CONFIG_PCCARD_NONSTATIC=y +CONFIG_RAPIDIO=y +CONFIG_RAPIDIO_DISC_TIMEOUT=30 +# CONFIG_RAPIDIO_ENABLE_RX_TX_PORTS is not set +CONFIG_RAPIDIO_DMA_ENGINE=y +# CONFIG_RAPIDIO_DEBUG is not set +CONFIG_RAPIDIO_ENUM_BASIC=m +CONFIG_RAPIDIO_CHMAN=m +CONFIG_RAPIDIO_MPORT_CDEV=m + +# +# RapidIO Switch drivers +# +CONFIG_RAPIDIO_TSI57X=m +CONFIG_RAPIDIO_CPS_XX=m +CONFIG_RAPIDIO_TSI568=m +CONFIG_RAPIDIO_CPS_GEN2=m +CONFIG_RAPIDIO_RXS_GEN3=m +# CONFIG_X86_SYSFB is not set + +# +# Binary Emulations +# +CONFIG_IA32_EMULATION=y +# CONFIG_IA32_AOUT is not set +CONFIG_X86_X32=y +CONFIG_COMPAT_32=y +CONFIG_COMPAT=y +CONFIG_COMPAT_FOR_U64_ALIGNMENT=y +CONFIG_SYSVIPC_COMPAT=y +CONFIG_X86_DEV_DMA_OPS=y +CONFIG_HAVE_GENERIC_GUP=y + +# +# Firmware Drivers +# +CONFIG_EDD=y +CONFIG_EDD_OFF=y +CONFIG_FIRMWARE_MEMMAP=y +CONFIG_DELL_RBU=m +CONFIG_DCDBAS=m +CONFIG_DMIID=y +CONFIG_DMI_SYSFS=m +CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK=y +CONFIG_ISCSI_IBFT_FIND=y +CONFIG_ISCSI_IBFT=m +CONFIG_FW_CFG_SYSFS=m +# CONFIG_FW_CFG_SYSFS_CMDLINE is not set +# CONFIG_GOOGLE_FIRMWARE is not set + +# +# EFI (Extensible Firmware Interface) Support +# +CONFIG_EFI_VARS=y +CONFIG_EFI_ESRT=y +CONFIG_EFI_VARS_PSTORE=m +# CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE is not set +CONFIG_EFI_RUNTIME_MAP=y +# CONFIG_EFI_FAKE_MEMMAP is not set +CONFIG_EFI_RUNTIME_WRAPPERS=y +CONFIG_EFI_BOOTLOADER_CONTROL=m +CONFIG_EFI_CAPSULE_LOADER=m +CONFIG_EFI_TEST=m +CONFIG_APPLE_PROPERTIES=y +# CONFIG_RESET_ATTACK_MITIGATION is not set +CONFIG_UEFI_CPER=y +CONFIG_UEFI_CPER_X86=y +CONFIG_EFI_DEV_PATH_PARSER=y + +# +# Tegra firmware driver +# +CONFIG_HAVE_KVM=y +CONFIG_HAVE_KVM_IRQCHIP=y +CONFIG_HAVE_KVM_IRQFD=y +CONFIG_HAVE_KVM_IRQ_ROUTING=y +CONFIG_HAVE_KVM_EVENTFD=y +CONFIG_KVM_MMIO=y +CONFIG_KVM_ASYNC_PF=y +CONFIG_HAVE_KVM_MSI=y +CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT=y +CONFIG_KVM_VFIO=y +CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT=y +CONFIG_KVM_COMPAT=y +CONFIG_HAVE_KVM_IRQ_BYPASS=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=m +CONFIG_KVM_INTEL=m +CONFIG_KVM_AMD=m +CONFIG_KVM_AMD_SEV=y +# CONFIG_KVM_MMU_AUDIT is not set +CONFIG_VHOST_NET=m +CONFIG_VHOST_SCSI=m +CONFIG_VHOST_VSOCK=m +CONFIG_VHOST=m +# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set + +# +# General architecture-dependent options +# +CONFIG_CRASH_CORE=y +CONFIG_KEXEC_CORE=y +CONFIG_HOTPLUG_SMT=y +CONFIG_OPROFILE=m +# CONFIG_OPROFILE_EVENT_MULTIPLEX is not set +CONFIG_HAVE_OPROFILE=y +CONFIG_OPROFILE_NMI_TIMER=y +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +# CONFIG_STATIC_KEYS_SELFTEST is not set +CONFIG_OPTPROBES=y +CONFIG_KPROBES_ON_FTRACE=y +CONFIG_UPROBES=y +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_KRETPROBES=y +CONFIG_USER_RETURN_NOTIFIER=y +CONFIG_HAVE_IOREMAP_PROT=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_KPROBES_ON_FTRACE=y +CONFIG_HAVE_FUNCTION_ERROR_INJECTION=y +CONFIG_HAVE_NMI=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_ARCH_HAS_FORTIFY_SOURCE=y +CONFIG_ARCH_HAS_SET_MEMORY=y +CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y +CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_RSEQ=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y +CONFIG_HAVE_USER_RETURN_NOTIFIER=y +CONFIG_HAVE_PERF_EVENTS_NMI=y +CONFIG_HAVE_HARDLOCKUP_DETECTOR_PERF=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_RCU_TABLE_FREE=y +CONFIG_HAVE_RCU_TABLE_INVALIDATE=y +CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y +CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y +CONFIG_HAVE_CMPXCHG_LOCAL=y +CONFIG_HAVE_CMPXCHG_DOUBLE=y +CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION=y +CONFIG_ARCH_WANT_OLD_COMPAT_IPC=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_SECCOMP_FILTER=y +CONFIG_HAVE_STACKPROTECTOR=y +CONFIG_CC_HAS_STACKPROTECTOR_NONE=y +CONFIG_STACKPROTECTOR=y +CONFIG_STACKPROTECTOR_STRONG=y +CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD=y +CONFIG_HAVE_ARCH_HUGE_VMAP=y +CONFIG_HAVE_ARCH_SOFT_DIRTY=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_HAVE_ARCH_MMAP_RND_BITS=y +CONFIG_HAVE_EXIT_THREAD=y +CONFIG_ARCH_MMAP_RND_BITS=28 +CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS=y +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=8 +CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES=y +CONFIG_HAVE_COPY_THREAD_TLS=y +CONFIG_HAVE_STACK_VALIDATION=y +CONFIG_HAVE_RELIABLE_STACKTRACE=y +CONFIG_ISA_BUS_API=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_COMPAT_OLD_SIGACTION=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_HAVE_ARCH_VMAP_STACK=y +CONFIG_VMAP_STACK=y +CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y +CONFIG_STRICT_KERNEL_RWX=y +CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y +CONFIG_STRICT_MODULE_RWX=y +CONFIG_ARCH_HAS_REFCOUNT=y +# CONFIG_REFCOUNT_FULL is not set +CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y +CONFIG_PLUGIN_HOSTCC="" +CONFIG_HAVE_GCC_PLUGINS=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_MODULE_SIG=y +# CONFIG_MODULE_SIG_FORCE is not set +CONFIG_MODULE_SIG_ALL=y +# CONFIG_MODULE_SIG_SHA1 is not set +# CONFIG_MODULE_SIG_SHA224 is not set +# CONFIG_MODULE_SIG_SHA256 is not set +# CONFIG_MODULE_SIG_SHA384 is not set +CONFIG_MODULE_SIG_SHA512=y +CONFIG_MODULE_SIG_HASH="sha512" +# CONFIG_MODULE_COMPRESS is not set +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_BLOCK=y +CONFIG_BLK_SCSI_REQUEST=y +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_BSGLIB=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_BLK_DEV_ZONED=y +CONFIG_BLK_DEV_THROTTLING=y +# CONFIG_BLK_DEV_THROTTLING_LOW is not set +CONFIG_BLK_CMDLINE_PARSER=y +CONFIG_BLK_WBT=y +# CONFIG_BLK_CGROUP_IOLATENCY is not set +# CONFIG_BLK_WBT_SQ is not set +CONFIG_BLK_WBT_MQ=y +CONFIG_BLK_DEBUG_FS=y +CONFIG_BLK_DEBUG_FS_ZONED=y +CONFIG_BLK_SED_OPAL=y + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +CONFIG_AIX_PARTITION=y +CONFIG_OSF_PARTITION=y +CONFIG_AMIGA_PARTITION=y +CONFIG_ATARI_PARTITION=y +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +CONFIG_LDM_PARTITION=y +# CONFIG_LDM_DEBUG is not set +CONFIG_SGI_PARTITION=y +CONFIG_ULTRIX_PARTITION=y +CONFIG_SUN_PARTITION=y +CONFIG_KARMA_PARTITION=y +CONFIG_EFI_PARTITION=y +CONFIG_SYSV68_PARTITION=y +CONFIG_CMDLINE_PARTITION=y +CONFIG_BLOCK_COMPAT=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_MQ_VIRTIO=y +CONFIG_BLK_MQ_RDMA=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CFQ_GROUP_IOSCHED is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_MQ_IOSCHED_DEADLINE=m +CONFIG_MQ_IOSCHED_KYBER=m +CONFIG_IOSCHED_BFQ=m +CONFIG_BFQ_GROUP_IOSCHED=y +CONFIG_PREEMPT_NOTIFIERS=y +CONFIG_PADATA=y +CONFIG_ASN1=y +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +CONFIG_INLINE_READ_UNLOCK=y +CONFIG_INLINE_READ_UNLOCK_IRQ=y +CONFIG_INLINE_WRITE_UNLOCK=y +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_ARCH_USE_QUEUED_RWLOCKS=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_ARCH_HAS_SYNC_CORE_BEFORE_USERMODE=y +CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y +CONFIG_FREEZER=y + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +CONFIG_COMPAT_BINFMT_ELF=y +CONFIG_ELFCORE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_BINFMT_SCRIPT=y +CONFIG_BINFMT_MISC=m +CONFIG_COREDUMP=y + +# +# Memory Management options +# +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_SPARSEMEM=y +CONFIG_NEED_MULTIPLE_NODES=y +CONFIG_HAVE_MEMORY_PRESENT=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +CONFIG_MEMORY_ISOLATION=y +CONFIG_HAVE_BOOTMEM_INFO_NODE=y +CONFIG_MEMORY_HOTPLUG=y +CONFIG_MEMORY_HOTPLUG_SPARSE=y +CONFIG_MEMORY_HOTPLUG_DEFAULT_ONLINE=y +CONFIG_MEMORY_HOTREMOVE=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_MEMORY_BALLOON=y +CONFIG_BALLOON_COMPACTION=y +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_MMU_NOTIFIER=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 +CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y +CONFIG_MEMORY_FAILURE=y +CONFIG_HWPOISON_INJECT=m +CONFIG_TRANSPARENT_HUGEPAGE=y +# CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS is not set +CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y +CONFIG_ARCH_WANTS_THP_SWAP=y +CONFIG_THP_SWAP=y +CONFIG_TRANSPARENT_HUGE_PAGECACHE=y +CONFIG_CLEANCACHE=y +CONFIG_FRONTSWAP=y +CONFIG_CMA=y +# CONFIG_CMA_DEBUG is not set +# CONFIG_CMA_DEBUGFS is not set +CONFIG_CMA_AREAS=7 +CONFIG_MEM_SOFT_DIRTY=y +CONFIG_ZSWAP=y +CONFIG_ZPOOL=y +CONFIG_ZBUD=y +CONFIG_Z3FOLD=m +CONFIG_ZSMALLOC=y +CONFIG_PGTABLE_MAPPING=y +# CONFIG_ZSMALLOC_STAT is not set +CONFIG_GENERIC_EARLY_IOREMAP=y +# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set +CONFIG_IDLE_PAGE_TRACKING=y +CONFIG_ARCH_HAS_ZONE_DEVICE=y +CONFIG_ZONE_DEVICE=y +CONFIG_ARCH_HAS_HMM=y +CONFIG_DEV_PAGEMAP_OPS=y +# CONFIG_HMM_MIRROR is not set +# CONFIG_DEVICE_PRIVATE is not set +# CONFIG_DEVICE_PUBLIC is not set +CONFIG_FRAME_VECTOR=y +CONFIG_ARCH_USES_HIGH_VMA_FLAGS=y +CONFIG_ARCH_HAS_PKEYS=y +# CONFIG_PERCPU_STATS is not set +# CONFIG_GUP_BENCHMARK is not set +CONFIG_ARCH_HAS_PTE_SPECIAL=y +CONFIG_NET=y +CONFIG_COMPAT_NETLINK_MESSAGES=y +CONFIG_NET_INGRESS=y +CONFIG_NET_EGRESS=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_DIAG=m +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=m +CONFIG_TLS=m +# CONFIG_TLS_DEVICE is not set +CONFIG_XFRM=y +CONFIG_XFRM_OFFLOAD=y +CONFIG_XFRM_ALGO=m +CONFIG_XFRM_USER=m +# CONFIG_XFRM_INTERFACE is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +CONFIG_XFRM_STATISTICS=y +CONFIG_XFRM_IPCOMP=m +CONFIG_NET_KEY=m +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_SMC=m +CONFIG_SMC_DIAG=m +# CONFIG_XDP_SOCKETS is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_FIB_TRIE_STATS=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_ROUTE_CLASSID=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NET_IP_TUNNEL=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE_COMMON=y +CONFIG_IP_MROUTE=y +# CONFIG_IP_MROUTE_MULTIPLE_TABLES is not set +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +CONFIG_NET_IPVTI=m +CONFIG_NET_UDP_TUNNEL=m +CONFIG_NET_FOU=m +CONFIG_NET_FOU_IP_TUNNELS=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_ESP_OFFLOAD=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_XFRM_TUNNEL=m +CONFIG_INET_TUNNEL=m +CONFIG_INET_XFRM_MODE_TRANSPORT=m +CONFIG_INET_XFRM_MODE_TUNNEL=m +CONFIG_INET_XFRM_MODE_BEET=m +CONFIG_INET_DIAG=m +CONFIG_INET_TCP_DIAG=m +CONFIG_INET_UDP_DIAG=m +CONFIG_INET_RAW_DIAG=m +CONFIG_INET_DIAG_DESTROY=y +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_BIC=m +CONFIG_TCP_CONG_CUBIC=y +CONFIG_TCP_CONG_WESTWOOD=m +CONFIG_TCP_CONG_HTCP=m +CONFIG_TCP_CONG_HSTCP=m +CONFIG_TCP_CONG_HYBLA=m +CONFIG_TCP_CONG_VEGAS=m +CONFIG_TCP_CONG_NV=m +CONFIG_TCP_CONG_SCALABLE=m +CONFIG_TCP_CONG_LP=m +CONFIG_TCP_CONG_VENO=m +CONFIG_TCP_CONG_YEAH=m +CONFIG_TCP_CONG_ILLINOIS=m +CONFIG_TCP_CONG_DCTCP=m +CONFIG_TCP_CONG_CDG=m +CONFIG_TCP_CONG_BBR=m +CONFIG_DEFAULT_CUBIC=y +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="cubic" +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_ESP_OFFLOAD=m +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MIP6=m +CONFIG_IPV6_ILA=m +CONFIG_INET6_XFRM_TUNNEL=m +CONFIG_INET6_TUNNEL=m +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m +CONFIG_IPV6_VTI=m +CONFIG_IPV6_SIT=m +CONFIG_IPV6_SIT_6RD=y +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=m +CONFIG_IPV6_GRE=m +CONFIG_IPV6_FOU=m +CONFIG_IPV6_FOU_TUNNEL=m +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MROUTE=y +CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y +CONFIG_IPV6_PIMSM_V2=y +CONFIG_IPV6_SEG6_LWTUNNEL=y +CONFIG_IPV6_SEG6_HMAC=y +CONFIG_IPV6_SEG6_BPF=y +CONFIG_NETLABEL=y +CONFIG_NETWORK_SECMARK=y +CONFIG_NET_PTP_CLASSIFY=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=m + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_INGRESS=y +CONFIG_NETFILTER_NETLINK=m +CONFIG_NETFILTER_FAMILY_BRIDGE=y +CONFIG_NETFILTER_FAMILY_ARP=y +CONFIG_NETFILTER_NETLINK_ACCT=m +CONFIG_NETFILTER_NETLINK_QUEUE=m +CONFIG_NETFILTER_NETLINK_LOG=m +CONFIG_NETFILTER_NETLINK_OSF=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_LOG_COMMON=m +CONFIG_NF_LOG_NETDEV=m +CONFIG_NETFILTER_CONNCOUNT=m +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_ZONES=y +# CONFIG_NF_CONNTRACK_PROCFS is not set +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMEOUT=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CONNTRACK_LABELS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=m +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_BROADCAST=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_SNMP=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +CONFIG_NF_CT_NETLINK_TIMEOUT=m +CONFIG_NF_CT_NETLINK_HELPER=m +CONFIG_NETFILTER_NETLINK_GLUE_CT=y +CONFIG_NF_NAT=m +CONFIG_NF_NAT_NEEDED=y +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_AMANDA=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_NAT_IRC=m +CONFIG_NF_NAT_SIP=m +CONFIG_NF_NAT_TFTP=m +CONFIG_NF_NAT_REDIRECT=y +CONFIG_NETFILTER_SYNPROXY=m +CONFIG_NF_TABLES=m +# CONFIG_NF_TABLES_SET is not set +# CONFIG_NF_TABLES_INET is not set +# CONFIG_NF_TABLES_NETDEV is not set +CONFIG_NFT_NUMGEN=m +CONFIG_NFT_CT=m +CONFIG_NFT_COUNTER=m +# CONFIG_NFT_CONNLIMIT is not set +CONFIG_NFT_LOG=m +CONFIG_NFT_LIMIT=m +CONFIG_NFT_MASQ=m +CONFIG_NFT_REDIR=m +CONFIG_NFT_NAT=m +# CONFIG_NFT_TUNNEL is not set +CONFIG_NFT_OBJREF=m +CONFIG_NFT_QUEUE=m +CONFIG_NFT_QUOTA=m +CONFIG_NFT_REJECT=m +CONFIG_NFT_COMPAT=m +CONFIG_NFT_HASH=m +CONFIG_NFT_FIB=m +# CONFIG_NFT_SOCKET is not set +# CONFIG_NFT_OSF is not set +# CONFIG_NFT_TPROXY is not set +# CONFIG_NF_FLOW_TABLE is not set +CONFIG_NETFILTER_XTABLES=m + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=m +CONFIG_NETFILTER_XT_CONNMARK=m +CONFIG_NETFILTER_XT_SET=m + +# +# Xtables targets +# +CONFIG_NETFILTER_XT_TARGET_AUDIT=m +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m +CONFIG_NETFILTER_XT_TARGET_CT=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HL=m +CONFIG_NETFILTER_XT_TARGET_HMARK=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LED=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_NAT=m +CONFIG_NETFILTER_XT_TARGET_NETMAP=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +CONFIG_NETFILTER_XT_TARGET_RATEEST=m +CONFIG_NETFILTER_XT_TARGET_REDIRECT=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_TPROXY=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +CONFIG_NETFILTER_XT_TARGET_SECMARK=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m + +# +# Xtables matches +# +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_BPF=m +CONFIG_NETFILTER_XT_MATCH_CGROUP=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ECN=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_HL=m +CONFIG_NETFILTER_XT_MATCH_IPCOMP=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_IPVS=m +CONFIG_NETFILTER_XT_MATCH_L2TP=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_NFACCT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_SOCKET=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_SET=m +CONFIG_IP_SET_MAX=256 +CONFIG_IP_SET_BITMAP_IP=m +CONFIG_IP_SET_BITMAP_IPMAC=m +CONFIG_IP_SET_BITMAP_PORT=m +CONFIG_IP_SET_HASH_IP=m +CONFIG_IP_SET_HASH_IPMARK=m +CONFIG_IP_SET_HASH_IPPORT=m +CONFIG_IP_SET_HASH_IPPORTIP=m +CONFIG_IP_SET_HASH_IPPORTNET=m +CONFIG_IP_SET_HASH_IPMAC=m +CONFIG_IP_SET_HASH_MAC=m +CONFIG_IP_SET_HASH_NETPORTNET=m +CONFIG_IP_SET_HASH_NET=m +CONFIG_IP_SET_HASH_NETNET=m +CONFIG_IP_SET_HASH_NETPORT=m +CONFIG_IP_SET_HASH_NETIFACE=m +CONFIG_IP_SET_LIST_SET=m +CONFIG_IP_VS=m +CONFIG_IP_VS_IPV6=y +# CONFIG_IP_VS_DEBUG is not set +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_AH_ESP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +CONFIG_IP_VS_PROTO_SCTP=y + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_FO=m +CONFIG_IP_VS_OVF=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +# CONFIG_IP_VS_MH is not set +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS SH scheduler +# +CONFIG_IP_VS_SH_TAB_BITS=8 + +# +# IPVS MH scheduler +# +CONFIG_IP_VS_MH_TAB_INDEX=12 + +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m +CONFIG_IP_VS_NFCT=y +CONFIG_IP_VS_PE_SIP=m + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=m +CONFIG_NF_SOCKET_IPV4=m +CONFIG_NF_TPROXY_IPV4=m +CONFIG_NF_TABLES_IPV4=y +CONFIG_NFT_CHAIN_ROUTE_IPV4=m +CONFIG_NFT_REJECT_IPV4=m +CONFIG_NFT_DUP_IPV4=m +CONFIG_NFT_FIB_IPV4=m +CONFIG_NF_TABLES_ARP=y +CONFIG_NF_DUP_IPV4=m +CONFIG_NF_LOG_ARP=m +CONFIG_NF_LOG_IPV4=m +CONFIG_NF_REJECT_IPV4=m +CONFIG_NF_NAT_IPV4=m +CONFIG_NF_NAT_MASQUERADE_IPV4=y +CONFIG_NFT_CHAIN_NAT_IPV4=m +CONFIG_NFT_MASQ_IPV4=m +CONFIG_NFT_REDIR_IPV4=m +CONFIG_NF_NAT_SNMP_BASIC=m +CONFIG_NF_NAT_PROTO_GRE=m +CONFIG_NF_NAT_PPTP=m +CONFIG_NF_NAT_H323=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_RPFILTER=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_SYNPROXY=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_SECURITY=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_SOCKET_IPV6=m +CONFIG_NF_TPROXY_IPV6=m +CONFIG_NF_TABLES_IPV6=y +CONFIG_NFT_CHAIN_ROUTE_IPV6=m +CONFIG_NFT_CHAIN_NAT_IPV6=m +CONFIG_NFT_MASQ_IPV6=m +CONFIG_NFT_REDIR_IPV6=m +CONFIG_NFT_REJECT_IPV6=m +CONFIG_NFT_DUP_IPV6=m +CONFIG_NFT_FIB_IPV6=m +CONFIG_NF_DUP_IPV6=m +CONFIG_NF_REJECT_IPV6=m +CONFIG_NF_LOG_IPV6=m +CONFIG_NF_NAT_IPV6=m +CONFIG_NF_NAT_MASQUERADE_IPV6=y +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RPFILTER=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_MATCH_SRH=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_SYNPROXY=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_RAW=m +CONFIG_IP6_NF_SECURITY=m +CONFIG_IP6_NF_NAT=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_IP6_NF_TARGET_NPT=m +CONFIG_NF_DEFRAG_IPV6=m + +# +# DECnet: Netfilter Configuration +# +CONFIG_DECNET_NF_GRABULATOR=m +# CONFIG_NF_TABLES_BRIDGE is not set +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +# CONFIG_BPFILTER is not set +CONFIG_IP_DCCP=m +CONFIG_INET_DCCP_DIAG=m + +# +# DCCP CCIDs Configuration +# +# CONFIG_IP_DCCP_CCID2_DEBUG is not set +# CONFIG_IP_DCCP_CCID3 is not set + +# +# DCCP Kernel Hacking +# +# CONFIG_IP_DCCP_DEBUG is not set +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_OBJCNT is not set +# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_MD5 is not set +CONFIG_SCTP_DEFAULT_COOKIE_HMAC_SHA1=y +# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_NONE is not set +CONFIG_SCTP_COOKIE_HMAC_MD5=y +CONFIG_SCTP_COOKIE_HMAC_SHA1=y +CONFIG_INET_SCTP_DIAG=m +CONFIG_RDS=m +CONFIG_RDS_RDMA=m +CONFIG_RDS_TCP=m +# CONFIG_RDS_DEBUG is not set +CONFIG_TIPC=m +CONFIG_TIPC_MEDIA_IB=y +CONFIG_TIPC_MEDIA_UDP=y +CONFIG_TIPC_DIAG=m +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +# CONFIG_ATM_CLIP_NO_ICMP is not set +CONFIG_ATM_LANE=m +CONFIG_ATM_MPOA=m +CONFIG_ATM_BR2684=m +# CONFIG_ATM_BR2684_IPFILTER is not set +CONFIG_L2TP=m +CONFIG_L2TP_DEBUGFS=m +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=m +CONFIG_L2TP_ETH=m +CONFIG_STP=m +CONFIG_GARP=m +CONFIG_MRP=m +CONFIG_BRIDGE=m +CONFIG_BRIDGE_IGMP_SNOOPING=y +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_HAVE_NET_DSA=y +CONFIG_NET_DSA=m +CONFIG_NET_DSA_LEGACY=y +CONFIG_NET_DSA_TAG_BRCM=y +CONFIG_NET_DSA_TAG_BRCM_PREPEND=y +CONFIG_NET_DSA_TAG_DSA=y +CONFIG_NET_DSA_TAG_EDSA=y +CONFIG_NET_DSA_TAG_LAN9303=y +CONFIG_NET_DSA_TAG_MTK=y +CONFIG_NET_DSA_TAG_TRAILER=y +CONFIG_NET_DSA_TAG_QCA=y +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +CONFIG_DECNET=m +# CONFIG_DECNET_ROUTER is not set +CONFIG_LLC=m +CONFIG_LLC2=m +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=m +# CONFIG_IPDDP is not set +CONFIG_X25=m +CONFIG_LAPB=m +CONFIG_PHONET=m +CONFIG_6LOWPAN=m +# CONFIG_6LOWPAN_DEBUGFS is not set +CONFIG_6LOWPAN_NHC=m +CONFIG_6LOWPAN_NHC_DEST=m +CONFIG_6LOWPAN_NHC_FRAGMENT=m +CONFIG_6LOWPAN_NHC_HOP=m +CONFIG_6LOWPAN_NHC_IPV6=m +CONFIG_6LOWPAN_NHC_MOBILITY=m +CONFIG_6LOWPAN_NHC_ROUTING=m +CONFIG_6LOWPAN_NHC_UDP=m +# CONFIG_6LOWPAN_GHC_EXT_HDR_HOP is not set +# CONFIG_6LOWPAN_GHC_UDP is not set +# CONFIG_6LOWPAN_GHC_ICMPV6 is not set +# CONFIG_6LOWPAN_GHC_EXT_HDR_DEST is not set +# CONFIG_6LOWPAN_GHC_EXT_HDR_FRAG is not set +# CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE is not set +CONFIG_IEEE802154=m +# CONFIG_IEEE802154_NL802154_EXPERIMENTAL is not set +CONFIG_IEEE802154_SOCKET=m +CONFIG_IEEE802154_6LOWPAN=m +CONFIG_MAC802154=m +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFB=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +# CONFIG_NET_SCH_CBS is not set +# CONFIG_NET_SCH_ETF is not set +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_MQPRIO=m +# CONFIG_NET_SCH_SKBPRIO is not set +CONFIG_NET_SCH_CHOKE=m +CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=m +# CONFIG_NET_SCH_CAKE is not set +CONFIG_NET_SCH_FQ=m +CONFIG_NET_SCH_HHF=m +CONFIG_NET_SCH_PIE=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_SCH_PLUG=m +# CONFIG_NET_SCH_DEFAULT is not set + +# +# Classification +# +CONFIG_NET_CLS=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +# CONFIG_CLS_U32_PERF is not set +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_CLS_CGROUP=m +CONFIG_NET_CLS_BPF=m +CONFIG_NET_CLS_FLOWER=m +CONFIG_NET_CLS_MATCHALL=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_EMATCH_CANID=m +CONFIG_NET_EMATCH_IPSET=m +# CONFIG_NET_EMATCH_IPT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_SAMPLE=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_NET_ACT_CSUM=m +CONFIG_NET_ACT_VLAN=m +CONFIG_NET_ACT_BPF=m +CONFIG_NET_ACT_CONNMARK=m +CONFIG_NET_ACT_SKBMOD=m +# CONFIG_NET_ACT_IFE is not set +CONFIG_NET_ACT_TUNNEL_KEY=m +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +CONFIG_DCB=y +CONFIG_DNS_RESOLVER=y +CONFIG_BATMAN_ADV=m +# CONFIG_BATMAN_ADV_BATMAN_V is not set +CONFIG_BATMAN_ADV_BLA=y +CONFIG_BATMAN_ADV_DAT=y +CONFIG_BATMAN_ADV_NC=y +CONFIG_BATMAN_ADV_MCAST=y +# CONFIG_BATMAN_ADV_DEBUGFS is not set +CONFIG_OPENVSWITCH=m +CONFIG_OPENVSWITCH_GRE=m +CONFIG_OPENVSWITCH_VXLAN=m +CONFIG_OPENVSWITCH_GENEVE=m +CONFIG_VSOCKETS=m +CONFIG_VSOCKETS_DIAG=m +CONFIG_VMWARE_VMCI_VSOCKETS=m +CONFIG_VIRTIO_VSOCKETS=m +CONFIG_VIRTIO_VSOCKETS_COMMON=m +# CONFIG_HYPERV_VSOCKETS is not set +CONFIG_NETLINK_DIAG=m +CONFIG_MPLS=y +CONFIG_NET_MPLS_GSO=m +CONFIG_MPLS_ROUTING=m +CONFIG_MPLS_IPTUNNEL=m +CONFIG_NET_NSH=m +CONFIG_HSR=m +CONFIG_NET_SWITCHDEV=y +CONFIG_NET_L3_MASTER_DEV=y +CONFIG_NET_NCSI=y +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +CONFIG_CGROUP_NET_PRIO=y +CONFIG_CGROUP_NET_CLASSID=y +CONFIG_NET_RX_BUSY_POLL=y +CONFIG_BQL=y +CONFIG_BPF_JIT=y +# CONFIG_BPF_STREAM_PARSER is not set +CONFIG_NET_FLOW_LIMIT=y + +# +# Network testing +# +CONFIG_NET_PKTGEN=m +CONFIG_NET_DROP_MONITOR=m +CONFIG_HAMRADIO=y + +# +# Packet Radio protocols +# +CONFIG_AX25=m +CONFIG_AX25_DAMA_SLAVE=y +CONFIG_NETROM=m +CONFIG_ROSE=m + +# +# AX.25 network device drivers +# +CONFIG_MKISS=m +CONFIG_6PACK=m +CONFIG_BPQETHER=m +CONFIG_BAYCOM_SER_FDX=m +CONFIG_BAYCOM_SER_HDX=m +CONFIG_BAYCOM_PAR=m +CONFIG_YAM=m +CONFIG_CAN=m +CONFIG_CAN_RAW=m +CONFIG_CAN_BCM=m +CONFIG_CAN_GW=m + +# +# CAN Device Drivers +# +CONFIG_CAN_VCAN=m +CONFIG_CAN_VXCAN=m +CONFIG_CAN_SLCAN=m +CONFIG_CAN_DEV=m +CONFIG_CAN_CALC_BITTIMING=y +CONFIG_CAN_JANZ_ICAN3=m +CONFIG_CAN_C_CAN=m +CONFIG_CAN_C_CAN_PLATFORM=m +CONFIG_CAN_C_CAN_PCI=m +CONFIG_CAN_CC770=m +CONFIG_CAN_CC770_ISA=m +CONFIG_CAN_CC770_PLATFORM=m +CONFIG_CAN_IFI_CANFD=m +CONFIG_CAN_M_CAN=m +CONFIG_CAN_PEAK_PCIEFD=m +CONFIG_CAN_SJA1000=m +CONFIG_CAN_SJA1000_ISA=m +CONFIG_CAN_SJA1000_PLATFORM=m +CONFIG_CAN_EMS_PCMCIA=m +CONFIG_CAN_EMS_PCI=m +CONFIG_CAN_PEAK_PCMCIA=m +CONFIG_CAN_PEAK_PCI=m +CONFIG_CAN_PEAK_PCIEC=y +CONFIG_CAN_KVASER_PCI=m +CONFIG_CAN_PLX_PCI=m +CONFIG_CAN_SOFTING=m +CONFIG_CAN_SOFTING_CS=m + +# +# CAN SPI interfaces +# +CONFIG_CAN_HI311X=m +CONFIG_CAN_MCP251X=m + +# +# CAN USB interfaces +# +CONFIG_CAN_8DEV_USB=m +CONFIG_CAN_EMS_USB=m +CONFIG_CAN_ESD_USB2=m +CONFIG_CAN_GS_USB=m +CONFIG_CAN_KVASER_USB=m +CONFIG_CAN_MCBA_USB=m +CONFIG_CAN_PEAK_USB=m +# CONFIG_CAN_UCAN is not set +# CONFIG_CAN_DEBUG_DEVICES is not set +CONFIG_BT=m +CONFIG_BT_BREDR=y +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_CMTP=m +CONFIG_BT_HIDP=m +CONFIG_BT_HS=y +CONFIG_BT_LE=y +CONFIG_BT_6LOWPAN=m +CONFIG_BT_LEDS=y +# CONFIG_BT_SELFTEST is not set +CONFIG_BT_DEBUGFS=y + +# +# Bluetooth device drivers +# +CONFIG_BT_INTEL=m +CONFIG_BT_BCM=m +CONFIG_BT_RTL=m +CONFIG_BT_QCA=m +CONFIG_BT_HCIBTUSB=m +# CONFIG_BT_HCIBTUSB_AUTOSUSPEND is not set +CONFIG_BT_HCIBTUSB_BCM=y +CONFIG_BT_HCIBTUSB_RTL=y +CONFIG_BT_HCIBTSDIO=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_SERDEV=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_NOKIA=m +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_3WIRE=y +CONFIG_BT_HCIUART_INTEL=y +CONFIG_BT_HCIUART_BCM=y +# CONFIG_BT_HCIUART_RTL is not set +CONFIG_BT_HCIUART_QCA=y +CONFIG_BT_HCIUART_AG6XX=y +CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBPA10X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIDTL1=m +CONFIG_BT_HCIBT3C=m +CONFIG_BT_HCIBLUECARD=m +CONFIG_BT_HCIVHCI=m +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m +CONFIG_BT_ATH3K=m +CONFIG_BT_WILINK=m +# CONFIG_BT_MTKUART is not set +CONFIG_BT_HCIRSI=m +CONFIG_AF_RXRPC=m +CONFIG_AF_RXRPC_IPV6=y +# CONFIG_AF_RXRPC_INJECT_LOSS is not set +# CONFIG_AF_RXRPC_DEBUG is not set +CONFIG_RXKAD=y +CONFIG_AF_KCM=m +CONFIG_STREAM_PARSER=m +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=m +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_CERTIFICATION_ONUS is not set +CONFIG_CFG80211_REQUIRE_SIGNED_REGDB=y +CONFIG_CFG80211_USE_KERNEL_REGDB_KEYS=y +# CONFIG_CFG80211_DEFAULT_PS is not set +CONFIG_CFG80211_DEBUGFS=y +CONFIG_CFG80211_CRDA_SUPPORT=y +CONFIG_CFG80211_WEXT=y +CONFIG_CFG80211_WEXT_EXPORT=y +CONFIG_LIB80211=m +CONFIG_LIB80211_CRYPT_WEP=m +CONFIG_LIB80211_CRYPT_CCMP=m +CONFIG_LIB80211_CRYPT_TKIP=m +# CONFIG_LIB80211_DEBUG is not set +CONFIG_MAC80211=m +CONFIG_MAC80211_HAS_RC=y +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +# CONFIG_MAC80211_RC_MINSTREL_VHT is not set +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +CONFIG_MAC80211_MESH=y +CONFIG_MAC80211_LEDS=y +CONFIG_MAC80211_DEBUGFS=y +CONFIG_MAC80211_MESSAGE_TRACING=y +# CONFIG_MAC80211_DEBUG_MENU is not set +CONFIG_MAC80211_STA_HASH_MAX_SIZE=0 +CONFIG_WIMAX=m +CONFIG_WIMAX_DEBUG_LEVEL=8 +CONFIG_RFKILL=y +CONFIG_RFKILL_LEDS=y +CONFIG_RFKILL_INPUT=y +CONFIG_RFKILL_GPIO=m +CONFIG_NET_9P=m +CONFIG_NET_9P_VIRTIO=m +CONFIG_NET_9P_XEN=m +CONFIG_NET_9P_RDMA=m +# CONFIG_NET_9P_DEBUG is not set +CONFIG_CAIF=m +# CONFIG_CAIF_DEBUG is not set +CONFIG_CAIF_NETDEV=m +CONFIG_CAIF_USB=m +CONFIG_CEPH_LIB=m +# CONFIG_CEPH_LIB_PRETTYDEBUG is not set +CONFIG_CEPH_LIB_USE_DNS_RESOLVER=y +CONFIG_NFC=m +CONFIG_NFC_DIGITAL=m +CONFIG_NFC_NCI=m +CONFIG_NFC_NCI_SPI=m +CONFIG_NFC_NCI_UART=m +CONFIG_NFC_HCI=m +CONFIG_NFC_SHDLC=y + +# +# Near Field Communication (NFC) devices +# +CONFIG_NFC_TRF7970A=m +CONFIG_NFC_MEI_PHY=m +CONFIG_NFC_SIM=m +CONFIG_NFC_PORT100=m +CONFIG_NFC_FDP=m +CONFIG_NFC_FDP_I2C=m +CONFIG_NFC_PN544=m +CONFIG_NFC_PN544_I2C=m +CONFIG_NFC_PN544_MEI=m +CONFIG_NFC_PN533=m +CONFIG_NFC_PN533_USB=m +CONFIG_NFC_PN533_I2C=m +CONFIG_NFC_MICROREAD=m +CONFIG_NFC_MICROREAD_I2C=m +CONFIG_NFC_MICROREAD_MEI=m +CONFIG_NFC_MRVL=m +CONFIG_NFC_MRVL_USB=m +CONFIG_NFC_MRVL_UART=m +CONFIG_NFC_MRVL_I2C=m +CONFIG_NFC_MRVL_SPI=m +CONFIG_NFC_ST21NFCA=m +CONFIG_NFC_ST21NFCA_I2C=m +CONFIG_NFC_ST_NCI=m +CONFIG_NFC_ST_NCI_I2C=m +CONFIG_NFC_ST_NCI_SPI=m +CONFIG_NFC_NXP_NCI=m +CONFIG_NFC_NXP_NCI_I2C=m +CONFIG_NFC_S3FWRN5=m +CONFIG_NFC_S3FWRN5_I2C=m +CONFIG_NFC_ST95HF=m +CONFIG_PSAMPLE=m +CONFIG_NET_IFE=m +CONFIG_LWTUNNEL=y +CONFIG_LWTUNNEL_BPF=y +CONFIG_DST_CACHE=y +CONFIG_GRO_CELLS=y +CONFIG_NET_DEVLINK=m +CONFIG_MAY_USE_DEVLINK=m +CONFIG_PAGE_POOL=y +CONFIG_FAILOVER=m +CONFIG_HAVE_EBPF_JIT=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER=y +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y + +# +# Firmware loader +# +CONFIG_FW_LOADER=y +CONFIG_EXTRA_FIRMWARE="" +CONFIG_FW_LOADER_USER_HELPER=y +# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set +CONFIG_WANT_DEV_COREDUMP=y +CONFIG_ALLOW_DEV_COREDUMP=y +CONFIG_DEV_COREDUMP=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set +# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set +CONFIG_SYS_HYPERVISOR=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_SPI=y +CONFIG_REGMAP_SPMI=m +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_IRQ=y +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_DMA_FENCE_TRACE is not set +# CONFIG_DMA_CMA is not set + +# +# Bus devices +# +CONFIG_CONNECTOR=y +CONFIG_PROC_EVENTS=y +# CONFIG_GNSS is not set +CONFIG_MTD=m +# CONFIG_MTD_TESTS is not set +CONFIG_MTD_REDBOOT_PARTS=m +CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1 +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +CONFIG_MTD_CMDLINE_PARTS=m +CONFIG_MTD_AR7_PARTS=m + +# +# Partition parsers +# + +# +# User Modules And Translation Layers +# +CONFIG_MTD_BLKDEVS=m +CONFIG_MTD_BLOCK=m +CONFIG_MTD_BLOCK_RO=m +CONFIG_FTL=m +CONFIG_NFTL=m +CONFIG_NFTL_RW=y +CONFIG_INFTL=m +CONFIG_RFD_FTL=m +CONFIG_SSFDC=m +CONFIG_SM_FTL=m +CONFIG_MTD_OOPS=m +CONFIG_MTD_SWAP=m +# CONFIG_MTD_PARTITIONED_MASTER is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=m +CONFIG_MTD_JEDECPROBE=m +CONFIG_MTD_GEN_PROBE=m +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +CONFIG_MTD_CFI_INTELEXT=m +CONFIG_MTD_CFI_AMDSTD=m +CONFIG_MTD_CFI_STAA=m +CONFIG_MTD_CFI_UTIL=m +CONFIG_MTD_RAM=m +CONFIG_MTD_ROM=m +CONFIG_MTD_ABSENT=m + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +CONFIG_MTD_PHYSMAP=m +# CONFIG_MTD_PHYSMAP_COMPAT is not set +CONFIG_MTD_SBC_GXX=m +CONFIG_MTD_AMD76XROM=m +CONFIG_MTD_ICHXROM=m +CONFIG_MTD_ESB2ROM=m +CONFIG_MTD_CK804XROM=m +CONFIG_MTD_SCB2_FLASH=m +CONFIG_MTD_NETtel=m +CONFIG_MTD_L440GX=m +CONFIG_MTD_PCI=m +CONFIG_MTD_PCMCIA=m +# CONFIG_MTD_PCMCIA_ANONYMOUS is not set +# CONFIG_MTD_GPIO_ADDR is not set +CONFIG_MTD_INTEL_VR_NOR=m +CONFIG_MTD_PLATRAM=m +# CONFIG_MTD_LATCH_ADDR is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_PMC551=m +# CONFIG_MTD_PMC551_BUGFIX is not set +# CONFIG_MTD_PMC551_DEBUG is not set +CONFIG_MTD_DATAFLASH=m +# CONFIG_MTD_DATAFLASH_WRITE_VERIFY is not set +CONFIG_MTD_DATAFLASH_OTP=y +CONFIG_MTD_M25P80=m +CONFIG_MTD_MCHP23K256=m +CONFIG_MTD_SST25L=m +CONFIG_MTD_SLRAM=m +CONFIG_MTD_PHRAM=m +CONFIG_MTD_MTDRAM=m +CONFIG_MTDRAM_TOTAL_SIZE=4096 +CONFIG_MTDRAM_ERASE_SIZE=128 +CONFIG_MTD_BLOCK2MTD=m + +# +# Disk-On-Chip Device Drivers +# +CONFIG_MTD_DOCG3=m +CONFIG_BCH_CONST_M=14 +CONFIG_BCH_CONST_T=4 +CONFIG_MTD_ONENAND=m +CONFIG_MTD_ONENAND_VERIFY_WRITE=y +CONFIG_MTD_ONENAND_GENERIC=m +# CONFIG_MTD_ONENAND_OTP is not set +CONFIG_MTD_ONENAND_2X_PROGRAM=y +CONFIG_MTD_NAND_ECC=m +# CONFIG_MTD_NAND_ECC_SMC is not set +CONFIG_MTD_NAND=m +CONFIG_MTD_NAND_BCH=m +CONFIG_MTD_NAND_ECC_BCH=y +CONFIG_MTD_SM_COMMON=m +CONFIG_MTD_NAND_DENALI=m +CONFIG_MTD_NAND_DENALI_PCI=m +CONFIG_MTD_NAND_GPIO=m +CONFIG_MTD_NAND_RICOH=m +CONFIG_MTD_NAND_DISKONCHIP=m +# CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED is not set +CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS=0 +# CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE is not set +# CONFIG_MTD_NAND_DOCG4 is not set +CONFIG_MTD_NAND_CAFE=m +CONFIG_MTD_NAND_NANDSIM=m +CONFIG_MTD_NAND_PLATFORM=m +# CONFIG_MTD_SPI_NAND is not set + +# +# LPDDR & LPDDR2 PCM memory drivers +# +CONFIG_MTD_LPDDR=m +CONFIG_MTD_QINFO_PROBE=m +CONFIG_MTD_SPI_NOR=m +CONFIG_MTD_MT81xx_NOR=m +CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y +# CONFIG_SPI_INTEL_SPI_PCI is not set +# CONFIG_SPI_INTEL_SPI_PLATFORM is not set +CONFIG_MTD_UBI=m +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_FASTMAP=y +CONFIG_MTD_UBI_GLUEBI=m +CONFIG_MTD_UBI_BLOCK=y +# CONFIG_OF is not set +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_PARPORT=m +CONFIG_PARPORT_PC=m +CONFIG_PARPORT_SERIAL=m +CONFIG_PARPORT_PC_FIFO=y +# CONFIG_PARPORT_PC_SUPERIO is not set +CONFIG_PARPORT_PC_PCMCIA=m +CONFIG_PARPORT_AX88796=m +CONFIG_PARPORT_1284=y +CONFIG_PARPORT_NOT_PC=y +CONFIG_PNP=y +# CONFIG_PNP_DEBUG_MESSAGES is not set + +# +# Protocols +# +CONFIG_PNPACPI=y +CONFIG_BLK_DEV=y +CONFIG_BLK_DEV_NULL_BLK=m +CONFIG_BLK_DEV_FD=m +CONFIG_CDROM=y +CONFIG_PARIDE=m + +# +# Parallel IDE high-level drivers +# +CONFIG_PARIDE_PD=m +CONFIG_PARIDE_PCD=m +CONFIG_PARIDE_PF=m +CONFIG_PARIDE_PT=m +CONFIG_PARIDE_PG=m + +# +# Parallel IDE protocol modules +# +CONFIG_PARIDE_ATEN=m +CONFIG_PARIDE_BPCK=m +CONFIG_PARIDE_COMM=m +CONFIG_PARIDE_DSTR=m +CONFIG_PARIDE_FIT2=m +CONFIG_PARIDE_FIT3=m +CONFIG_PARIDE_EPAT=m +CONFIG_PARIDE_EPATC8=y +CONFIG_PARIDE_EPIA=m +CONFIG_PARIDE_FRIQ=m +CONFIG_PARIDE_FRPW=m +CONFIG_PARIDE_KBIC=m +CONFIG_PARIDE_KTTI=m +CONFIG_PARIDE_ON20=m +CONFIG_PARIDE_ON26=m +CONFIG_BLK_DEV_PCIESSD_MTIP32XX=m +CONFIG_ZRAM=m +# CONFIG_ZRAM_WRITEBACK is not set +# CONFIG_ZRAM_MEMORY_TRACKING is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_UMEM=m +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +CONFIG_BLK_DEV_CRYPTOLOOP=m +CONFIG_BLK_DEV_DRBD=m +# CONFIG_DRBD_FAULT_INJECTION is not set +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_SKD=m +CONFIG_BLK_DEV_SX8=m +CONFIG_BLK_DEV_RAM=m +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_CDROM_PKTCDVD=m +CONFIG_CDROM_PKTCDVD_BUFFERS=8 +# CONFIG_CDROM_PKTCDVD_WCACHE is not set +CONFIG_ATA_OVER_ETH=m +CONFIG_XEN_BLKDEV_FRONTEND=y +CONFIG_XEN_BLKDEV_BACKEND=m +CONFIG_VIRTIO_BLK=m +CONFIG_VIRTIO_BLK_SCSI=y +CONFIG_BLK_DEV_RBD=m +CONFIG_BLK_DEV_RSXX=m + +# +# NVME Support +# +CONFIG_NVME_CORE=y +CONFIG_BLK_DEV_NVME=y +# CONFIG_NVME_MULTIPATH is not set +CONFIG_NVME_FABRICS=m +CONFIG_NVME_RDMA=m +CONFIG_NVME_FC=m +CONFIG_NVME_TARGET=m +CONFIG_NVME_TARGET_LOOP=m +CONFIG_NVME_TARGET_RDMA=m +CONFIG_NVME_TARGET_FC=m +# CONFIG_NVME_TARGET_FCLOOP is not set + +# +# Misc devices +# +CONFIG_SENSORS_LIS3LV02D=m +CONFIG_AD525X_DPOT=m +CONFIG_AD525X_DPOT_I2C=m +CONFIG_AD525X_DPOT_SPI=m +CONFIG_DUMMY_IRQ=m +CONFIG_IBM_ASM=m +CONFIG_PHANTOM=m +CONFIG_SGI_IOC4=m +CONFIG_TIFM_CORE=m +CONFIG_TIFM_7XX1=m +CONFIG_ICS932S401=m +CONFIG_ENCLOSURE_SERVICES=m +CONFIG_HP_ILO=m +CONFIG_APDS9802ALS=m +CONFIG_ISL29003=m +CONFIG_ISL29020=m +CONFIG_SENSORS_TSL2550=m +CONFIG_SENSORS_BH1770=m +CONFIG_SENSORS_APDS990X=m +CONFIG_HMC6352=m +CONFIG_DS1682=m +CONFIG_VMWARE_BALLOON=m +CONFIG_USB_SWITCH_FSA9480=m +CONFIG_LATTICE_ECP3_CONFIG=m +CONFIG_SRAM=y +# CONFIG_PCI_ENDPOINT_TEST is not set +CONFIG_MISC_RTSX=m +CONFIG_C2PORT=m +CONFIG_C2PORT_DURAMAR_2150=m + +# +# EEPROM support +# +CONFIG_EEPROM_AT24=m +CONFIG_EEPROM_AT25=m +CONFIG_EEPROM_LEGACY=m +CONFIG_EEPROM_MAX6875=m +CONFIG_EEPROM_93CX6=m +CONFIG_EEPROM_93XX46=m +CONFIG_EEPROM_IDT_89HPESX=m +CONFIG_CB710_CORE=m +# CONFIG_CB710_DEBUG is not set +CONFIG_CB710_DEBUG_ASSUMPTIONS=y + +# +# Texas Instruments shared transport line discipline +# +CONFIG_TI_ST=m +CONFIG_SENSORS_LIS3_I2C=m +CONFIG_ALTERA_STAPL=m +CONFIG_INTEL_MEI=m +CONFIG_INTEL_MEI_ME=m +CONFIG_INTEL_MEI_TXE=m +CONFIG_INTEL_IPTS=m +CONFIG_VMWARE_VMCI=m + +# +# Intel MIC & related support +# + +# +# Intel MIC Bus Driver +# +CONFIG_INTEL_MIC_BUS=m + +# +# SCIF Bus Driver +# +CONFIG_SCIF_BUS=m + +# +# VOP Bus Driver +# +CONFIG_VOP_BUS=m + +# +# Intel MIC Host Driver +# +CONFIG_INTEL_MIC_HOST=m + +# +# Intel MIC Card Driver +# +CONFIG_INTEL_MIC_CARD=m + +# +# SCIF Driver +# +CONFIG_SCIF=m + +# +# Intel MIC Coprocessor State Management (COSM) Drivers +# +CONFIG_MIC_COSM=m + +# +# VOP Driver +# +CONFIG_VOP=m +CONFIG_VHOST_RING=m +CONFIG_GENWQE=m +CONFIG_GENWQE_PLATFORM_ERROR_RECOVERY=0 +CONFIG_ECHO=m +CONFIG_MISC_RTSX_PCI=m +# CONFIG_MISC_RTSX_USB is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +CONFIG_RAID_ATTRS=m +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +CONFIG_SCSI_NETLINK=y +CONFIG_SCSI_MQ_DEFAULT=y +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_OSST=m +CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=m +CONFIG_SCSI_ENCLOSURE=m +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y + +# +# SCSI Transports +# +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m +CONFIG_SCSI_ISCSI_ATTRS=m +CONFIG_SCSI_SAS_ATTRS=m +CONFIG_SCSI_SAS_LIBSAS=m +CONFIG_SCSI_SAS_ATA=y +CONFIG_SCSI_SAS_HOST_SMP=y +CONFIG_SCSI_SRP_ATTRS=m +CONFIG_SCSI_LOWLEVEL=y +CONFIG_ISCSI_TCP=m +CONFIG_ISCSI_BOOT_SYSFS=m +CONFIG_SCSI_CXGB3_ISCSI=m +CONFIG_SCSI_CXGB4_ISCSI=m +CONFIG_SCSI_BNX2_ISCSI=m +CONFIG_SCSI_BNX2X_FCOE=m +CONFIG_BE2ISCSI=m +CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_SCSI_HPSA=m +CONFIG_SCSI_3W_9XXX=m +CONFIG_SCSI_3W_SAS=m +CONFIG_SCSI_ACARD=m +CONFIG_SCSI_AACRAID=m +CONFIG_SCSI_AIC7XXX=m +CONFIG_AIC7XXX_CMDS_PER_DEVICE=8 +CONFIG_AIC7XXX_RESET_DELAY_MS=5000 +# CONFIG_AIC7XXX_DEBUG_ENABLE is not set +CONFIG_AIC7XXX_DEBUG_MASK=0 +CONFIG_AIC7XXX_REG_PRETTY_PRINT=y +CONFIG_SCSI_AIC79XX=m +CONFIG_AIC79XX_CMDS_PER_DEVICE=32 +CONFIG_AIC79XX_RESET_DELAY_MS=5000 +# CONFIG_AIC79XX_DEBUG_ENABLE is not set +CONFIG_AIC79XX_DEBUG_MASK=0 +CONFIG_AIC79XX_REG_PRETTY_PRINT=y +CONFIG_SCSI_AIC94XX=m +# CONFIG_AIC94XX_DEBUG is not set +CONFIG_SCSI_MVSAS=m +# CONFIG_SCSI_MVSAS_DEBUG is not set +# CONFIG_SCSI_MVSAS_TASKLET is not set +CONFIG_SCSI_MVUMI=m +CONFIG_SCSI_DPT_I2O=m +CONFIG_SCSI_ADVANSYS=m +CONFIG_SCSI_ARCMSR=m +CONFIG_SCSI_ESAS2R=m +CONFIG_MEGARAID_NEWGEN=y +CONFIG_MEGARAID_MM=m +CONFIG_MEGARAID_MAILBOX=m +CONFIG_MEGARAID_LEGACY=m +CONFIG_MEGARAID_SAS=m +CONFIG_SCSI_MPT3SAS=m +CONFIG_SCSI_MPT2SAS_MAX_SGE=128 +CONFIG_SCSI_MPT3SAS_MAX_SGE=128 +CONFIG_SCSI_MPT2SAS=m +CONFIG_SCSI_SMARTPQI=m +CONFIG_SCSI_UFSHCD=m +CONFIG_SCSI_UFSHCD_PCI=m +CONFIG_SCSI_UFS_DWC_TC_PCI=m +CONFIG_SCSI_UFSHCD_PLATFORM=m +CONFIG_SCSI_UFS_DWC_TC_PLATFORM=m +CONFIG_SCSI_HPTIOP=m +CONFIG_SCSI_BUSLOGIC=m +CONFIG_SCSI_FLASHPOINT=y +CONFIG_VMWARE_PVSCSI=m +CONFIG_XEN_SCSI_FRONTEND=m +CONFIG_HYPERV_STORAGE=m +CONFIG_LIBFC=m +CONFIG_LIBFCOE=m +CONFIG_FCOE=m +CONFIG_FCOE_FNIC=m +CONFIG_SCSI_SNIC=m +# CONFIG_SCSI_SNIC_DEBUG_FS is not set +CONFIG_SCSI_DMX3191D=m +CONFIG_SCSI_GDTH=m +CONFIG_SCSI_ISCI=m +CONFIG_SCSI_IPS=m +CONFIG_SCSI_INITIO=m +CONFIG_SCSI_INIA100=m +CONFIG_SCSI_PPA=m +CONFIG_SCSI_IMM=m +# CONFIG_SCSI_IZIP_EPP16 is not set +# CONFIG_SCSI_IZIP_SLOW_CTR is not set +CONFIG_SCSI_STEX=m +CONFIG_SCSI_SYM53C8XX_2=m +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +CONFIG_SCSI_SYM53C8XX_MMIO=y +CONFIG_SCSI_IPR=m +CONFIG_SCSI_IPR_TRACE=y +CONFIG_SCSI_IPR_DUMP=y +CONFIG_SCSI_QLOGIC_1280=m +CONFIG_SCSI_QLA_FC=m +CONFIG_TCM_QLA2XXX=m +# CONFIG_TCM_QLA2XXX_DEBUG is not set +CONFIG_SCSI_QLA_ISCSI=m +CONFIG_QEDI=m +CONFIG_QEDF=m +CONFIG_SCSI_LPFC=m +# CONFIG_SCSI_LPFC_DEBUG_FS is not set +CONFIG_SCSI_DC395x=m +CONFIG_SCSI_AM53C974=m +CONFIG_SCSI_WD719X=m +CONFIG_SCSI_DEBUG=m +CONFIG_SCSI_PMCRAID=m +CONFIG_SCSI_PM8001=m +CONFIG_SCSI_BFA_FC=m +CONFIG_SCSI_VIRTIO=m +CONFIG_SCSI_CHELSIO_FCOE=m +CONFIG_SCSI_LOWLEVEL_PCMCIA=y +CONFIG_PCMCIA_AHA152X=m +CONFIG_PCMCIA_QLOGIC=m +CONFIG_PCMCIA_SYM53C500=m +CONFIG_SCSI_DH=y +CONFIG_SCSI_DH_RDAC=m +CONFIG_SCSI_DH_HP_SW=m +CONFIG_SCSI_DH_EMC=m +CONFIG_SCSI_DH_ALUA=m +CONFIG_SCSI_OSD_INITIATOR=m +CONFIG_SCSI_OSD_ULD=m +CONFIG_SCSI_OSD_DPRINT_SENSE=1 +# CONFIG_SCSI_OSD_DEBUG is not set +CONFIG_ATA=y +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_ATA_ACPI=y +CONFIG_SATA_ZPODD=y +CONFIG_SATA_PMP=y + +# +# Controllers with non-SFF native interface +# +CONFIG_SATA_AHCI=m +CONFIG_SATA_MOBILE_LPM_POLICY=0 +CONFIG_SATA_AHCI_PLATFORM=m +CONFIG_SATA_INIC162X=m +CONFIG_SATA_ACARD_AHCI=m +CONFIG_SATA_SIL24=m +CONFIG_ATA_SFF=y + +# +# SFF controllers with custom DMA interface +# +CONFIG_PDC_ADMA=m +CONFIG_SATA_QSTOR=m +CONFIG_SATA_SX4=m +CONFIG_ATA_BMDMA=y + +# +# SATA SFF controllers with BMDMA +# +CONFIG_ATA_PIIX=y +CONFIG_SATA_DWC=m +CONFIG_SATA_DWC_OLD_DMA=y +# CONFIG_SATA_DWC_DEBUG is not set +CONFIG_SATA_MV=m +CONFIG_SATA_NV=m +CONFIG_SATA_PROMISE=m +CONFIG_SATA_SIL=m +CONFIG_SATA_SIS=m +CONFIG_SATA_SVW=m +CONFIG_SATA_ULI=m +CONFIG_SATA_VIA=m +CONFIG_SATA_VITESSE=m + +# +# PATA SFF controllers with BMDMA +# +CONFIG_PATA_ALI=m +CONFIG_PATA_AMD=m +CONFIG_PATA_ARTOP=m +CONFIG_PATA_ATIIXP=m +CONFIG_PATA_ATP867X=m +CONFIG_PATA_CMD64X=m +CONFIG_PATA_CYPRESS=m +CONFIG_PATA_EFAR=m +CONFIG_PATA_HPT366=m +CONFIG_PATA_HPT37X=m +CONFIG_PATA_HPT3X2N=m +CONFIG_PATA_HPT3X3=m +# CONFIG_PATA_HPT3X3_DMA is not set +CONFIG_PATA_IT8213=m +CONFIG_PATA_IT821X=m +CONFIG_PATA_JMICRON=m +CONFIG_PATA_MARVELL=m +CONFIG_PATA_NETCELL=m +CONFIG_PATA_NINJA32=m +CONFIG_PATA_NS87415=m +CONFIG_PATA_OLDPIIX=m +CONFIG_PATA_OPTIDMA=m +CONFIG_PATA_PDC2027X=m +CONFIG_PATA_PDC_OLD=m +CONFIG_PATA_RADISYS=m +CONFIG_PATA_RDC=m +CONFIG_PATA_SCH=m +CONFIG_PATA_SERVERWORKS=m +CONFIG_PATA_SIL680=m +CONFIG_PATA_SIS=y +CONFIG_PATA_TOSHIBA=m +CONFIG_PATA_TRIFLEX=m +CONFIG_PATA_VIA=m +CONFIG_PATA_WINBOND=m + +# +# PIO-only SFF controllers +# +CONFIG_PATA_CMD640_PCI=m +CONFIG_PATA_MPIIX=m +CONFIG_PATA_NS87410=m +CONFIG_PATA_OPTI=m +CONFIG_PATA_PCMCIA=m +CONFIG_PATA_PLATFORM=m +CONFIG_PATA_RZ1000=m + +# +# Generic fallback / legacy drivers +# +CONFIG_PATA_ACPI=m +CONFIG_ATA_GENERIC=y +CONFIG_PATA_LEGACY=m +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_AUTODETECT=y +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID456=m +CONFIG_MD_MULTIPATH=m +CONFIG_MD_FAULTY=m +CONFIG_MD_CLUSTER=m +CONFIG_BCACHE=m +# CONFIG_BCACHE_DEBUG is not set +# CONFIG_BCACHE_CLOSURES_DEBUG is not set +CONFIG_BLK_DEV_DM_BUILTIN=y +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_MQ_DEFAULT is not set +# CONFIG_DM_DEBUG is not set +CONFIG_DM_BUFIO=m +# CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING is not set +CONFIG_DM_BIO_PRISON=m +CONFIG_DM_PERSISTENT_DATA=m +# CONFIG_DM_UNSTRIPED is not set +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_THIN_PROVISIONING=m +CONFIG_DM_CACHE=m +CONFIG_DM_CACHE_SMQ=m +# CONFIG_DM_WRITECACHE is not set +CONFIG_DM_ERA=m +CONFIG_DM_MIRROR=m +CONFIG_DM_LOG_USERSPACE=m +CONFIG_DM_RAID=m +CONFIG_DM_ZERO=m +CONFIG_DM_MULTIPATH=m +CONFIG_DM_MULTIPATH_QL=m +CONFIG_DM_MULTIPATH_ST=m +CONFIG_DM_DELAY=m +CONFIG_DM_UEVENT=y +CONFIG_DM_FLAKEY=m +CONFIG_DM_VERITY=m +# CONFIG_DM_VERITY_FEC is not set +CONFIG_DM_SWITCH=m +CONFIG_DM_LOG_WRITES=m +CONFIG_DM_INTEGRITY=m +CONFIG_DM_ZONED=m +CONFIG_TARGET_CORE=m +CONFIG_TCM_IBLOCK=m +CONFIG_TCM_FILEIO=m +CONFIG_TCM_PSCSI=m +CONFIG_TCM_USER2=m +CONFIG_LOOPBACK_TARGET=m +CONFIG_TCM_FC=m +CONFIG_ISCSI_TARGET=m +CONFIG_ISCSI_TARGET_CXGB4=m +CONFIG_SBP_TARGET=m +CONFIG_FUSION=y +CONFIG_FUSION_SPI=m +CONFIG_FUSION_FC=m +CONFIG_FUSION_SAS=m +CONFIG_FUSION_MAX_SGE=128 +CONFIG_FUSION_CTL=m +CONFIG_FUSION_LAN=m +CONFIG_FUSION_LOGGING=y + +# +# IEEE 1394 (FireWire) support +# +CONFIG_FIREWIRE=m +CONFIG_FIREWIRE_OHCI=m +CONFIG_FIREWIRE_SBP2=m +CONFIG_FIREWIRE_NET=m +CONFIG_FIREWIRE_NOSY=m +CONFIG_MACINTOSH_DRIVERS=y +CONFIG_MAC_EMUMOUSEBTN=m +CONFIG_NETDEVICES=y +CONFIG_MII=m +CONFIG_NET_CORE=y +CONFIG_BONDING=m +CONFIG_DUMMY=m +CONFIG_EQUALIZER=m +CONFIG_NET_FC=y +CONFIG_IFB=m +CONFIG_NET_TEAM=m +CONFIG_NET_TEAM_MODE_BROADCAST=m +CONFIG_NET_TEAM_MODE_ROUNDROBIN=m +CONFIG_NET_TEAM_MODE_RANDOM=m +CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=m +CONFIG_NET_TEAM_MODE_LOADBALANCE=m +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_IPVLAN=m +CONFIG_IPVTAP=m +CONFIG_VXLAN=m +CONFIG_GENEVE=m +CONFIG_GTP=m +CONFIG_MACSEC=m +CONFIG_NETCONSOLE=m +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_NETPOLL=y +CONFIG_NET_POLL_CONTROLLER=y +CONFIG_NTB_NETDEV=m +CONFIG_RIONET=m +CONFIG_RIONET_TX_SIZE=128 +CONFIG_RIONET_RX_SIZE=128 +CONFIG_TUN=y +CONFIG_TAP=m +# CONFIG_TUN_VNET_CROSS_LE is not set +CONFIG_VETH=m +CONFIG_VIRTIO_NET=m +CONFIG_NLMON=m +CONFIG_NET_VRF=m +CONFIG_VSOCKMON=m +CONFIG_SUNGEM_PHY=m +CONFIG_ARCNET=m +CONFIG_ARCNET_1201=m +CONFIG_ARCNET_1051=m +CONFIG_ARCNET_RAW=m +CONFIG_ARCNET_CAP=m +CONFIG_ARCNET_COM90xx=m +CONFIG_ARCNET_COM90xxIO=m +CONFIG_ARCNET_RIM_I=m +CONFIG_ARCNET_COM20020=m +CONFIG_ARCNET_COM20020_PCI=m +CONFIG_ARCNET_COM20020_CS=m +CONFIG_ATM_DRIVERS=y +CONFIG_ATM_DUMMY=m +CONFIG_ATM_TCP=m +CONFIG_ATM_LANAI=m +CONFIG_ATM_ENI=m +# CONFIG_ATM_ENI_DEBUG is not set +# CONFIG_ATM_ENI_TUNE_BURST is not set +CONFIG_ATM_FIRESTREAM=m +CONFIG_ATM_ZATM=m +# CONFIG_ATM_ZATM_DEBUG is not set +CONFIG_ATM_NICSTAR=m +# CONFIG_ATM_NICSTAR_USE_SUNI is not set +# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set +CONFIG_ATM_IDT77252=m +# CONFIG_ATM_IDT77252_DEBUG is not set +# CONFIG_ATM_IDT77252_RCV_ALL is not set +CONFIG_ATM_IDT77252_USE_SUNI=y +CONFIG_ATM_AMBASSADOR=m +# CONFIG_ATM_AMBASSADOR_DEBUG is not set +CONFIG_ATM_HORIZON=m +# CONFIG_ATM_HORIZON_DEBUG is not set +CONFIG_ATM_IA=m +# CONFIG_ATM_IA_DEBUG is not set +CONFIG_ATM_FORE200E=m +# CONFIG_ATM_FORE200E_USE_TASKLET is not set +CONFIG_ATM_FORE200E_TX_RETRY=16 +CONFIG_ATM_FORE200E_DEBUG=0 +CONFIG_ATM_HE=m +CONFIG_ATM_HE_USE_SUNI=y +CONFIG_ATM_SOLOS=m + +# +# CAIF transport drivers +# +CONFIG_CAIF_TTY=m +CONFIG_CAIF_SPI_SLAVE=m +# CONFIG_CAIF_SPI_SYNC is not set +CONFIG_CAIF_HSI=m +CONFIG_CAIF_VIRTIO=m + +# +# Distributed Switch Architecture drivers +# +CONFIG_B53=m +CONFIG_B53_SPI_DRIVER=m +CONFIG_B53_MDIO_DRIVER=m +CONFIG_B53_MMAP_DRIVER=m +CONFIG_B53_SRAB_DRIVER=m +# CONFIG_NET_DSA_BCM_SF2 is not set +# CONFIG_NET_DSA_LOOP is not set +CONFIG_NET_DSA_MT7530=m +CONFIG_NET_DSA_MV88E6060=m +# CONFIG_MICROCHIP_KSZ is not set +CONFIG_NET_DSA_MV88E6XXX=m +CONFIG_NET_DSA_MV88E6XXX_GLOBAL2=y +# CONFIG_NET_DSA_MV88E6XXX_PTP is not set +CONFIG_NET_DSA_QCA8K=m +# CONFIG_NET_DSA_REALTEK_SMI is not set +CONFIG_NET_DSA_SMSC_LAN9303=m +CONFIG_NET_DSA_SMSC_LAN9303_I2C=m +CONFIG_NET_DSA_SMSC_LAN9303_MDIO=m +CONFIG_ETHERNET=y +CONFIG_MDIO=m +CONFIG_NET_VENDOR_3COM=y +CONFIG_PCMCIA_3C574=m +CONFIG_PCMCIA_3C589=m +CONFIG_VORTEX=m +CONFIG_TYPHOON=m +CONFIG_NET_VENDOR_ADAPTEC=y +CONFIG_ADAPTEC_STARFIRE=m +CONFIG_NET_VENDOR_AGERE=y +CONFIG_ET131X=m +CONFIG_NET_VENDOR_ALACRITECH=y +CONFIG_SLICOSS=m +CONFIG_NET_VENDOR_ALTEON=y +CONFIG_ACENIC=m +# CONFIG_ACENIC_OMIT_TIGON_I is not set +CONFIG_ALTERA_TSE=m +CONFIG_NET_VENDOR_AMAZON=y +CONFIG_ENA_ETHERNET=m +CONFIG_NET_VENDOR_AMD=y +CONFIG_AMD8111_ETH=m +CONFIG_PCNET32=m +CONFIG_PCMCIA_NMCLAN=m +CONFIG_AMD_XGBE=m +CONFIG_AMD_XGBE_DCB=y +CONFIG_AMD_XGBE_HAVE_ECC=y +CONFIG_NET_VENDOR_AQUANTIA=y +CONFIG_AQTION=m +CONFIG_NET_VENDOR_ARC=y +CONFIG_NET_VENDOR_ATHEROS=y +CONFIG_ATL2=m +CONFIG_ATL1=m +CONFIG_ATL1E=m +CONFIG_ATL1C=m +CONFIG_ALX=m +CONFIG_NET_VENDOR_AURORA=y +CONFIG_AURORA_NB8800=m +CONFIG_NET_VENDOR_BROADCOM=y +CONFIG_B44=m +CONFIG_B44_PCI_AUTOSELECT=y +CONFIG_B44_PCICORE_AUTOSELECT=y +CONFIG_B44_PCI=y +# CONFIG_BCMGENET is not set +CONFIG_BNX2=m +CONFIG_CNIC=m +CONFIG_TIGON3=m +CONFIG_TIGON3_HWMON=y +CONFIG_BNX2X=m +CONFIG_BNX2X_SRIOV=y +# CONFIG_SYSTEMPORT is not set +CONFIG_BNXT=m +CONFIG_BNXT_SRIOV=y +CONFIG_BNXT_FLOWER_OFFLOAD=y +CONFIG_BNXT_DCB=y +CONFIG_BNXT_HWMON=y +CONFIG_NET_VENDOR_BROCADE=y +CONFIG_BNA=m +CONFIG_NET_VENDOR_CADENCE=y +CONFIG_MACB=m +CONFIG_MACB_USE_HWSTAMP=y +CONFIG_MACB_PCI=m +CONFIG_NET_VENDOR_CAVIUM=y +CONFIG_THUNDER_NIC_PF=m +CONFIG_THUNDER_NIC_VF=m +CONFIG_THUNDER_NIC_BGX=m +CONFIG_THUNDER_NIC_RGX=m +CONFIG_CAVIUM_PTP=y +CONFIG_LIQUIDIO=m +CONFIG_LIQUIDIO_VF=m +CONFIG_NET_VENDOR_CHELSIO=y +CONFIG_CHELSIO_T1=m +CONFIG_CHELSIO_T1_1G=y +CONFIG_CHELSIO_T3=m +CONFIG_CHELSIO_T4=m +CONFIG_CHELSIO_T4_DCB=y +CONFIG_CHELSIO_T4_FCOE=y +CONFIG_CHELSIO_T4VF=m +CONFIG_CHELSIO_LIB=m +CONFIG_NET_VENDOR_CISCO=y +CONFIG_ENIC=m +CONFIG_NET_VENDOR_CORTINA=y +CONFIG_CX_ECAT=m +CONFIG_DNET=m +CONFIG_NET_VENDOR_DEC=y +CONFIG_NET_TULIP=y +CONFIG_DE2104X=m +CONFIG_DE2104X_DSL=0 +CONFIG_TULIP=m +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_TULIP_NAPI is not set +CONFIG_DE4X5=m +CONFIG_WINBOND_840=m +CONFIG_DM9102=m +CONFIG_ULI526X=m +CONFIG_PCMCIA_XIRCOM=m +CONFIG_NET_VENDOR_DLINK=y +CONFIG_DL2K=m +CONFIG_SUNDANCE=m +# CONFIG_SUNDANCE_MMIO is not set +CONFIG_NET_VENDOR_EMULEX=y +CONFIG_BE2NET=m +CONFIG_BE2NET_HWMON=y +CONFIG_BE2NET_BE2=y +CONFIG_BE2NET_BE3=y +CONFIG_BE2NET_LANCER=y +CONFIG_BE2NET_SKYHAWK=y +CONFIG_NET_VENDOR_EZCHIP=y +CONFIG_NET_VENDOR_FUJITSU=y +CONFIG_PCMCIA_FMVJ18X=m +CONFIG_NET_VENDOR_HP=y +CONFIG_HP100=m +CONFIG_NET_VENDOR_HUAWEI=y +# CONFIG_HINIC is not set +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_INTEL=y +CONFIG_E100=m +CONFIG_E1000=m +CONFIG_E1000E=m +CONFIG_E1000E_HWTS=y +CONFIG_IGB=m +CONFIG_IGB_HWMON=y +CONFIG_IGB_DCA=y +CONFIG_IGBVF=m +CONFIG_IXGB=m +CONFIG_IXGBE=m +CONFIG_IXGBE_HWMON=y +CONFIG_IXGBE_DCA=y +CONFIG_IXGBE_DCB=y +CONFIG_IXGBEVF=m +CONFIG_I40E=m +CONFIG_I40E_DCB=y +CONFIG_I40EVF=m +# CONFIG_ICE is not set +CONFIG_FM10K=m +CONFIG_JME=m +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_MVMDIO=m +CONFIG_SKGE=m +# CONFIG_SKGE_DEBUG is not set +CONFIG_SKGE_GENESIS=y +CONFIG_SKY2=m +# CONFIG_SKY2_DEBUG is not set +CONFIG_NET_VENDOR_MELLANOX=y +CONFIG_MLX4_EN=m +CONFIG_MLX4_EN_DCB=y +CONFIG_MLX4_CORE=m +CONFIG_MLX4_DEBUG=y +CONFIG_MLX4_CORE_GEN2=y +CONFIG_MLX5_CORE=m +CONFIG_MLX5_ACCEL=y +CONFIG_MLX5_FPGA=y +CONFIG_MLX5_CORE_EN=y +CONFIG_MLX5_EN_ARFS=y +CONFIG_MLX5_EN_RXNFC=y +CONFIG_MLX5_MPFS=y +CONFIG_MLX5_ESWITCH=y +CONFIG_MLX5_CORE_EN_DCB=y +CONFIG_MLX5_CORE_IPOIB=y +CONFIG_MLX5_EN_IPSEC=y +CONFIG_MLXSW_CORE=m +CONFIG_MLXSW_CORE_HWMON=y +CONFIG_MLXSW_CORE_THERMAL=y +CONFIG_MLXSW_PCI=m +CONFIG_MLXSW_I2C=m +CONFIG_MLXSW_SWITCHIB=m +CONFIG_MLXSW_SWITCHX2=m +CONFIG_MLXSW_SPECTRUM=m +CONFIG_MLXSW_SPECTRUM_DCB=y +CONFIG_MLXSW_MINIMAL=m +CONFIG_MLXFW=m +CONFIG_NET_VENDOR_MICREL=y +CONFIG_KS8842=m +CONFIG_KS8851=m +CONFIG_KS8851_MLL=m +CONFIG_KSZ884X_PCI=m +CONFIG_NET_VENDOR_MICROCHIP=y +CONFIG_ENC28J60=m +# CONFIG_ENC28J60_WRITEVERIFY is not set +CONFIG_ENCX24J600=m +# CONFIG_LAN743X is not set +CONFIG_NET_VENDOR_MICROSEMI=y +# CONFIG_MSCC_OCELOT_SWITCH is not set +CONFIG_NET_VENDOR_MYRI=y +CONFIG_MYRI10GE=m +CONFIG_MYRI10GE_DCA=y +CONFIG_FEALNX=m +CONFIG_NET_VENDOR_NATSEMI=y +CONFIG_NATSEMI=m +CONFIG_NS83820=m +CONFIG_NET_VENDOR_NETERION=y +CONFIG_S2IO=m +CONFIG_VXGE=m +# CONFIG_VXGE_DEBUG_TRACE_ALL is not set +CONFIG_NET_VENDOR_NETRONOME=y +CONFIG_NFP=m +CONFIG_NFP_APP_FLOWER=y +CONFIG_NFP_APP_ABM_NIC=y +# CONFIG_NFP_DEBUG is not set +CONFIG_NET_VENDOR_NI=y +CONFIG_NET_VENDOR_8390=y +CONFIG_PCMCIA_AXNET=m +CONFIG_NE2K_PCI=m +CONFIG_PCMCIA_PCNET=m +CONFIG_NET_VENDOR_NVIDIA=y +CONFIG_FORCEDETH=m +CONFIG_NET_VENDOR_OKI=y +CONFIG_ETHOC=m +CONFIG_NET_VENDOR_PACKET_ENGINES=y +CONFIG_HAMACHI=m +CONFIG_YELLOWFIN=m +CONFIG_NET_VENDOR_QLOGIC=y +CONFIG_QLA3XXX=m +CONFIG_QLCNIC=m +CONFIG_QLCNIC_SRIOV=y +CONFIG_QLCNIC_DCB=y +CONFIG_QLCNIC_HWMON=y +CONFIG_QLGE=m +CONFIG_NETXEN_NIC=m +CONFIG_QED=m +CONFIG_QED_LL2=y +CONFIG_QED_SRIOV=y +CONFIG_QEDE=m +CONFIG_QED_RDMA=y +CONFIG_QED_ISCSI=y +CONFIG_QED_FCOE=y +CONFIG_QED_OOO=y +CONFIG_NET_VENDOR_QUALCOMM=y +CONFIG_QCOM_EMAC=m +# CONFIG_RMNET is not set +CONFIG_NET_VENDOR_RDC=y +CONFIG_R6040=m +CONFIG_NET_VENDOR_REALTEK=y +CONFIG_ATP=m +CONFIG_8139CP=m +CONFIG_8139TOO=m +CONFIG_8139TOO_PIO=y +# CONFIG_8139TOO_TUNE_TWISTER is not set +CONFIG_8139TOO_8129=y +# CONFIG_8139_OLD_RX_RESET is not set +CONFIG_R8169=m +CONFIG_NET_VENDOR_RENESAS=y +CONFIG_NET_VENDOR_ROCKER=y +CONFIG_ROCKER=m +CONFIG_NET_VENDOR_SAMSUNG=y +CONFIG_SXGBE_ETH=m +CONFIG_NET_VENDOR_SEEQ=y +CONFIG_NET_VENDOR_SOLARFLARE=y +CONFIG_SFC=m +CONFIG_SFC_MTD=y +CONFIG_SFC_MCDI_MON=y +CONFIG_SFC_SRIOV=y +CONFIG_SFC_MCDI_LOGGING=y +CONFIG_SFC_FALCON=m +CONFIG_SFC_FALCON_MTD=y +CONFIG_NET_VENDOR_SILAN=y +CONFIG_SC92031=m +CONFIG_NET_VENDOR_SIS=y +CONFIG_SIS900=m +CONFIG_SIS190=m +CONFIG_NET_VENDOR_SMSC=y +CONFIG_PCMCIA_SMC91C92=m +CONFIG_EPIC100=m +CONFIG_SMSC911X=m +CONFIG_SMSC9420=m +CONFIG_NET_VENDOR_SOCIONEXT=y +CONFIG_NET_VENDOR_STMICRO=y +CONFIG_STMMAC_ETH=m +CONFIG_STMMAC_PLATFORM=m +CONFIG_DWMAC_GENERIC=m +# CONFIG_STMMAC_PCI is not set +CONFIG_NET_VENDOR_SUN=y +CONFIG_HAPPYMEAL=m +CONFIG_SUNGEM=m +CONFIG_CASSINI=m +CONFIG_NIU=m +CONFIG_NET_VENDOR_SYNOPSYS=y +CONFIG_DWC_XLGMAC=m +CONFIG_DWC_XLGMAC_PCI=m +CONFIG_NET_VENDOR_TEHUTI=y +CONFIG_TEHUTI=m +CONFIG_NET_VENDOR_TI=y +CONFIG_TI_CPSW_ALE=m +CONFIG_TLAN=m +CONFIG_NET_VENDOR_VIA=y +CONFIG_VIA_RHINE=m +CONFIG_VIA_RHINE_MMIO=y +CONFIG_VIA_VELOCITY=m +CONFIG_NET_VENDOR_WIZNET=y +CONFIG_WIZNET_W5100=m +CONFIG_WIZNET_W5300=m +# CONFIG_WIZNET_BUS_DIRECT is not set +# CONFIG_WIZNET_BUS_INDIRECT is not set +CONFIG_WIZNET_BUS_ANY=y +CONFIG_WIZNET_W5100_SPI=m +CONFIG_NET_VENDOR_XIRCOM=y +CONFIG_PCMCIA_XIRC2PS=m +CONFIG_FDDI=y +CONFIG_DEFXX=m +# CONFIG_DEFXX_MMIO is not set +CONFIG_SKFP=m +# CONFIG_HIPPI is not set +CONFIG_NET_SB1000=m +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_BUS=y +# CONFIG_MDIO_BCM_UNIMAC is not set +CONFIG_MDIO_BITBANG=m +CONFIG_MDIO_CAVIUM=m +CONFIG_MDIO_GPIO=m +# CONFIG_MDIO_MSCC_MIIM is not set +CONFIG_MDIO_THUNDER=m +CONFIG_PHYLINK=m +CONFIG_PHYLIB=y +CONFIG_SWPHY=y +CONFIG_LED_TRIGGER_PHY=y + +# +# MII PHY device drivers +# +# CONFIG_SFP is not set +CONFIG_AMD_PHY=m +CONFIG_AQUANTIA_PHY=m +# CONFIG_ASIX_PHY is not set +CONFIG_AT803X_PHY=m +CONFIG_BCM7XXX_PHY=m +CONFIG_BCM87XX_PHY=m +CONFIG_BCM_NET_PHYLIB=m +CONFIG_BROADCOM_PHY=m +CONFIG_CICADA_PHY=m +CONFIG_CORTINA_PHY=m +CONFIG_DAVICOM_PHY=m +# CONFIG_DP83822_PHY is not set +# CONFIG_DP83TC811_PHY is not set +CONFIG_DP83848_PHY=m +CONFIG_DP83867_PHY=m +CONFIG_FIXED_PHY=y +CONFIG_ICPLUS_PHY=m +CONFIG_INTEL_XWAY_PHY=m +CONFIG_LSI_ET1011C_PHY=m +CONFIG_LXT_PHY=m +CONFIG_MARVELL_PHY=m +CONFIG_MARVELL_10G_PHY=m +CONFIG_MICREL_PHY=m +CONFIG_MICROCHIP_PHY=m +# CONFIG_MICROCHIP_T1_PHY is not set +CONFIG_MICROSEMI_PHY=m +CONFIG_NATIONAL_PHY=m +CONFIG_QSEMI_PHY=m +CONFIG_REALTEK_PHY=m +# CONFIG_RENESAS_PHY is not set +# CONFIG_ROCKCHIP_PHY is not set +CONFIG_SMSC_PHY=m +CONFIG_STE10XP=m +CONFIG_TERANETICS_PHY=m +CONFIG_VITESSE_PHY=m +CONFIG_XILINX_GMII2RGMII=m +CONFIG_MICREL_KS8995MA=m +CONFIG_PLIP=m +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOATM=m +CONFIG_PPPOE=m +CONFIG_PPTP=m +CONFIG_PPPOL2TP=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_SLIP=m +CONFIG_SLHC=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +CONFIG_SLIP_MODE_SLIP6=y +CONFIG_USB_NET_DRIVERS=m +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_LAN78XX=m +CONFIG_USB_USBNET=m +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_HUAWEI_CDC_NCM=m +CONFIG_USB_NET_CDC_MBIM=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SR9700=m +CONFIG_USB_NET_SR9800=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_GL620A=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_RNDIS_HOST=m +CONFIG_USB_NET_CDC_SUBSET_ENABLE=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_KC2190=y +CONFIG_USB_NET_ZAURUS=m +CONFIG_USB_NET_CX82310_ETH=m +CONFIG_USB_NET_KALMIA=m +CONFIG_USB_NET_QMI_WWAN=m +CONFIG_USB_HSO=m +CONFIG_USB_NET_INT51X1=m +CONFIG_USB_CDC_PHONET=m +CONFIG_USB_IPHETH=m +CONFIG_USB_SIERRA_NET=m +CONFIG_USB_VL600=m +CONFIG_USB_NET_CH9200=m +CONFIG_WLAN=y +# CONFIG_WIRELESS_WDS is not set +CONFIG_WLAN_VENDOR_ADMTEK=y +CONFIG_ADM8211=m +CONFIG_ATH_COMMON=m +CONFIG_WLAN_VENDOR_ATH=y +# CONFIG_ATH_DEBUG is not set +CONFIG_ATH5K=m +# CONFIG_ATH5K_DEBUG is not set +# CONFIG_ATH5K_TRACER is not set +CONFIG_ATH5K_PCI=y +CONFIG_ATH9K_HW=m +CONFIG_ATH9K_COMMON=m +CONFIG_ATH9K_COMMON_DEBUG=y +CONFIG_ATH9K_BTCOEX_SUPPORT=y +CONFIG_ATH9K=m +CONFIG_ATH9K_PCI=y +CONFIG_ATH9K_AHB=y +CONFIG_ATH9K_DEBUGFS=y +CONFIG_ATH9K_STATION_STATISTICS=y +# CONFIG_ATH9K_DYNACK is not set +CONFIG_ATH9K_WOW=y +CONFIG_ATH9K_RFKILL=y +CONFIG_ATH9K_CHANNEL_CONTEXT=y +CONFIG_ATH9K_PCOEM=y +CONFIG_ATH9K_HTC=m +CONFIG_ATH9K_HTC_DEBUGFS=y +CONFIG_ATH9K_HWRNG=y +# CONFIG_ATH9K_COMMON_SPECTRAL is not set +CONFIG_CARL9170=m +CONFIG_CARL9170_LEDS=y +# CONFIG_CARL9170_DEBUGFS is not set +CONFIG_CARL9170_WPC=y +CONFIG_CARL9170_HWRNG=y +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +CONFIG_ATH6KL_USB=m +# CONFIG_ATH6KL_DEBUG is not set +# CONFIG_ATH6KL_TRACING is not set +CONFIG_AR5523=m +CONFIG_WIL6210=m +CONFIG_WIL6210_ISR_COR=y +CONFIG_WIL6210_TRACING=y +CONFIG_WIL6210_DEBUGFS=y +CONFIG_ATH10K=m +CONFIG_ATH10K_CE=y +CONFIG_ATH10K_PCI=m +CONFIG_ATH10K_SDIO=m +# CONFIG_ATH10K_USB is not set +# CONFIG_ATH10K_DEBUG is not set +CONFIG_ATH10K_DEBUGFS=y +# CONFIG_ATH10K_SPECTRAL is not set +CONFIG_ATH10K_TRACING=y +CONFIG_WCN36XX=m +# CONFIG_WCN36XX_DEBUGFS is not set +CONFIG_WLAN_VENDOR_ATMEL=y +CONFIG_ATMEL=m +CONFIG_PCI_ATMEL=m +CONFIG_PCMCIA_ATMEL=m +CONFIG_AT76C50X_USB=m +CONFIG_WLAN_VENDOR_BROADCOM=y +CONFIG_B43=m +CONFIG_B43_BCMA=y +CONFIG_B43_SSB=y +CONFIG_B43_BUSES_BCMA_AND_SSB=y +# CONFIG_B43_BUSES_BCMA is not set +# CONFIG_B43_BUSES_SSB is not set +CONFIG_B43_PCI_AUTOSELECT=y +CONFIG_B43_PCICORE_AUTOSELECT=y +# CONFIG_B43_SDIO is not set +CONFIG_B43_BCMA_PIO=y +CONFIG_B43_PIO=y +CONFIG_B43_PHY_G=y +CONFIG_B43_PHY_N=y +CONFIG_B43_PHY_LP=y +CONFIG_B43_PHY_HT=y +CONFIG_B43_LEDS=y +CONFIG_B43_HWRNG=y +# CONFIG_B43_DEBUG is not set +CONFIG_B43LEGACY=m +CONFIG_B43LEGACY_PCI_AUTOSELECT=y +CONFIG_B43LEGACY_PCICORE_AUTOSELECT=y +CONFIG_B43LEGACY_LEDS=y +CONFIG_B43LEGACY_HWRNG=y +# CONFIG_B43LEGACY_DEBUG is not set +CONFIG_B43LEGACY_DMA=y +CONFIG_B43LEGACY_PIO=y +CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y +# CONFIG_B43LEGACY_DMA_MODE is not set +# CONFIG_B43LEGACY_PIO_MODE is not set +CONFIG_BRCMUTIL=m +CONFIG_BRCMSMAC=m +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_PROTO_BCDC=y +CONFIG_BRCMFMAC_PROTO_MSGBUF=y +CONFIG_BRCMFMAC_SDIO=y +CONFIG_BRCMFMAC_USB=y +CONFIG_BRCMFMAC_PCIE=y +CONFIG_BRCM_TRACING=y +# CONFIG_BRCMDBG is not set +CONFIG_WLAN_VENDOR_CISCO=y +CONFIG_AIRO=m +CONFIG_AIRO_CS=m +CONFIG_WLAN_VENDOR_INTEL=y +CONFIG_IPW2100=m +CONFIG_IPW2100_MONITOR=y +# CONFIG_IPW2100_DEBUG is not set +CONFIG_IPW2200=m +CONFIG_IPW2200_MONITOR=y +CONFIG_IPW2200_RADIOTAP=y +CONFIG_IPW2200_PROMISCUOUS=y +CONFIG_IPW2200_QOS=y +# CONFIG_IPW2200_DEBUG is not set +CONFIG_LIBIPW=m +# CONFIG_LIBIPW_DEBUG is not set +CONFIG_IWLEGACY=m +CONFIG_IWL4965=m +CONFIG_IWL3945=m + +# +# iwl3945 / iwl4965 Debugging Options +# +# CONFIG_IWLEGACY_DEBUG is not set +CONFIG_IWLEGACY_DEBUGFS=y +CONFIG_IWLWIFI=m +CONFIG_IWLWIFI_LEDS=y +CONFIG_IWLDVM=m +CONFIG_IWLMVM=m +CONFIG_IWLWIFI_OPMODE_MODULAR=y +# CONFIG_IWLWIFI_BCAST_FILTERING is not set +# CONFIG_IWLWIFI_PCIE_RTPM is not set + +# +# Debugging Options +# +# CONFIG_IWLWIFI_DEBUG is not set +CONFIG_IWLWIFI_DEBUGFS=y +CONFIG_IWLWIFI_DEVICE_TRACING=y +CONFIG_WLAN_VENDOR_INTERSIL=y +CONFIG_HOSTAP=m +CONFIG_HOSTAP_FIRMWARE=y +CONFIG_HOSTAP_FIRMWARE_NVRAM=y +CONFIG_HOSTAP_PLX=m +CONFIG_HOSTAP_PCI=m +CONFIG_HOSTAP_CS=m +CONFIG_HERMES=m +# CONFIG_HERMES_PRISM is not set +CONFIG_HERMES_CACHE_FW_ON_INIT=y +CONFIG_PLX_HERMES=m +CONFIG_TMD_HERMES=m +CONFIG_NORTEL_HERMES=m +CONFIG_PCMCIA_HERMES=m +CONFIG_PCMCIA_SPECTRUM=m +CONFIG_ORINOCO_USB=m +CONFIG_P54_COMMON=m +CONFIG_P54_USB=m +CONFIG_P54_PCI=m +CONFIG_P54_SPI=m +# CONFIG_P54_SPI_DEFAULT_EEPROM is not set +CONFIG_P54_LEDS=y +# CONFIG_PRISM54 is not set +CONFIG_WLAN_VENDOR_MARVELL=y +CONFIG_LIBERTAS=m +CONFIG_LIBERTAS_USB=m +CONFIG_LIBERTAS_CS=m +CONFIG_LIBERTAS_SDIO=m +CONFIG_LIBERTAS_SPI=m +# CONFIG_LIBERTAS_DEBUG is not set +CONFIG_LIBERTAS_MESH=y +CONFIG_LIBERTAS_THINFIRM=m +# CONFIG_LIBERTAS_THINFIRM_DEBUG is not set +CONFIG_LIBERTAS_THINFIRM_USB=m +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_SDIO=m +CONFIG_MWIFIEX_PCIE=m +CONFIG_MWIFIEX_USB=m +# CONFIG_MWLWIFI is not set +CONFIG_MWL8K=m +CONFIG_WLAN_VENDOR_MEDIATEK=y +CONFIG_MT7601U=m +# CONFIG_MT76x0U is not set +# CONFIG_MT76x2E is not set +# CONFIG_MT76x2U is not set +CONFIG_WLAN_VENDOR_RALINK=y +CONFIG_RT2X00=m +CONFIG_RT2400PCI=m +CONFIG_RT2500PCI=m +CONFIG_RT61PCI=m +CONFIG_RT2800PCI=m +CONFIG_RT2800PCI_RT33XX=y +CONFIG_RT2800PCI_RT35XX=y +CONFIG_RT2800PCI_RT53XX=y +CONFIG_RT2800PCI_RT3290=y +CONFIG_RT2500USB=m +CONFIG_RT73USB=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT33XX=y +CONFIG_RT2800USB_RT35XX=y +CONFIG_RT2800USB_RT3573=y +CONFIG_RT2800USB_RT53XX=y +CONFIG_RT2800USB_RT55XX=y +CONFIG_RT2800USB_UNKNOWN=y +CONFIG_RT2800_LIB=m +CONFIG_RT2800_LIB_MMIO=m +CONFIG_RT2X00_LIB_MMIO=m +CONFIG_RT2X00_LIB_PCI=m +CONFIG_RT2X00_LIB_USB=m +CONFIG_RT2X00_LIB=m +CONFIG_RT2X00_LIB_FIRMWARE=y +CONFIG_RT2X00_LIB_CRYPTO=y +CONFIG_RT2X00_LIB_LEDS=y +# CONFIG_RT2X00_LIB_DEBUGFS is not set +# CONFIG_RT2X00_DEBUG is not set +CONFIG_WLAN_VENDOR_REALTEK=y +CONFIG_RTL8180=m +CONFIG_RTL8187=m +CONFIG_RTL8187_LEDS=y +CONFIG_RTL_CARDS=m +CONFIG_RTL8192CE=m +CONFIG_RTL8192SE=m +CONFIG_RTL8192DE=m +CONFIG_RTL8723AE=m +CONFIG_RTL8723BE=m +CONFIG_RTL8188EE=m +CONFIG_RTL8192EE=m +CONFIG_RTL8821AE=m +CONFIG_RTL8192CU=m +CONFIG_RTLWIFI=m +CONFIG_RTLWIFI_PCI=m +CONFIG_RTLWIFI_USB=m +# CONFIG_RTLWIFI_DEBUG is not set +CONFIG_RTL8192C_COMMON=m +CONFIG_RTL8723_COMMON=m +CONFIG_RTLBTCOEXIST=m +CONFIG_RTL8XXXU=m +CONFIG_RTL8XXXU_UNTESTED=y +CONFIG_WLAN_VENDOR_RSI=y +CONFIG_RSI_91X=m +# CONFIG_RSI_DEBUGFS is not set +CONFIG_RSI_SDIO=m +CONFIG_RSI_USB=m +CONFIG_RSI_COEX=y +CONFIG_WLAN_VENDOR_ST=y +CONFIG_CW1200=m +CONFIG_CW1200_WLAN_SDIO=m +CONFIG_CW1200_WLAN_SPI=m +CONFIG_WLAN_VENDOR_TI=y +CONFIG_WL1251=m +CONFIG_WL1251_SPI=m +CONFIG_WL1251_SDIO=m +CONFIG_WL12XX=m +CONFIG_WL18XX=m +CONFIG_WLCORE=m +CONFIG_WLCORE_SDIO=m +CONFIG_WILINK_PLATFORM_DATA=y +CONFIG_WLAN_VENDOR_ZYDAS=y +CONFIG_USB_ZD1201=m +CONFIG_ZD1211RW=m +# CONFIG_ZD1211RW_DEBUG is not set +CONFIG_WLAN_VENDOR_QUANTENNA=y +# CONFIG_QTNFMAC_PEARL_PCIE is not set +CONFIG_PCMCIA_RAYCS=m +CONFIG_PCMCIA_WL3501=m +CONFIG_MAC80211_HWSIM=m +CONFIG_USB_NET_RNDIS_WLAN=m + +# +# WiMAX Wireless Broadband devices +# +CONFIG_WIMAX_I2400M=m +CONFIG_WIMAX_I2400M_USB=m +CONFIG_WIMAX_I2400M_DEBUG_LEVEL=8 +CONFIG_WAN=y +CONFIG_LANMEDIA=m +CONFIG_HDLC=m +CONFIG_HDLC_RAW=m +CONFIG_HDLC_RAW_ETH=m +CONFIG_HDLC_CISCO=m +CONFIG_HDLC_FR=m +CONFIG_HDLC_PPP=m +CONFIG_HDLC_X25=m +CONFIG_PCI200SYN=m +CONFIG_WANXL=m +CONFIG_PC300TOO=m +CONFIG_FARSYNC=m +CONFIG_DSCC4=m +CONFIG_DSCC4_PCISYNC=y +CONFIG_DSCC4_PCI_RST=y +CONFIG_DLCI=m +CONFIG_DLCI_MAX=8 +CONFIG_LAPBETHER=m +CONFIG_X25_ASY=m +CONFIG_SBNI=m +# CONFIG_SBNI_MULTILINE is not set +CONFIG_IEEE802154_DRIVERS=m +CONFIG_IEEE802154_FAKELB=m +CONFIG_IEEE802154_AT86RF230=m +CONFIG_IEEE802154_AT86RF230_DEBUGFS=y +CONFIG_IEEE802154_MRF24J40=m +CONFIG_IEEE802154_CC2520=m +CONFIG_IEEE802154_ATUSB=m +CONFIG_IEEE802154_ADF7242=m +CONFIG_IEEE802154_CA8210=m +CONFIG_IEEE802154_CA8210_DEBUGFS=y +# CONFIG_IEEE802154_MCR20A is not set +# CONFIG_IEEE802154_HWSIM is not set +CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_NETDEV_BACKEND=m +CONFIG_VMXNET3=m +CONFIG_FUJITSU_ES=m +# CONFIG_THUNDERBOLT_NET is not set +CONFIG_HYPERV_NET=m +# CONFIG_NETDEVSIM is not set +CONFIG_NET_FAILOVER=m +CONFIG_ISDN=y +CONFIG_ISDN_I4L=m +CONFIG_ISDN_PPP=y +CONFIG_ISDN_PPP_VJ=y +CONFIG_ISDN_MPP=y +CONFIG_IPPP_FILTER=y +CONFIG_ISDN_PPP_BSDCOMP=m +CONFIG_ISDN_AUDIO=y +CONFIG_ISDN_TTY_FAX=y +CONFIG_ISDN_X25=y + +# +# ISDN feature submodules +# +CONFIG_ISDN_DIVERSION=m + +# +# ISDN4Linux hardware drivers +# + +# +# Passive cards +# +CONFIG_ISDN_DRV_HISAX=m + +# +# D-channel protocol features +# +CONFIG_HISAX_EURO=y +CONFIG_DE_AOC=y +# CONFIG_HISAX_NO_SENDCOMPLETE is not set +# CONFIG_HISAX_NO_LLC is not set +# CONFIG_HISAX_NO_KEYPAD is not set +CONFIG_HISAX_1TR6=y +CONFIG_HISAX_NI1=y +CONFIG_HISAX_MAX_CARDS=8 + +# +# HiSax supported cards +# +CONFIG_HISAX_16_3=y +CONFIG_HISAX_TELESPCI=y +CONFIG_HISAX_S0BOX=y +CONFIG_HISAX_FRITZPCI=y +CONFIG_HISAX_AVM_A1_PCMCIA=y +CONFIG_HISAX_ELSA=y +CONFIG_HISAX_DIEHLDIVA=y +CONFIG_HISAX_SEDLBAUER=y +CONFIG_HISAX_NETJET=y +CONFIG_HISAX_NETJET_U=y +CONFIG_HISAX_NICCY=y +CONFIG_HISAX_BKM_A4T=y +CONFIG_HISAX_SCT_QUADRO=y +CONFIG_HISAX_GAZEL=y +CONFIG_HISAX_HFC_PCI=y +CONFIG_HISAX_W6692=y +CONFIG_HISAX_HFC_SX=y +CONFIG_HISAX_ENTERNOW_PCI=y +# CONFIG_HISAX_DEBUG is not set + +# +# HiSax PCMCIA card service modules +# +CONFIG_HISAX_SEDLBAUER_CS=m +CONFIG_HISAX_ELSA_CS=m +CONFIG_HISAX_AVM_A1_CS=m +CONFIG_HISAX_TELES_CS=m + +# +# HiSax sub driver modules +# +CONFIG_HISAX_ST5481=m +CONFIG_HISAX_HFCUSB=m +CONFIG_HISAX_HFC4S8S=m +CONFIG_HISAX_FRITZ_PCIPNP=m +CONFIG_ISDN_CAPI=m +CONFIG_CAPI_TRACE=y +CONFIG_ISDN_CAPI_CAPI20=m +CONFIG_ISDN_CAPI_MIDDLEWARE=y +CONFIG_ISDN_CAPI_CAPIDRV=m +# CONFIG_ISDN_CAPI_CAPIDRV_VERBOSE is not set + +# +# CAPI hardware drivers +# +CONFIG_CAPI_AVM=y +CONFIG_ISDN_DRV_AVMB1_B1PCI=m +CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y +CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m +CONFIG_ISDN_DRV_AVMB1_AVM_CS=m +CONFIG_ISDN_DRV_AVMB1_T1PCI=m +CONFIG_ISDN_DRV_AVMB1_C4=m +# CONFIG_CAPI_EICON is not set +CONFIG_ISDN_DRV_GIGASET=m +# CONFIG_GIGASET_CAPI is not set +CONFIG_GIGASET_I4L=y +CONFIG_GIGASET_BASE=m +CONFIG_GIGASET_M105=m +CONFIG_GIGASET_M101=m +# CONFIG_GIGASET_DEBUG is not set +CONFIG_HYSDN=m +CONFIG_HYSDN_CAPI=y +CONFIG_MISDN=m +CONFIG_MISDN_DSP=m +CONFIG_MISDN_L1OIP=m + +# +# mISDN hardware drivers +# +CONFIG_MISDN_HFCPCI=m +CONFIG_MISDN_HFCMULTI=m +CONFIG_MISDN_HFCUSB=m +CONFIG_MISDN_AVMFRITZ=m +CONFIG_MISDN_SPEEDFAX=m +CONFIG_MISDN_INFINEON=m +CONFIG_MISDN_W6692=m +CONFIG_MISDN_NETJET=m +CONFIG_MISDN_IPAC=m +CONFIG_MISDN_ISAR=m +CONFIG_ISDN_HDLC=m +CONFIG_NVM=y +CONFIG_NVM_PBLK=m +# CONFIG_NVM_PBLK_DEBUG is not set + +# +# Input device support +# +CONFIG_INPUT=y +CONFIG_INPUT_LEDS=m +CONFIG_INPUT_FF_MEMLESS=m +CONFIG_INPUT_POLLDEV=m +CONFIG_INPUT_SPARSEKMAP=m +CONFIG_INPUT_MATRIXKMAP=m + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +CONFIG_INPUT_JOYDEV=m +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ADC=m +CONFIG_KEYBOARD_ADP5520=m +CONFIG_KEYBOARD_ADP5588=m +CONFIG_KEYBOARD_ADP5589=m +CONFIG_KEYBOARD_ATKBD=y +CONFIG_KEYBOARD_QT1070=m +CONFIG_KEYBOARD_QT2160=m +CONFIG_KEYBOARD_DLINK_DIR685=m +CONFIG_KEYBOARD_LKKBD=m +CONFIG_KEYBOARD_GPIO=m +CONFIG_KEYBOARD_GPIO_POLLED=m +CONFIG_KEYBOARD_TCA6416=m +CONFIG_KEYBOARD_TCA8418=m +CONFIG_KEYBOARD_MATRIX=m +CONFIG_KEYBOARD_LM8323=m +CONFIG_KEYBOARD_LM8333=m +CONFIG_KEYBOARD_MAX7359=m +CONFIG_KEYBOARD_MCS=m +CONFIG_KEYBOARD_MPR121=m +CONFIG_KEYBOARD_NEWTON=m +CONFIG_KEYBOARD_OPENCORES=m +CONFIG_KEYBOARD_SAMSUNG=m +CONFIG_KEYBOARD_STOWAWAY=m +CONFIG_KEYBOARD_SUNKBD=m +CONFIG_KEYBOARD_TM2_TOUCHKEY=m +CONFIG_KEYBOARD_TWL4030=m +CONFIG_KEYBOARD_XTKBD=m +CONFIG_KEYBOARD_CROS_EC=m +# CONFIG_KEYBOARD_MTK_PMIC is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=m +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_BYD=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS=y +CONFIG_MOUSE_PS2_CYPRESS=y +CONFIG_MOUSE_PS2_LIFEBOOK=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +CONFIG_MOUSE_PS2_ELANTECH=y +CONFIG_MOUSE_PS2_ELANTECH_SMBUS=y +CONFIG_MOUSE_PS2_SENTELIC=y +CONFIG_MOUSE_PS2_TOUCHKIT=y +CONFIG_MOUSE_PS2_FOCALTECH=y +CONFIG_MOUSE_PS2_VMMOUSE=y +CONFIG_MOUSE_PS2_SMBUS=y +CONFIG_MOUSE_SERIAL=m +CONFIG_MOUSE_APPLETOUCH=m +CONFIG_MOUSE_BCM5974=m +CONFIG_MOUSE_CYAPA=m +CONFIG_MOUSE_ELAN_I2C=m +CONFIG_MOUSE_ELAN_I2C_I2C=y +CONFIG_MOUSE_ELAN_I2C_SMBUS=y +CONFIG_MOUSE_VSXXXAA=m +CONFIG_MOUSE_GPIO=m +CONFIG_MOUSE_SYNAPTICS_I2C=m +CONFIG_MOUSE_SYNAPTICS_USB=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_ANALOG=m +CONFIG_JOYSTICK_A3D=m +CONFIG_JOYSTICK_ADI=m +CONFIG_JOYSTICK_COBRA=m +CONFIG_JOYSTICK_GF2K=m +CONFIG_JOYSTICK_GRIP=m +CONFIG_JOYSTICK_GRIP_MP=m +CONFIG_JOYSTICK_GUILLEMOT=m +CONFIG_JOYSTICK_INTERACT=m +CONFIG_JOYSTICK_SIDEWINDER=m +CONFIG_JOYSTICK_TMDC=m +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=y +CONFIG_JOYSTICK_IFORCE_232=y +CONFIG_JOYSTICK_WARRIOR=m +CONFIG_JOYSTICK_MAGELLAN=m +CONFIG_JOYSTICK_SPACEORB=m +CONFIG_JOYSTICK_SPACEBALL=m +CONFIG_JOYSTICK_STINGER=m +CONFIG_JOYSTICK_TWIDJOY=m +CONFIG_JOYSTICK_ZHENHUA=m +CONFIG_JOYSTICK_DB9=m +CONFIG_JOYSTICK_GAMECON=m +CONFIG_JOYSTICK_TURBOGRAFX=m +CONFIG_JOYSTICK_AS5011=m +CONFIG_JOYSTICK_JOYDUMP=m +CONFIG_JOYSTICK_XPAD=m +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_JOYSTICK_WALKERA0701=m +CONFIG_JOYSTICK_PSXPAD_SPI=m +CONFIG_JOYSTICK_PSXPAD_SPI_FF=y +# CONFIG_JOYSTICK_PXRC is not set +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=m +CONFIG_TABLET_USB_AIPTEK=m +CONFIG_TABLET_USB_GTCO=m +CONFIG_TABLET_USB_HANWANG=m +CONFIG_TABLET_USB_KBTAB=m +CONFIG_TABLET_USB_PEGASUS=m +CONFIG_TABLET_SERIAL_WACOM4=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_PROPERTIES=y +CONFIG_TOUCHSCREEN_88PM860X=m +CONFIG_TOUCHSCREEN_ADS7846=m +CONFIG_TOUCHSCREEN_AD7877=m +CONFIG_TOUCHSCREEN_AD7879=m +CONFIG_TOUCHSCREEN_AD7879_I2C=m +CONFIG_TOUCHSCREEN_AD7879_SPI=m +# CONFIG_TOUCHSCREEN_ADC is not set +CONFIG_TOUCHSCREEN_ATMEL_MXT=m +CONFIG_TOUCHSCREEN_ATMEL_MXT_T37=y +CONFIG_TOUCHSCREEN_AUO_PIXCIR=m +CONFIG_TOUCHSCREEN_BU21013=m +# CONFIG_TOUCHSCREEN_BU21029 is not set +# CONFIG_TOUCHSCREEN_CHIPONE_ICN8505 is not set +CONFIG_TOUCHSCREEN_CY8CTMG110=m +CONFIG_TOUCHSCREEN_CYTTSP_CORE=m +CONFIG_TOUCHSCREEN_CYTTSP_I2C=m +CONFIG_TOUCHSCREEN_CYTTSP_SPI=m +CONFIG_TOUCHSCREEN_CYTTSP4_CORE=m +CONFIG_TOUCHSCREEN_CYTTSP4_I2C=m +CONFIG_TOUCHSCREEN_CYTTSP4_SPI=m +CONFIG_TOUCHSCREEN_DA9034=m +CONFIG_TOUCHSCREEN_DA9052=m +CONFIG_TOUCHSCREEN_DYNAPRO=m +CONFIG_TOUCHSCREEN_HAMPSHIRE=m +CONFIG_TOUCHSCREEN_EETI=m +CONFIG_TOUCHSCREEN_EGALAX_SERIAL=m +# CONFIG_TOUCHSCREEN_EXC3000 is not set +CONFIG_TOUCHSCREEN_FUJITSU=m +CONFIG_TOUCHSCREEN_GOODIX=m +# CONFIG_TOUCHSCREEN_HIDEEP is not set +CONFIG_TOUCHSCREEN_ILI210X=m +# CONFIG_TOUCHSCREEN_S6SY761 is not set +CONFIG_TOUCHSCREEN_GUNZE=m +CONFIG_TOUCHSCREEN_EKTF2127=m +CONFIG_TOUCHSCREEN_ELAN=y +CONFIG_TOUCHSCREEN_ELO=m +CONFIG_TOUCHSCREEN_WACOM_W8001=m +CONFIG_TOUCHSCREEN_WACOM_I2C=m +CONFIG_TOUCHSCREEN_MAX11801=m +CONFIG_TOUCHSCREEN_MCS5000=m +CONFIG_TOUCHSCREEN_MMS114=m +CONFIG_TOUCHSCREEN_MELFAS_MIP4=m +CONFIG_TOUCHSCREEN_MTOUCH=m +CONFIG_TOUCHSCREEN_INEXIO=m +CONFIG_TOUCHSCREEN_MK712=m +CONFIG_TOUCHSCREEN_PENMOUNT=m +CONFIG_TOUCHSCREEN_EDT_FT5X06=m +CONFIG_TOUCHSCREEN_TOUCHRIGHT=m +CONFIG_TOUCHSCREEN_TOUCHWIN=m +CONFIG_TOUCHSCREEN_TI_AM335X_TSC=m +CONFIG_TOUCHSCREEN_UCB1400=m +CONFIG_TOUCHSCREEN_PIXCIR=m +CONFIG_TOUCHSCREEN_WDT87XX_I2C=m +CONFIG_TOUCHSCREEN_WM831X=m +CONFIG_TOUCHSCREEN_WM97XX=m +CONFIG_TOUCHSCREEN_WM9705=y +CONFIG_TOUCHSCREEN_WM9712=y +CONFIG_TOUCHSCREEN_WM9713=y +CONFIG_TOUCHSCREEN_USB_COMPOSITE=m +CONFIG_TOUCHSCREEN_MC13783=m +CONFIG_TOUCHSCREEN_USB_EGALAX=y +CONFIG_TOUCHSCREEN_USB_PANJIT=y +CONFIG_TOUCHSCREEN_USB_3M=y +CONFIG_TOUCHSCREEN_USB_ITM=y +CONFIG_TOUCHSCREEN_USB_ETURBO=y +CONFIG_TOUCHSCREEN_USB_GUNZE=y +CONFIG_TOUCHSCREEN_USB_DMC_TSC10=y +CONFIG_TOUCHSCREEN_USB_IRTOUCH=y +CONFIG_TOUCHSCREEN_USB_IDEALTEK=y +CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH=y +CONFIG_TOUCHSCREEN_USB_GOTOP=y +CONFIG_TOUCHSCREEN_USB_JASTEC=y +CONFIG_TOUCHSCREEN_USB_ELO=y +CONFIG_TOUCHSCREEN_USB_E2I=y +CONFIG_TOUCHSCREEN_USB_ZYTRONIC=y +CONFIG_TOUCHSCREEN_USB_ETT_TC45USB=y +CONFIG_TOUCHSCREEN_USB_NEXIO=y +CONFIG_TOUCHSCREEN_USB_EASYTOUCH=y +CONFIG_TOUCHSCREEN_TOUCHIT213=m +CONFIG_TOUCHSCREEN_TSC_SERIO=m +CONFIG_TOUCHSCREEN_TSC200X_CORE=m +CONFIG_TOUCHSCREEN_TSC2004=m +CONFIG_TOUCHSCREEN_TSC2005=m +CONFIG_TOUCHSCREEN_TSC2007=m +CONFIG_TOUCHSCREEN_TSC2007_IIO=y +CONFIG_TOUCHSCREEN_PCAP=m +CONFIG_TOUCHSCREEN_RM_TS=m +CONFIG_TOUCHSCREEN_SILEAD=m +CONFIG_TOUCHSCREEN_SIS_I2C=m +CONFIG_TOUCHSCREEN_ST1232=m +CONFIG_TOUCHSCREEN_STMFTS=m +CONFIG_TOUCHSCREEN_SUR40=m +CONFIG_TOUCHSCREEN_SURFACE3_SPI=m +CONFIG_TOUCHSCREEN_SX8654=m +CONFIG_TOUCHSCREEN_TPS6507X=m +CONFIG_TOUCHSCREEN_ZET6223=m +CONFIG_TOUCHSCREEN_ZFORCE=m +CONFIG_TOUCHSCREEN_ROHM_BU21023=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_88PM860X_ONKEY=m +CONFIG_INPUT_88PM80X_ONKEY=m +CONFIG_INPUT_AD714X=m +CONFIG_INPUT_AD714X_I2C=m +CONFIG_INPUT_AD714X_SPI=m +CONFIG_INPUT_ARIZONA_HAPTICS=m +CONFIG_INPUT_BMA150=m +CONFIG_INPUT_E3X0_BUTTON=m +CONFIG_INPUT_PCSPKR=m +CONFIG_INPUT_MAX77693_HAPTIC=m +CONFIG_INPUT_MAX8925_ONKEY=m +CONFIG_INPUT_MAX8997_HAPTIC=m +CONFIG_INPUT_MC13783_PWRBUTTON=m +CONFIG_INPUT_MMA8450=m +CONFIG_INPUT_APANEL=m +CONFIG_INPUT_GP2A=m +CONFIG_INPUT_GPIO_BEEPER=m +CONFIG_INPUT_GPIO_DECODER=m +CONFIG_INPUT_ATLAS_BTNS=m +CONFIG_INPUT_ATI_REMOTE2=m +CONFIG_INPUT_KEYSPAN_REMOTE=m +CONFIG_INPUT_KXTJ9=m +# CONFIG_INPUT_KXTJ9_POLLED_MODE is not set +CONFIG_INPUT_POWERMATE=m +CONFIG_INPUT_YEALINK=m +CONFIG_INPUT_CM109=m +CONFIG_INPUT_REGULATOR_HAPTIC=m +CONFIG_INPUT_RETU_PWRBUTTON=m +CONFIG_INPUT_AXP20X_PEK=m +CONFIG_INPUT_TWL4030_PWRBUTTON=m +CONFIG_INPUT_TWL4030_VIBRA=m +CONFIG_INPUT_TWL6040_VIBRA=m +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_PALMAS_PWRBUTTON=m +CONFIG_INPUT_PCF50633_PMU=m +CONFIG_INPUT_PCF8574=m +CONFIG_INPUT_PWM_BEEPER=m +# CONFIG_INPUT_PWM_VIBRA is not set +CONFIG_INPUT_GPIO_ROTARY_ENCODER=m +CONFIG_INPUT_DA9052_ONKEY=m +CONFIG_INPUT_DA9055_ONKEY=m +CONFIG_INPUT_DA9063_ONKEY=m +CONFIG_INPUT_WM831X_ON=m +CONFIG_INPUT_PCAP=m +CONFIG_INPUT_ADXL34X=m +CONFIG_INPUT_ADXL34X_I2C=m +CONFIG_INPUT_ADXL34X_SPI=m +CONFIG_INPUT_IMS_PCU=m +CONFIG_INPUT_CMA3000=m +CONFIG_INPUT_CMA3000_I2C=m +CONFIG_INPUT_XEN_KBDDEV_FRONTEND=m +CONFIG_INPUT_IDEAPAD_SLIDEBAR=m +CONFIG_INPUT_SOC_BUTTON_ARRAY=m +CONFIG_INPUT_DRV260X_HAPTICS=m +CONFIG_INPUT_DRV2665_HAPTICS=m +CONFIG_INPUT_DRV2667_HAPTICS=m +CONFIG_RMI4_CORE=m +CONFIG_RMI4_I2C=m +CONFIG_RMI4_SPI=m +CONFIG_RMI4_SMB=m +CONFIG_RMI4_F03=y +CONFIG_RMI4_F03_SERIO=m +CONFIG_RMI4_2D_SENSOR=y +CONFIG_RMI4_F11=y +CONFIG_RMI4_F12=y +CONFIG_RMI4_F30=y +CONFIG_RMI4_F34=y +CONFIG_RMI4_F54=y +CONFIG_RMI4_F55=y + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=m +CONFIG_SERIO_CT82C710=m +CONFIG_SERIO_PARKBD=m +CONFIG_SERIO_PCIPS2=m +CONFIG_SERIO_LIBPS2=y +CONFIG_SERIO_RAW=m +CONFIG_SERIO_ALTERA_PS2=m +CONFIG_SERIO_PS2MULT=m +CONFIG_SERIO_ARC_PS2=m +CONFIG_HYPERV_KEYBOARD=m +# CONFIG_SERIO_GPIO_PS2 is not set +CONFIG_USERIO=m +CONFIG_GAMEPORT=m +CONFIG_GAMEPORT_NS558=m +CONFIG_GAMEPORT_L4=m +CONFIG_GAMEPORT_EMU10K1=m +CONFIG_GAMEPORT_FM801=m + +# +# Character devices +# +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_CONSOLE_SLEEP=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=0 +CONFIG_SERIAL_NONSTANDARD=y +CONFIG_ROCKETPORT=m +CONFIG_CYCLADES=m +# CONFIG_CYZ_INTR is not set +CONFIG_MOXA_INTELLIO=m +CONFIG_MOXA_SMARTIO=m +CONFIG_SYNCLINK=m +CONFIG_SYNCLINKMP=m +CONFIG_SYNCLINK_GT=m +CONFIG_NOZOMI=m +CONFIG_ISI=m +CONFIG_N_HDLC=m +CONFIG_N_GSM=m +CONFIG_TRACE_ROUTER=m +CONFIG_TRACE_SINK=m +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +CONFIG_SERIAL_EARLYCON=y +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_PNP=y +CONFIG_SERIAL_8250_FINTEK=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_DMA=y +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_EXAR=m +CONFIG_SERIAL_8250_CS=m +# CONFIG_SERIAL_8250_MEN_MCB is not set +CONFIG_SERIAL_8250_NR_UARTS=48 +CONFIG_SERIAL_8250_RUNTIME_UARTS=32 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +CONFIG_SERIAL_8250_RSA=y +CONFIG_SERIAL_8250_DW=m +CONFIG_SERIAL_8250_RT288X=y +CONFIG_SERIAL_8250_LPSS=m +CONFIG_SERIAL_8250_MID=m +CONFIG_SERIAL_8250_MOXA=m + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_KGDB_NMI=y +CONFIG_SERIAL_MAX3100=m +CONFIG_SERIAL_MAX310X=y +CONFIG_SERIAL_UARTLITE=m +CONFIG_SERIAL_UARTLITE_NR_UARTS=1 +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_CONSOLE_POLL=y +CONFIG_SERIAL_JSM=m +CONFIG_SERIAL_SCCNXP=y +CONFIG_SERIAL_SCCNXP_CONSOLE=y +CONFIG_SERIAL_SC16IS7XX_CORE=m +CONFIG_SERIAL_SC16IS7XX=m +CONFIG_SERIAL_SC16IS7XX_I2C=y +CONFIG_SERIAL_SC16IS7XX_SPI=y +CONFIG_SERIAL_ALTERA_JTAGUART=m +CONFIG_SERIAL_ALTERA_UART=m +CONFIG_SERIAL_ALTERA_UART_MAXPORTS=4 +CONFIG_SERIAL_ALTERA_UART_BAUDRATE=115200 +# CONFIG_SERIAL_IFX6X60 is not set +CONFIG_SERIAL_ARC=m +CONFIG_SERIAL_ARC_NR_PORTS=1 +CONFIG_SERIAL_RP2=m +CONFIG_SERIAL_RP2_NR_UARTS=32 +CONFIG_SERIAL_FSL_LPUART=m +CONFIG_SERIAL_MEN_Z135=m +CONFIG_SERIAL_DEV_BUS=y +CONFIG_SERIAL_DEV_CTRL_TTYPORT=y +CONFIG_TTY_PRINTK=y +CONFIG_PRINTER=m +# CONFIG_LP_CONSOLE is not set +CONFIG_PPDEV=m +CONFIG_HVC_DRIVER=y +CONFIG_HVC_IRQ=y +CONFIG_HVC_XEN=y +CONFIG_HVC_XEN_FRONTEND=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_IPMI_HANDLER=m +CONFIG_IPMI_DMI_DECODE=y +# CONFIG_IPMI_PANIC_EVENT is not set +CONFIG_IPMI_DEVICE_INTERFACE=m +CONFIG_IPMI_SI=m +CONFIG_IPMI_SSIF=m +CONFIG_IPMI_WATCHDOG=m +CONFIG_IPMI_POWEROFF=m +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_TIMERIOMEM=m +CONFIG_HW_RANDOM_INTEL=m +CONFIG_HW_RANDOM_AMD=m +CONFIG_HW_RANDOM_VIA=m +CONFIG_HW_RANDOM_VIRTIO=m +CONFIG_NVRAM=m +CONFIG_R3964=m +CONFIG_APPLICOM=m + +# +# PCMCIA character devices +# +CONFIG_SYNCLINK_CS=m +CONFIG_CARDMAN_4000=m +CONFIG_CARDMAN_4040=m +CONFIG_SCR24X=m +CONFIG_IPWIRELESS=m +CONFIG_MWAVE=m +CONFIG_RAW_DRIVER=m +CONFIG_MAX_RAW_DEVS=256 +CONFIG_HPET=y +CONFIG_HPET_MMAP=y +CONFIG_HPET_MMAP_DEFAULT=y +CONFIG_HANGCHECK_TIMER=m +CONFIG_TCG_TPM=y +CONFIG_HW_RANDOM_TPM=y +CONFIG_TCG_TIS_CORE=y +CONFIG_TCG_TIS=y +CONFIG_TCG_TIS_SPI=m +CONFIG_TCG_TIS_I2C_ATMEL=m +CONFIG_TCG_TIS_I2C_INFINEON=m +CONFIG_TCG_TIS_I2C_NUVOTON=m +CONFIG_TCG_NSC=m +CONFIG_TCG_ATMEL=m +CONFIG_TCG_INFINEON=m +CONFIG_TCG_XEN=m +CONFIG_TCG_CRB=y +CONFIG_TCG_VTPM_PROXY=m +CONFIG_TCG_TIS_ST33ZP24=m +CONFIG_TCG_TIS_ST33ZP24_I2C=m +CONFIG_TCG_TIS_ST33ZP24_SPI=m +CONFIG_TELCLOCK=m +CONFIG_DEVPORT=y +CONFIG_XILLYBUS=m +CONFIG_XILLYBUS_PCIE=m +# CONFIG_RANDOM_TRUST_CPU is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_ACPI_I2C_OPREGION=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=m + +# +# Multiplexer I2C Chip support +# +CONFIG_I2C_MUX_GPIO=m +CONFIG_I2C_MUX_LTC4306=m +CONFIG_I2C_MUX_PCA9541=m +CONFIG_I2C_MUX_PCA954x=m +CONFIG_I2C_MUX_REG=m +CONFIG_I2C_MUX_MLXCPLD=m +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_SMBUS=m +CONFIG_I2C_ALGOBIT=m +CONFIG_I2C_ALGOPCA=m + +# +# I2C Hardware Bus support +# + +# +# PC SMBus host controller drivers +# +CONFIG_I2C_ALI1535=m +CONFIG_I2C_ALI1563=m +CONFIG_I2C_ALI15X3=m +CONFIG_I2C_AMD756=m +CONFIG_I2C_AMD756_S4882=m +CONFIG_I2C_AMD8111=m +CONFIG_I2C_I801=m +CONFIG_I2C_ISCH=m +CONFIG_I2C_ISMT=m +CONFIG_I2C_PIIX4=m +CONFIG_I2C_NFORCE2=m +CONFIG_I2C_NFORCE2_S4985=m +CONFIG_I2C_SIS5595=m +CONFIG_I2C_SIS630=m +CONFIG_I2C_SIS96X=m +CONFIG_I2C_VIA=m +CONFIG_I2C_VIAPRO=m + +# +# ACPI drivers +# +CONFIG_I2C_SCMI=m + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +CONFIG_I2C_CBUS_GPIO=m +CONFIG_I2C_DESIGNWARE_CORE=y +CONFIG_I2C_DESIGNWARE_PLATFORM=y +# CONFIG_I2C_DESIGNWARE_SLAVE is not set +CONFIG_I2C_DESIGNWARE_PCI=m +CONFIG_I2C_DESIGNWARE_BAYTRAIL=y +# CONFIG_I2C_EMEV2 is not set +CONFIG_I2C_GPIO=m +# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set +CONFIG_I2C_KEMPLD=m +CONFIG_I2C_OCORES=m +CONFIG_I2C_PCA_PLATFORM=m +CONFIG_I2C_SIMTEC=m +CONFIG_I2C_XILINX=m + +# +# External I2C/SMBus adapter drivers +# +CONFIG_I2C_DIOLAN_U2C=m +CONFIG_I2C_DLN2=m +CONFIG_I2C_PARPORT=m +CONFIG_I2C_PARPORT_LIGHT=m +CONFIG_I2C_ROBOTFUZZ_OSIF=m +CONFIG_I2C_TAOS_EVM=m +CONFIG_I2C_TINY_USB=m +CONFIG_I2C_VIPERBOARD=m + +# +# Other I2C/SMBus bus drivers +# +CONFIG_I2C_MLXCPLD=m +CONFIG_I2C_CROS_EC_TUNNEL=m +CONFIG_I2C_STUB=m +# CONFIG_I2C_SLAVE is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y + +# +# SPI Master Controller Drivers +# +CONFIG_SPI_ALTERA=m +CONFIG_SPI_AXI_SPI_ENGINE=m +CONFIG_SPI_BITBANG=m +CONFIG_SPI_BUTTERFLY=m +CONFIG_SPI_CADENCE=m +CONFIG_SPI_DESIGNWARE=m +CONFIG_SPI_DW_PCI=m +CONFIG_SPI_DW_MID_DMA=y +CONFIG_SPI_DW_MMIO=m +CONFIG_SPI_DLN2=m +CONFIG_SPI_GPIO=m +CONFIG_SPI_LM70_LLP=m +CONFIG_SPI_OC_TINY=m +CONFIG_SPI_PXA2XX=m +CONFIG_SPI_PXA2XX_PCI=m +# CONFIG_SPI_ROCKCHIP is not set +CONFIG_SPI_SC18IS602=m +CONFIG_SPI_XCOMM=m +# CONFIG_SPI_XILINX is not set +CONFIG_SPI_ZYNQMP_GQSPI=m + +# +# SPI Protocol Masters +# +CONFIG_SPI_SPIDEV=m +CONFIG_SPI_LOOPBACK_TEST=m +CONFIG_SPI_TLE62X0=m +CONFIG_SPI_SLAVE=y +CONFIG_SPI_SLAVE_TIME=m +CONFIG_SPI_SLAVE_SYSTEM_CONTROL=m +CONFIG_SPMI=m +CONFIG_HSI=m +CONFIG_HSI_BOARDINFO=y + +# +# HSI controllers +# + +# +# HSI clients +# +CONFIG_HSI_CHAR=m +CONFIG_PPS=y +# CONFIG_PPS_DEBUG is not set + +# +# PPS clients support +# +# CONFIG_PPS_CLIENT_KTIMER is not set +CONFIG_PPS_CLIENT_LDISC=m +CONFIG_PPS_CLIENT_PARPORT=m +CONFIG_PPS_CLIENT_GPIO=m + +# +# PPS generators support +# + +# +# PTP clock support +# +CONFIG_PTP_1588_CLOCK=y + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +CONFIG_PTP_1588_CLOCK_KVM=m +CONFIG_PINCTRL=y +CONFIG_PINMUX=y +CONFIG_PINCONF=y +CONFIG_GENERIC_PINCONF=y +# CONFIG_DEBUG_PINCTRL is not set +CONFIG_PINCTRL_AMD=y +CONFIG_PINCTRL_MCP23S08=m +CONFIG_PINCTRL_SX150X=y +CONFIG_PINCTRL_BAYTRAIL=y +CONFIG_PINCTRL_CHERRYVIEW=y +CONFIG_PINCTRL_INTEL=m +CONFIG_PINCTRL_BROXTON=m +CONFIG_PINCTRL_CANNONLAKE=m +# CONFIG_PINCTRL_CEDARFORK is not set +CONFIG_PINCTRL_DENVERTON=m +CONFIG_PINCTRL_GEMINILAKE=m +# CONFIG_PINCTRL_ICELAKE is not set +# CONFIG_PINCTRL_LEWISBURG is not set +CONFIG_PINCTRL_SUNRISEPOINT=m +CONFIG_GPIOLIB=y +CONFIG_GPIOLIB_FASTPATH_LIMIT=512 +CONFIG_GPIO_ACPI=y +CONFIG_GPIOLIB_IRQCHIP=y +# CONFIG_DEBUG_GPIO is not set +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_GENERIC=m +CONFIG_GPIO_MAX730X=m + +# +# Memory mapped GPIO drivers +# +CONFIG_GPIO_AMDPT=m +CONFIG_GPIO_DWAPB=m +CONFIG_GPIO_EXAR=m +CONFIG_GPIO_GENERIC_PLATFORM=m +CONFIG_GPIO_ICH=m +CONFIG_GPIO_LYNXPOINT=y +# CONFIG_GPIO_MB86S7X is not set +CONFIG_GPIO_MENZ127=m +CONFIG_GPIO_MOCKUP=m +CONFIG_GPIO_VX855=m + +# +# Port-mapped I/O GPIO drivers +# +CONFIG_GPIO_104_DIO_48E=m +CONFIG_GPIO_104_IDIO_16=m +CONFIG_GPIO_104_IDI_48=m +CONFIG_GPIO_F7188X=m +CONFIG_GPIO_GPIO_MM=m +CONFIG_GPIO_IT87=m +CONFIG_GPIO_SCH=m +CONFIG_GPIO_SCH311X=m +# CONFIG_GPIO_WINBOND is not set +CONFIG_GPIO_WS16C48=m + +# +# I2C GPIO expanders +# +CONFIG_GPIO_ADP5588=m +CONFIG_GPIO_MAX7300=m +CONFIG_GPIO_MAX732X=m +CONFIG_GPIO_PCA953X=m +CONFIG_GPIO_PCF857X=m +CONFIG_GPIO_TPIC2810=m + +# +# MFD GPIO expanders +# +CONFIG_GPIO_ADP5520=m +CONFIG_GPIO_ARIZONA=m +CONFIG_GPIO_CRYSTAL_COVE=m +CONFIG_GPIO_DA9052=m +CONFIG_GPIO_DA9055=m +CONFIG_GPIO_DLN2=m +CONFIG_GPIO_JANZ_TTL=m +CONFIG_GPIO_KEMPLD=m +CONFIG_GPIO_LP3943=m +CONFIG_GPIO_LP873X=m +CONFIG_GPIO_PALMAS=y +CONFIG_GPIO_RC5T583=y +CONFIG_GPIO_TPS65086=m +CONFIG_GPIO_TPS6586X=y +CONFIG_GPIO_TPS65910=y +CONFIG_GPIO_TPS65912=m +CONFIG_GPIO_TWL4030=m +CONFIG_GPIO_TWL6040=m +CONFIG_GPIO_UCB1400=m +CONFIG_GPIO_WHISKEY_COVE=m +CONFIG_GPIO_WM831X=m +CONFIG_GPIO_WM8350=m +CONFIG_GPIO_WM8994=m + +# +# PCI GPIO expanders +# +CONFIG_GPIO_AMD8111=m +CONFIG_GPIO_ML_IOH=m +CONFIG_GPIO_PCI_IDIO_16=m +# CONFIG_GPIO_PCIE_IDIO_24 is not set +CONFIG_GPIO_RDC321X=m + +# +# SPI GPIO expanders +# +# CONFIG_GPIO_MAX3191X is not set +CONFIG_GPIO_MAX7301=m +CONFIG_GPIO_MC33880=m +CONFIG_GPIO_PISOSR=m +CONFIG_GPIO_XRA1403=m + +# +# USB GPIO expanders +# +CONFIG_GPIO_VIPERBOARD=m +CONFIG_W1=m +CONFIG_W1_CON=y + +# +# 1-wire Bus Masters +# +CONFIG_W1_MASTER_MATROX=m +CONFIG_W1_MASTER_DS2490=m +CONFIG_W1_MASTER_DS2482=m +CONFIG_W1_MASTER_DS1WM=m +CONFIG_W1_MASTER_GPIO=m + +# +# 1-wire Slaves +# +CONFIG_W1_SLAVE_THERM=m +CONFIG_W1_SLAVE_SMEM=m +CONFIG_W1_SLAVE_DS2405=m +CONFIG_W1_SLAVE_DS2408=m +CONFIG_W1_SLAVE_DS2408_READBACK=y +CONFIG_W1_SLAVE_DS2413=m +CONFIG_W1_SLAVE_DS2406=m +CONFIG_W1_SLAVE_DS2423=m +# CONFIG_W1_SLAVE_DS2805 is not set +CONFIG_W1_SLAVE_DS2431=m +CONFIG_W1_SLAVE_DS2433=m +# CONFIG_W1_SLAVE_DS2433_CRC is not set +CONFIG_W1_SLAVE_DS2438=m +CONFIG_W1_SLAVE_DS2780=m +CONFIG_W1_SLAVE_DS2781=m +CONFIG_W1_SLAVE_DS28E04=m +# CONFIG_W1_SLAVE_DS28E17 is not set +CONFIG_POWER_AVS=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_RESTART=y +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +CONFIG_PDA_POWER=m +CONFIG_GENERIC_ADC_BATTERY=m +CONFIG_MAX8925_POWER=m +CONFIG_WM831X_BACKUP=m +CONFIG_WM831X_POWER=m +CONFIG_WM8350_POWER=m +CONFIG_TEST_POWER=m +CONFIG_BATTERY_88PM860X=m +# CONFIG_CHARGER_ADP5061 is not set +# CONFIG_BATTERY_DS2760 is not set +CONFIG_BATTERY_DS2780=m +CONFIG_BATTERY_DS2781=m +CONFIG_BATTERY_DS2782=m +CONFIG_BATTERY_SBS=m +CONFIG_CHARGER_SBS=m +# CONFIG_MANAGER_SBS is not set +CONFIG_BATTERY_BQ27XXX=m +CONFIG_BATTERY_BQ27XXX_I2C=m +CONFIG_BATTERY_BQ27XXX_HDQ=m +# CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM is not set +CONFIG_BATTERY_DA9030=m +CONFIG_BATTERY_DA9052=m +CONFIG_CHARGER_DA9150=m +CONFIG_BATTERY_DA9150=m +CONFIG_CHARGER_AXP20X=m +CONFIG_BATTERY_AXP20X=m +CONFIG_AXP20X_POWER=m +CONFIG_AXP288_CHARGER=m +CONFIG_AXP288_FUEL_GAUGE=m +CONFIG_BATTERY_MAX17040=m +CONFIG_BATTERY_MAX17042=m +# CONFIG_BATTERY_MAX1721X is not set +CONFIG_BATTERY_TWL4030_MADC=m +CONFIG_CHARGER_88PM860X=m +CONFIG_CHARGER_PCF50633=m +CONFIG_BATTERY_RX51=m +CONFIG_CHARGER_ISP1704=m +CONFIG_CHARGER_MAX8903=m +CONFIG_CHARGER_TWL4030=m +CONFIG_CHARGER_LP8727=m +CONFIG_CHARGER_LP8788=m +CONFIG_CHARGER_GPIO=m +CONFIG_CHARGER_MANAGER=y +CONFIG_CHARGER_LTC3651=m +CONFIG_CHARGER_MAX14577=m +CONFIG_CHARGER_MAX77693=m +CONFIG_CHARGER_MAX8997=m +CONFIG_CHARGER_MAX8998=m +CONFIG_CHARGER_BQ2415X=m +CONFIG_CHARGER_BQ24190=m +CONFIG_CHARGER_BQ24257=m +CONFIG_CHARGER_BQ24735=m +CONFIG_CHARGER_BQ25890=m +CONFIG_CHARGER_SMB347=m +CONFIG_CHARGER_TPS65090=m +CONFIG_BATTERY_GAUGE_LTC2941=m +CONFIG_BATTERY_RT5033=m +CONFIG_CHARGER_RT9455=m +# CONFIG_CHARGER_CROS_USBPD is not set +CONFIG_HWMON=y +CONFIG_HWMON_VID=m +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +CONFIG_SENSORS_ABITUGURU=m +CONFIG_SENSORS_ABITUGURU3=m +CONFIG_SENSORS_AD7314=m +CONFIG_SENSORS_AD7414=m +CONFIG_SENSORS_AD7418=m +CONFIG_SENSORS_ADM1021=m +CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1026=m +CONFIG_SENSORS_ADM1029=m +CONFIG_SENSORS_ADM1031=m +CONFIG_SENSORS_ADM9240=m +CONFIG_SENSORS_ADT7X10=m +CONFIG_SENSORS_ADT7310=m +CONFIG_SENSORS_ADT7410=m +CONFIG_SENSORS_ADT7411=m +CONFIG_SENSORS_ADT7462=m +CONFIG_SENSORS_ADT7470=m +CONFIG_SENSORS_ADT7475=m +CONFIG_SENSORS_ASC7621=m +CONFIG_SENSORS_K8TEMP=m +CONFIG_SENSORS_K10TEMP=m +CONFIG_SENSORS_FAM15H_POWER=m +CONFIG_SENSORS_APPLESMC=m +CONFIG_SENSORS_ASB100=m +CONFIG_SENSORS_ASPEED=m +CONFIG_SENSORS_ATXP1=m +CONFIG_SENSORS_DS620=m +CONFIG_SENSORS_DS1621=m +CONFIG_SENSORS_DELL_SMM=m +CONFIG_SENSORS_DA9052_ADC=m +CONFIG_SENSORS_DA9055=m +CONFIG_SENSORS_I5K_AMB=m +CONFIG_SENSORS_F71805F=m +CONFIG_SENSORS_F71882FG=m +CONFIG_SENSORS_F75375S=m +CONFIG_SENSORS_MC13783_ADC=m +CONFIG_SENSORS_FSCHMD=m +CONFIG_SENSORS_FTSTEUTATES=m +CONFIG_SENSORS_GL518SM=m +CONFIG_SENSORS_GL520SM=m +CONFIG_SENSORS_G760A=m +CONFIG_SENSORS_G762=m +CONFIG_SENSORS_HIH6130=m +CONFIG_SENSORS_IBMAEM=m +CONFIG_SENSORS_IBMPEX=m +CONFIG_SENSORS_IIO_HWMON=m +CONFIG_SENSORS_I5500=m +CONFIG_SENSORS_CORETEMP=m +CONFIG_SENSORS_IT87=m +CONFIG_SENSORS_JC42=m +CONFIG_SENSORS_POWR1220=m +CONFIG_SENSORS_LINEAGE=m +CONFIG_SENSORS_LTC2945=m +CONFIG_SENSORS_LTC2990=m +CONFIG_SENSORS_LTC4151=m +CONFIG_SENSORS_LTC4215=m +CONFIG_SENSORS_LTC4222=m +CONFIG_SENSORS_LTC4245=m +CONFIG_SENSORS_LTC4260=m +CONFIG_SENSORS_LTC4261=m +CONFIG_SENSORS_MAX1111=m +CONFIG_SENSORS_MAX16065=m +CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_MAX1668=m +CONFIG_SENSORS_MAX197=m +CONFIG_SENSORS_MAX31722=m +# CONFIG_SENSORS_MAX6621 is not set +CONFIG_SENSORS_MAX6639=m +CONFIG_SENSORS_MAX6642=m +CONFIG_SENSORS_MAX6650=m +CONFIG_SENSORS_MAX6697=m +CONFIG_SENSORS_MAX31790=m +CONFIG_SENSORS_MCP3021=m +CONFIG_SENSORS_TC654=m +CONFIG_SENSORS_MENF21BMC_HWMON=m +CONFIG_SENSORS_ADCXX=m +CONFIG_SENSORS_LM63=m +CONFIG_SENSORS_LM70=m +CONFIG_SENSORS_LM73=m +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM77=m +CONFIG_SENSORS_LM78=m +CONFIG_SENSORS_LM80=m +CONFIG_SENSORS_LM83=m +CONFIG_SENSORS_LM85=m +CONFIG_SENSORS_LM87=m +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_LM92=m +CONFIG_SENSORS_LM93=m +CONFIG_SENSORS_LM95234=m +CONFIG_SENSORS_LM95241=m +CONFIG_SENSORS_LM95245=m +CONFIG_SENSORS_PC87360=m +CONFIG_SENSORS_PC87427=m +CONFIG_SENSORS_NTC_THERMISTOR=m +CONFIG_SENSORS_NCT6683=m +CONFIG_SENSORS_NCT6775=m +CONFIG_SENSORS_NCT7802=m +CONFIG_SENSORS_NCT7904=m +# CONFIG_SENSORS_NPCM7XX is not set +CONFIG_SENSORS_PCF8591=m +CONFIG_PMBUS=m +CONFIG_SENSORS_PMBUS=m +CONFIG_SENSORS_ADM1275=m +# CONFIG_SENSORS_IBM_CFFPS is not set +CONFIG_SENSORS_IR35221=m +CONFIG_SENSORS_LM25066=m +CONFIG_SENSORS_LTC2978=m +CONFIG_SENSORS_LTC2978_REGULATOR=y +CONFIG_SENSORS_LTC3815=m +CONFIG_SENSORS_MAX16064=m +CONFIG_SENSORS_MAX20751=m +# CONFIG_SENSORS_MAX31785 is not set +CONFIG_SENSORS_MAX34440=m +CONFIG_SENSORS_MAX8688=m +CONFIG_SENSORS_TPS40422=m +# CONFIG_SENSORS_TPS53679 is not set +CONFIG_SENSORS_UCD9000=m +CONFIG_SENSORS_UCD9200=m +CONFIG_SENSORS_ZL6100=m +CONFIG_SENSORS_SHT15=m +CONFIG_SENSORS_SHT21=m +CONFIG_SENSORS_SHT3x=m +CONFIG_SENSORS_SHTC1=m +CONFIG_SENSORS_SIS5595=m +CONFIG_SENSORS_DME1737=m +CONFIG_SENSORS_EMC1403=m +CONFIG_SENSORS_EMC2103=m +CONFIG_SENSORS_EMC6W201=m +CONFIG_SENSORS_SMSC47M1=m +CONFIG_SENSORS_SMSC47M192=m +CONFIG_SENSORS_SMSC47B397=m +CONFIG_SENSORS_SCH56XX_COMMON=m +CONFIG_SENSORS_SCH5627=m +CONFIG_SENSORS_SCH5636=m +CONFIG_SENSORS_STTS751=m +CONFIG_SENSORS_SMM665=m +CONFIG_SENSORS_ADC128D818=m +CONFIG_SENSORS_ADS1015=m +CONFIG_SENSORS_ADS7828=m +CONFIG_SENSORS_ADS7871=m +CONFIG_SENSORS_AMC6821=m +CONFIG_SENSORS_INA209=m +CONFIG_SENSORS_INA2XX=m +CONFIG_SENSORS_INA3221=m +CONFIG_SENSORS_TC74=m +CONFIG_SENSORS_THMC50=m +CONFIG_SENSORS_TMP102=m +CONFIG_SENSORS_TMP103=m +CONFIG_SENSORS_TMP108=m +CONFIG_SENSORS_TMP401=m +CONFIG_SENSORS_TMP421=m +CONFIG_SENSORS_VIA_CPUTEMP=m +CONFIG_SENSORS_VIA686A=m +CONFIG_SENSORS_VT1211=m +CONFIG_SENSORS_VT8231=m +# CONFIG_SENSORS_W83773G is not set +CONFIG_SENSORS_W83781D=m +CONFIG_SENSORS_W83791D=m +CONFIG_SENSORS_W83792D=m +CONFIG_SENSORS_W83793=m +CONFIG_SENSORS_W83795=m +# CONFIG_SENSORS_W83795_FANCTRL is not set +CONFIG_SENSORS_W83L785TS=m +CONFIG_SENSORS_W83L786NG=m +CONFIG_SENSORS_W83627HF=m +CONFIG_SENSORS_W83627EHF=m +CONFIG_SENSORS_WM831X=m +CONFIG_SENSORS_WM8350=m +CONFIG_SENSORS_XGENE=m + +# +# ACPI drivers +# +CONFIG_SENSORS_ACPI_POWER=m +CONFIG_SENSORS_ATK0110=m +CONFIG_THERMAL=y +# CONFIG_THERMAL_STATISTICS is not set +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_HWMON=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +# CONFIG_CLOCK_THERMAL is not set +# CONFIG_DEVFREQ_THERMAL is not set +CONFIG_THERMAL_EMULATION=y +CONFIG_INTEL_POWERCLAMP=m +CONFIG_X86_PKG_TEMP_THERMAL=m +CONFIG_INTEL_SOC_DTS_IOSF_CORE=m +CONFIG_INTEL_SOC_DTS_THERMAL=m + +# +# ACPI INT340X thermal drivers +# +CONFIG_INT340X_THERMAL=m +CONFIG_ACPI_THERMAL_REL=m +CONFIG_INT3406_THERMAL=m +CONFIG_INTEL_BXT_PMIC_THERMAL=m +CONFIG_INTEL_PCH_THERMAL=m +CONFIG_GENERIC_ADC_THERMAL=m +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WATCHDOG_NOWAYOUT is not set +CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y +CONFIG_WATCHDOG_SYSFS=y + +# +# Watchdog Device Drivers +# +CONFIG_SOFT_WATCHDOG=m +CONFIG_SOFT_WATCHDOG_PRETIMEOUT=y +CONFIG_DA9052_WATCHDOG=m +CONFIG_DA9055_WATCHDOG=m +CONFIG_DA9063_WATCHDOG=m +CONFIG_DA9062_WATCHDOG=m +CONFIG_MENF21BMC_WATCHDOG=m +# CONFIG_MENZ069_WATCHDOG is not set +CONFIG_WDAT_WDT=m +CONFIG_WM831X_WATCHDOG=m +CONFIG_WM8350_WATCHDOG=m +CONFIG_XILINX_WATCHDOG=m +CONFIG_ZIIRAVE_WATCHDOG=m +CONFIG_CADENCE_WATCHDOG=m +CONFIG_DW_WATCHDOG=m +CONFIG_TWL4030_WATCHDOG=m +CONFIG_MAX63XX_WATCHDOG=m +CONFIG_RETU_WATCHDOG=m +CONFIG_ACQUIRE_WDT=m +CONFIG_ADVANTECH_WDT=m +CONFIG_ALIM1535_WDT=m +CONFIG_ALIM7101_WDT=m +CONFIG_EBC_C384_WDT=m +CONFIG_F71808E_WDT=m +CONFIG_SP5100_TCO=m +CONFIG_SBC_FITPC2_WATCHDOG=m +CONFIG_EUROTECH_WDT=m +CONFIG_IB700_WDT=m +CONFIG_IBMASR=m +CONFIG_WAFER_WDT=m +CONFIG_I6300ESB_WDT=m +CONFIG_IE6XX_WDT=m +CONFIG_ITCO_WDT=m +CONFIG_ITCO_VENDOR_SUPPORT=y +CONFIG_IT8712F_WDT=m +CONFIG_IT87_WDT=m +CONFIG_HP_WATCHDOG=m +CONFIG_KEMPLD_WDT=m +CONFIG_HPWDT_NMI_DECODING=y +CONFIG_SC1200_WDT=m +CONFIG_PC87413_WDT=m +CONFIG_NV_TCO=m +CONFIG_60XX_WDT=m +CONFIG_CPU5_WDT=m +CONFIG_SMSC_SCH311X_WDT=m +CONFIG_SMSC37B787_WDT=m +CONFIG_VIA_WDT=m +CONFIG_W83627HF_WDT=m +CONFIG_W83877F_WDT=m +CONFIG_W83977F_WDT=m +CONFIG_MACHZ_WDT=m +CONFIG_SBC_EPX_C3_WATCHDOG=m +CONFIG_INTEL_MEI_WDT=m +CONFIG_NI903X_WDT=m +CONFIG_NIC7018_WDT=m +CONFIG_MEN_A21_WDT=m +CONFIG_XEN_WDT=m + +# +# PCI-based Watchdog Cards +# +CONFIG_PCIPCWATCHDOG=m +CONFIG_WDTPCI=m + +# +# USB-based Watchdog Cards +# +CONFIG_USBPCWATCHDOG=m + +# +# Watchdog Pretimeout Governors +# +CONFIG_WATCHDOG_PRETIMEOUT_GOV=y +CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP=y +# CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC is not set +CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP=y +CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC=m +CONFIG_SSB_POSSIBLE=y +CONFIG_SSB=m +CONFIG_SSB_SPROM=y +CONFIG_SSB_BLOCKIO=y +CONFIG_SSB_PCIHOST_POSSIBLE=y +CONFIG_SSB_PCIHOST=y +CONFIG_SSB_B43_PCI_BRIDGE=y +CONFIG_SSB_PCMCIAHOST_POSSIBLE=y +# CONFIG_SSB_PCMCIAHOST is not set +CONFIG_SSB_SDIOHOST_POSSIBLE=y +CONFIG_SSB_SDIOHOST=y +CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y +CONFIG_SSB_DRIVER_PCICORE=y +CONFIG_SSB_DRIVER_GPIO=y +CONFIG_BCMA_POSSIBLE=y +CONFIG_BCMA=m +CONFIG_BCMA_BLOCKIO=y +CONFIG_BCMA_HOST_PCI_POSSIBLE=y +CONFIG_BCMA_HOST_PCI=y +CONFIG_BCMA_HOST_SOC=y +CONFIG_BCMA_DRIVER_PCI=y +CONFIG_BCMA_SFLASH=y +CONFIG_BCMA_DRIVER_GMAC_CMN=y +CONFIG_BCMA_DRIVER_GPIO=y +# CONFIG_BCMA_DEBUG is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +CONFIG_MFD_AS3711=y +CONFIG_PMIC_ADP5520=y +CONFIG_MFD_AAT2870_CORE=y +CONFIG_MFD_BCM590XX=m +# CONFIG_MFD_BD9571MWV is not set +CONFIG_MFD_AXP20X=m +CONFIG_MFD_AXP20X_I2C=m +CONFIG_MFD_CROS_EC=m +# CONFIG_MFD_CROS_EC_CHARDEV is not set +# CONFIG_MFD_MADERA is not set +CONFIG_PMIC_DA903X=y +CONFIG_PMIC_DA9052=y +CONFIG_MFD_DA9052_SPI=y +CONFIG_MFD_DA9052_I2C=y +CONFIG_MFD_DA9055=y +CONFIG_MFD_DA9062=m +CONFIG_MFD_DA9063=y +CONFIG_MFD_DA9150=m +CONFIG_MFD_DLN2=m +CONFIG_MFD_MC13XXX=m +CONFIG_MFD_MC13XXX_SPI=m +CONFIG_MFD_MC13XXX_I2C=m +CONFIG_HTC_PASIC3=m +CONFIG_HTC_I2CPLD=y +CONFIG_MFD_INTEL_QUARK_I2C_GPIO=m +CONFIG_LPC_ICH=m +CONFIG_LPC_SCH=m +CONFIG_INTEL_SOC_PMIC=y +CONFIG_INTEL_SOC_PMIC_BXTWC=m +# CONFIG_INTEL_SOC_PMIC_CHTWC is not set +# CONFIG_INTEL_SOC_PMIC_CHTDC_TI is not set +CONFIG_MFD_INTEL_LPSS=m +CONFIG_MFD_INTEL_LPSS_ACPI=m +CONFIG_MFD_INTEL_LPSS_PCI=m +CONFIG_MFD_JANZ_CMODIO=m +CONFIG_MFD_KEMPLD=m +CONFIG_MFD_88PM800=m +CONFIG_MFD_88PM805=m +CONFIG_MFD_88PM860X=y +CONFIG_MFD_MAX14577=y +CONFIG_MFD_MAX77693=y +CONFIG_MFD_MAX77843=y +CONFIG_MFD_MAX8907=m +CONFIG_MFD_MAX8925=y +CONFIG_MFD_MAX8997=y +CONFIG_MFD_MAX8998=y +CONFIG_MFD_MT6397=m +CONFIG_MFD_MENF21BMC=m +CONFIG_EZX_PCAP=y +CONFIG_MFD_VIPERBOARD=m +CONFIG_MFD_RETU=m +CONFIG_MFD_PCF50633=m +CONFIG_PCF50633_ADC=m +CONFIG_PCF50633_GPIO=m +CONFIG_UCB1400_CORE=m +CONFIG_MFD_RDC321X=m +CONFIG_MFD_RT5033=m +CONFIG_MFD_RC5T583=y +CONFIG_MFD_SEC_CORE=y +CONFIG_MFD_SI476X_CORE=m +CONFIG_MFD_SM501=m +CONFIG_MFD_SM501_GPIO=y +CONFIG_MFD_SKY81452=m +CONFIG_MFD_SMSC=y +CONFIG_ABX500_CORE=y +CONFIG_AB3100_CORE=y +CONFIG_AB3100_OTP=m +CONFIG_MFD_SYSCON=y +CONFIG_MFD_TI_AM335X_TSCADC=m +CONFIG_MFD_LP3943=m +CONFIG_MFD_LP8788=y +CONFIG_MFD_TI_LMU=m +CONFIG_MFD_PALMAS=y +CONFIG_TPS6105X=m +CONFIG_TPS65010=m +CONFIG_TPS6507X=m +CONFIG_MFD_TPS65086=m +CONFIG_MFD_TPS65090=y +# CONFIG_MFD_TPS68470 is not set +CONFIG_MFD_TI_LP873X=m +CONFIG_MFD_TPS6586X=y +CONFIG_MFD_TPS65910=y +CONFIG_MFD_TPS65912=y +CONFIG_MFD_TPS65912_I2C=y +CONFIG_MFD_TPS65912_SPI=y +CONFIG_MFD_TPS80031=y +CONFIG_TWL4030_CORE=y +CONFIG_MFD_TWL4030_AUDIO=y +CONFIG_TWL6040_CORE=y +CONFIG_MFD_WL1273_CORE=m +CONFIG_MFD_LM3533=m +CONFIG_MFD_VX855=m +CONFIG_MFD_ARIZONA=y +CONFIG_MFD_ARIZONA_I2C=m +CONFIG_MFD_ARIZONA_SPI=m +CONFIG_MFD_CS47L24=y +CONFIG_MFD_WM5102=y +CONFIG_MFD_WM5110=y +CONFIG_MFD_WM8997=y +CONFIG_MFD_WM8998=y +CONFIG_MFD_WM8400=y +CONFIG_MFD_WM831X=y +CONFIG_MFD_WM831X_I2C=y +CONFIG_MFD_WM831X_SPI=y +CONFIG_MFD_WM8350=y +CONFIG_MFD_WM8350_I2C=y +CONFIG_MFD_WM8994=m +# CONFIG_RAVE_SP_CORE is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +CONFIG_REGULATOR_FIXED_VOLTAGE=m +CONFIG_REGULATOR_VIRTUAL_CONSUMER=m +CONFIG_REGULATOR_USERSPACE_CONSUMER=m +# CONFIG_REGULATOR_88PG86X is not set +CONFIG_REGULATOR_88PM800=m +CONFIG_REGULATOR_88PM8607=m +CONFIG_REGULATOR_ACT8865=m +CONFIG_REGULATOR_AD5398=m +CONFIG_REGULATOR_ANATOP=m +CONFIG_REGULATOR_AAT2870=m +CONFIG_REGULATOR_AB3100=m +CONFIG_REGULATOR_ARIZONA_LDO1=m +CONFIG_REGULATOR_ARIZONA_MICSUPP=m +CONFIG_REGULATOR_AS3711=m +CONFIG_REGULATOR_AXP20X=m +CONFIG_REGULATOR_BCM590XX=m +CONFIG_REGULATOR_DA903X=m +CONFIG_REGULATOR_DA9052=m +CONFIG_REGULATOR_DA9055=m +CONFIG_REGULATOR_DA9062=m +CONFIG_REGULATOR_DA9063=m +CONFIG_REGULATOR_DA9210=m +CONFIG_REGULATOR_DA9211=m +CONFIG_REGULATOR_FAN53555=m +CONFIG_REGULATOR_GPIO=m +CONFIG_REGULATOR_ISL9305=m +CONFIG_REGULATOR_ISL6271A=m +CONFIG_REGULATOR_LM363X=m +CONFIG_REGULATOR_LP3971=m +CONFIG_REGULATOR_LP3972=m +CONFIG_REGULATOR_LP872X=m +CONFIG_REGULATOR_LP8755=m +CONFIG_REGULATOR_LP8788=m +CONFIG_REGULATOR_LTC3589=m +CONFIG_REGULATOR_LTC3676=m +CONFIG_REGULATOR_MAX14577=m +CONFIG_REGULATOR_MAX1586=m +CONFIG_REGULATOR_MAX8649=m +CONFIG_REGULATOR_MAX8660=m +CONFIG_REGULATOR_MAX8907=m +CONFIG_REGULATOR_MAX8925=m +CONFIG_REGULATOR_MAX8952=m +CONFIG_REGULATOR_MAX8997=m +CONFIG_REGULATOR_MAX8998=m +CONFIG_REGULATOR_MAX77693=m +CONFIG_REGULATOR_MC13XXX_CORE=m +CONFIG_REGULATOR_MC13783=m +CONFIG_REGULATOR_MC13892=m +CONFIG_REGULATOR_MT6311=m +CONFIG_REGULATOR_MT6323=m +CONFIG_REGULATOR_MT6397=m +CONFIG_REGULATOR_PALMAS=m +CONFIG_REGULATOR_PCAP=m +CONFIG_REGULATOR_PCF50633=m +CONFIG_REGULATOR_PFUZE100=m +CONFIG_REGULATOR_PV88060=m +CONFIG_REGULATOR_PV88080=m +CONFIG_REGULATOR_PV88090=m +CONFIG_REGULATOR_PWM=m +CONFIG_REGULATOR_QCOM_SPMI=m +CONFIG_REGULATOR_RC5T583=m +CONFIG_REGULATOR_RT5033=m +CONFIG_REGULATOR_S2MPA01=m +CONFIG_REGULATOR_S2MPS11=m +CONFIG_REGULATOR_S5M8767=m +CONFIG_REGULATOR_SKY81452=m +CONFIG_REGULATOR_TPS51632=m +CONFIG_REGULATOR_TPS6105X=m +CONFIG_REGULATOR_TPS62360=m +CONFIG_REGULATOR_TPS65023=m +CONFIG_REGULATOR_TPS6507X=m +CONFIG_REGULATOR_TPS65086=m +CONFIG_REGULATOR_TPS65090=m +CONFIG_REGULATOR_TPS65132=m +CONFIG_REGULATOR_TPS6524X=m +CONFIG_REGULATOR_TPS6586X=m +CONFIG_REGULATOR_TPS65910=m +CONFIG_REGULATOR_TPS65912=m +CONFIG_REGULATOR_TPS80031=m +CONFIG_REGULATOR_TWL4030=m +CONFIG_REGULATOR_WM831X=m +CONFIG_REGULATOR_WM8350=m +CONFIG_REGULATOR_WM8400=m +CONFIG_REGULATOR_WM8994=m +CONFIG_CEC_CORE=m +CONFIG_RC_CORE=m +CONFIG_RC_MAP=m +# CONFIG_LIRC is not set +CONFIG_RC_DECODERS=y +CONFIG_IR_NEC_DECODER=m +CONFIG_IR_RC5_DECODER=m +CONFIG_IR_RC6_DECODER=m +CONFIG_IR_JVC_DECODER=m +CONFIG_IR_SONY_DECODER=m +CONFIG_IR_SANYO_DECODER=m +CONFIG_IR_SHARP_DECODER=m +CONFIG_IR_MCE_KBD_DECODER=m +CONFIG_IR_XMP_DECODER=m +# CONFIG_IR_IMON_DECODER is not set +CONFIG_RC_DEVICES=y +CONFIG_RC_ATI_REMOTE=m +CONFIG_IR_ENE=m +CONFIG_IR_IMON=m +# CONFIG_IR_IMON_RAW is not set +CONFIG_IR_MCEUSB=m +CONFIG_IR_ITE_CIR=m +CONFIG_IR_FINTEK=m +CONFIG_IR_NUVOTON=m +CONFIG_IR_REDRAT3=m +CONFIG_IR_STREAMZAP=m +CONFIG_IR_WINBOND_CIR=m +CONFIG_IR_IGORPLUGUSB=m +CONFIG_IR_IGUANA=m +CONFIG_IR_TTUSBIR=m +CONFIG_RC_LOOPBACK=m +CONFIG_IR_SERIAL=m +CONFIG_IR_SERIAL_TRANSMITTER=y +CONFIG_IR_SIR=m +CONFIG_MEDIA_SUPPORT=m + +# +# Multimedia core support +# +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_ANALOG_TV_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_RADIO_SUPPORT=y +CONFIG_MEDIA_SDR_SUPPORT=y +CONFIG_MEDIA_CEC_SUPPORT=y +CONFIG_MEDIA_CEC_RC=y +CONFIG_MEDIA_CONTROLLER=y +# CONFIG_MEDIA_CONTROLLER_DVB is not set +CONFIG_VIDEO_DEV=m +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_V4L2=m +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_PCI_SKELETON is not set +CONFIG_VIDEO_TUNER=m +CONFIG_V4L2_MEM2MEM_DEV=m +CONFIG_V4L2_FLASH_LED_CLASS=m +CONFIG_V4L2_FWNODE=m +CONFIG_VIDEOBUF_GEN=m +CONFIG_VIDEOBUF_DMA_SG=m +CONFIG_VIDEOBUF_VMALLOC=m +CONFIG_DVB_CORE=m +# CONFIG_DVB_MMAP is not set +CONFIG_DVB_NET=y +CONFIG_TTPCI_EEPROM=m +CONFIG_DVB_MAX_ADAPTERS=8 +CONFIG_DVB_DYNAMIC_MINORS=y +# CONFIG_DVB_DEMUX_SECTION_LOSS_LOG is not set +# CONFIG_DVB_ULE_DEBUG is not set + +# +# Media drivers +# +CONFIG_MEDIA_USB_SUPPORT=y + +# +# Webcam devices +# +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +CONFIG_USB_M5602=m +CONFIG_USB_STV06XX=m +CONFIG_USB_GL860=m +CONFIG_USB_GSPCA_BENQ=m +CONFIG_USB_GSPCA_CONEX=m +CONFIG_USB_GSPCA_CPIA1=m +CONFIG_USB_GSPCA_DTCS033=m +CONFIG_USB_GSPCA_ETOMS=m +CONFIG_USB_GSPCA_FINEPIX=m +CONFIG_USB_GSPCA_JEILINJ=m +CONFIG_USB_GSPCA_JL2005BCD=m +CONFIG_USB_GSPCA_KINECT=m +CONFIG_USB_GSPCA_KONICA=m +CONFIG_USB_GSPCA_MARS=m +CONFIG_USB_GSPCA_MR97310A=m +CONFIG_USB_GSPCA_NW80X=m +CONFIG_USB_GSPCA_OV519=m +CONFIG_USB_GSPCA_OV534=m +CONFIG_USB_GSPCA_OV534_9=m +CONFIG_USB_GSPCA_PAC207=m +CONFIG_USB_GSPCA_PAC7302=m +CONFIG_USB_GSPCA_PAC7311=m +CONFIG_USB_GSPCA_SE401=m +CONFIG_USB_GSPCA_SN9C2028=m +CONFIG_USB_GSPCA_SN9C20X=m +CONFIG_USB_GSPCA_SONIXB=m +CONFIG_USB_GSPCA_SONIXJ=m +CONFIG_USB_GSPCA_SPCA500=m +CONFIG_USB_GSPCA_SPCA501=m +CONFIG_USB_GSPCA_SPCA505=m +CONFIG_USB_GSPCA_SPCA506=m +CONFIG_USB_GSPCA_SPCA508=m +CONFIG_USB_GSPCA_SPCA561=m +CONFIG_USB_GSPCA_SPCA1528=m +CONFIG_USB_GSPCA_SQ905=m +CONFIG_USB_GSPCA_SQ905C=m +CONFIG_USB_GSPCA_SQ930X=m +CONFIG_USB_GSPCA_STK014=m +CONFIG_USB_GSPCA_STK1135=m +CONFIG_USB_GSPCA_STV0680=m +CONFIG_USB_GSPCA_SUNPLUS=m +CONFIG_USB_GSPCA_T613=m +CONFIG_USB_GSPCA_TOPRO=m +CONFIG_USB_GSPCA_TOUPTEK=m +CONFIG_USB_GSPCA_TV8532=m +CONFIG_USB_GSPCA_VC032X=m +CONFIG_USB_GSPCA_VICAM=m +CONFIG_USB_GSPCA_XIRLINK_CIT=m +CONFIG_USB_GSPCA_ZC3XX=m +CONFIG_USB_PWC=m +# CONFIG_USB_PWC_DEBUG is not set +CONFIG_USB_PWC_INPUT_EVDEV=y +CONFIG_VIDEO_CPIA2=m +CONFIG_USB_ZR364XX=m +CONFIG_USB_STKWEBCAM=m +CONFIG_USB_S2255=m +CONFIG_VIDEO_USBTV=m + +# +# Analog TV USB devices +# +CONFIG_VIDEO_PVRUSB2=m +CONFIG_VIDEO_PVRUSB2_SYSFS=y +CONFIG_VIDEO_PVRUSB2_DVB=y +# CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set +CONFIG_VIDEO_HDPVR=m +CONFIG_VIDEO_USBVISION=m +CONFIG_VIDEO_STK1160_COMMON=m +CONFIG_VIDEO_STK1160=m +CONFIG_VIDEO_GO7007=m +CONFIG_VIDEO_GO7007_USB=m +CONFIG_VIDEO_GO7007_LOADER=m +CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m + +# +# Analog/digital TV USB devices +# +CONFIG_VIDEO_AU0828=m +CONFIG_VIDEO_AU0828_V4L2=y +CONFIG_VIDEO_AU0828_RC=y +CONFIG_VIDEO_CX231XX=m +CONFIG_VIDEO_CX231XX_RC=y +CONFIG_VIDEO_CX231XX_ALSA=m +CONFIG_VIDEO_CX231XX_DVB=m +CONFIG_VIDEO_TM6000=m +CONFIG_VIDEO_TM6000_ALSA=m +CONFIG_VIDEO_TM6000_DVB=m + +# +# Digital TV USB devices +# +CONFIG_DVB_USB=m +# CONFIG_DVB_USB_DEBUG is not set +CONFIG_DVB_USB_DIB3000MC=m +CONFIG_DVB_USB_A800=m +CONFIG_DVB_USB_DIBUSB_MB=m +# CONFIG_DVB_USB_DIBUSB_MB_FAULTY is not set +CONFIG_DVB_USB_DIBUSB_MC=m +CONFIG_DVB_USB_DIB0700=m +CONFIG_DVB_USB_UMT_010=m +CONFIG_DVB_USB_CXUSB=m +CONFIG_DVB_USB_M920X=m +CONFIG_DVB_USB_DIGITV=m +CONFIG_DVB_USB_VP7045=m +CONFIG_DVB_USB_VP702X=m +CONFIG_DVB_USB_GP8PSK=m +CONFIG_DVB_USB_NOVA_T_USB2=m +CONFIG_DVB_USB_TTUSB2=m +CONFIG_DVB_USB_DTT200U=m +CONFIG_DVB_USB_OPERA1=m +CONFIG_DVB_USB_AF9005=m +CONFIG_DVB_USB_AF9005_REMOTE=m +CONFIG_DVB_USB_PCTV452E=m +CONFIG_DVB_USB_DW2102=m +CONFIG_DVB_USB_CINERGY_T2=m +CONFIG_DVB_USB_DTV5100=m +CONFIG_DVB_USB_AZ6027=m +CONFIG_DVB_USB_TECHNISAT_USB2=m +CONFIG_DVB_USB_V2=m +CONFIG_DVB_USB_AF9015=m +CONFIG_DVB_USB_AF9035=m +CONFIG_DVB_USB_ANYSEE=m +CONFIG_DVB_USB_AU6610=m +CONFIG_DVB_USB_AZ6007=m +CONFIG_DVB_USB_CE6230=m +CONFIG_DVB_USB_EC168=m +CONFIG_DVB_USB_GL861=m +CONFIG_DVB_USB_LME2510=m +CONFIG_DVB_USB_MXL111SF=m +CONFIG_DVB_USB_RTL28XXU=m +CONFIG_DVB_USB_DVBSKY=m +CONFIG_DVB_USB_ZD1301=m +CONFIG_DVB_TTUSB_BUDGET=m +CONFIG_DVB_TTUSB_DEC=m +CONFIG_SMS_USB_DRV=m +CONFIG_DVB_B2C2_FLEXCOP_USB=m +# CONFIG_DVB_B2C2_FLEXCOP_USB_DEBUG is not set +CONFIG_DVB_AS102=m + +# +# Webcam, TV (analog/digital) USB devices +# +CONFIG_VIDEO_EM28XX=m +CONFIG_VIDEO_EM28XX_V4L2=m +CONFIG_VIDEO_EM28XX_ALSA=m +CONFIG_VIDEO_EM28XX_DVB=m +CONFIG_VIDEO_EM28XX_RC=m + +# +# Software defined radio USB devices +# +CONFIG_USB_AIRSPY=m +CONFIG_USB_HACKRF=m +CONFIG_USB_MSI2500=m + +# +# USB HDMI CEC adapters +# +CONFIG_USB_PULSE8_CEC=m +CONFIG_USB_RAINSHADOW_CEC=m +CONFIG_MEDIA_PCI_SUPPORT=y + +# +# Media capture support +# +CONFIG_VIDEO_MEYE=m +CONFIG_VIDEO_SOLO6X10=m +CONFIG_VIDEO_TW5864=m +CONFIG_VIDEO_TW68=m +CONFIG_VIDEO_TW686X=m + +# +# Media capture/analog TV support +# +CONFIG_VIDEO_IVTV=m +# CONFIG_VIDEO_IVTV_DEPRECATED_IOCTLS is not set +CONFIG_VIDEO_IVTV_ALSA=m +CONFIG_VIDEO_FB_IVTV=m +CONFIG_VIDEO_HEXIUM_GEMINI=m +CONFIG_VIDEO_HEXIUM_ORION=m +CONFIG_VIDEO_MXB=m +CONFIG_VIDEO_DT3155=m + +# +# Media capture/analog/hybrid TV support +# +CONFIG_VIDEO_CX18=m +CONFIG_VIDEO_CX18_ALSA=m +CONFIG_VIDEO_CX23885=m +CONFIG_MEDIA_ALTERA_CI=m +CONFIG_VIDEO_CX25821=m +CONFIG_VIDEO_CX25821_ALSA=m +CONFIG_VIDEO_CX88=m +CONFIG_VIDEO_CX88_ALSA=m +CONFIG_VIDEO_CX88_BLACKBIRD=m +CONFIG_VIDEO_CX88_DVB=m +CONFIG_VIDEO_CX88_ENABLE_VP3054=y +CONFIG_VIDEO_CX88_VP3054=m +CONFIG_VIDEO_CX88_MPEG=m +CONFIG_VIDEO_BT848=m +CONFIG_DVB_BT8XX=m +CONFIG_VIDEO_SAA7134=m +CONFIG_VIDEO_SAA7134_ALSA=m +CONFIG_VIDEO_SAA7134_RC=y +CONFIG_VIDEO_SAA7134_DVB=m +CONFIG_VIDEO_SAA7134_GO7007=m +CONFIG_VIDEO_SAA7164=m +CONFIG_VIDEO_COBALT=m + +# +# Media digital TV PCI Adapters +# +CONFIG_DVB_AV7110_IR=y +CONFIG_DVB_AV7110=m +CONFIG_DVB_AV7110_OSD=y +CONFIG_DVB_BUDGET_CORE=m +CONFIG_DVB_BUDGET=m +CONFIG_DVB_BUDGET_CI=m +CONFIG_DVB_BUDGET_AV=m +CONFIG_DVB_BUDGET_PATCH=m +CONFIG_DVB_B2C2_FLEXCOP_PCI=m +# CONFIG_DVB_B2C2_FLEXCOP_PCI_DEBUG is not set +CONFIG_DVB_PLUTO2=m +CONFIG_DVB_DM1105=m +CONFIG_DVB_PT1=m +CONFIG_DVB_PT3=m +CONFIG_MANTIS_CORE=m +CONFIG_DVB_MANTIS=m +CONFIG_DVB_HOPPER=m +CONFIG_DVB_NGENE=m +CONFIG_DVB_DDBRIDGE=m +# CONFIG_DVB_DDBRIDGE_MSIENABLE is not set +CONFIG_DVB_SMIPCIE=m +CONFIG_DVB_NETUP_UNIDVB=m +CONFIG_VIDEO_IPU3_CIO2=m +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_CAFE_CCIC=m +CONFIG_VIDEO_VIA_CAMERA=m +# CONFIG_VIDEO_CADENCE is not set +CONFIG_SOC_CAMERA=m +CONFIG_SOC_CAMERA_PLATFORM=m +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_MEM2MEM_DEINTERLACE=m +CONFIG_VIDEO_SH_VEU=m +CONFIG_V4L_TEST_DRIVERS=y +# CONFIG_VIDEO_VIMC is not set +CONFIG_VIDEO_VIVID=m +CONFIG_VIDEO_VIVID_CEC=y +CONFIG_VIDEO_VIVID_MAX_DEVS=64 +CONFIG_VIDEO_VIM2M=m +# CONFIG_VIDEO_VICODEC is not set +CONFIG_DVB_PLATFORM_DRIVERS=y +CONFIG_CEC_PLATFORM_DRIVERS=y +# CONFIG_VIDEO_CROS_EC_CEC is not set +CONFIG_SDR_PLATFORM_DRIVERS=y + +# +# Supported MMC/SDIO adapters +# +CONFIG_SMS_SDIO_DRV=m +CONFIG_RADIO_ADAPTERS=y +CONFIG_RADIO_TEA575X=m +CONFIG_RADIO_SI470X=m +CONFIG_USB_SI470X=m +CONFIG_I2C_SI470X=m +CONFIG_RADIO_SI4713=m +CONFIG_USB_SI4713=m +CONFIG_PLATFORM_SI4713=m +CONFIG_I2C_SI4713=m +CONFIG_RADIO_SI476X=m +CONFIG_USB_MR800=m +CONFIG_USB_DSBR=m +CONFIG_RADIO_MAXIRADIO=m +CONFIG_RADIO_SHARK=m +CONFIG_RADIO_SHARK2=m +CONFIG_USB_KEENE=m +CONFIG_USB_RAREMONO=m +CONFIG_USB_MA901=m +CONFIG_RADIO_TEA5764=m +CONFIG_RADIO_SAA7706H=m +CONFIG_RADIO_TEF6862=m +CONFIG_RADIO_WL1273=m + +# +# Texas Instruments WL128x FM driver (ST based) +# +CONFIG_RADIO_WL128X=m + +# +# Supported FireWire (IEEE 1394) Adapters +# +CONFIG_DVB_FIREDTV=m +CONFIG_DVB_FIREDTV_INPUT=y +CONFIG_MEDIA_COMMON_OPTIONS=y + +# +# common driver options +# +CONFIG_VIDEO_CX2341X=m +CONFIG_VIDEO_TVEEPROM=m +CONFIG_CYPRESS_FIRMWARE=m +CONFIG_VIDEOBUF2_CORE=m +CONFIG_VIDEOBUF2_V4L2=m +CONFIG_VIDEOBUF2_MEMOPS=m +CONFIG_VIDEOBUF2_DMA_CONTIG=m +CONFIG_VIDEOBUF2_VMALLOC=m +CONFIG_VIDEOBUF2_DMA_SG=m +CONFIG_VIDEOBUF2_DVB=m +CONFIG_DVB_B2C2_FLEXCOP=m +CONFIG_VIDEO_SAA7146=m +CONFIG_VIDEO_SAA7146_VV=m +CONFIG_SMS_SIANO_MDTV=m +CONFIG_SMS_SIANO_RC=y +CONFIG_SMS_SIANO_DEBUGFS=y +CONFIG_VIDEO_V4L2_TPG=m + +# +# Media ancillary drivers (tuners, sensors, i2c, spi, frontends) +# +CONFIG_MEDIA_SUBDRV_AUTOSELECT=y +CONFIG_MEDIA_ATTACH=y +CONFIG_VIDEO_IR_I2C=m + +# +# Audio decoders, processors and mixers +# +CONFIG_VIDEO_TVAUDIO=m +CONFIG_VIDEO_TDA7432=m +CONFIG_VIDEO_TDA9840=m +CONFIG_VIDEO_TEA6415C=m +CONFIG_VIDEO_TEA6420=m +CONFIG_VIDEO_MSP3400=m +CONFIG_VIDEO_CS3308=m +CONFIG_VIDEO_CS5345=m +CONFIG_VIDEO_CS53L32A=m +CONFIG_VIDEO_UDA1342=m +CONFIG_VIDEO_WM8775=m +CONFIG_VIDEO_WM8739=m +CONFIG_VIDEO_VP27SMPX=m +CONFIG_VIDEO_SONY_BTF_MPX=m + +# +# RDS decoders +# +CONFIG_VIDEO_SAA6588=m + +# +# Video decoders +# +CONFIG_VIDEO_ADV7604=m +CONFIG_VIDEO_ADV7842=m +CONFIG_VIDEO_BT819=m +CONFIG_VIDEO_BT856=m +CONFIG_VIDEO_BT866=m +CONFIG_VIDEO_KS0127=m +CONFIG_VIDEO_SAA7110=m +CONFIG_VIDEO_SAA711X=m +CONFIG_VIDEO_TVP5150=m +CONFIG_VIDEO_TW2804=m +CONFIG_VIDEO_TW9903=m +CONFIG_VIDEO_TW9906=m +CONFIG_VIDEO_VPX3220=m + +# +# Video and audio decoders +# +CONFIG_VIDEO_SAA717X=m +CONFIG_VIDEO_CX25840=m + +# +# Video encoders +# +CONFIG_VIDEO_SAA7127=m +CONFIG_VIDEO_SAA7185=m +CONFIG_VIDEO_ADV7170=m +CONFIG_VIDEO_ADV7175=m +CONFIG_VIDEO_ADV7511=m + +# +# Camera sensor devices +# +CONFIG_VIDEO_OV2640=m +CONFIG_VIDEO_OV7640=m +CONFIG_VIDEO_OV7670=m +CONFIG_VIDEO_MT9M111=m +CONFIG_VIDEO_MT9V011=m + +# +# Flash devices +# + +# +# Video improvement chips +# +CONFIG_VIDEO_UPD64031A=m +CONFIG_VIDEO_UPD64083=m + +# +# Audio/Video compression chips +# +CONFIG_VIDEO_SAA6752HS=m + +# +# SDR tuner chips +# + +# +# Miscellaneous helper chips +# +CONFIG_VIDEO_M52790=m + +# +# Sensors used on soc_camera driver +# + +# +# soc_camera sensor drivers +# +CONFIG_SOC_CAMERA_MT9M001=m +CONFIG_SOC_CAMERA_MT9M111=m +CONFIG_SOC_CAMERA_MT9T112=m +CONFIG_SOC_CAMERA_MT9V022=m +CONFIG_SOC_CAMERA_OV5642=m +CONFIG_SOC_CAMERA_OV772X=m +CONFIG_SOC_CAMERA_OV9640=m +CONFIG_SOC_CAMERA_OV9740=m +CONFIG_SOC_CAMERA_RJ54N1=m +CONFIG_SOC_CAMERA_TW9910=m + +# +# Media SPI Adapters +# +# CONFIG_CXD2880_SPI_DRV is not set +CONFIG_MEDIA_TUNER=m +CONFIG_MEDIA_TUNER_SIMPLE=m +CONFIG_MEDIA_TUNER_TDA18250=m +CONFIG_MEDIA_TUNER_TDA8290=m +CONFIG_MEDIA_TUNER_TDA827X=m +CONFIG_MEDIA_TUNER_TDA18271=m +CONFIG_MEDIA_TUNER_TDA9887=m +CONFIG_MEDIA_TUNER_TEA5761=m +CONFIG_MEDIA_TUNER_TEA5767=m +CONFIG_MEDIA_TUNER_MSI001=m +CONFIG_MEDIA_TUNER_MT20XX=m +CONFIG_MEDIA_TUNER_MT2060=m +CONFIG_MEDIA_TUNER_MT2063=m +CONFIG_MEDIA_TUNER_MT2266=m +CONFIG_MEDIA_TUNER_MT2131=m +CONFIG_MEDIA_TUNER_QT1010=m +CONFIG_MEDIA_TUNER_XC2028=m +CONFIG_MEDIA_TUNER_XC5000=m +CONFIG_MEDIA_TUNER_XC4000=m +CONFIG_MEDIA_TUNER_MXL5005S=m +CONFIG_MEDIA_TUNER_MXL5007T=m +CONFIG_MEDIA_TUNER_MC44S803=m +CONFIG_MEDIA_TUNER_MAX2165=m +CONFIG_MEDIA_TUNER_TDA18218=m +CONFIG_MEDIA_TUNER_FC0011=m +CONFIG_MEDIA_TUNER_FC0012=m +CONFIG_MEDIA_TUNER_FC0013=m +CONFIG_MEDIA_TUNER_TDA18212=m +CONFIG_MEDIA_TUNER_E4000=m +CONFIG_MEDIA_TUNER_FC2580=m +CONFIG_MEDIA_TUNER_M88RS6000T=m +CONFIG_MEDIA_TUNER_TUA9001=m +CONFIG_MEDIA_TUNER_SI2157=m +CONFIG_MEDIA_TUNER_IT913X=m +CONFIG_MEDIA_TUNER_R820T=m +CONFIG_MEDIA_TUNER_MXL301RF=m +CONFIG_MEDIA_TUNER_QM1D1C0042=m +CONFIG_MEDIA_TUNER_QM1D1B0004=m + +# +# Multistandard (satellite) frontends +# +CONFIG_DVB_STB0899=m +CONFIG_DVB_STB6100=m +CONFIG_DVB_STV090x=m +CONFIG_DVB_STV0910=m +CONFIG_DVB_STV6110x=m +CONFIG_DVB_STV6111=m +CONFIG_DVB_MXL5XX=m +CONFIG_DVB_M88DS3103=m + +# +# Multistandard (cable + terrestrial) frontends +# +CONFIG_DVB_DRXK=m +CONFIG_DVB_TDA18271C2DD=m +CONFIG_DVB_SI2165=m +CONFIG_DVB_MN88472=m +CONFIG_DVB_MN88473=m + +# +# DVB-S (satellite) frontends +# +CONFIG_DVB_CX24110=m +CONFIG_DVB_CX24123=m +CONFIG_DVB_MT312=m +CONFIG_DVB_ZL10036=m +CONFIG_DVB_ZL10039=m +CONFIG_DVB_S5H1420=m +CONFIG_DVB_STV0288=m +CONFIG_DVB_STB6000=m +CONFIG_DVB_STV0299=m +CONFIG_DVB_STV6110=m +CONFIG_DVB_STV0900=m +CONFIG_DVB_TDA8083=m +CONFIG_DVB_TDA10086=m +CONFIG_DVB_TDA8261=m +CONFIG_DVB_VES1X93=m +CONFIG_DVB_TUNER_ITD1000=m +CONFIG_DVB_TUNER_CX24113=m +CONFIG_DVB_TDA826X=m +CONFIG_DVB_TUA6100=m +CONFIG_DVB_CX24116=m +CONFIG_DVB_CX24117=m +CONFIG_DVB_CX24120=m +CONFIG_DVB_SI21XX=m +CONFIG_DVB_TS2020=m +CONFIG_DVB_DS3000=m +CONFIG_DVB_MB86A16=m +CONFIG_DVB_TDA10071=m + +# +# DVB-T (terrestrial) frontends +# +CONFIG_DVB_SP8870=m +CONFIG_DVB_SP887X=m +CONFIG_DVB_CX22700=m +CONFIG_DVB_CX22702=m +CONFIG_DVB_DRXD=m +CONFIG_DVB_L64781=m +CONFIG_DVB_TDA1004X=m +CONFIG_DVB_NXT6000=m +CONFIG_DVB_MT352=m +CONFIG_DVB_ZL10353=m +CONFIG_DVB_DIB3000MB=m +CONFIG_DVB_DIB3000MC=m +CONFIG_DVB_DIB7000M=m +CONFIG_DVB_DIB7000P=m +CONFIG_DVB_TDA10048=m +CONFIG_DVB_AF9013=m +CONFIG_DVB_EC100=m +CONFIG_DVB_STV0367=m +CONFIG_DVB_CXD2820R=m +CONFIG_DVB_CXD2841ER=m +CONFIG_DVB_RTL2830=m +CONFIG_DVB_RTL2832=m +CONFIG_DVB_RTL2832_SDR=m +CONFIG_DVB_SI2168=m +CONFIG_DVB_AS102_FE=m +CONFIG_DVB_ZD1301_DEMOD=m +CONFIG_DVB_GP8PSK_FE=m + +# +# DVB-C (cable) frontends +# +CONFIG_DVB_VES1820=m +CONFIG_DVB_TDA10021=m +CONFIG_DVB_TDA10023=m +CONFIG_DVB_STV0297=m + +# +# ATSC (North American/Korean Terrestrial/Cable DTV) frontends +# +CONFIG_DVB_NXT200X=m +CONFIG_DVB_OR51211=m +CONFIG_DVB_OR51132=m +CONFIG_DVB_BCM3510=m +CONFIG_DVB_LGDT330X=m +CONFIG_DVB_LGDT3305=m +CONFIG_DVB_LGDT3306A=m +CONFIG_DVB_LG2160=m +CONFIG_DVB_S5H1409=m +CONFIG_DVB_AU8522=m +CONFIG_DVB_AU8522_DTV=m +CONFIG_DVB_AU8522_V4L=m +CONFIG_DVB_S5H1411=m + +# +# ISDB-T (terrestrial) frontends +# +CONFIG_DVB_S921=m +CONFIG_DVB_DIB8000=m +CONFIG_DVB_MB86A20S=m + +# +# ISDB-S (satellite) & ISDB-T (terrestrial) frontends +# +CONFIG_DVB_TC90522=m + +# +# Digital terrestrial only tuners/PLL +# +CONFIG_DVB_PLL=m +CONFIG_DVB_TUNER_DIB0070=m +CONFIG_DVB_TUNER_DIB0090=m + +# +# SEC control devices for DVB-S +# +CONFIG_DVB_DRX39XYJ=m +CONFIG_DVB_LNBH25=m +CONFIG_DVB_LNBP21=m +CONFIG_DVB_LNBP22=m +CONFIG_DVB_ISL6405=m +CONFIG_DVB_ISL6421=m +CONFIG_DVB_ISL6423=m +CONFIG_DVB_A8293=m +CONFIG_DVB_LGS8GXX=m +CONFIG_DVB_ATBM8830=m +CONFIG_DVB_TDA665x=m +CONFIG_DVB_IX2505V=m +CONFIG_DVB_M88RS2000=m +CONFIG_DVB_AF9033=m +CONFIG_DVB_HORUS3A=m +CONFIG_DVB_ASCOT2E=m +CONFIG_DVB_HELENE=m + +# +# Common Interface (EN50221) controller drivers +# +CONFIG_DVB_CXD2099=m +CONFIG_DVB_SP2=m + +# +# Tools to develop new frontends +# +CONFIG_DVB_DUMMY_FE=m + +# +# Graphics support +# +CONFIG_AGP=y +CONFIG_AGP_AMD64=y +CONFIG_AGP_INTEL=y +CONFIG_AGP_SIS=m +CONFIG_AGP_VIA=y +CONFIG_INTEL_GTT=y +CONFIG_VGA_ARB=y +CONFIG_VGA_ARB_MAX_GPUS=16 +CONFIG_VGA_SWITCHEROO=y +CONFIG_DRM=m +CONFIG_DRM_MIPI_DSI=y +CONFIG_DRM_DP_AUX_CHARDEV=y +# CONFIG_DRM_DEBUG_SELFTEST is not set +CONFIG_DRM_KMS_HELPER=m +CONFIG_DRM_KMS_FB_HELPER=y +CONFIG_DRM_FBDEV_EMULATION=y +CONFIG_DRM_FBDEV_OVERALLOC=100 +# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set +CONFIG_DRM_LOAD_EDID_FIRMWARE=y +# CONFIG_DRM_DP_CEC is not set +CONFIG_DRM_TTM=m +CONFIG_DRM_GEM_CMA_HELPER=y +CONFIG_DRM_KMS_CMA_HELPER=y +CONFIG_DRM_VM=y +CONFIG_DRM_SCHED=m + +# +# I2C encoder or helper chips +# +CONFIG_DRM_I2C_CH7006=m +CONFIG_DRM_I2C_SIL164=m +CONFIG_DRM_I2C_NXP_TDA998X=m +# CONFIG_DRM_I2C_NXP_TDA9950 is not set +CONFIG_DRM_RADEON=m +# CONFIG_DRM_RADEON_USERPTR is not set +CONFIG_DRM_AMDGPU=m +CONFIG_DRM_AMDGPU_SI=y +CONFIG_DRM_AMDGPU_CIK=y +CONFIG_DRM_AMDGPU_USERPTR=y +# CONFIG_DRM_AMDGPU_GART_DEBUGFS is not set + +# +# ACP (Audio CoProcessor) Configuration +# +CONFIG_DRM_AMD_ACP=y + +# +# Display Engine Configuration +# +CONFIG_DRM_AMD_DC=y +CONFIG_DRM_AMD_DC_DCN1_0=y +# CONFIG_DEBUG_KERNEL_DC is not set + +# +# AMD Library routines +# +CONFIG_CHASH=m +# CONFIG_CHASH_STATS is not set +# CONFIG_CHASH_SELFTEST is not set +CONFIG_DRM_NOUVEAU=m +CONFIG_NOUVEAU_DEBUG=5 +CONFIG_NOUVEAU_DEBUG_DEFAULT=3 +# CONFIG_NOUVEAU_DEBUG_MMU is not set +CONFIG_DRM_NOUVEAU_BACKLIGHT=y +CONFIG_DRM_I915=m +CONFIG_DRM_I915_ALPHA_SUPPORT=y +CONFIG_DRM_I915_CAPTURE_ERROR=y +CONFIG_DRM_I915_COMPRESS_ERROR=y +CONFIG_DRM_I915_USERPTR=y +CONFIG_DRM_I915_GVT=y +CONFIG_DRM_I915_GVT_KVMGT=m + +# +# drm/i915 Debugging +# +# CONFIG_DRM_I915_WERROR is not set +# CONFIG_DRM_I915_DEBUG is not set +# CONFIG_DRM_I915_SW_FENCE_DEBUG_OBJECTS is not set +# CONFIG_DRM_I915_SW_FENCE_CHECK_DAG is not set +# CONFIG_DRM_I915_DEBUG_GUC is not set +# CONFIG_DRM_I915_SELFTEST is not set +# CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS is not set +# CONFIG_DRM_I915_DEBUG_VBLANK_EVADE is not set +CONFIG_DRM_VGEM=m +# CONFIG_DRM_VKMS is not set +CONFIG_DRM_VMWGFX=m +CONFIG_DRM_VMWGFX_FBCON=y +CONFIG_DRM_GMA500=m +CONFIG_DRM_GMA600=y +CONFIG_DRM_GMA3600=y +CONFIG_DRM_UDL=m +CONFIG_DRM_AST=m +CONFIG_DRM_MGAG200=m +CONFIG_DRM_CIRRUS_QEMU=m +CONFIG_DRM_QXL=m +# CONFIG_DRM_BOCHS is not set +CONFIG_DRM_VIRTIO_GPU=m +CONFIG_DRM_PANEL=y + +# +# Display Panels +# +# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set +CONFIG_DRM_BRIDGE=y +CONFIG_DRM_PANEL_BRIDGE=y + +# +# Display Interface Bridges +# +CONFIG_DRM_ANALOGIX_ANX78XX=m +# CONFIG_HSA_AMD is not set +CONFIG_DRM_HISI_HIBMC=m +CONFIG_DRM_TINYDRM=m +CONFIG_TINYDRM_MIPI_DBI=m +# CONFIG_TINYDRM_ILI9225 is not set +# CONFIG_TINYDRM_ILI9341 is not set +CONFIG_TINYDRM_MI0283QT=m +# CONFIG_TINYDRM_REPAPER is not set +# CONFIG_TINYDRM_ST7586 is not set +# CONFIG_TINYDRM_ST7735R is not set +# CONFIG_DRM_XEN is not set +# CONFIG_DRM_LEGACY is not set +CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y + +# +# Frame buffer Devices +# +CONFIG_FB=y +CONFIG_FIRMWARE_EDID=y +CONFIG_FB_CMDLINE=y +CONFIG_FB_NOTIFY=y +CONFIG_FB_DDC=m +CONFIG_FB_BOOT_VESA_SUPPORT=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +CONFIG_FB_SYS_FILLRECT=m +CONFIG_FB_SYS_COPYAREA=m +CONFIG_FB_SYS_IMAGEBLIT=m +# CONFIG_FB_FOREIGN_ENDIAN is not set +CONFIG_FB_SYS_FOPS=m +CONFIG_FB_DEFERRED_IO=y +CONFIG_FB_HECUBA=m +CONFIG_FB_SVGALIB=m +CONFIG_FB_BACKLIGHT=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y + +# +# Frame buffer hardware drivers +# +CONFIG_FB_CIRRUS=m +CONFIG_FB_PM2=m +CONFIG_FB_PM2_FIFO_DISCONNECT=y +CONFIG_FB_CYBER2000=m +CONFIG_FB_CYBER2000_DDC=y +CONFIG_FB_ARC=m +CONFIG_FB_ASILIANT=y +CONFIG_FB_IMSTT=y +CONFIG_FB_VGA16=m +CONFIG_FB_UVESA=m +CONFIG_FB_VESA=y +CONFIG_FB_EFI=y +CONFIG_FB_N411=m +CONFIG_FB_HGA=m +CONFIG_FB_OPENCORES=m +CONFIG_FB_S1D13XXX=m +CONFIG_FB_NVIDIA=m +CONFIG_FB_NVIDIA_I2C=y +# CONFIG_FB_NVIDIA_DEBUG is not set +CONFIG_FB_NVIDIA_BACKLIGHT=y +CONFIG_FB_RIVA=m +CONFIG_FB_RIVA_I2C=y +# CONFIG_FB_RIVA_DEBUG is not set +CONFIG_FB_RIVA_BACKLIGHT=y +CONFIG_FB_I740=m +CONFIG_FB_LE80578=m +CONFIG_FB_CARILLO_RANCH=m +CONFIG_FB_INTEL=m +# CONFIG_FB_INTEL_DEBUG is not set +CONFIG_FB_INTEL_I2C=y +CONFIG_FB_MATROX=m +CONFIG_FB_MATROX_MILLENIUM=y +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G=y +CONFIG_FB_MATROX_I2C=m +CONFIG_FB_MATROX_MAVEN=m +CONFIG_FB_RADEON=m +CONFIG_FB_RADEON_I2C=y +CONFIG_FB_RADEON_BACKLIGHT=y +# CONFIG_FB_RADEON_DEBUG is not set +CONFIG_FB_ATY128=m +CONFIG_FB_ATY128_BACKLIGHT=y +CONFIG_FB_ATY=m +CONFIG_FB_ATY_CT=y +# CONFIG_FB_ATY_GENERIC_LCD is not set +CONFIG_FB_ATY_GX=y +CONFIG_FB_ATY_BACKLIGHT=y +CONFIG_FB_S3=m +CONFIG_FB_S3_DDC=y +CONFIG_FB_SAVAGE=m +CONFIG_FB_SAVAGE_I2C=y +# CONFIG_FB_SAVAGE_ACCEL is not set +CONFIG_FB_SIS=m +CONFIG_FB_SIS_300=y +CONFIG_FB_SIS_315=y +CONFIG_FB_VIA=m +# CONFIG_FB_VIA_DIRECT_PROCFS is not set +CONFIG_FB_VIA_X_COMPATIBILITY=y +CONFIG_FB_NEOMAGIC=m +CONFIG_FB_KYRO=m +CONFIG_FB_3DFX=m +# CONFIG_FB_3DFX_ACCEL is not set +# CONFIG_FB_3DFX_I2C is not set +CONFIG_FB_VOODOO1=m +CONFIG_FB_VT8623=m +CONFIG_FB_TRIDENT=m +CONFIG_FB_ARK=m +CONFIG_FB_PM3=m +CONFIG_FB_CARMINE=m +CONFIG_FB_CARMINE_DRAM_EVAL=y +# CONFIG_CARMINE_DRAM_CUSTOM is not set +CONFIG_FB_SM501=m +CONFIG_FB_SMSCUFX=m +CONFIG_FB_UDL=m +# CONFIG_FB_IBM_GXT4500 is not set +# CONFIG_FB_VIRTUAL is not set +CONFIG_XEN_FBDEV_FRONTEND=m +CONFIG_FB_METRONOME=m +CONFIG_FB_MB862XX=m +CONFIG_FB_MB862XX_PCI_GDC=y +CONFIG_FB_MB862XX_I2C=y +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_HYPERV=m +CONFIG_FB_SIMPLE=y +CONFIG_FB_SM712=m +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=m +CONFIG_LCD_L4F00242T03=m +CONFIG_LCD_LMS283GF05=m +CONFIG_LCD_LTV350QV=m +CONFIG_LCD_ILI922X=m +CONFIG_LCD_ILI9320=m +CONFIG_LCD_TDO24M=m +CONFIG_LCD_VGG2432A4=m +CONFIG_LCD_PLATFORM=m +# CONFIG_LCD_S6E63M0 is not set +# CONFIG_LCD_LD9040 is not set +CONFIG_LCD_AMS369FG06=m +CONFIG_LCD_LMS501KF03=m +CONFIG_LCD_HX8357=m +# CONFIG_LCD_OTM3225A is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=m +CONFIG_BACKLIGHT_LM3533=m +CONFIG_BACKLIGHT_CARILLO_RANCH=m +CONFIG_BACKLIGHT_PWM=m +CONFIG_BACKLIGHT_DA903X=m +CONFIG_BACKLIGHT_DA9052=m +CONFIG_BACKLIGHT_MAX8925=m +CONFIG_BACKLIGHT_APPLE=m +CONFIG_BACKLIGHT_PM8941_WLED=m +CONFIG_BACKLIGHT_SAHARA=m +CONFIG_BACKLIGHT_WM831X=m +CONFIG_BACKLIGHT_ADP5520=m +CONFIG_BACKLIGHT_ADP8860=m +CONFIG_BACKLIGHT_ADP8870=m +CONFIG_BACKLIGHT_88PM860X=m +CONFIG_BACKLIGHT_PCF50633=m +CONFIG_BACKLIGHT_AAT2870=m +CONFIG_BACKLIGHT_LM3630A=m +CONFIG_BACKLIGHT_LM3639=m +CONFIG_BACKLIGHT_LP855X=m +CONFIG_BACKLIGHT_LP8788=m +CONFIG_BACKLIGHT_PANDORA=m +CONFIG_BACKLIGHT_SKY81452=m +CONFIG_BACKLIGHT_AS3711=m +CONFIG_BACKLIGHT_GPIO=m +CONFIG_BACKLIGHT_LV5207LP=m +CONFIG_BACKLIGHT_BD6107=m +CONFIG_BACKLIGHT_ARCXCNN=m +CONFIG_VGASTATE=m +CONFIG_HDMI=y + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +# CONFIG_VGACON_SOFT_SCROLLBACK is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_DUMMY_CONSOLE_COLUMNS=80 +CONFIG_DUMMY_CONSOLE_ROWS=25 +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set +# CONFIG_LOGO is not set +CONFIG_SOUND=m +CONFIG_SOUND_OSS_CORE=y +# CONFIG_SOUND_OSS_CORE_PRECLAIM is not set +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +CONFIG_SND_PCM_ELD=y +CONFIG_SND_PCM_IEC958=y +CONFIG_SND_DMAENGINE_PCM=m +CONFIG_SND_HWDEP=m +CONFIG_SND_SEQ_DEVICE=m +CONFIG_SND_RAWMIDI=m +CONFIG_SND_COMPRESS_OFFLOAD=m +CONFIG_SND_JACK=y +CONFIG_SND_JACK_INPUT_DEV=y +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=m +# CONFIG_SND_PCM_OSS is not set +CONFIG_SND_PCM_TIMER=y +CONFIG_SND_HRTIMER=m +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_MAX_CARDS=32 +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_PROC_FS=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +CONFIG_SND_VMASTER=y +CONFIG_SND_DMA_SGBUF=y +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +# CONFIG_SND_SEQUENCER_OSS is not set +CONFIG_SND_SEQ_HRTIMER_DEFAULT=y +CONFIG_SND_SEQ_MIDI_EVENT=m +CONFIG_SND_SEQ_MIDI=m +CONFIG_SND_SEQ_MIDI_EMUL=m +CONFIG_SND_SEQ_VIRMIDI=m +CONFIG_SND_MPU401_UART=m +CONFIG_SND_OPL3_LIB=m +CONFIG_SND_OPL3_LIB_SEQ=m +CONFIG_SND_VX_LIB=m +CONFIG_SND_AC97_CODEC=m +CONFIG_SND_DRIVERS=y +CONFIG_SND_PCSP=m +CONFIG_SND_DUMMY=m +CONFIG_SND_ALOOP=m +CONFIG_SND_VIRMIDI=m +CONFIG_SND_MTPAV=m +CONFIG_SND_MTS64=m +CONFIG_SND_SERIAL_U16550=m +CONFIG_SND_MPU401=m +CONFIG_SND_PORTMAN2X4=m +CONFIG_SND_AC97_POWER_SAVE=y +CONFIG_SND_AC97_POWER_SAVE_DEFAULT=0 +CONFIG_SND_SB_COMMON=m +CONFIG_SND_PCI=y +CONFIG_SND_AD1889=m +CONFIG_SND_ALS300=m +CONFIG_SND_ALS4000=m +CONFIG_SND_ALI5451=m +CONFIG_SND_ASIHPI=m +CONFIG_SND_ATIIXP=m +CONFIG_SND_ATIIXP_MODEM=m +CONFIG_SND_AU8810=m +CONFIG_SND_AU8820=m +CONFIG_SND_AU8830=m +CONFIG_SND_AW2=m +CONFIG_SND_AZT3328=m +CONFIG_SND_BT87X=m +# CONFIG_SND_BT87X_OVERCLOCK is not set +CONFIG_SND_CA0106=m +CONFIG_SND_CMIPCI=m +CONFIG_SND_OXYGEN_LIB=m +CONFIG_SND_OXYGEN=m +CONFIG_SND_CS4281=m +CONFIG_SND_CS46XX=m +CONFIG_SND_CS46XX_NEW_DSP=y +CONFIG_SND_CTXFI=m +CONFIG_SND_DARLA20=m +CONFIG_SND_GINA20=m +CONFIG_SND_LAYLA20=m +CONFIG_SND_DARLA24=m +CONFIG_SND_GINA24=m +CONFIG_SND_LAYLA24=m +CONFIG_SND_MONA=m +CONFIG_SND_MIA=m +CONFIG_SND_ECHO3G=m +CONFIG_SND_INDIGO=m +CONFIG_SND_INDIGOIO=m +CONFIG_SND_INDIGODJ=m +CONFIG_SND_INDIGOIOX=m +CONFIG_SND_INDIGODJX=m +CONFIG_SND_EMU10K1=m +CONFIG_SND_EMU10K1_SEQ=m +CONFIG_SND_EMU10K1X=m +CONFIG_SND_ENS1370=m +CONFIG_SND_ENS1371=m +CONFIG_SND_ES1938=m +CONFIG_SND_ES1968=m +CONFIG_SND_ES1968_INPUT=y +CONFIG_SND_ES1968_RADIO=y +CONFIG_SND_FM801=m +CONFIG_SND_FM801_TEA575X_BOOL=y +CONFIG_SND_HDSP=m +CONFIG_SND_HDSPM=m +CONFIG_SND_ICE1712=m +CONFIG_SND_ICE1724=m +CONFIG_SND_INTEL8X0=m +CONFIG_SND_INTEL8X0M=m +CONFIG_SND_KORG1212=m +CONFIG_SND_LOLA=m +CONFIG_SND_LX6464ES=m +CONFIG_SND_MAESTRO3=m +CONFIG_SND_MAESTRO3_INPUT=y +CONFIG_SND_MIXART=m +CONFIG_SND_NM256=m +CONFIG_SND_PCXHR=m +CONFIG_SND_RIPTIDE=m +CONFIG_SND_RME32=m +CONFIG_SND_RME96=m +CONFIG_SND_RME9652=m +CONFIG_SND_SONICVIBES=m +CONFIG_SND_TRIDENT=m +CONFIG_SND_VIA82XX=m +CONFIG_SND_VIA82XX_MODEM=m +CONFIG_SND_VIRTUOSO=m +CONFIG_SND_VX222=m +CONFIG_SND_YMFPCI=m + +# +# HD-Audio +# +CONFIG_SND_HDA=m +CONFIG_SND_HDA_INTEL=m +CONFIG_SND_HDA_HWDEP=y +CONFIG_SND_HDA_RECONFIG=y +CONFIG_SND_HDA_INPUT_BEEP=y +CONFIG_SND_HDA_INPUT_BEEP_MODE=0 +CONFIG_SND_HDA_PATCH_LOADER=y +CONFIG_SND_HDA_CODEC_REALTEK=m +CONFIG_SND_HDA_CODEC_ANALOG=m +CONFIG_SND_HDA_CODEC_SIGMATEL=m +CONFIG_SND_HDA_CODEC_VIA=m +CONFIG_SND_HDA_CODEC_HDMI=m +CONFIG_SND_HDA_CODEC_CIRRUS=m +CONFIG_SND_HDA_CODEC_CONEXANT=m +CONFIG_SND_HDA_CODEC_CA0110=m +CONFIG_SND_HDA_CODEC_CA0132=m +CONFIG_SND_HDA_CODEC_CA0132_DSP=y +CONFIG_SND_HDA_CODEC_CMEDIA=m +CONFIG_SND_HDA_CODEC_SI3054=m +CONFIG_SND_HDA_GENERIC=m +CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0 +CONFIG_SND_HDA_CORE=m +CONFIG_SND_HDA_DSP_LOADER=y +CONFIG_SND_HDA_COMPONENT=y +CONFIG_SND_HDA_I915=y +CONFIG_SND_HDA_PREALLOC_SIZE=64 +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_UA101=m +CONFIG_SND_USB_USX2Y=m +CONFIG_SND_USB_CAIAQ=m +CONFIG_SND_USB_CAIAQ_INPUT=y +CONFIG_SND_USB_US122L=m +CONFIG_SND_USB_6FIRE=m +CONFIG_SND_USB_HIFACE=m +CONFIG_SND_BCD2000=m +CONFIG_SND_USB_LINE6=m +CONFIG_SND_USB_POD=m +CONFIG_SND_USB_PODHD=m +CONFIG_SND_USB_TONEPORT=m +CONFIG_SND_USB_VARIAX=m +CONFIG_SND_FIREWIRE=y +CONFIG_SND_FIREWIRE_LIB=m +CONFIG_SND_DICE=m +CONFIG_SND_OXFW=m +CONFIG_SND_ISIGHT=m +CONFIG_SND_FIREWORKS=m +CONFIG_SND_BEBOB=m +CONFIG_SND_FIREWIRE_DIGI00X=m +CONFIG_SND_FIREWIRE_TASCAM=m +CONFIG_SND_FIREWIRE_MOTU=m +CONFIG_SND_FIREFACE=m +CONFIG_SND_PCMCIA=y +CONFIG_SND_VXPOCKET=m +CONFIG_SND_PDAUDIOCF=m +CONFIG_SND_SOC=m +CONFIG_SND_SOC_AC97_BUS=y +CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y +CONFIG_SND_SOC_COMPRESS=y +CONFIG_SND_SOC_ACPI=m +CONFIG_SND_SOC_AMD_ACP=m +# CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH is not set +CONFIG_SND_SOC_AMD_CZ_RT5645_MACH=m +CONFIG_SND_ATMEL_SOC=m +CONFIG_SND_DESIGNWARE_I2S=m +CONFIG_SND_DESIGNWARE_PCM=y + +# +# SoC Audio for Freescale CPUs +# + +# +# Common SoC Audio options for Freescale CPUs: +# +CONFIG_SND_SOC_FSL_ASRC=m +CONFIG_SND_SOC_FSL_SAI=m +CONFIG_SND_SOC_FSL_SSI=m +CONFIG_SND_SOC_FSL_SPDIF=m +CONFIG_SND_SOC_FSL_ESAI=m +CONFIG_SND_SOC_IMX_AUDMUX=m +CONFIG_SND_I2S_HI6210_I2S=m +CONFIG_SND_SOC_IMG=y +CONFIG_SND_SOC_IMG_I2S_IN=m +CONFIG_SND_SOC_IMG_I2S_OUT=m +CONFIG_SND_SOC_IMG_PARALLEL_OUT=m +CONFIG_SND_SOC_IMG_SPDIF_IN=m +CONFIG_SND_SOC_IMG_SPDIF_OUT=m +CONFIG_SND_SOC_IMG_PISTACHIO_INTERNAL_DAC=m +CONFIG_SND_SOC_INTEL_SST_TOPLEVEL=y +CONFIG_SND_SST_IPC=m +CONFIG_SND_SST_IPC_ACPI=m +# CONFIG_SND_SOC_INTEL_HASWELL is not set +CONFIG_SND_SST_ATOM_HIFI2_PLATFORM=m +# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set +CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI=m +# CONFIG_SND_SOC_INTEL_SKYLAKE is not set +CONFIG_SND_SOC_ACPI_INTEL_MATCH=m +CONFIG_SND_SOC_INTEL_MACH=y +# CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH is not set +# CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH is not set +CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH=m +# CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH is not set + +# +# STMicroelectronics STM32 SOC audio support +# +CONFIG_SND_SOC_XTFPGA_I2S=m +CONFIG_ZX_TDM=m +CONFIG_SND_SOC_I2C_AND_SPI=m + +# +# CODEC drivers +# +CONFIG_SND_SOC_AC97_CODEC=m +CONFIG_SND_SOC_ADAU_UTILS=m +CONFIG_SND_SOC_ADAU1701=m +CONFIG_SND_SOC_ADAU17X1=m +CONFIG_SND_SOC_ADAU1761=m +CONFIG_SND_SOC_ADAU1761_I2C=m +CONFIG_SND_SOC_ADAU1761_SPI=m +CONFIG_SND_SOC_ADAU7002=m +CONFIG_SND_SOC_AK4104=m +# CONFIG_SND_SOC_AK4458 is not set +CONFIG_SND_SOC_AK4554=m +CONFIG_SND_SOC_AK4613=m +CONFIG_SND_SOC_AK4642=m +CONFIG_SND_SOC_AK5386=m +# CONFIG_SND_SOC_AK5558 is not set +CONFIG_SND_SOC_ALC5623=m +# CONFIG_SND_SOC_BD28623 is not set +CONFIG_SND_SOC_BT_SCO=m +CONFIG_SND_SOC_CS35L32=m +CONFIG_SND_SOC_CS35L33=m +CONFIG_SND_SOC_CS35L34=m +CONFIG_SND_SOC_CS35L35=m +CONFIG_SND_SOC_CS42L42=m +CONFIG_SND_SOC_CS42L51=m +CONFIG_SND_SOC_CS42L51_I2C=m +CONFIG_SND_SOC_CS42L52=m +CONFIG_SND_SOC_CS42L56=m +CONFIG_SND_SOC_CS42L73=m +CONFIG_SND_SOC_CS4265=m +CONFIG_SND_SOC_CS4270=m +CONFIG_SND_SOC_CS4271=m +CONFIG_SND_SOC_CS4271_I2C=m +CONFIG_SND_SOC_CS4271_SPI=m +CONFIG_SND_SOC_CS42XX8=m +CONFIG_SND_SOC_CS42XX8_I2C=m +# CONFIG_SND_SOC_CS43130 is not set +CONFIG_SND_SOC_CS4349=m +CONFIG_SND_SOC_CS53L30=m +CONFIG_SND_SOC_HDMI_CODEC=m +CONFIG_SND_SOC_ES7134=m +# CONFIG_SND_SOC_ES7241 is not set +CONFIG_SND_SOC_ES8316=m +CONFIG_SND_SOC_ES8328=m +CONFIG_SND_SOC_ES8328_I2C=m +CONFIG_SND_SOC_ES8328_SPI=m +CONFIG_SND_SOC_GTM601=m +CONFIG_SND_SOC_INNO_RK3036=m +CONFIG_SND_SOC_MAX98504=m +# CONFIG_SND_SOC_MAX9867 is not set +CONFIG_SND_SOC_MAX98927=m +# CONFIG_SND_SOC_MAX98373 is not set +CONFIG_SND_SOC_MAX9860=m +CONFIG_SND_SOC_MSM8916_WCD_ANALOG=m +CONFIG_SND_SOC_MSM8916_WCD_DIGITAL=m +CONFIG_SND_SOC_PCM1681=m +# CONFIG_SND_SOC_PCM1789_I2C is not set +CONFIG_SND_SOC_PCM179X=m +CONFIG_SND_SOC_PCM179X_I2C=m +CONFIG_SND_SOC_PCM179X_SPI=m +# CONFIG_SND_SOC_PCM186X_I2C is not set +# CONFIG_SND_SOC_PCM186X_SPI is not set +CONFIG_SND_SOC_PCM3168A=m +CONFIG_SND_SOC_PCM3168A_I2C=m +CONFIG_SND_SOC_PCM3168A_SPI=m +CONFIG_SND_SOC_PCM512x=m +CONFIG_SND_SOC_PCM512x_I2C=m +CONFIG_SND_SOC_PCM512x_SPI=m +CONFIG_SND_SOC_RL6231=m +CONFIG_SND_SOC_RT5616=m +CONFIG_SND_SOC_RT5631=m +CONFIG_SND_SOC_RT5645=m +CONFIG_SND_SOC_SGTL5000=m +CONFIG_SND_SOC_SI476X=m +CONFIG_SND_SOC_SIGMADSP=m +CONFIG_SND_SOC_SIGMADSP_I2C=m +CONFIG_SND_SOC_SIGMADSP_REGMAP=m +# CONFIG_SND_SOC_SIMPLE_AMPLIFIER is not set +CONFIG_SND_SOC_SIRF_AUDIO_CODEC=m +CONFIG_SND_SOC_SPDIF=m +# CONFIG_SND_SOC_SSM2305 is not set +CONFIG_SND_SOC_SSM2602=m +CONFIG_SND_SOC_SSM2602_SPI=m +CONFIG_SND_SOC_SSM2602_I2C=m +CONFIG_SND_SOC_SSM4567=m +CONFIG_SND_SOC_STA32X=m +CONFIG_SND_SOC_STA350=m +CONFIG_SND_SOC_STI_SAS=m +CONFIG_SND_SOC_TAS2552=m +CONFIG_SND_SOC_TAS5086=m +CONFIG_SND_SOC_TAS571X=m +CONFIG_SND_SOC_TAS5720=m +# CONFIG_SND_SOC_TAS6424 is not set +# CONFIG_SND_SOC_TDA7419 is not set +CONFIG_SND_SOC_TFA9879=m +CONFIG_SND_SOC_TLV320AIC23=m +CONFIG_SND_SOC_TLV320AIC23_I2C=m +CONFIG_SND_SOC_TLV320AIC23_SPI=m +CONFIG_SND_SOC_TLV320AIC31XX=m +# CONFIG_SND_SOC_TLV320AIC32X4_I2C is not set +# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set +CONFIG_SND_SOC_TLV320AIC3X=m +CONFIG_SND_SOC_TS3A227E=m +# CONFIG_SND_SOC_TSCS42XX is not set +# CONFIG_SND_SOC_TSCS454 is not set +CONFIG_SND_SOC_WM8510=m +CONFIG_SND_SOC_WM8523=m +# CONFIG_SND_SOC_WM8524 is not set +CONFIG_SND_SOC_WM8580=m +CONFIG_SND_SOC_WM8711=m +CONFIG_SND_SOC_WM8728=m +CONFIG_SND_SOC_WM8731=m +CONFIG_SND_SOC_WM8737=m +CONFIG_SND_SOC_WM8741=m +CONFIG_SND_SOC_WM8750=m +CONFIG_SND_SOC_WM8753=m +CONFIG_SND_SOC_WM8770=m +CONFIG_SND_SOC_WM8776=m +# CONFIG_SND_SOC_WM8782 is not set +CONFIG_SND_SOC_WM8804=m +CONFIG_SND_SOC_WM8804_I2C=m +CONFIG_SND_SOC_WM8804_SPI=m +CONFIG_SND_SOC_WM8903=m +CONFIG_SND_SOC_WM8960=m +CONFIG_SND_SOC_WM8962=m +CONFIG_SND_SOC_WM8974=m +CONFIG_SND_SOC_WM8978=m +CONFIG_SND_SOC_WM8985=m +CONFIG_SND_SOC_ZX_AUD96P22=m +# CONFIG_SND_SOC_MAX9759 is not set +# CONFIG_SND_SOC_MT6351 is not set +CONFIG_SND_SOC_NAU8540=m +CONFIG_SND_SOC_NAU8810=m +CONFIG_SND_SOC_NAU8824=m +CONFIG_SND_SOC_TPA6130A2=m +CONFIG_SND_SIMPLE_CARD_UTILS=m +CONFIG_SND_SIMPLE_CARD=m +CONFIG_SND_X86=y +CONFIG_HDMI_LPE_AUDIO=m +CONFIG_SND_SYNTH_EMUX=m +# CONFIG_SND_XEN_FRONTEND is not set +CONFIG_AC97_BUS=m + +# +# HID support +# +CONFIG_HID=m +CONFIG_HID_BATTERY_STRENGTH=y +CONFIG_HIDRAW=y +CONFIG_UHID=m +CONFIG_HID_GENERIC=m + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=m +CONFIG_HID_ACCUTOUCH=m +CONFIG_HID_ACRUX=m +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=m +CONFIG_HID_APPLEIR=m +CONFIG_HID_ASUS=m +CONFIG_HID_AUREAL=m +CONFIG_HID_BELKIN=m +CONFIG_HID_BETOP_FF=m +CONFIG_HID_CHERRY=m +CONFIG_HID_CHICONY=m +CONFIG_HID_CORSAIR=m +# CONFIG_HID_COUGAR is not set +CONFIG_HID_PRODIKEYS=m +CONFIG_HID_CMEDIA=m +CONFIG_HID_CP2112=m +CONFIG_HID_CYPRESS=m +CONFIG_HID_DRAGONRISE=m +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=m +# CONFIG_HID_ELAN is not set +CONFIG_HID_ELECOM=m +CONFIG_HID_ELO=m +CONFIG_HID_EZKEY=m +CONFIG_HID_GEMBIRD=m +CONFIG_HID_GFRM=m +CONFIG_HID_HOLTEK=m +CONFIG_HOLTEK_FF=y +# CONFIG_HID_GOOGLE_HAMMER is not set +CONFIG_HID_GT683R=m +CONFIG_HID_KEYTOUCH=m +CONFIG_HID_KYE=m +CONFIG_HID_UCLOGIC=m +CONFIG_HID_WALTOP=m +CONFIG_HID_GYRATION=m +CONFIG_HID_ICADE=m +CONFIG_HID_ITE=m +# CONFIG_HID_JABRA is not set +CONFIG_HID_TWINHAN=m +CONFIG_HID_KENSINGTON=m +CONFIG_HID_LCPOWER=m +CONFIG_HID_LED=m +CONFIG_HID_LENOVO=m +CONFIG_HID_LOGITECH=m +CONFIG_HID_LOGITECH_DJ=m +CONFIG_HID_LOGITECH_HIDPP=m +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_LOGIWHEELS_FF=y +CONFIG_HID_MAGICMOUSE=m +CONFIG_HID_MAYFLASH=m +# CONFIG_HID_REDRAGON is not set +CONFIG_HID_MICROSOFT=m +CONFIG_HID_MONTEREY=m +CONFIG_HID_MULTITOUCH=m +CONFIG_HID_NTI=m +CONFIG_HID_NTRIG=m +CONFIG_HID_ORTEK=m +CONFIG_HID_PANTHERLORD=m +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PENMOUNT=m +CONFIG_HID_PETALYNX=m +CONFIG_HID_PICOLCD=m +CONFIG_HID_PICOLCD_FB=y +CONFIG_HID_PICOLCD_BACKLIGHT=y +CONFIG_HID_PICOLCD_LCD=y +CONFIG_HID_PICOLCD_LEDS=y +CONFIG_HID_PICOLCD_CIR=y +CONFIG_HID_PLANTRONICS=m +CONFIG_HID_PRIMAX=m +CONFIG_HID_RETRODE=m +CONFIG_HID_ROCCAT=m +CONFIG_HID_SAITEK=m +CONFIG_HID_SAMSUNG=m +CONFIG_HID_SONY=m +CONFIG_SONY_FF=y +CONFIG_HID_SPEEDLINK=m +# CONFIG_HID_STEAM is not set +CONFIG_HID_STEELSERIES=m +CONFIG_HID_SUNPLUS=m +CONFIG_HID_RMI=m +CONFIG_HID_GREENASIA=m +CONFIG_GREENASIA_FF=y +CONFIG_HID_HYPERV_MOUSE=m +CONFIG_HID_SMARTJOYPLUS=m +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=m +CONFIG_HID_TOPSEED=m +CONFIG_HID_THINGM=m +CONFIG_HID_THRUSTMASTER=m +CONFIG_THRUSTMASTER_FF=y +CONFIG_HID_UDRAW_PS3=m +CONFIG_HID_WACOM=m +CONFIG_HID_WIIMOTE=m +CONFIG_HID_XINMO=m +CONFIG_HID_ZEROPLUS=m +CONFIG_ZEROPLUS_FF=y +CONFIG_HID_ZYDACRON=m +CONFIG_HID_SENSOR_HUB=m +CONFIG_HID_SENSOR_CUSTOM_SENSOR=m +CONFIG_HID_ALPS=m + +# +# USB HID support +# +CONFIG_USB_HID=m +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y + +# +# USB HID Boot Protocol drivers +# +CONFIG_USB_KBD=m +CONFIG_USB_MOUSE=m + +# +# I2C HID support +# +CONFIG_I2C_HID=m + +# +# Intel ISH HID support +# +CONFIG_INTEL_ISH_HID=m +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +CONFIG_USB_PCI=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +CONFIG_USB_DEFAULT_PERSIST=y +CONFIG_USB_DYNAMIC_MINORS=y +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_LEDS_TRIGGER_USBPORT=m +CONFIG_USB_MON=m +CONFIG_USB_WUSB=m +CONFIG_USB_WUSB_CBAF=m +# CONFIG_USB_WUSB_CBAF_DEBUG is not set + +# +# USB Host Controller Drivers +# +CONFIG_USB_C67X00_HCD=m +CONFIG_USB_XHCI_HCD=y +# CONFIG_USB_XHCI_DBGCAP is not set +CONFIG_USB_XHCI_PCI=y +CONFIG_USB_XHCI_PLATFORM=m +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_EHCI_PCI=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OXU210HP_HCD=m +CONFIG_USB_ISP116X_HCD=m +CONFIG_USB_FOTG210_HCD=m +CONFIG_USB_MAX3421_HCD=m +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PCI=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_UHCI_HCD=y +CONFIG_USB_U132_HCD=m +CONFIG_USB_SL811_HCD=m +CONFIG_USB_SL811_HCD_ISO=y +CONFIG_USB_SL811_CS=m +CONFIG_USB_R8A66597_HCD=m +CONFIG_USB_WHCI_HCD=m +CONFIG_USB_HWA_HCD=m +CONFIG_USB_HCD_BCMA=m +CONFIG_USB_HCD_SSB=m +# CONFIG_USB_HCD_TEST_MODE is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +CONFIG_USB_WDM=m +CONFIG_USB_TMC=m + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_REALTEK=m +CONFIG_REALTEK_AUTOPM=y +CONFIG_USB_STORAGE_DATAFAB=m +CONFIG_USB_STORAGE_FREECOM=m +CONFIG_USB_STORAGE_ISD200=m +CONFIG_USB_STORAGE_USBAT=m +CONFIG_USB_STORAGE_SDDR09=m +CONFIG_USB_STORAGE_SDDR55=m +CONFIG_USB_STORAGE_JUMPSHOT=m +CONFIG_USB_STORAGE_ALAUDA=m +CONFIG_USB_STORAGE_ONETOUCH=m +CONFIG_USB_STORAGE_KARMA=m +CONFIG_USB_STORAGE_CYPRESS_ATACB=m +CONFIG_USB_STORAGE_ENE_UB6250=m +CONFIG_USB_UAS=m + +# +# USB Imaging devices +# +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USBIP_CORE=m +CONFIG_USBIP_VHCI_HCD=m +CONFIG_USBIP_VHCI_HC_PORTS=8 +CONFIG_USBIP_VHCI_NR_HCS=1 +CONFIG_USBIP_HOST=m +CONFIG_USBIP_VUDC=m +# CONFIG_USBIP_DEBUG is not set +CONFIG_USB_MUSB_HDRC=m +# CONFIG_USB_MUSB_HOST is not set +# CONFIG_USB_MUSB_GADGET is not set +CONFIG_USB_MUSB_DUAL_ROLE=y + +# +# Platform Glue Layer +# + +# +# MUSB DMA mode +# +CONFIG_MUSB_PIO_ONLY=y +CONFIG_USB_DWC3=m +CONFIG_USB_DWC3_ULPI=y +# CONFIG_USB_DWC3_HOST is not set +# CONFIG_USB_DWC3_GADGET is not set +CONFIG_USB_DWC3_DUAL_ROLE=y + +# +# Platform Glue Driver Support +# +CONFIG_USB_DWC3_PCI=m +CONFIG_USB_DWC3_HAPS=m +CONFIG_USB_DWC2=y +CONFIG_USB_DWC2_HOST=y + +# +# Gadget/Dual-role mode requires USB Gadget support to be enabled +# +CONFIG_USB_DWC2_PCI=m +# CONFIG_USB_DWC2_DEBUG is not set +# CONFIG_USB_DWC2_TRACK_MISSED_SOFS is not set +CONFIG_USB_CHIPIDEA=m +CONFIG_USB_CHIPIDEA_PCI=m +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_ISP1760=m +CONFIG_USB_ISP1760_HCD=y +CONFIG_USB_ISP1761_UDC=y +# CONFIG_USB_ISP1760_HOST_ROLE is not set +# CONFIG_USB_ISP1760_GADGET_ROLE is not set +CONFIG_USB_ISP1760_DUAL_ROLE=y + +# +# USB port drivers +# +CONFIG_USB_USS720=m +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_SIMPLE=m +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_F81232=m +CONFIG_USB_SERIAL_F8153X=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_METRO=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7715_PARPORT=y +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_MXUPORT=m +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_QCAUX=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SPCP8X5=m +CONFIG_USB_SERIAL_SAFE=m +# CONFIG_USB_SERIAL_SAFE_PADDED is not set +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_SYMBOL=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_WWAN=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTICON=m +CONFIG_USB_SERIAL_XSENS_MT=m +CONFIG_USB_SERIAL_WISHBONE=m +CONFIG_USB_SERIAL_SSU100=m +CONFIG_USB_SERIAL_QT2=m +CONFIG_USB_SERIAL_UPD78F0730=m +CONFIG_USB_SERIAL_DEBUG=m + +# +# USB Miscellaneous drivers +# +CONFIG_USB_EMI62=m +CONFIG_USB_EMI26=m +CONFIG_USB_ADUTUX=m +CONFIG_USB_SEVSEG=m +CONFIG_USB_RIO500=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +CONFIG_USB_CYPRESS_CY7C63=m +CONFIG_USB_CYTHERM=m +CONFIG_USB_IDMOUSE=m +CONFIG_USB_FTDI_ELAN=m +CONFIG_USB_APPLEDISPLAY=m +CONFIG_USB_SISUSBVGA=m +# CONFIG_USB_SISUSBVGA_CON is not set +CONFIG_USB_LD=m +CONFIG_USB_TRANCEVIBRATOR=m +CONFIG_USB_IOWARRIOR=m +CONFIG_USB_TEST=m +CONFIG_USB_EHSET_TEST_FIXTURE=m +CONFIG_USB_ISIGHTFW=m +CONFIG_USB_YUREX=m +CONFIG_USB_EZUSB_FX2=m +CONFIG_USB_HUB_USB251XB=m +CONFIG_USB_HSIC_USB3503=m +CONFIG_USB_HSIC_USB4604=m +CONFIG_USB_LINK_LAYER_TEST=m +CONFIG_USB_CHAOSKEY=m +CONFIG_USB_ATM=m +CONFIG_USB_SPEEDTOUCH=m +CONFIG_USB_CXACRU=m +CONFIG_USB_UEAGLEATM=m +CONFIG_USB_XUSBATM=m + +# +# USB Physical Layer drivers +# +CONFIG_USB_PHY=y +CONFIG_NOP_USB_XCEIV=m +CONFIG_USB_GPIO_VBUS=m +CONFIG_TAHVO_USB=m +CONFIG_TAHVO_USB_HOST_BY_DEFAULT=y +CONFIG_USB_ISP1301=m +CONFIG_USB_GADGET=m +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +CONFIG_U_SERIAL_CONSOLE=y + +# +# USB Peripheral Controller +# +CONFIG_USB_FOTG210_UDC=m +CONFIG_USB_GR_UDC=m +CONFIG_USB_R8A66597=m +CONFIG_USB_PXA27X=m +CONFIG_USB_MV_UDC=m +CONFIG_USB_MV_U3D=m +CONFIG_USB_SNP_CORE=m +# CONFIG_USB_M66592 is not set +CONFIG_USB_BDC_UDC=m + +# +# Platform Support +# +CONFIG_USB_BDC_PCI=m +CONFIG_USB_AMD5536UDC=m +CONFIG_USB_NET2272=m +CONFIG_USB_NET2272_DMA=y +CONFIG_USB_NET2280=m +CONFIG_USB_GOKU=m +CONFIG_USB_EG20T=m +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_LIBCOMPOSITE=m +CONFIG_USB_F_ACM=m +CONFIG_USB_F_SS_LB=m +CONFIG_USB_U_SERIAL=m +CONFIG_USB_U_ETHER=m +CONFIG_USB_U_AUDIO=m +CONFIG_USB_F_SERIAL=m +CONFIG_USB_F_OBEX=m +CONFIG_USB_F_NCM=m +CONFIG_USB_F_ECM=m +CONFIG_USB_F_PHONET=m +CONFIG_USB_F_EEM=m +CONFIG_USB_F_SUBSET=m +CONFIG_USB_F_RNDIS=m +CONFIG_USB_F_MASS_STORAGE=m +CONFIG_USB_F_FS=m +CONFIG_USB_F_UAC1=m +CONFIG_USB_F_UAC1_LEGACY=m +CONFIG_USB_F_UAC2=m +CONFIG_USB_F_UVC=m +CONFIG_USB_F_MIDI=m +CONFIG_USB_F_HID=m +CONFIG_USB_F_PRINTER=m +CONFIG_USB_F_TCM=m +CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_OBEX=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_ECM_SUBSET=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_PHONET=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_LB_SS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC1_LEGACY=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_CONFIGFS_F_PRINTER=y +CONFIG_USB_CONFIGFS_F_TCM=y +CONFIG_USB_ZERO=m +CONFIG_USB_AUDIO=m +CONFIG_GADGET_UAC1=y +# CONFIG_GADGET_UAC1_LEGACY is not set +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +CONFIG_USB_ETH_EEM=y +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_FUNCTIONFS_ETH=y +CONFIG_USB_FUNCTIONFS_RNDIS=y +CONFIG_USB_FUNCTIONFS_GENERIC=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_GADGET_TARGET=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_MIDI_GADGET=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_NOKIA=m +CONFIG_USB_G_ACM_MS=m +# CONFIG_USB_G_MULTI is not set +CONFIG_USB_G_HID=m +CONFIG_USB_G_DBGP=m +# CONFIG_USB_G_DBGP_PRINTK is not set +CONFIG_USB_G_DBGP_SERIAL=y +CONFIG_USB_G_WEBCAM=m +CONFIG_TYPEC=m +CONFIG_TYPEC_TCPM=m +CONFIG_TYPEC_TCPCI=m +# CONFIG_TYPEC_RT1711H is not set +CONFIG_TYPEC_FUSB302=m +CONFIG_TYPEC_UCSI=m +CONFIG_UCSI_ACPI=m +# CONFIG_TYPEC_TPS6598X is not set + +# +# USB Type-C Multiplexer/DeMultiplexer Switch support +# +# CONFIG_TYPEC_MUX_PI3USB30532 is not set + +# +# USB Type-C Alternate Mode drivers +# +# CONFIG_TYPEC_DP_ALTMODE is not set +CONFIG_USB_ROLE_SWITCH=m +# CONFIG_USB_ROLES_INTEL_XHCI is not set +CONFIG_USB_LED_TRIG=y +CONFIG_USB_ULPI_BUS=m +CONFIG_UWB=m +CONFIG_UWB_HWA=m +CONFIG_UWB_WHCI=m +CONFIG_UWB_I1480U=m +CONFIG_MMC=y +CONFIG_MMC_BLOCK=m +CONFIG_MMC_BLOCK_MINORS=8 +CONFIG_SDIO_UART=m +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_SDHCI=m +CONFIG_MMC_SDHCI_PCI=m +CONFIG_MMC_RICOH_MMC=y +CONFIG_MMC_SDHCI_ACPI=m +CONFIG_MMC_SDHCI_PLTFM=m +# CONFIG_MMC_SDHCI_F_SDH30 is not set +CONFIG_MMC_WBSD=m +CONFIG_MMC_TIFM_SD=m +CONFIG_MMC_SPI=m +CONFIG_MMC_SDRICOH_CS=m +CONFIG_MMC_CB710=m +CONFIG_MMC_VIA_SDMMC=m +CONFIG_MMC_VUB300=m +CONFIG_MMC_USHC=m +CONFIG_MMC_USDHI6ROL0=m +CONFIG_MMC_REALTEK_PCI=m +CONFIG_MMC_CQHCI=m +CONFIG_MMC_TOSHIBA_PCI=m +CONFIG_MMC_MTK=m +CONFIG_MMC_SDHCI_XENON=m +CONFIG_MEMSTICK=m +# CONFIG_MEMSTICK_DEBUG is not set + +# +# MemoryStick drivers +# +# CONFIG_MEMSTICK_UNSAFE_RESUME is not set +CONFIG_MSPRO_BLOCK=m +CONFIG_MS_BLOCK=m + +# +# MemoryStick Host Controller Drivers +# +CONFIG_MEMSTICK_TIFM_MS=m +CONFIG_MEMSTICK_JMICRON_38X=m +CONFIG_MEMSTICK_R592=m +CONFIG_MEMSTICK_REALTEK_PCI=m +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_CLASS_FLASH=m +CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y + +# +# LED drivers +# +CONFIG_LEDS_88PM860X=m +# CONFIG_LEDS_APU is not set +# CONFIG_LEDS_AS3645A is not set +CONFIG_LEDS_LM3530=m +CONFIG_LEDS_LM3533=m +CONFIG_LEDS_LM3642=m +# CONFIG_LEDS_LM3601X is not set +CONFIG_LEDS_MT6323=m +CONFIG_LEDS_PCA9532=m +CONFIG_LEDS_PCA9532_GPIO=y +CONFIG_LEDS_GPIO=m +CONFIG_LEDS_LP3944=m +CONFIG_LEDS_LP3952=m +CONFIG_LEDS_LP55XX_COMMON=m +CONFIG_LEDS_LP5521=m +CONFIG_LEDS_LP5523=m +CONFIG_LEDS_LP5562=m +CONFIG_LEDS_LP8501=m +CONFIG_LEDS_LP8788=m +CONFIG_LEDS_CLEVO_MAIL=m +CONFIG_LEDS_PCA955X=m +# CONFIG_LEDS_PCA955X_GPIO is not set +CONFIG_LEDS_PCA963X=m +CONFIG_LEDS_WM831X_STATUS=m +CONFIG_LEDS_WM8350=m +CONFIG_LEDS_DA903X=m +CONFIG_LEDS_DA9052=m +CONFIG_LEDS_DAC124S085=m +CONFIG_LEDS_PWM=m +CONFIG_LEDS_REGULATOR=m +CONFIG_LEDS_BD2802=m +CONFIG_LEDS_INTEL_SS4200=m +CONFIG_LEDS_LT3593=m +CONFIG_LEDS_ADP5520=m +CONFIG_LEDS_MC13783=m +CONFIG_LEDS_TCA6507=m +CONFIG_LEDS_TLC591XX=m +CONFIG_LEDS_MAX8997=m +CONFIG_LEDS_LM355x=m +CONFIG_LEDS_MENF21BMC=m + +# +# LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM) +# +CONFIG_LEDS_BLINKM=m +CONFIG_LEDS_MLXCPLD=m +# CONFIG_LEDS_MLXREG is not set +CONFIG_LEDS_USER=m +CONFIG_LEDS_NIC78BX=m + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=m +CONFIG_LEDS_TRIGGER_ONESHOT=m +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LEDS_TRIGGER_MTD=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=m +CONFIG_LEDS_TRIGGER_BACKLIGHT=m +CONFIG_LEDS_TRIGGER_CPU=y +# CONFIG_LEDS_TRIGGER_ACTIVITY is not set +CONFIG_LEDS_TRIGGER_GPIO=m +CONFIG_LEDS_TRIGGER_DEFAULT_ON=m + +# +# iptables trigger is under Netfilter config (LED target) +# +CONFIG_LEDS_TRIGGER_TRANSIENT=m +CONFIG_LEDS_TRIGGER_CAMERA=m +CONFIG_LEDS_TRIGGER_PANIC=y +# CONFIG_LEDS_TRIGGER_NETDEV is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_INFINIBAND=m +CONFIG_INFINIBAND_USER_MAD=m +CONFIG_INFINIBAND_USER_ACCESS=m +# CONFIG_INFINIBAND_EXP_LEGACY_VERBS_NEW_UAPI is not set +CONFIG_INFINIBAND_USER_MEM=y +CONFIG_INFINIBAND_ON_DEMAND_PAGING=y +CONFIG_INFINIBAND_ADDR_TRANS=y +CONFIG_INFINIBAND_ADDR_TRANS_CONFIGFS=y +CONFIG_INFINIBAND_MTHCA=m +# CONFIG_INFINIBAND_MTHCA_DEBUG is not set +CONFIG_INFINIBAND_QIB=m +CONFIG_INFINIBAND_QIB_DCA=y +CONFIG_INFINIBAND_CXGB3=m +CONFIG_INFINIBAND_CXGB4=m +CONFIG_INFINIBAND_I40IW=m +CONFIG_MLX4_INFINIBAND=m +CONFIG_MLX5_INFINIBAND=m +CONFIG_INFINIBAND_NES=m +# CONFIG_INFINIBAND_NES_DEBUG is not set +CONFIG_INFINIBAND_OCRDMA=m +CONFIG_INFINIBAND_VMWARE_PVRDMA=m +CONFIG_INFINIBAND_USNIC=m +CONFIG_INFINIBAND_IPOIB=m +CONFIG_INFINIBAND_IPOIB_CM=y +# CONFIG_INFINIBAND_IPOIB_DEBUG is not set +CONFIG_INFINIBAND_SRP=m +CONFIG_INFINIBAND_SRPT=m +CONFIG_INFINIBAND_ISER=m +CONFIG_INFINIBAND_ISERT=m +CONFIG_INFINIBAND_OPA_VNIC=m +CONFIG_INFINIBAND_RDMAVT=m +CONFIG_RDMA_RXE=m +CONFIG_INFINIBAND_HFI1=m +# CONFIG_HFI1_DEBUG_SDMA_ORDER is not set +# CONFIG_SDMA_VERBOSITY is not set +CONFIG_INFINIBAND_QEDR=m +CONFIG_INFINIBAND_BNXT_RE=m +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EDAC=y +# CONFIG_EDAC_LEGACY_SYSFS is not set +# CONFIG_EDAC_DEBUG is not set +CONFIG_EDAC_DECODE_MCE=m +# CONFIG_EDAC_GHES is not set +CONFIG_EDAC_AMD64=m +# CONFIG_EDAC_AMD64_ERROR_INJECTION is not set +CONFIG_EDAC_E752X=m +CONFIG_EDAC_I82975X=m +CONFIG_EDAC_I3000=m +CONFIG_EDAC_I3200=m +CONFIG_EDAC_IE31200=m +CONFIG_EDAC_X38=m +CONFIG_EDAC_I5400=m +CONFIG_EDAC_I7CORE=m +CONFIG_EDAC_I5000=m +CONFIG_EDAC_I5100=m +CONFIG_EDAC_I7300=m +CONFIG_EDAC_SBRIDGE=m +CONFIG_EDAC_SKX=m +CONFIG_EDAC_PND2=m +CONFIG_RTC_LIB=y +CONFIG_RTC_MC146818_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_SYSTOHC_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set +CONFIG_RTC_NVMEM=y + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +CONFIG_RTC_DRV_88PM860X=m +CONFIG_RTC_DRV_88PM80X=m +CONFIG_RTC_DRV_ABB5ZES3=m +CONFIG_RTC_DRV_ABX80X=m +CONFIG_RTC_DRV_DS1307=m +CONFIG_RTC_DRV_DS1307_CENTURY=y +CONFIG_RTC_DRV_DS1374=m +CONFIG_RTC_DRV_DS1374_WDT=y +CONFIG_RTC_DRV_DS1672=m +CONFIG_RTC_DRV_LP8788=m +CONFIG_RTC_DRV_MAX6900=m +CONFIG_RTC_DRV_MAX8907=m +CONFIG_RTC_DRV_MAX8925=m +CONFIG_RTC_DRV_MAX8998=m +CONFIG_RTC_DRV_MAX8997=m +CONFIG_RTC_DRV_RS5C372=m +CONFIG_RTC_DRV_ISL1208=m +CONFIG_RTC_DRV_ISL12022=m +CONFIG_RTC_DRV_X1205=m +CONFIG_RTC_DRV_PCF8523=m +CONFIG_RTC_DRV_PCF85063=m +# CONFIG_RTC_DRV_PCF85363 is not set +CONFIG_RTC_DRV_PCF8563=m +CONFIG_RTC_DRV_PCF8583=m +CONFIG_RTC_DRV_M41T80=m +CONFIG_RTC_DRV_M41T80_WDT=y +CONFIG_RTC_DRV_BQ32K=m +CONFIG_RTC_DRV_PALMAS=m +CONFIG_RTC_DRV_TPS6586X=m +CONFIG_RTC_DRV_TPS65910=m +CONFIG_RTC_DRV_TPS80031=m +CONFIG_RTC_DRV_RC5T583=m +CONFIG_RTC_DRV_S35390A=m +CONFIG_RTC_DRV_FM3130=m +CONFIG_RTC_DRV_RX8010=m +CONFIG_RTC_DRV_RX8581=m +CONFIG_RTC_DRV_RX8025=m +CONFIG_RTC_DRV_EM3027=m +CONFIG_RTC_DRV_RV8803=m +CONFIG_RTC_DRV_S5M=m + +# +# SPI RTC drivers +# +CONFIG_RTC_DRV_M41T93=m +CONFIG_RTC_DRV_M41T94=m +CONFIG_RTC_DRV_DS1302=m +CONFIG_RTC_DRV_DS1305=m +CONFIG_RTC_DRV_DS1343=m +CONFIG_RTC_DRV_DS1347=m +CONFIG_RTC_DRV_DS1390=m +CONFIG_RTC_DRV_MAX6916=m +CONFIG_RTC_DRV_R9701=m +CONFIG_RTC_DRV_RX4581=m +CONFIG_RTC_DRV_RX6110=m +CONFIG_RTC_DRV_RS5C348=m +CONFIG_RTC_DRV_MAX6902=m +CONFIG_RTC_DRV_PCF2123=m +CONFIG_RTC_DRV_MCP795=m +CONFIG_RTC_I2C_AND_SPI=y + +# +# SPI and I2C RTC drivers +# +CONFIG_RTC_DRV_DS3232=m +CONFIG_RTC_DRV_DS3232_HWMON=y +CONFIG_RTC_DRV_PCF2127=m +CONFIG_RTC_DRV_RV3029C2=m +CONFIG_RTC_DRV_RV3029_HWMON=y + +# +# Platform RTC drivers +# +CONFIG_RTC_DRV_CMOS=y +CONFIG_RTC_DRV_DS1286=m +CONFIG_RTC_DRV_DS1511=m +CONFIG_RTC_DRV_DS1553=m +CONFIG_RTC_DRV_DS1685_FAMILY=m +CONFIG_RTC_DRV_DS1685=y +# CONFIG_RTC_DRV_DS1689 is not set +# CONFIG_RTC_DRV_DS17285 is not set +# CONFIG_RTC_DRV_DS17485 is not set +# CONFIG_RTC_DRV_DS17885 is not set +# CONFIG_RTC_DS1685_PROC_REGS is not set +CONFIG_RTC_DRV_DS1742=m +CONFIG_RTC_DRV_DS2404=m +CONFIG_RTC_DRV_DA9052=m +CONFIG_RTC_DRV_DA9055=m +CONFIG_RTC_DRV_DA9063=m +CONFIG_RTC_DRV_STK17TA8=m +CONFIG_RTC_DRV_M48T86=m +CONFIG_RTC_DRV_M48T35=m +CONFIG_RTC_DRV_M48T59=m +CONFIG_RTC_DRV_MSM6242=m +CONFIG_RTC_DRV_BQ4802=m +CONFIG_RTC_DRV_RP5C01=m +CONFIG_RTC_DRV_V3020=m +CONFIG_RTC_DRV_WM831X=m +CONFIG_RTC_DRV_WM8350=m +CONFIG_RTC_DRV_PCF50633=m +CONFIG_RTC_DRV_AB3100=m +# CONFIG_RTC_DRV_CROS_EC is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_FTRTC010=m +CONFIG_RTC_DRV_PCAP=m +CONFIG_RTC_DRV_MC13XXX=m +CONFIG_RTC_DRV_MT6397=m + +# +# HID Sensor RTC drivers +# +CONFIG_RTC_DRV_HID_SENSOR_TIME=m +CONFIG_DMADEVICES=y +# CONFIG_DMADEVICES_DEBUG is not set + +# +# DMA Devices +# +CONFIG_DMA_ENGINE=y +CONFIG_DMA_VIRTUAL_CHANNELS=m +CONFIG_DMA_ACPI=y +# CONFIG_ALTERA_MSGDMA is not set +CONFIG_INTEL_IDMA64=m +CONFIG_INTEL_IOATDMA=m +CONFIG_INTEL_MIC_X100_DMA=m +CONFIG_QCOM_HIDMA_MGMT=m +CONFIG_QCOM_HIDMA=m +CONFIG_DW_DMAC_CORE=m +CONFIG_DW_DMAC=m +CONFIG_DW_DMAC_PCI=m +CONFIG_HSU_DMA=m + +# +# DMA Clients +# +CONFIG_ASYNC_TX_DMA=y +# CONFIG_DMATEST is not set +CONFIG_DMA_ENGINE_RAID=y + +# +# DMABUF options +# +CONFIG_SYNC_FILE=y +CONFIG_SW_SYNC=y +CONFIG_DCA=m +CONFIG_AUXDISPLAY=y +CONFIG_HD44780=m +CONFIG_KS0108=m +CONFIG_KS0108_PORT=0x378 +CONFIG_KS0108_DELAY=2 +CONFIG_CFAG12864B=m +CONFIG_CFAG12864B_RATE=20 +CONFIG_IMG_ASCII_LCD=m +CONFIG_PANEL=m +CONFIG_PANEL_PARPORT=0 +CONFIG_PANEL_PROFILE=5 +# CONFIG_PANEL_CHANGE_MESSAGE is not set +CONFIG_CHARLCD=m +CONFIG_UIO=m +CONFIG_UIO_CIF=m +CONFIG_UIO_PDRV_GENIRQ=m +CONFIG_UIO_DMEM_GENIRQ=m +CONFIG_UIO_AEC=m +CONFIG_UIO_SERCOS3=m +CONFIG_UIO_PCI_GENERIC=m +CONFIG_UIO_NETX=m +CONFIG_UIO_PRUSS=m +CONFIG_UIO_MF624=m +CONFIG_UIO_HV_GENERIC=m +CONFIG_VFIO_IOMMU_TYPE1=m +CONFIG_VFIO_VIRQFD=m +CONFIG_VFIO=m +CONFIG_VFIO_NOIOMMU=y +CONFIG_VFIO_PCI=m +CONFIG_VFIO_PCI_VGA=y +CONFIG_VFIO_PCI_MMAP=y +CONFIG_VFIO_PCI_INTX=y +CONFIG_VFIO_PCI_IGD=y +CONFIG_VFIO_MDEV=m +CONFIG_VFIO_MDEV_DEVICE=m +CONFIG_IRQ_BYPASS_MANAGER=m +CONFIG_VIRT_DRIVERS=y +# CONFIG_VBOXGUEST is not set +CONFIG_VIRTIO=y +CONFIG_VIRTIO_MENU=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_PCI_LEGACY=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_INPUT=m +CONFIG_VIRTIO_MMIO=y +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y + +# +# Microsoft Hyper-V guest support +# +CONFIG_HYPERV=m +CONFIG_HYPERV_TSCPAGE=y +CONFIG_HYPERV_UTILS=m +CONFIG_HYPERV_BALLOON=m + +# +# Xen driver support +# +CONFIG_XEN_BALLOON=y +CONFIG_XEN_SELFBALLOONING=y +CONFIG_XEN_BALLOON_MEMORY_HOTPLUG=y +CONFIG_XEN_BALLOON_MEMORY_HOTPLUG_LIMIT=512 +CONFIG_XEN_SCRUB_PAGES_DEFAULT=y +CONFIG_XEN_DEV_EVTCHN=m +CONFIG_XEN_BACKEND=y +CONFIG_XENFS=m +CONFIG_XEN_COMPAT_XENFS=y +CONFIG_XEN_SYS_HYPERVISOR=y +CONFIG_XEN_XENBUS_FRONTEND=y +CONFIG_XEN_GNTDEV=m +CONFIG_XEN_GRANT_DEV_ALLOC=m +# CONFIG_XEN_GRANT_DMA_ALLOC is not set +CONFIG_SWIOTLB_XEN=y +CONFIG_XEN_TMEM=m +CONFIG_XEN_PCIDEV_BACKEND=m +# CONFIG_XEN_PVCALLS_FRONTEND is not set +# CONFIG_XEN_PVCALLS_BACKEND is not set +CONFIG_XEN_SCSI_BACKEND=m +CONFIG_XEN_PRIVCMD=m +CONFIG_XEN_ACPI_PROCESSOR=y +CONFIG_XEN_MCE_LOG=y +CONFIG_XEN_HAVE_PVMMU=y +CONFIG_XEN_EFI=y +CONFIG_XEN_AUTO_XLATE=y +CONFIG_XEN_ACPI=y +CONFIG_XEN_SYMS=y +CONFIG_XEN_HAVE_VPMU=y +CONFIG_STAGING=y +CONFIG_PRISM2_USB=m +CONFIG_COMEDI=m +# CONFIG_COMEDI_DEBUG is not set +CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB=2048 +CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB=20480 +CONFIG_COMEDI_MISC_DRIVERS=y +CONFIG_COMEDI_BOND=m +CONFIG_COMEDI_TEST=m +CONFIG_COMEDI_PARPORT=m +CONFIG_COMEDI_ISA_DRIVERS=y +CONFIG_COMEDI_PCL711=m +CONFIG_COMEDI_PCL724=m +CONFIG_COMEDI_PCL726=m +CONFIG_COMEDI_PCL730=m +CONFIG_COMEDI_PCL812=m +CONFIG_COMEDI_PCL816=m +CONFIG_COMEDI_PCL818=m +CONFIG_COMEDI_PCM3724=m +CONFIG_COMEDI_AMPLC_DIO200_ISA=m +CONFIG_COMEDI_AMPLC_PC236_ISA=m +CONFIG_COMEDI_AMPLC_PC263_ISA=m +CONFIG_COMEDI_RTI800=m +CONFIG_COMEDI_RTI802=m +CONFIG_COMEDI_DAC02=m +CONFIG_COMEDI_DAS16M1=m +CONFIG_COMEDI_DAS08_ISA=m +CONFIG_COMEDI_DAS16=m +CONFIG_COMEDI_DAS800=m +CONFIG_COMEDI_DAS1800=m +CONFIG_COMEDI_DAS6402=m +CONFIG_COMEDI_DT2801=m +CONFIG_COMEDI_DT2811=m +CONFIG_COMEDI_DT2814=m +CONFIG_COMEDI_DT2815=m +CONFIG_COMEDI_DT2817=m +CONFIG_COMEDI_DT282X=m +CONFIG_COMEDI_DMM32AT=m +CONFIG_COMEDI_FL512=m +CONFIG_COMEDI_AIO_AIO12_8=m +CONFIG_COMEDI_AIO_IIRO_16=m +CONFIG_COMEDI_II_PCI20KC=m +CONFIG_COMEDI_C6XDIGIO=m +CONFIG_COMEDI_MPC624=m +CONFIG_COMEDI_ADQ12B=m +CONFIG_COMEDI_NI_AT_A2150=m +CONFIG_COMEDI_NI_AT_AO=m +CONFIG_COMEDI_NI_ATMIO=m +CONFIG_COMEDI_NI_ATMIO16D=m +CONFIG_COMEDI_NI_LABPC_ISA=m +CONFIG_COMEDI_PCMAD=m +CONFIG_COMEDI_PCMDA12=m +CONFIG_COMEDI_PCMMIO=m +CONFIG_COMEDI_PCMUIO=m +CONFIG_COMEDI_MULTIQ3=m +CONFIG_COMEDI_S526=m +CONFIG_COMEDI_PCI_DRIVERS=m +CONFIG_COMEDI_8255_PCI=m +CONFIG_COMEDI_ADDI_WATCHDOG=m +CONFIG_COMEDI_ADDI_APCI_1032=m +CONFIG_COMEDI_ADDI_APCI_1500=m +CONFIG_COMEDI_ADDI_APCI_1516=m +CONFIG_COMEDI_ADDI_APCI_1564=m +CONFIG_COMEDI_ADDI_APCI_16XX=m +CONFIG_COMEDI_ADDI_APCI_2032=m +CONFIG_COMEDI_ADDI_APCI_2200=m +CONFIG_COMEDI_ADDI_APCI_3120=m +CONFIG_COMEDI_ADDI_APCI_3501=m +CONFIG_COMEDI_ADDI_APCI_3XXX=m +CONFIG_COMEDI_ADL_PCI6208=m +CONFIG_COMEDI_ADL_PCI7X3X=m +CONFIG_COMEDI_ADL_PCI8164=m +CONFIG_COMEDI_ADL_PCI9111=m +CONFIG_COMEDI_ADL_PCI9118=m +CONFIG_COMEDI_ADV_PCI1710=m +CONFIG_COMEDI_ADV_PCI1720=m +CONFIG_COMEDI_ADV_PCI1723=m +CONFIG_COMEDI_ADV_PCI1724=m +CONFIG_COMEDI_ADV_PCI1760=m +CONFIG_COMEDI_ADV_PCI_DIO=m +CONFIG_COMEDI_AMPLC_DIO200_PCI=m +CONFIG_COMEDI_AMPLC_PC236_PCI=m +CONFIG_COMEDI_AMPLC_PC263_PCI=m +CONFIG_COMEDI_AMPLC_PCI224=m +CONFIG_COMEDI_AMPLC_PCI230=m +CONFIG_COMEDI_CONTEC_PCI_DIO=m +CONFIG_COMEDI_DAS08_PCI=m +CONFIG_COMEDI_DT3000=m +CONFIG_COMEDI_DYNA_PCI10XX=m +CONFIG_COMEDI_GSC_HPDI=m +CONFIG_COMEDI_MF6X4=m +CONFIG_COMEDI_ICP_MULTI=m +CONFIG_COMEDI_DAQBOARD2000=m +CONFIG_COMEDI_JR3_PCI=m +CONFIG_COMEDI_KE_COUNTER=m +CONFIG_COMEDI_CB_PCIDAS64=m +CONFIG_COMEDI_CB_PCIDAS=m +CONFIG_COMEDI_CB_PCIDDA=m +CONFIG_COMEDI_CB_PCIMDAS=m +CONFIG_COMEDI_CB_PCIMDDA=m +CONFIG_COMEDI_ME4000=m +CONFIG_COMEDI_ME_DAQ=m +CONFIG_COMEDI_NI_6527=m +CONFIG_COMEDI_NI_65XX=m +CONFIG_COMEDI_NI_660X=m +CONFIG_COMEDI_NI_670X=m +CONFIG_COMEDI_NI_LABPC_PCI=m +CONFIG_COMEDI_NI_PCIDIO=m +CONFIG_COMEDI_NI_PCIMIO=m +CONFIG_COMEDI_RTD520=m +CONFIG_COMEDI_S626=m +CONFIG_COMEDI_MITE=m +CONFIG_COMEDI_NI_TIOCMD=m +CONFIG_COMEDI_PCMCIA_DRIVERS=m +CONFIG_COMEDI_CB_DAS16_CS=m +CONFIG_COMEDI_DAS08_CS=m +CONFIG_COMEDI_NI_DAQ_700_CS=m +CONFIG_COMEDI_NI_DAQ_DIO24_CS=m +CONFIG_COMEDI_NI_LABPC_CS=m +CONFIG_COMEDI_NI_MIO_CS=m +CONFIG_COMEDI_QUATECH_DAQP_CS=m +CONFIG_COMEDI_USB_DRIVERS=m +CONFIG_COMEDI_DT9812=m +CONFIG_COMEDI_NI_USB6501=m +CONFIG_COMEDI_USBDUX=m +CONFIG_COMEDI_USBDUXFAST=m +CONFIG_COMEDI_USBDUXSIGMA=m +CONFIG_COMEDI_VMK80XX=m +CONFIG_COMEDI_8254=m +CONFIG_COMEDI_8255=m +CONFIG_COMEDI_8255_SA=m +CONFIG_COMEDI_KCOMEDILIB=m +CONFIG_COMEDI_AMPLC_DIO200=m +CONFIG_COMEDI_AMPLC_PC236=m +CONFIG_COMEDI_DAS08=m +CONFIG_COMEDI_ISADMA=m +CONFIG_COMEDI_NI_LABPC=m +CONFIG_COMEDI_NI_LABPC_ISADMA=m +CONFIG_COMEDI_NI_TIO=m +CONFIG_RTL8192U=m +CONFIG_RTLLIB=m +CONFIG_RTLLIB_CRYPTO_CCMP=m +CONFIG_RTLLIB_CRYPTO_TKIP=m +CONFIG_RTLLIB_CRYPTO_WEP=m +CONFIG_RTL8192E=m +CONFIG_RTL8723BS=m +CONFIG_R8712U=m +CONFIG_R8188EU=m +CONFIG_88EU_AP_MODE=y +# CONFIG_R8822BE is not set +CONFIG_RTS5208=m +CONFIG_VT6655=m +CONFIG_VT6656=m + +# +# IIO staging drivers +# + +# +# Accelerometers +# +CONFIG_ADIS16203=m +CONFIG_ADIS16240=m + +# +# Analog to digital converters +# +CONFIG_AD7606=m +CONFIG_AD7606_IFACE_PARALLEL=m +CONFIG_AD7606_IFACE_SPI=m +CONFIG_AD7780=m +CONFIG_AD7816=m +CONFIG_AD7192=m +CONFIG_AD7280=m + +# +# Analog digital bi-direction converters +# +CONFIG_ADT7316=m +CONFIG_ADT7316_SPI=m +CONFIG_ADT7316_I2C=m + +# +# Capacitance to digital converters +# +CONFIG_AD7150=m +CONFIG_AD7152=m +CONFIG_AD7746=m + +# +# Direct Digital Synthesis +# +CONFIG_AD9832=m +CONFIG_AD9834=m + +# +# Network Analyzer, Impedance Converters +# +CONFIG_AD5933=m + +# +# Active energy metering IC +# +CONFIG_ADE7854=m +CONFIG_ADE7854_I2C=m +CONFIG_ADE7854_SPI=m + +# +# Resolver to digital converters +# +CONFIG_AD2S90=m +CONFIG_AD2S1210=m +CONFIG_FB_SM750=m +CONFIG_FB_XGI=m + +# +# Speakup console speech +# +CONFIG_SPEAKUP=m +CONFIG_SPEAKUP_SYNTH_ACNTSA=m +CONFIG_SPEAKUP_SYNTH_APOLLO=m +CONFIG_SPEAKUP_SYNTH_AUDPTR=m +CONFIG_SPEAKUP_SYNTH_BNS=m +CONFIG_SPEAKUP_SYNTH_DECTLK=m +CONFIG_SPEAKUP_SYNTH_DECEXT=m +CONFIG_SPEAKUP_SYNTH_LTLK=m +CONFIG_SPEAKUP_SYNTH_SOFT=m +CONFIG_SPEAKUP_SYNTH_SPKOUT=m +CONFIG_SPEAKUP_SYNTH_TXPRT=m +CONFIG_SPEAKUP_SYNTH_DUMMY=m +CONFIG_STAGING_MEDIA=y +CONFIG_I2C_BCM2048=m +CONFIG_SOC_CAMERA_IMX074=m +CONFIG_SOC_CAMERA_MT9T031=m +CONFIG_VIDEO_ZORAN=m +CONFIG_VIDEO_ZORAN_DC30=m +CONFIG_VIDEO_ZORAN_ZR36060=m +CONFIG_VIDEO_ZORAN_BUZ=m +CONFIG_VIDEO_ZORAN_DC10=m +CONFIG_VIDEO_ZORAN_LML33=m +CONFIG_VIDEO_ZORAN_LML33R10=m +CONFIG_VIDEO_ZORAN_AVS6EYES=m + +# +# Android +# +CONFIG_LTE_GDM724X=m +CONFIG_FIREWIRE_SERIAL=m +CONFIG_FWTTY_MAX_TOTAL_PORTS=64 +CONFIG_FWTTY_MAX_CARD_PORTS=32 +# CONFIG_MTD_SPINAND_MT29F is not set +# CONFIG_DGNC is not set +CONFIG_GS_FPGABOOT=m +CONFIG_UNISYSSPAR=y +CONFIG_UNISYS_VISORNIC=m +CONFIG_UNISYS_VISORINPUT=m +CONFIG_UNISYS_VISORHBA=m +CONFIG_FB_TFT=m +CONFIG_FB_TFT_AGM1264K_FL=m +CONFIG_FB_TFT_BD663474=m +CONFIG_FB_TFT_HX8340BN=m +CONFIG_FB_TFT_HX8347D=m +CONFIG_FB_TFT_HX8353D=m +CONFIG_FB_TFT_HX8357D=m +CONFIG_FB_TFT_ILI9163=m +CONFIG_FB_TFT_ILI9320=m +CONFIG_FB_TFT_ILI9325=m +CONFIG_FB_TFT_ILI9340=m +CONFIG_FB_TFT_ILI9341=m +CONFIG_FB_TFT_ILI9481=m +CONFIG_FB_TFT_ILI9486=m +CONFIG_FB_TFT_PCD8544=m +CONFIG_FB_TFT_RA8875=m +CONFIG_FB_TFT_S6D02A1=m +CONFIG_FB_TFT_S6D1121=m +CONFIG_FB_TFT_SH1106=m +CONFIG_FB_TFT_SSD1289=m +CONFIG_FB_TFT_SSD1305=m +CONFIG_FB_TFT_SSD1306=m +CONFIG_FB_TFT_SSD1331=m +CONFIG_FB_TFT_SSD1351=m +CONFIG_FB_TFT_ST7735R=m +CONFIG_FB_TFT_ST7789V=m +CONFIG_FB_TFT_TINYLCD=m +CONFIG_FB_TFT_TLS8204=m +CONFIG_FB_TFT_UC1611=m +CONFIG_FB_TFT_UC1701=m +CONFIG_FB_TFT_UPD161704=m +CONFIG_FB_TFT_WATTEROTT=m +CONFIG_FB_FLEX=m +CONFIG_FB_TFT_FBTFT_DEVICE=m +CONFIG_WILC1000=m +CONFIG_WILC1000_SDIO=m +CONFIG_WILC1000_SPI=m +CONFIG_WILC1000_HW_OOB_INTR=y +CONFIG_MOST=m +# CONFIG_MOST_CDEV is not set +# CONFIG_MOST_NET is not set +# CONFIG_MOST_SOUND is not set +# CONFIG_MOST_VIDEO is not set +# CONFIG_MOST_I2C is not set +# CONFIG_MOST_USB is not set +CONFIG_KS7010=m +CONFIG_GREYBUS=m +CONFIG_GREYBUS_ES2=m +CONFIG_GREYBUS_AUDIO=m +CONFIG_GREYBUS_BOOTROM=m +CONFIG_GREYBUS_FIRMWARE=m +CONFIG_GREYBUS_HID=m +CONFIG_GREYBUS_LIGHT=m +CONFIG_GREYBUS_LOG=m +CONFIG_GREYBUS_LOOPBACK=m +CONFIG_GREYBUS_POWER=m +CONFIG_GREYBUS_RAW=m +CONFIG_GREYBUS_VIBRATOR=m +CONFIG_GREYBUS_BRIDGED_PHY=m +CONFIG_GREYBUS_GPIO=m +CONFIG_GREYBUS_I2C=m +CONFIG_GREYBUS_PWM=m +CONFIG_GREYBUS_SDIO=m +CONFIG_GREYBUS_SPI=m +CONFIG_GREYBUS_UART=m +CONFIG_GREYBUS_USB=m +# CONFIG_DRM_VBOXVIDEO is not set +# CONFIG_PI433 is not set +# CONFIG_MTK_MMC is not set + +# +# Gasket devices +# +# CONFIG_STAGING_GASKET_FRAMEWORK is not set +# CONFIG_XIL_AXIS_FIFO is not set +# CONFIG_EROFS_FS is not set +CONFIG_X86_PLATFORM_DEVICES=y +CONFIG_ACER_WMI=m +# CONFIG_ACER_WIRELESS is not set +CONFIG_ACERHDF=m +CONFIG_ALIENWARE_WMI=m +CONFIG_ASUS_LAPTOP=m +CONFIG_DELL_SMBIOS=m +CONFIG_DELL_SMBIOS_WMI=y +CONFIG_DELL_SMBIOS_SMM=y +CONFIG_DELL_LAPTOP=m +CONFIG_DELL_WMI=m +CONFIG_DELL_WMI_DESCRIPTOR=m +CONFIG_DELL_WMI_AIO=m +CONFIG_DELL_WMI_LED=m +CONFIG_DELL_SMO8800=m +CONFIG_DELL_RBTN=m +CONFIG_FUJITSU_LAPTOP=m +CONFIG_FUJITSU_TABLET=m +CONFIG_AMILO_RFKILL=m +# CONFIG_GPD_POCKET_FAN is not set +CONFIG_HP_ACCEL=m +CONFIG_HP_WIRELESS=m +CONFIG_HP_WMI=m +CONFIG_MSI_LAPTOP=m +CONFIG_PANASONIC_LAPTOP=m +CONFIG_COMPAL_LAPTOP=m +CONFIG_SONY_LAPTOP=m +CONFIG_SONYPI_COMPAT=y +CONFIG_IDEAPAD_LAPTOP=m +CONFIG_SURFACE3_WMI=m +CONFIG_THINKPAD_ACPI=m +CONFIG_THINKPAD_ACPI_ALSA_SUPPORT=y +CONFIG_THINKPAD_ACPI_DEBUGFACILITIES=y +# CONFIG_THINKPAD_ACPI_DEBUG is not set +# CONFIG_THINKPAD_ACPI_UNSAFE_LEDS is not set +CONFIG_THINKPAD_ACPI_VIDEO=y +CONFIG_THINKPAD_ACPI_HOTKEY_POLL=y +CONFIG_SURFACE_ACPI=m +CONFIG_SURFACE_ACPI_SSH=y +CONFIG_SURFACE_ACPI_SAN=y +CONFIG_SURFACE_ACPI_VHF=y +CONFIG_SENSORS_HDAPS=m +CONFIG_INTEL_MENLOW=m +CONFIG_EEEPC_LAPTOP=m +CONFIG_ASUS_WMI=m +CONFIG_ASUS_NB_WMI=m +CONFIG_EEEPC_WMI=m +CONFIG_ASUS_WIRELESS=m +CONFIG_ACPI_WMI=m +CONFIG_WMI_BMOF=m +# CONFIG_INTEL_WMI_THUNDERBOLT is not set +CONFIG_MSI_WMI=m +CONFIG_PEAQ_WMI=m +CONFIG_TOPSTAR_LAPTOP=m +CONFIG_ACPI_TOSHIBA=m +CONFIG_TOSHIBA_BT_RFKILL=m +CONFIG_TOSHIBA_HAPS=m +# CONFIG_TOSHIBA_WMI is not set +CONFIG_ACPI_CMPC=m +# CONFIG_INTEL_CHT_INT33FE is not set +CONFIG_INTEL_INT0002_VGPIO=m +CONFIG_INTEL_HID_EVENT=m +CONFIG_INTEL_VBTN=m +CONFIG_INTEL_IPS=m +CONFIG_INTEL_PMC_CORE=y +CONFIG_IBM_RTL=m +CONFIG_SAMSUNG_LAPTOP=m +CONFIG_MXM_WMI=m +CONFIG_INTEL_OAKTRAIL=m +CONFIG_SAMSUNG_Q10=m +CONFIG_APPLE_GMUX=m +CONFIG_INTEL_RST=m +CONFIG_INTEL_SMARTCONNECT=m +CONFIG_PVPANIC=m +CONFIG_INTEL_PMC_IPC=m +CONFIG_INTEL_BXTWC_PMIC_TMU=m +CONFIG_SURFACE_PRO3_BUTTON=m +CONFIG_SURFACE_3_BUTTON=m +CONFIG_SURFACE_3_POWER_OPREGION=m +CONFIG_INTEL_PUNIT_IPC=m +CONFIG_INTEL_TELEMETRY=m +CONFIG_MLX_PLATFORM=m +CONFIG_INTEL_TURBO_MAX_3=y +# CONFIG_TOUCHSCREEN_DMI is not set +# CONFIG_I2C_MULTI_INSTANTIATE is not set +CONFIG_PMC_ATOM=y +CONFIG_CHROME_PLATFORMS=y +CONFIG_CHROMEOS_LAPTOP=m +CONFIG_CHROMEOS_PSTORE=m +# CONFIG_CHROMEOS_TBMC is not set +# CONFIG_CROS_EC_I2C is not set +# CONFIG_CROS_EC_SPI is not set +CONFIG_CROS_EC_LPC=m +CONFIG_CROS_EC_LPC_MEC=y +CONFIG_CROS_EC_PROTO=y +CONFIG_CROS_KBD_LED_BACKLIGHT=m +# CONFIG_MELLANOX_PLATFORM is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Common Clock Framework +# +CONFIG_COMMON_CLK_WM831X=m +# CONFIG_COMMON_CLK_MAX9485 is not set +CONFIG_COMMON_CLK_SI5351=m +# CONFIG_COMMON_CLK_SI544 is not set +CONFIG_COMMON_CLK_CDCE706=m +CONFIG_COMMON_CLK_CS2000_CP=m +CONFIG_COMMON_CLK_S2MPS11=m +CONFIG_CLK_TWL6040=m +CONFIG_COMMON_CLK_PALMAS=m +CONFIG_COMMON_CLK_PWM=m +# CONFIG_HWSPINLOCK is not set + +# +# Clock Source drivers +# +CONFIG_CLKEVT_I8253=y +CONFIG_I8253_LOCK=y +CONFIG_CLKBLD_I8253=y +CONFIG_MAILBOX=y +CONFIG_PCC=y +CONFIG_ALTERA_MBOX=m +CONFIG_IOMMU_API=y +CONFIG_IOMMU_SUPPORT=y + +# +# Generic IOMMU Pagetable Support +# +# CONFIG_IOMMU_DEBUGFS is not set +# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set +CONFIG_IOMMU_IOVA=y +CONFIG_AMD_IOMMU=y +CONFIG_AMD_IOMMU_V2=m +CONFIG_DMAR_TABLE=y +CONFIG_INTEL_IOMMU=y +CONFIG_INTEL_IOMMU_SVM=y +# CONFIG_INTEL_IOMMU_DEFAULT_ON is not set +CONFIG_INTEL_IOMMU_FLOPPY_WA=y +CONFIG_IRQ_REMAP=y + +# +# Remoteproc drivers +# +CONFIG_REMOTEPROC=m + +# +# Rpmsg drivers +# +CONFIG_RPMSG=m +CONFIG_RPMSG_CHAR=m +CONFIG_RPMSG_QCOM_GLINK_NATIVE=m +CONFIG_RPMSG_QCOM_GLINK_RPM=m +# CONFIG_RPMSG_VIRTIO is not set +# CONFIG_SOUNDWIRE is not set + +# +# SOC (System On Chip) specific Drivers +# + +# +# Amlogic SoC drivers +# + +# +# Broadcom SoC drivers +# + +# +# NXP/Freescale QorIQ SoC drivers +# + +# +# i.MX SoC drivers +# + +# +# Qualcomm SoC drivers +# +CONFIG_SOC_TI=y + +# +# Xilinx SoC drivers +# +# CONFIG_XILINX_VCU is not set +CONFIG_PM_DEVFREQ=y + +# +# DEVFREQ Governors +# +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +CONFIG_DEVFREQ_GOV_PASSIVE=y + +# +# DEVFREQ Drivers +# +CONFIG_PM_DEVFREQ_EVENT=y +CONFIG_EXTCON=y + +# +# Extcon Device Drivers +# +CONFIG_EXTCON_ADC_JACK=m +CONFIG_EXTCON_ARIZONA=m +CONFIG_EXTCON_AXP288=m +CONFIG_EXTCON_GPIO=m +CONFIG_EXTCON_INTEL_INT3496=m +CONFIG_EXTCON_MAX14577=m +CONFIG_EXTCON_MAX3355=m +CONFIG_EXTCON_MAX77693=m +CONFIG_EXTCON_MAX77843=m +CONFIG_EXTCON_MAX8997=m +CONFIG_EXTCON_PALMAS=m +CONFIG_EXTCON_RT8973A=m +CONFIG_EXTCON_SM5502=m +CONFIG_EXTCON_USB_GPIO=m +# CONFIG_EXTCON_USBC_CROS_EC is not set +CONFIG_MEMORY=y +CONFIG_IIO=m +CONFIG_IIO_BUFFER=y +CONFIG_IIO_BUFFER_CB=m +# CONFIG_IIO_BUFFER_HW_CONSUMER is not set +CONFIG_IIO_KFIFO_BUF=m +CONFIG_IIO_TRIGGERED_BUFFER=m +CONFIG_IIO_CONFIGFS=m +CONFIG_IIO_TRIGGER=y +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 +CONFIG_IIO_SW_DEVICE=m +CONFIG_IIO_SW_TRIGGER=m +CONFIG_IIO_TRIGGERED_EVENT=m + +# +# Accelerometers +# +CONFIG_ADIS16201=m +CONFIG_ADIS16209=m +CONFIG_BMA180=m +CONFIG_BMA220=m +CONFIG_BMC150_ACCEL=m +CONFIG_BMC150_ACCEL_I2C=m +CONFIG_BMC150_ACCEL_SPI=m +CONFIG_DA280=m +CONFIG_DA311=m +CONFIG_DMARD09=m +CONFIG_DMARD10=m +CONFIG_HID_SENSOR_ACCEL_3D=m +# CONFIG_IIO_CROS_EC_ACCEL_LEGACY is not set +CONFIG_IIO_ST_ACCEL_3AXIS=m +CONFIG_IIO_ST_ACCEL_I2C_3AXIS=m +CONFIG_IIO_ST_ACCEL_SPI_3AXIS=m +CONFIG_KXSD9=m +CONFIG_KXSD9_SPI=m +CONFIG_KXSD9_I2C=m +CONFIG_KXCJK1013=m +CONFIG_MC3230=m +CONFIG_MMA7455=m +CONFIG_MMA7455_I2C=m +CONFIG_MMA7455_SPI=m +CONFIG_MMA7660=m +CONFIG_MMA8452=m +CONFIG_MMA9551_CORE=m +CONFIG_MMA9551=m +CONFIG_MMA9553=m +CONFIG_MXC4005=m +CONFIG_MXC6255=m +CONFIG_SCA3000=m +CONFIG_STK8312=m +CONFIG_STK8BA50=m + +# +# Analog to digital converters +# +CONFIG_AD_SIGMA_DELTA=m +CONFIG_AD7266=m +CONFIG_AD7291=m +CONFIG_AD7298=m +CONFIG_AD7476=m +CONFIG_AD7766=m +CONFIG_AD7791=m +CONFIG_AD7793=m +CONFIG_AD7887=m +CONFIG_AD7923=m +CONFIG_AD799X=m +CONFIG_AXP20X_ADC=m +CONFIG_AXP288_ADC=m +CONFIG_CC10001_ADC=m +CONFIG_DA9150_GPADC=m +# CONFIG_DLN2_ADC is not set +CONFIG_HI8435=m +CONFIG_HX711=m +CONFIG_INA2XX_ADC=m +CONFIG_LP8788_ADC=m +# CONFIG_LTC2471 is not set +CONFIG_LTC2485=m +CONFIG_LTC2497=m +CONFIG_MAX1027=m +CONFIG_MAX11100=m +CONFIG_MAX1118=m +CONFIG_MAX1363=m +CONFIG_MAX9611=m +CONFIG_MCP320X=m +CONFIG_MCP3422=m +CONFIG_MEN_Z188_ADC=m +CONFIG_NAU7802=m +CONFIG_PALMAS_GPADC=m +CONFIG_QCOM_VADC_COMMON=m +CONFIG_QCOM_SPMI_IADC=m +CONFIG_QCOM_SPMI_VADC=m +CONFIG_STX104=m +CONFIG_TI_ADC081C=m +CONFIG_TI_ADC0832=m +CONFIG_TI_ADC084S021=m +CONFIG_TI_ADC12138=m +CONFIG_TI_ADC108S102=m +CONFIG_TI_ADC128S052=m +CONFIG_TI_ADC161S626=m +CONFIG_TI_ADS1015=m +CONFIG_TI_ADS7950=m +CONFIG_TI_AM335X_ADC=m +CONFIG_TI_TLC4541=m +CONFIG_TWL4030_MADC=m +CONFIG_TWL6030_GPADC=m +CONFIG_VIPERBOARD_ADC=m + +# +# Analog Front Ends +# + +# +# Amplifiers +# +CONFIG_AD8366=m + +# +# Chemical Sensors +# +CONFIG_ATLAS_PH_SENSOR=m +# CONFIG_BME680 is not set +# CONFIG_CCS811 is not set +CONFIG_IAQCORE=m +CONFIG_VZ89X=m +CONFIG_IIO_CROS_EC_SENSORS_CORE=m +CONFIG_IIO_CROS_EC_SENSORS=m + +# +# Hid Sensor IIO Common +# +CONFIG_HID_SENSOR_IIO_COMMON=m +CONFIG_HID_SENSOR_IIO_TRIGGER=m +CONFIG_IIO_MS_SENSORS_I2C=m + +# +# SSP Sensor Common +# +CONFIG_IIO_SSP_SENSORS_COMMONS=m +CONFIG_IIO_SSP_SENSORHUB=m +CONFIG_IIO_ST_SENSORS_I2C=m +CONFIG_IIO_ST_SENSORS_SPI=m +CONFIG_IIO_ST_SENSORS_CORE=m + +# +# Counters +# +CONFIG_104_QUAD_8=m + +# +# Digital to analog converters +# +CONFIG_AD5064=m +CONFIG_AD5360=m +CONFIG_AD5380=m +CONFIG_AD5421=m +CONFIG_AD5446=m +CONFIG_AD5449=m +CONFIG_AD5592R_BASE=m +CONFIG_AD5592R=m +CONFIG_AD5593R=m +CONFIG_AD5504=m +CONFIG_AD5624R_SPI=m +CONFIG_LTC2632=m +# CONFIG_AD5686_SPI is not set +# CONFIG_AD5696_I2C is not set +CONFIG_AD5755=m +# CONFIG_AD5758 is not set +CONFIG_AD5761=m +CONFIG_AD5764=m +CONFIG_AD5791=m +CONFIG_AD7303=m +CONFIG_CIO_DAC=m +CONFIG_AD8801=m +# CONFIG_DS4424 is not set +CONFIG_M62332=m +CONFIG_MAX517=m +CONFIG_MCP4725=m +CONFIG_MCP4922=m +# CONFIG_TI_DAC082S085 is not set +# CONFIG_TI_DAC5571 is not set + +# +# IIO dummy driver +# +CONFIG_IIO_SIMPLE_DUMMY=m +# CONFIG_IIO_SIMPLE_DUMMY_EVENTS is not set +# CONFIG_IIO_SIMPLE_DUMMY_BUFFER is not set + +# +# Frequency Synthesizers DDS/PLL +# + +# +# Clock Generator/Distribution +# +CONFIG_AD9523=m + +# +# Phase-Locked Loop (PLL) frequency synthesizers +# +CONFIG_ADF4350=m + +# +# Digital gyroscope sensors +# +CONFIG_ADIS16080=m +CONFIG_ADIS16130=m +CONFIG_ADIS16136=m +CONFIG_ADIS16260=m +CONFIG_ADXRS450=m +CONFIG_BMG160=m +CONFIG_BMG160_I2C=m +CONFIG_BMG160_SPI=m +CONFIG_HID_SENSOR_GYRO_3D=m +CONFIG_MPU3050=m +CONFIG_MPU3050_I2C=m +CONFIG_IIO_ST_GYRO_3AXIS=m +CONFIG_IIO_ST_GYRO_I2C_3AXIS=m +CONFIG_IIO_ST_GYRO_SPI_3AXIS=m +CONFIG_ITG3200=m + +# +# Health Sensors +# + +# +# Heart Rate Monitors +# +CONFIG_AFE4403=m +CONFIG_AFE4404=m +CONFIG_MAX30100=m +CONFIG_MAX30102=m + +# +# Humidity sensors +# +CONFIG_AM2315=m +CONFIG_DHT11=m +CONFIG_HDC100X=m +CONFIG_HID_SENSOR_HUMIDITY=m +CONFIG_HTS221=m +CONFIG_HTS221_I2C=m +CONFIG_HTS221_SPI=m +CONFIG_HTU21=m +CONFIG_SI7005=m +CONFIG_SI7020=m + +# +# Inertial measurement units +# +CONFIG_ADIS16400=m +CONFIG_ADIS16480=m +CONFIG_BMI160=m +CONFIG_BMI160_I2C=m +CONFIG_BMI160_SPI=m +CONFIG_KMX61=m +CONFIG_INV_MPU6050_IIO=m +CONFIG_INV_MPU6050_I2C=m +CONFIG_INV_MPU6050_SPI=m +CONFIG_IIO_ST_LSM6DSX=m +CONFIG_IIO_ST_LSM6DSX_I2C=m +CONFIG_IIO_ST_LSM6DSX_SPI=m +CONFIG_IIO_ADIS_LIB=m +CONFIG_IIO_ADIS_LIB_BUFFER=y + +# +# Light sensors +# +CONFIG_ACPI_ALS=m +CONFIG_ADJD_S311=m +CONFIG_AL3320A=m +CONFIG_APDS9300=m +CONFIG_APDS9960=m +CONFIG_BH1750=m +CONFIG_BH1780=m +CONFIG_CM32181=m +CONFIG_CM3232=m +CONFIG_CM3323=m +CONFIG_CM36651=m +CONFIG_IIO_CROS_EC_LIGHT_PROX=m +CONFIG_GP2AP020A00F=m +CONFIG_SENSORS_ISL29018=m +CONFIG_SENSORS_ISL29028=m +CONFIG_ISL29125=m +CONFIG_HID_SENSOR_ALS=m +CONFIG_HID_SENSOR_PROX=m +CONFIG_JSA1212=m +CONFIG_RPR0521=m +CONFIG_SENSORS_LM3533=m +CONFIG_LTR501=m +# CONFIG_LV0104CS is not set +CONFIG_MAX44000=m +CONFIG_OPT3001=m +CONFIG_PA12203001=m +# CONFIG_SI1133 is not set +CONFIG_SI1145=m +CONFIG_STK3310=m +# CONFIG_ST_UVIS25 is not set +CONFIG_TCS3414=m +CONFIG_TCS3472=m +CONFIG_SENSORS_TSL2563=m +CONFIG_TSL2583=m +# CONFIG_TSL2772 is not set +CONFIG_TSL4531=m +CONFIG_US5182D=m +CONFIG_VCNL4000=m +CONFIG_VEML6070=m +CONFIG_VL6180=m +# CONFIG_ZOPT2201 is not set + +# +# Magnetometer sensors +# +CONFIG_AK8975=m +CONFIG_AK09911=m +CONFIG_BMC150_MAGN=m +CONFIG_BMC150_MAGN_I2C=m +CONFIG_BMC150_MAGN_SPI=m +CONFIG_MAG3110=m +CONFIG_HID_SENSOR_MAGNETOMETER_3D=m +CONFIG_MMC35240=m +CONFIG_IIO_ST_MAGN_3AXIS=m +CONFIG_IIO_ST_MAGN_I2C_3AXIS=m +CONFIG_IIO_ST_MAGN_SPI_3AXIS=m +CONFIG_SENSORS_HMC5843=m +CONFIG_SENSORS_HMC5843_I2C=m +CONFIG_SENSORS_HMC5843_SPI=m + +# +# Multiplexers +# + +# +# Inclinometer sensors +# +CONFIG_HID_SENSOR_INCLINOMETER_3D=m +CONFIG_HID_SENSOR_DEVICE_ROTATION=m + +# +# Triggers - standalone +# +CONFIG_IIO_HRTIMER_TRIGGER=m +CONFIG_IIO_INTERRUPT_TRIGGER=m +CONFIG_IIO_TIGHTLOOP_TRIGGER=m +CONFIG_IIO_SYSFS_TRIGGER=m + +# +# Digital potentiometers +# +# CONFIG_AD5272 is not set +CONFIG_DS1803=m +CONFIG_MAX5481=m +CONFIG_MAX5487=m +# CONFIG_MCP4018 is not set +CONFIG_MCP4131=m +CONFIG_MCP4531=m +CONFIG_TPL0102=m + +# +# Digital potentiostats +# +CONFIG_LMP91000=m + +# +# Pressure sensors +# +CONFIG_ABP060MG=m +CONFIG_BMP280=m +CONFIG_BMP280_I2C=m +CONFIG_BMP280_SPI=m +CONFIG_IIO_CROS_EC_BARO=m +CONFIG_HID_SENSOR_PRESS=m +CONFIG_HP03=m +CONFIG_MPL115=m +CONFIG_MPL115_I2C=m +CONFIG_MPL115_SPI=m +CONFIG_MPL3115=m +CONFIG_MS5611=m +CONFIG_MS5611_I2C=m +CONFIG_MS5611_SPI=m +CONFIG_MS5637=m +CONFIG_IIO_ST_PRESS=m +CONFIG_IIO_ST_PRESS_I2C=m +CONFIG_IIO_ST_PRESS_SPI=m +CONFIG_T5403=m +CONFIG_HP206C=m +CONFIG_ZPA2326=m +CONFIG_ZPA2326_I2C=m +CONFIG_ZPA2326_SPI=m + +# +# Lightning sensors +# +CONFIG_AS3935=m + +# +# Proximity and distance sensors +# +# CONFIG_ISL29501 is not set +CONFIG_LIDAR_LITE_V2=m +# CONFIG_RFD77402 is not set +CONFIG_SRF04=m +CONFIG_SX9500=m +CONFIG_SRF08=m + +# +# Resolver to digital converters +# +CONFIG_AD2S1200=m + +# +# Temperature sensors +# +CONFIG_MAXIM_THERMOCOUPLE=m +CONFIG_HID_SENSOR_TEMP=m +CONFIG_MLX90614=m +# CONFIG_MLX90632 is not set +CONFIG_TMP006=m +CONFIG_TMP007=m +CONFIG_TSYS01=m +CONFIG_TSYS02D=m +CONFIG_NTB=m +# CONFIG_NTB_AMD is not set +CONFIG_NTB_IDT=m +CONFIG_NTB_INTEL=m +# CONFIG_NTB_SWITCHTEC is not set +CONFIG_NTB_PINGPONG=m +CONFIG_NTB_TOOL=m +CONFIG_NTB_PERF=m +CONFIG_NTB_TRANSPORT=m +CONFIG_VME_BUS=y + +# +# VME Bridge Drivers +# +CONFIG_VME_CA91CX42=m +CONFIG_VME_TSI148=m +CONFIG_VME_FAKE=m + +# +# VME Board Drivers +# +CONFIG_VMIVME_7805=m + +# +# VME Device Drivers +# +CONFIG_VME_USER=m +CONFIG_PWM=y +CONFIG_PWM_SYSFS=y +CONFIG_PWM_CRC=y +CONFIG_PWM_CROS_EC=m +CONFIG_PWM_LP3943=m +CONFIG_PWM_LPSS=m +CONFIG_PWM_LPSS_PCI=m +CONFIG_PWM_LPSS_PLATFORM=m +CONFIG_PWM_PCA9685=m +CONFIG_PWM_TWL=m +CONFIG_PWM_TWL_LED=m + +# +# IRQ chip support +# +CONFIG_ARM_GIC_MAX_NR=1 +CONFIG_IPACK_BUS=m +CONFIG_BOARD_TPCI200=m +CONFIG_SERIAL_IPOCTAL=m +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_TI_SYSCON=m +CONFIG_FMC=m +CONFIG_FMC_FAKEDEV=m +CONFIG_FMC_TRIVIAL=m +CONFIG_FMC_WRITE_EEPROM=m +CONFIG_FMC_CHARDEV=m + +# +# PHY Subsystem +# +CONFIG_GENERIC_PHY=y +CONFIG_BCM_KONA_USB2_PHY=m +CONFIG_PHY_PXA_28NM_HSIC=m +CONFIG_PHY_PXA_28NM_USB2=m +CONFIG_PHY_CPCAP_USB=m +CONFIG_PHY_QCOM_USB_HS=m +CONFIG_PHY_QCOM_USB_HSIC=m +CONFIG_PHY_SAMSUNG_USB2=m +CONFIG_PHY_TUSB1210=m +CONFIG_POWERCAP=y +CONFIG_INTEL_RAPL=m +# CONFIG_IDLE_INJECT is not set +CONFIG_MCB=m +CONFIG_MCB_PCI=m +CONFIG_MCB_LPC=m + +# +# Performance monitor support +# +CONFIG_RAS=y +CONFIG_RAS_CEC=y +CONFIG_THUNDERBOLT=m + +# +# Android +# +# CONFIG_ANDROID is not set +CONFIG_LIBNVDIMM=y +CONFIG_BLK_DEV_PMEM=m +CONFIG_ND_BLK=m +CONFIG_ND_CLAIM=y +CONFIG_ND_BTT=m +CONFIG_BTT=y +CONFIG_ND_PFN=m +CONFIG_NVDIMM_PFN=y +CONFIG_NVDIMM_DAX=y +CONFIG_DAX_DRIVER=y +CONFIG_DAX=y +CONFIG_DEV_DAX=m +CONFIG_DEV_DAX_PMEM=m +CONFIG_NVMEM=y + +# +# HW tracing support +# +CONFIG_STM=m +CONFIG_STM_DUMMY=m +CONFIG_STM_SOURCE_CONSOLE=m +CONFIG_STM_SOURCE_HEARTBEAT=m +CONFIG_STM_SOURCE_FTRACE=m +CONFIG_INTEL_TH=m +CONFIG_INTEL_TH_PCI=m +# CONFIG_INTEL_TH_ACPI is not set +CONFIG_INTEL_TH_GTH=m +CONFIG_INTEL_TH_STH=m +CONFIG_INTEL_TH_MSU=m +CONFIG_INTEL_TH_PTI=m +# CONFIG_INTEL_TH_DEBUG is not set +CONFIG_FPGA=m +CONFIG_ALTERA_PR_IP_CORE=m +# CONFIG_FPGA_MGR_ALTERA_PS_SPI is not set +# CONFIG_FPGA_MGR_ALTERA_CVP is not set +CONFIG_FPGA_MGR_XILINX_SPI=m +# CONFIG_FPGA_MGR_MACHXO2_SPI is not set +# CONFIG_FPGA_BRIDGE is not set +# CONFIG_FPGA_DFL is not set +CONFIG_PM_OPP=y +CONFIG_UNISYS_VISORBUS=m +# CONFIG_SIOX is not set +# CONFIG_SLIMBUS is not set + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_FS_IOMAP=y +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT2=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_EXT4_FS_ENCRYPTION=y +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +CONFIG_REISERFS_FS_SECURITY=y +CONFIG_JFS_FS=m +CONFIG_JFS_POSIX_ACL=y +CONFIG_JFS_SECURITY=y +# CONFIG_JFS_DEBUG is not set +CONFIG_JFS_STATISTICS=y +CONFIG_XFS_FS=m +CONFIG_XFS_QUOTA=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_XFS_RT=y +# CONFIG_XFS_ONLINE_SCRUB is not set +# CONFIG_XFS_WARN is not set +# CONFIG_XFS_DEBUG is not set +CONFIG_GFS2_FS=m +CONFIG_GFS2_FS_LOCKING_DLM=y +CONFIG_OCFS2_FS=m +CONFIG_OCFS2_FS_O2CB=m +CONFIG_OCFS2_FS_USERSPACE_CLUSTER=m +CONFIG_OCFS2_FS_STATS=y +CONFIG_OCFS2_DEBUG_MASKLOG=y +# CONFIG_OCFS2_DEBUG_FS is not set +CONFIG_BTRFS_FS=m +CONFIG_BTRFS_FS_POSIX_ACL=y +# CONFIG_BTRFS_FS_CHECK_INTEGRITY is not set +# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set +# CONFIG_BTRFS_DEBUG is not set +# CONFIG_BTRFS_ASSERT is not set +# CONFIG_BTRFS_FS_REF_VERIFY is not set +CONFIG_NILFS2_FS=m +CONFIG_F2FS_FS=m +CONFIG_F2FS_STAT_FS=y +CONFIG_F2FS_FS_XATTR=y +CONFIG_F2FS_FS_POSIX_ACL=y +CONFIG_F2FS_FS_SECURITY=y +# CONFIG_F2FS_CHECK_FS is not set +CONFIG_F2FS_FS_ENCRYPTION=y +# CONFIG_F2FS_IO_TRACE is not set +# CONFIG_F2FS_FAULT_INJECTION is not set +CONFIG_FS_DAX=y +CONFIG_FS_DAX_PMD=y +CONFIG_FS_POSIX_ACL=y +CONFIG_EXPORTFS=y +CONFIG_EXPORTFS_BLOCK_OPS=y +CONFIG_FILE_LOCKING=y +CONFIG_MANDATORY_FILE_LOCKING=y +CONFIG_FS_ENCRYPTION=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +# CONFIG_QUOTA_DEBUG is not set +CONFIG_QUOTA_TREE=m +CONFIG_QFMT_V1=m +CONFIG_QFMT_V2=m +CONFIG_QUOTACTL=y +CONFIG_QUOTACTL_COMPAT=y +CONFIG_AUTOFS4_FS=m +CONFIG_AUTOFS_FS=m +CONFIG_FUSE_FS=y +CONFIG_CUSE=m +CONFIG_OVERLAY_FS=m +# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set +CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y +# CONFIG_OVERLAY_FS_INDEX is not set +# CONFIG_OVERLAY_FS_XINO_AUTO is not set +# CONFIG_OVERLAY_FS_METACOPY is not set + +# +# Caches +# +CONFIG_FSCACHE=m +CONFIG_FSCACHE_STATS=y +# CONFIG_FSCACHE_HISTOGRAM is not set +# CONFIG_FSCACHE_DEBUG is not set +# CONFIG_FSCACHE_OBJECT_LIST is not set +CONFIG_CACHEFILES=m +# CONFIG_CACHEFILES_DEBUG is not set +# CONFIG_CACHEFILES_HISTOGRAM is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_FAT_DEFAULT_UTF8 is not set +CONFIG_NTFS_FS=m +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_RW is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_VMCORE=y +# CONFIG_PROC_VMCORE_DEVICE_DUMP is not set +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PROC_CHILDREN=y +CONFIG_KERNFS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_MEMFD_CREATE=y +CONFIG_ARCH_HAS_GIGANTIC_PAGE=y +CONFIG_CONFIGFS_FS=y +CONFIG_EFIVAR_FS=y +CONFIG_MISC_FILESYSTEMS=y +CONFIG_ORANGEFS_FS=m +CONFIG_ADFS_FS=m +# CONFIG_ADFS_FS_RW is not set +CONFIG_AFFS_FS=m +CONFIG_ECRYPT_FS=y +CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +CONFIG_BEFS_FS=m +# CONFIG_BEFS_DEBUG is not set +CONFIG_BFS_FS=m +CONFIG_EFS_FS=m +CONFIG_JFFS2_FS=m +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_FS_POSIX_ACL=y +CONFIG_JFFS2_FS_SECURITY=y +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_JFFS2_CMODE_NONE is not set +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +CONFIG_JFFS2_CMODE_FAVOURLZO=y +CONFIG_UBIFS_FS=m +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_ATIME_SUPPORT is not set +CONFIG_UBIFS_FS_XATTR=y +CONFIG_UBIFS_FS_ENCRYPTION=y +CONFIG_UBIFS_FS_SECURITY=y +CONFIG_CRAMFS=m +CONFIG_CRAMFS_BLOCKDEV=y +# CONFIG_CRAMFS_MTD is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_FILE_CACHE is not set +CONFIG_SQUASHFS_FILE_DIRECT=y +CONFIG_SQUASHFS_DECOMP_SINGLE=y +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set +CONFIG_SQUASHFS_XATTR=y +CONFIG_SQUASHFS_ZLIB=y +CONFIG_SQUASHFS_LZ4=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_ZSTD is not set +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +CONFIG_VXFS_FS=m +CONFIG_MINIX_FS=m +CONFIG_OMFS_FS=m +CONFIG_HPFS_FS=m +CONFIG_QNX4FS_FS=m +CONFIG_QNX6FS_FS=m +# CONFIG_QNX6FS_DEBUG is not set +CONFIG_ROMFS_FS=m +CONFIG_ROMFS_BACKED_BY_BLOCK=y +# CONFIG_ROMFS_BACKED_BY_MTD is not set +# CONFIG_ROMFS_BACKED_BY_BOTH is not set +CONFIG_ROMFS_ON_BLOCK=y +CONFIG_PSTORE=y +CONFIG_PSTORE_DEFLATE_COMPRESS=y +# CONFIG_PSTORE_LZO_COMPRESS is not set +# CONFIG_PSTORE_LZ4_COMPRESS is not set +# CONFIG_PSTORE_LZ4HC_COMPRESS is not set +# CONFIG_PSTORE_842_COMPRESS is not set +# CONFIG_PSTORE_ZSTD_COMPRESS is not set +CONFIG_PSTORE_COMPRESS=y +CONFIG_PSTORE_DEFLATE_COMPRESS_DEFAULT=y +CONFIG_PSTORE_COMPRESS_DEFAULT="deflate" +# CONFIG_PSTORE_CONSOLE is not set +# CONFIG_PSTORE_PMSG is not set +# CONFIG_PSTORE_FTRACE is not set +CONFIG_PSTORE_RAM=m +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set +# CONFIG_UFS_DEBUG is not set +CONFIG_EXOFS_FS=m +# CONFIG_EXOFS_DEBUG is not set +CONFIG_ORE=m +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=m +CONFIG_NFS_V2=m +CONFIG_NFS_V3=m +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=m +CONFIG_NFS_SWAP=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_PNFS_FILE_LAYOUT=m +CONFIG_PNFS_BLOCK=m +CONFIG_PNFS_FLEXFILE_LAYOUT=m +CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="kernel.org" +CONFIG_NFS_V4_1_MIGRATION=y +CONFIG_NFS_V4_SECURITY_LABEL=y +CONFIG_NFS_FSCACHE=y +# CONFIG_NFS_USE_LEGACY_DNS is not set +CONFIG_NFS_USE_KERNEL_DNS=y +CONFIG_NFS_DEBUG=y +CONFIG_NFSD=m +CONFIG_NFSD_V2_ACL=y +CONFIG_NFSD_V3=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_PNFS=y +CONFIG_NFSD_BLOCKLAYOUT=y +CONFIG_NFSD_SCSILAYOUT=y +CONFIG_NFSD_FLEXFILELAYOUT=y +CONFIG_NFSD_V4_SECURITY_LABEL=y +# CONFIG_NFSD_FAULT_INJECTION is not set +CONFIG_GRACE_PERIOD=m +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=m +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_SUNRPC_BACKCHANNEL=y +CONFIG_SUNRPC_SWAP=y +CONFIG_RPCSEC_GSS_KRB5=m +CONFIG_SUNRPC_DEBUG=y +CONFIG_SUNRPC_XPRT_RDMA=m +CONFIG_CEPH_FS=m +CONFIG_CEPH_FSCACHE=y +CONFIG_CEPH_FS_POSIX_ACL=y +CONFIG_CIFS=m +# CONFIG_CIFS_STATS2 is not set +CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_UPCALL=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_CIFS_ACL=y +CONFIG_CIFS_DEBUG=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_DEBUG_DUMP_KEYS is not set +CONFIG_CIFS_DFS_UPCALL=y +# CONFIG_CIFS_SMB_DIRECT is not set +CONFIG_CIFS_FSCACHE=y +CONFIG_CODA_FS=m +CONFIG_AFS_FS=m +# CONFIG_AFS_DEBUG is not set +CONFIG_AFS_FSCACHE=y +CONFIG_9P_FS=m +CONFIG_9P_FSCACHE=y +CONFIG_9P_FS_POSIX_ACL=y +CONFIG_9P_FS_SECURITY=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_MAC_ROMAN=m +CONFIG_NLS_MAC_CELTIC=m +CONFIG_NLS_MAC_CENTEURO=m +CONFIG_NLS_MAC_CROATIAN=m +CONFIG_NLS_MAC_CYRILLIC=m +CONFIG_NLS_MAC_GAELIC=m +CONFIG_NLS_MAC_GREEK=m +CONFIG_NLS_MAC_ICELAND=m +CONFIG_NLS_MAC_INUIT=m +CONFIG_NLS_MAC_ROMANIAN=m +CONFIG_NLS_MAC_TURKISH=m +CONFIG_NLS_UTF8=m +CONFIG_DLM=m +# CONFIG_DLM_DEBUG is not set + +# +# Security options +# +CONFIG_KEYS=y +CONFIG_KEYS_COMPAT=y +CONFIG_PERSISTENT_KEYRINGS=y +CONFIG_BIG_KEYS=y +CONFIG_TRUSTED_KEYS=y +CONFIG_ENCRYPTED_KEYS=y +CONFIG_KEY_DH_OPERATIONS=y +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +CONFIG_SECURITY_NETWORK=y +CONFIG_PAGE_TABLE_ISOLATION=y +CONFIG_SECURITY_INFINIBAND=y +CONFIG_SECURITY_NETWORK_XFRM=y +CONFIG_SECURITY_PATH=y +CONFIG_INTEL_TXT=y +CONFIG_LSM_MMAP_MIN_ADDR=0 +CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y +CONFIG_HARDENED_USERCOPY=y +CONFIG_HARDENED_USERCOPY_FALLBACK=y +# CONFIG_HARDENED_USERCOPY_PAGESPAN is not set +CONFIG_FORTIFY_SOURCE=y +# CONFIG_STATIC_USERMODEHELPER is not set +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=0 +# CONFIG_SECURITY_SELINUX_DISABLE is not set +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +CONFIG_SECURITY_SMACK=y +# CONFIG_SECURITY_SMACK_BRINGUP is not set +CONFIG_SECURITY_SMACK_NETFILTER=y +CONFIG_SECURITY_SMACK_APPEND_SIGNALS=y +CONFIG_SECURITY_TOMOYO=y +CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY=2048 +CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG=1024 +# CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER is not set +CONFIG_SECURITY_TOMOYO_POLICY_LOADER="/sbin/tomoyo-init" +CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER="/sbin/init" +CONFIG_SECURITY_APPARMOR=y +CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1 +CONFIG_SECURITY_APPARMOR_HASH=y +CONFIG_SECURITY_APPARMOR_HASH_DEFAULT=y +# CONFIG_SECURITY_APPARMOR_DEBUG is not set +# CONFIG_SECURITY_LOADPIN is not set +CONFIG_SECURITY_YAMA=y +CONFIG_INTEGRITY=y +CONFIG_INTEGRITY_SIGNATURE=y +CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y +CONFIG_INTEGRITY_TRUSTED_KEYRING=y +CONFIG_INTEGRITY_AUDIT=y +CONFIG_IMA=y +CONFIG_IMA_MEASURE_PCR_IDX=10 +CONFIG_IMA_LSM_RULES=y +# CONFIG_IMA_TEMPLATE is not set +CONFIG_IMA_NG_TEMPLATE=y +# CONFIG_IMA_SIG_TEMPLATE is not set +CONFIG_IMA_DEFAULT_TEMPLATE="ima-ng" +CONFIG_IMA_DEFAULT_HASH_SHA1=y +# CONFIG_IMA_DEFAULT_HASH_SHA256 is not set +# CONFIG_IMA_DEFAULT_HASH_SHA512 is not set +CONFIG_IMA_DEFAULT_HASH="sha1" +# CONFIG_IMA_WRITE_POLICY is not set +# CONFIG_IMA_READ_POLICY is not set +CONFIG_IMA_APPRAISE=y +# CONFIG_IMA_APPRAISE_BUILD_POLICY is not set +CONFIG_IMA_APPRAISE_BOOTPARAM=y +CONFIG_IMA_TRUSTED_KEYRING=y +# CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY is not set +# CONFIG_IMA_BLACKLIST_KEYRING is not set +# CONFIG_IMA_LOAD_X509 is not set +CONFIG_EVM=y +CONFIG_EVM_ATTR_FSUUID=y +CONFIG_EVM_EXTRA_SMACK_XATTRS=y +# CONFIG_EVM_ADD_XATTRS is not set +# CONFIG_EVM_LOAD_X509 is not set +CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +# CONFIG_DEFAULT_SECURITY_APPARMOR is not set +# CONFIG_DEFAULT_SECURITY_DAC is not set +CONFIG_DEFAULT_SECURITY="selinux" +CONFIG_XOR_BLOCKS=m +CONFIG_ASYNC_CORE=m +CONFIG_ASYNC_MEMCPY=m +CONFIG_ASYNC_XOR=m +CONFIG_ASYNC_PQ=m +CONFIG_ASYNC_RAID6_RECOV=m +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_AKCIPHER2=y +CONFIG_CRYPTO_AKCIPHER=y +CONFIG_CRYPTO_KPP2=y +CONFIG_CRYPTO_KPP=y +CONFIG_CRYPTO_ACOMP2=y +CONFIG_CRYPTO_RSA=y +CONFIG_CRYPTO_DH=y +CONFIG_CRYPTO_ECDH=m +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_NULL2=y +CONFIG_CRYPTO_PCRYPT=m +CONFIG_CRYPTO_WORKQUEUE=y +CONFIG_CRYPTO_CRYPTD=m +# CONFIG_CRYPTO_MCRYPTD is not set +CONFIG_CRYPTO_AUTHENC=m +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_SIMD=m +CONFIG_CRYPTO_GLUE_HELPER_X86=m +CONFIG_CRYPTO_ENGINE=m + +# +# Authenticated Encryption with Associated Data +# +CONFIG_CRYPTO_CCM=m +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CHACHA20POLY1305=m +# CONFIG_CRYPTO_AEGIS128 is not set +# CONFIG_CRYPTO_AEGIS128L is not set +# CONFIG_CRYPTO_AEGIS256 is not set +# CONFIG_CRYPTO_AEGIS128_AESNI_SSE2 is not set +# CONFIG_CRYPTO_AEGIS128L_AESNI_SSE2 is not set +# CONFIG_CRYPTO_AEGIS256_AESNI_SSE2 is not set +# CONFIG_CRYPTO_MORUS640 is not set +# CONFIG_CRYPTO_MORUS640_SSE2 is not set +# CONFIG_CRYPTO_MORUS1280 is not set +# CONFIG_CRYPTO_MORUS1280_SSE2 is not set +# CONFIG_CRYPTO_MORUS1280_AVX2 is not set +CONFIG_CRYPTO_SEQIV=y +CONFIG_CRYPTO_ECHAINIV=m + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CFB is not set +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=m +CONFIG_CRYPTO_PCBC=m +CONFIG_CRYPTO_XTS=y +CONFIG_CRYPTO_KEYWRAP=m + +# +# Hash modes +# +CONFIG_CRYPTO_CMAC=m +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_XCBC=m +CONFIG_CRYPTO_VMAC=m + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_CRC32C_INTEL=y +CONFIG_CRYPTO_CRC32=m +CONFIG_CRYPTO_CRC32_PCLMUL=m +CONFIG_CRYPTO_CRCT10DIF=y +CONFIG_CRYPTO_CRCT10DIF_PCLMUL=m +CONFIG_CRYPTO_GHASH=y +CONFIG_CRYPTO_POLY1305=m +CONFIG_CRYPTO_POLY1305_X86_64=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_RMD128=m +CONFIG_CRYPTO_RMD160=m +CONFIG_CRYPTO_RMD256=m +CONFIG_CRYPTO_RMD320=m +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA1_SSSE3=m +CONFIG_CRYPTO_SHA256_SSSE3=m +CONFIG_CRYPTO_SHA512_SSSE3=m +# CONFIG_CRYPTO_SHA1_MB is not set +# CONFIG_CRYPTO_SHA256_MB is not set +# CONFIG_CRYPTO_SHA512_MB is not set +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_SHA3=m +# CONFIG_CRYPTO_SM3 is not set +CONFIG_CRYPTO_TGR192=m +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL=m + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_AES_TI=m +CONFIG_CRYPTO_AES_X86_64=m +CONFIG_CRYPTO_AES_NI_INTEL=m +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_BLOWFISH_COMMON=m +CONFIG_CRYPTO_BLOWFISH_X86_64=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_CAMELLIA_X86_64=m +CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64=m +CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64=m +CONFIG_CRYPTO_CAST_COMMON=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST5_AVX_X86_64=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_CAST6_AVX_X86_64=m +CONFIG_CRYPTO_DES=m +CONFIG_CRYPTO_DES3_EDE_X86_64=m +CONFIG_CRYPTO_FCRYPT=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_SALSA20=m +CONFIG_CRYPTO_CHACHA20=m +CONFIG_CRYPTO_CHACHA20_X86_64=m +CONFIG_CRYPTO_SEED=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_SERPENT_SSE2_X86_64=m +CONFIG_CRYPTO_SERPENT_AVX_X86_64=m +CONFIG_CRYPTO_SERPENT_AVX2_X86_64=m +# CONFIG_CRYPTO_SM4 is not set +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_TWOFISH_COMMON=m +CONFIG_CRYPTO_TWOFISH_X86_64=m +CONFIG_CRYPTO_TWOFISH_X86_64_3WAY=m +CONFIG_CRYPTO_TWOFISH_AVX_X86_64=m + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_842=m +CONFIG_CRYPTO_LZ4=m +CONFIG_CRYPTO_LZ4HC=m +# CONFIG_CRYPTO_ZSTD is not set + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=m +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_DRBG_HMAC=y +CONFIG_CRYPTO_DRBG_HASH=y +CONFIG_CRYPTO_DRBG_CTR=y +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_USER_API=m +CONFIG_CRYPTO_USER_API_HASH=m +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=m +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_DEV_PADLOCK=y +CONFIG_CRYPTO_DEV_PADLOCK_AES=m +CONFIG_CRYPTO_DEV_PADLOCK_SHA=m +CONFIG_CRYPTO_DEV_CCP=y +CONFIG_CRYPTO_DEV_CCP_DD=m +CONFIG_CRYPTO_DEV_SP_CCP=y +CONFIG_CRYPTO_DEV_CCP_CRYPTO=m +CONFIG_CRYPTO_DEV_SP_PSP=y +CONFIG_CRYPTO_DEV_QAT=m +CONFIG_CRYPTO_DEV_QAT_DH895xCC=m +# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set +# CONFIG_CRYPTO_DEV_QAT_C62X is not set +CONFIG_CRYPTO_DEV_QAT_DH895xCCVF=m +# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set +# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set +CONFIG_CRYPTO_DEV_NITROX=m +CONFIG_CRYPTO_DEV_NITROX_CNN55XX=m +CONFIG_CRYPTO_DEV_CHELSIO=m +# CONFIG_CHELSIO_IPSEC_INLINE is not set +# CONFIG_CRYPTO_DEV_CHELSIO_TLS is not set +CONFIG_CRYPTO_DEV_VIRTIO=m +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_PKCS7_MESSAGE_PARSER=y +CONFIG_PKCS7_TEST_KEY=m +CONFIG_SIGNED_PE_FILE_VERIFICATION=y + +# +# Certificates for signature checking +# +CONFIG_MODULE_SIG_KEY="certs/signing_key.pem" +CONFIG_SYSTEM_TRUSTED_KEYRING=y +CONFIG_SYSTEM_TRUSTED_KEYS="" +CONFIG_SYSTEM_EXTRA_CERTIFICATE=y +CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE=4096 +CONFIG_SECONDARY_TRUSTED_KEYRING=y +CONFIG_SYSTEM_BLACKLIST_KEYRING=y +CONFIG_SYSTEM_BLACKLIST_HASH_LIST="" +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_RAID6_PQ=m +CONFIG_BITREVERSE=y +CONFIG_RATIONAL=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_NET_UTILS=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IOMAP=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y +CONFIG_ARCH_HAS_FAST_MULTIPLIER=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC_ITU_T=m +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +CONFIG_CRC64=m +CONFIG_CRC4=m +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m +CONFIG_CRC8=m +CONFIG_XXHASH=m +# CONFIG_RANDOM32_SELFTEST is not set +CONFIG_842_COMPRESS=m +CONFIG_842_DECOMPRESS=m +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_COMPRESS=m +CONFIG_LZ4HC_COMPRESS=m +CONFIG_LZ4_DECOMPRESS=y +CONFIG_ZSTD_COMPRESS=m +CONFIG_ZSTD_DECOMPRESS=m +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_XZ_DEC_TEST=m +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_DECOMPRESS_LZ4=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=m +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_REED_SOLOMON_DEC16=y +CONFIG_BCH=m +CONFIG_BCH_CONST_PARAMS=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=m +CONFIG_TEXTSEARCH_BM=m +CONFIG_TEXTSEARCH_FSM=m +CONFIG_BTREE=y +CONFIG_INTERVAL_TREE=y +CONFIG_RADIX_TREE_MULTIORDER=y +CONFIG_ASSOCIATIVE_ARRAY=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HAS_DMA=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_DMA_DIRECT_OPS=y +CONFIG_DMA_VIRT_OPS=y +CONFIG_SWIOTLB=y +CONFIG_SGL_ALLOC=y +CONFIG_IOMMU_HELPER=y +CONFIG_CHECK_SIGNATURE=y +CONFIG_CPUMASK_OFFSTACK=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_GLOB=y +# CONFIG_GLOB_SELFTEST is not set +CONFIG_NLATTR=y +CONFIG_LRU_CACHE=m +CONFIG_CLZ_TAB=y +CONFIG_CORDIC=m +CONFIG_DDR=y +CONFIG_IRQ_POLL=y +CONFIG_MPILIB=y +CONFIG_SIGNATURE=y +CONFIG_OID_REGISTRY=y +CONFIG_UCS2_STRING=y +CONFIG_FONT_SUPPORT=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_SG_POOL=y +CONFIG_ARCH_HAS_SG_CHAIN=y +CONFIG_ARCH_HAS_PMEM_API=y +CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE=y +CONFIG_ARCH_HAS_UACCESS_MCSAFE=y +CONFIG_SBITMAP=y +CONFIG_PARMAN=m +# CONFIG_STRING_SELFTEST is not set + +# +# Kernel hacking +# + +# +# printk and dmesg options +# +CONFIG_PRINTK_TIME=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 +CONFIG_CONSOLE_LOGLEVEL_QUIET=4 +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 +CONFIG_BOOT_PRINTK_DELAY=y +CONFIG_DYNAMIC_DEBUG=y + +# +# Compile-time checks and compiler options +# +# CONFIG_DEBUG_INFO is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=1024 +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +CONFIG_UNUSED_SYMBOLS=y +# CONFIG_PAGE_OWNER is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_SECTION_MISMATCH_WARN_ONLY=y +CONFIG_FRAME_POINTER=y +CONFIG_STACK_VALIDATION=y +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x01b6 +CONFIG_MAGIC_SYSRQ_SERIAL=y +CONFIG_DEBUG_KERNEL=y + +# +# Memory Debugging +# +# CONFIG_PAGE_EXTENSION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_PAGE_POISONING is not set +# CONFIG_DEBUG_PAGE_REF is not set +# CONFIG_DEBUG_RODATA_TEST is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_VM is not set +CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y +# CONFIG_DEBUG_VIRTUAL is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +CONFIG_MEMORY_NOTIFIER_ERROR_INJECT=m +# CONFIG_DEBUG_PER_CPU_MAPS is not set +CONFIG_HAVE_DEBUG_STACKOVERFLOW=y +# CONFIG_DEBUG_STACKOVERFLOW is not set +CONFIG_HAVE_ARCH_KASAN=y +# CONFIG_KASAN is not set +CONFIG_ARCH_HAS_KCOV=y +CONFIG_CC_HAS_SANCOV_TRACE_PC=y +# CONFIG_KCOV is not set +# CONFIG_DEBUG_SHIRQ is not set + +# +# Debug Lockups and Hangs +# +CONFIG_LOCKUP_DETECTOR=y +CONFIG_SOFTLOCKUP_DETECTOR=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_HARDLOCKUP_DETECTOR_PERF=y +CONFIG_HARDLOCKUP_CHECK_TIMESTAMP=y +CONFIG_HARDLOCKUP_DETECTOR=y +# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +# CONFIG_WQ_WATCHDOG is not set +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHED_INFO=y +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_STACK_END_CHECK=y +# CONFIG_DEBUG_TIMEKEEPING is not set + +# +# Lock Debugging (spinlocks, mutexes, etc...) +# +CONFIG_LOCK_DEBUGGING_SUPPORT=y +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set +# CONFIG_DEBUG_RWSEMS is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_LOCK_TORTURE_TEST is not set +# CONFIG_WW_MUTEX_SELFTEST is not set +CONFIG_STACKTRACE=y +# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_PI_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set + +# +# RCU Debugging +# +CONFIG_TORTURE_TEST=m +CONFIG_RCU_PERF_TEST=m +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_RCU_TRACE is not set +# CONFIG_RCU_EQS_DEBUG is not set +# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set +CONFIG_NOTIFIER_ERROR_INJECTION=m +CONFIG_PM_NOTIFIER_ERROR_INJECT=m +# CONFIG_NETDEV_NOTIFIER_ERROR_INJECT is not set +CONFIG_FUNCTION_ERROR_INJECTION=y +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +CONFIG_USER_STACKTRACE_SUPPORT=y +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_FENTRY=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_TRACE_CLOCK=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_RING_BUFFER_ALLOW_SWAP=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +CONFIG_FUNCTION_TRACER=y +CONFIG_FUNCTION_GRAPH_TRACER=y +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_IRQSOFF_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_HWLAT_TRACER=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_TRACER_SNAPSHOT=y +# CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +CONFIG_STACK_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_KPROBE_EVENTS=y +# CONFIG_KPROBE_EVENTS_ON_NOTRACE is not set +CONFIG_UPROBE_EVENTS=y +CONFIG_BPF_EVENTS=y +CONFIG_PROBE_EVENTS=y +CONFIG_DYNAMIC_FTRACE=y +CONFIG_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_FUNCTION_PROFILER=y +# CONFIG_BPF_KPROBE_OVERRIDE is not set +CONFIG_FTRACE_MCOUNT_RECORD=y +# CONFIG_FTRACE_STARTUP_TEST is not set +CONFIG_MMIOTRACE=y +CONFIG_TRACING_MAP=y +CONFIG_HIST_TRIGGERS=y +# CONFIG_MMIOTRACE_TEST is not set +# CONFIG_TRACEPOINT_BENCHMARK is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_RING_BUFFER_STARTUP_TEST is not set +# CONFIG_PREEMPTIRQ_DELAY_TEST is not set +# CONFIG_TRACE_EVAL_MAP_FILE is not set +CONFIG_TRACING_EVENTS_GPIO=y +# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set +# CONFIG_DMA_API_DEBUG is not set +CONFIG_RUNTIME_TESTING_MENU=y +# CONFIG_LKDTM is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_TEST_SORT is not set +# CONFIG_KPROBES_SANITY_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_INTERVAL_TREE_TEST is not set +# CONFIG_PERCPU_TEST is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_ASYNC_RAID6_TEST is not set +# CONFIG_TEST_HEXDUMP is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_TEST_PRINTF is not set +# CONFIG_TEST_BITMAP is not set +# CONFIG_TEST_BITFIELD is not set +# CONFIG_TEST_UUID is not set +# CONFIG_TEST_OVERFLOW is not set +# CONFIG_TEST_RHASHTABLE is not set +# CONFIG_TEST_HASH is not set +# CONFIG_TEST_IDA is not set +# CONFIG_TEST_PARMAN is not set +CONFIG_TEST_LKM=m +CONFIG_TEST_USER_COPY=m +CONFIG_TEST_BPF=m +# CONFIG_FIND_BIT_BENCHMARK is not set +CONFIG_TEST_FIRMWARE=m +# CONFIG_TEST_SYSCTL is not set +CONFIG_TEST_UDELAY=m +CONFIG_TEST_STATIC_KEYS=m +# CONFIG_TEST_KMOD is not set +CONFIG_MEMTEST=y +# CONFIG_BUG_ON_DATA_CORRUPTION is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_KGDB=y +CONFIG_KGDB_SERIAL_CONSOLE=y +# CONFIG_KGDB_TESTS is not set +CONFIG_KGDB_LOW_LEVEL_TRAP=y +CONFIG_KGDB_KDB=y +CONFIG_KDB_DEFAULT_ENABLE=0x1 +CONFIG_KDB_KEYBOARD=y +CONFIG_KDB_CONTINUE_CATASTROPHIC=0 +CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y +# CONFIG_UBSAN is not set +CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y +CONFIG_STRICT_DEVMEM=y +# CONFIG_IO_STRICT_DEVMEM is not set +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_EARLY_PRINTK_USB=y +# CONFIG_X86_VERBOSE_BOOTUP is not set +CONFIG_EARLY_PRINTK=y +CONFIG_EARLY_PRINTK_DBGP=y +CONFIG_EARLY_PRINTK_EFI=y +# CONFIG_EARLY_PRINTK_USB_XDBC is not set +CONFIG_X86_PTDUMP_CORE=y +# CONFIG_X86_PTDUMP is not set +# CONFIG_EFI_PGT_DUMP is not set +CONFIG_DEBUG_WX=y +CONFIG_DOUBLEFAULT=y +# CONFIG_DEBUG_TLBFLUSH is not set +# CONFIG_IOMMU_DEBUG is not set +CONFIG_HAVE_MMIOTRACE_SUPPORT=y +# CONFIG_X86_DECODER_SELFTEST is not set +CONFIG_IO_DELAY_TYPE_0X80=0 +CONFIG_IO_DELAY_TYPE_0XED=1 +CONFIG_IO_DELAY_TYPE_UDELAY=2 +CONFIG_IO_DELAY_TYPE_NONE=3 +# CONFIG_IO_DELAY_0X80 is not set +CONFIG_IO_DELAY_0XED=y +# CONFIG_IO_DELAY_UDELAY is not set +# CONFIG_IO_DELAY_NONE is not set +CONFIG_DEFAULT_IO_DELAY_TYPE=1 +# CONFIG_DEBUG_BOOT_PARAMS is not set +# CONFIG_CPA_DEBUG is not set +CONFIG_OPTIMIZE_INLINING=y +# CONFIG_DEBUG_ENTRY is not set +# CONFIG_DEBUG_NMI_SELFTEST is not set +CONFIG_X86_DEBUG_FPU=y +CONFIG_PUNIT_ATOM_DEBUG=m +# CONFIG_UNWINDER_ORC is not set +CONFIG_UNWINDER_FRAME_POINTER=y +# CONFIG_UNWINDER_GUESS is not set diff --git a/linux_surface/patches/modinst-arg-list-too-long.patch b/linux_surface/patches/modinst-arg-list-too-long.patch new file mode 100644 index 0000000..58a9191 --- /dev/null +++ b/linux_surface/patches/modinst-arg-list-too-long.patch @@ -0,0 +1,14 @@ +diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst +index 07650ee..934a7a8 100644 +--- a/scripts/Makefile.modinst ++++ b/scripts/Makefile.modinst +@@ -9,7 +9,8 @@ include scripts/Kbuild.include + + # + +-__modules := $(sort $(shell grep -h '\.ko$$' /dev/null $(wildcard $(MODVERDIR)/*.mod))) ++__modules := $(sort $(foreach f,$(wildcard $(MODVERDIR)/*.mod),$(shell \ ++ grep -h '\.ko$$' '$f'))) + modules := $(patsubst %.o,%.ko,$(wildcard $(__modules:.ko=.o))) + + PHONY += $(modules) diff --git a/mu-git/default.nix b/mu-git/default.nix new file mode 100644 index 0000000..40755e3 --- /dev/null +++ b/mu-git/default.nix @@ -0,0 +1,56 @@ +{ stdenv, sqlite, pkgconfig, autoreconfHook, pmccabe +, xapian, glib, gmime, texinfo , emacs, guile +, gtk3, gmime3, webkitgtk24x-gtk3, libsoup, icu +, withMug ? false }: + +stdenv.mkDerivation rec { + name = "mu-${version}"; + version = "git"; + + src = fetchTarball https://github.com/djcb/mu/archive/master.tar.gz; + + # 0.9.18 and 1.0 have 2 failing tests but previously we had no tests + patches = [ + ./failing_tests.patch + ]; + + # test-utils coredumps so don't run those + postPatch = '' + sed -i -e '/test-utils/d' lib/parser/Makefile.am + ''; + + buildInputs = [ + sqlite xapian glib gmime texinfo emacs gmime3 guile libsoup icu + ] ++ stdenv.lib.optionals withMug [ gtk3 webkitgtk24x-gtk3 ]; + + nativeBuildInputs = [ pkgconfig autoreconfHook pmccabe ]; + + enableParallelBuilding = true; + + preBuild = '' + # Fix mu4e-builddir (set it to $out) + substituteInPlace mu4e/mu4e-meta.el.in \ + --replace "@abs_top_builddir@" "$out" + + # We install msg2pdf to bin/msg2pdf, fix its location in elisp + substituteInPlace mu4e/mu4e-actions.el \ + --replace "/toys/msg2pdf/msg2pdf" "/bin/msg2pdf" + ''; + + # Install mug and msg2pdf + postInstall = stdenv.lib.optionalString withMug '' + for f in msg2pdf mug ; do + install -m755 toys/$f/$f $out/bin/$f + done + ''; + + doCheck = true; + + meta = with stdenv.lib; { + description = "A collection of utilties for indexing and searching Maildirs"; + license = licenses.gpl3Plus; + homepage = http://www.djcbsoftware.nl/code/mu/; + platforms = platforms.mesaPlatforms; + maintainers = with maintainers; [ antono the-kenny peterhoeg ]; + }; +} diff --git a/mu-git/failing_tests.patch b/mu-git/failing_tests.patch new file mode 100644 index 0000000..c45834f --- /dev/null +++ b/mu-git/failing_tests.patch @@ -0,0 +1,18 @@ +diff --git a/mu/tests/test-mu-query.c b/mu/tests/test-mu-query.c +index 73cbd3f4..46a0b131 100644 +--- a/mu/tests/test-mu-query.c ++++ b/mu/tests/test-mu-query.c +@@ -753,10 +753,10 @@ main (int argc, char *argv[]) + g_test_add_func ("/mu-query/test-mu-query-sizes", + test_mu_query_sizes); + +- g_test_add_func ("/mu-query/test-mu-query-dates-helsinki", +- test_mu_query_dates_helsinki); +- g_test_add_func ("/mu-query/test-mu-query-dates-sydney", +- test_mu_query_dates_sydney); ++ /* g_test_add_func ("/mu-query/test-mu-query-dates-helsinki", */ ++ /* test_mu_query_dates_helsinki); */ ++ /* g_test_add_func ("/mu-query/test-mu-query-dates-sydney", */ ++ /* test_mu_query_dates_sydney); */ + g_test_add_func ("/mu-query/test-mu-query-dates-la", + test_mu_query_dates_la); diff --git a/release.nix b/release.nix new file mode 100644 index 0000000..b19cc28 --- /dev/null +++ b/release.nix @@ -0,0 +1,19 @@ +{ ... }: + +let + pkgs = import { config = { allowUnfree = true; allowBroken = true;}; }; + + jobs = rec { + linux_surface = pkgs.callPackage ./linux_surface/default.nix {}; + mu-git = pkgs.callPackage ./mu-git {}; + xdiskusage = pkgs.callPackage ./xdiskusage {}; + }; + + +in jobs // { + channel = pkgs.releaseTools.channel { + name = "nixpkgs_local"; + src = ./.; + constituents = jobs; + }; +} diff --git a/xdiskusage/default.nix b/xdiskusage/default.nix new file mode 100644 index 0000000..17a425f --- /dev/null +++ b/xdiskusage/default.nix @@ -0,0 +1,13 @@ +{ stdenv, fetchurl, fltk }: + +stdenv.mkDerivation rec { + name = "xdiskusage"; + version = "1.51"; + + buildInputs = [ fltk ]; + + src = fetchurl { + url = "http://xdiskusage.sourceforge.net/${name}-${version}.tgz"; + sha256 = "1il9200a2yd6023sbmixspvhj4ip6frm8sczjk5zk68j65zl9ckg"; + }; +}