]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/commitdiff
Thunderstrike 9-axis sensor
authorSpencer Sutterlin <ssutterlin@nvidia.com>
Thu, 25 Aug 2016 02:06:40 +0000 (19:06 -0700)
committermobile promotions <svcmobile_promotions@nvidia.com>
Tue, 7 Feb 2017 23:16:47 +0000 (15:16 -0800)
hid: jarvis: TS sensor parsing

iio: imu: tsfw_icm20628: Initial stub driver

Fragmentation - working on syn packets

Dynamic stuff
- Add DYNAMIC_SENSOR_MASK flag
- Add nvs_dsm DT node

Did not integrate dtsi files because they have been moved. They will
need to be integrated before dynamic sensors work.

Conflicts:
arch/arm64/boot/dts/tegra210-darcy-p2894-common.dtsi
arch/arm64/boot/dts/tegra210-foster-e-p2530-common.dtsi

Bug 1807528

Change-Id: I54d9d8615790bd90f343da57272cb332ab8bf165
Signed-off-by: Spencer Sutterlin <ssutterlin@nvidia.com>
Reviewed-on: http://git-master/r/1228099
(cherry picked from commit 4e2afd88fc7408c6045f711070d9b9067316f6fb)
Reviewed-on: http://git-master/r/1291721
Reviewed-by: Jon Mayo <jmayo@nvidia.com>
GVS: Gerrit_Virtual_Submit

Documentation/devicetree/bindings/iio/nvs_dsm.txt [new file with mode: 0644]
drivers/hid/hid-atv-jarvis.c
drivers/iio/imu/Kconfig
drivers/iio/imu/Makefile
drivers/iio/imu/tsfw_icm20628/Kconfig [new file with mode: 0644]
drivers/iio/imu/tsfw_icm20628/Makefile [new file with mode: 0644]
drivers/iio/imu/tsfw_icm20628/tsfw_icm20628.c [new file with mode: 0644]
include/linux/iio/imu/tsfw_icm20628.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/iio/nvs_dsm.txt b/Documentation/devicetree/bindings/iio/nvs_dsm.txt
new file mode 100644 (file)
index 0000000..af68e11
--- /dev/null
@@ -0,0 +1,24 @@
+* NVidia Sensor (NVS) Dynamic Sensor Meta (DSM)
+
+This driver uses the NVidia Sensor (NVS) framework.
+See the nvs.txt documentation for NVS DT capabilities.
+The nvs_dsm sensor driver supports dynamic sensors.
+
+Required properties:
+- compatible: Device or generic name.
+             Supported device names:
+             - nvidia,nvs_dsm
+
+Optional properties:
+- status: set to "ok" or "okay" for normal operation.  Set to anything else
+         to unload the driver without ever communicating with the device.
+  Note: The "anything else" above is typically "disabled".
+       Since the driver will unload without communicating with the device, the
+       device will be left in its POR state.
+
+Example:
+       nvs_dsm {
+               compatible = "nvidia,nvs_dsm";
+               status = "okay";
+       };
+
index 03d2a65201d0fea90769ee251b55bba27833eab3..843f0b7774fc6bb33418f5bf280965195f143ee5 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/hid.h>
 #include <linux/hiddev.h>
 #include <linux/hardirq.h>
+#include <linux/iio/imu/tsfw_icm20628.h>
 #include <linux/jiffies.h>
 #include <linux/slab.h>
 #include <linux/time.h>
@@ -169,6 +170,8 @@ struct fifo_packet {
 struct shdr_device {
        struct hid_device       *hdev;
        struct snd_card *shdr_card;
+       struct tsfw_icm20628_fn_dev *snsr_fns;
+       struct tsfw_icm20628_state *st;
 };
 static int num_remotes;
 static struct mutex snd_cards_lock;
@@ -417,6 +420,36 @@ static int atvr_mic_ctrl(struct hid_device *hdev, bool enable)
        return ret;
 }
 
+int atvr_ts_sensor_set(struct hid_device *hdev, bool enable)
+{
+       u8 report[TS_HOSTCMD_REPORT_SIZE] = { 0x04, 0x5a };
+       u8 sample_rate = 70;
+       int ret;
+
+       hid_info(hdev, "%s enable: %d\n",  __func__, enable);
+
+       if (enable) {
+               report[3] = 0x01; /* set */
+               report[4] = 0x01; /* enable */
+               report[5] = 0x00; /* real data source, 0x01 for dummy */
+               report[6] = sample_rate;
+               hid_info(hdev, "enable ts sensor %dHz\n", sample_rate);
+       } else {
+               report[3] = 0x01; /* set */
+               hid_info(hdev, "disable ts sensor\n");
+       }
+
+       ret =  hdev->hid_output_raw_report(hdev, report,
+                       TS_HOSTCMD_REPORT_SIZE, HID_OUTPUT_REPORT);
+       if (ret < 0)
+               hid_info(hdev, "failed to send ts sensor ctrl report, err=%d\n", ret);
+       else
+               ret = 0;
+
+       return ret;
+}
+EXPORT_SYMBOL(atvr_ts_sensor_set);
+
 /***************************************************************************/
 /************* Atomic FIFO *************************************************/
 /***************************************************************************/
@@ -1492,6 +1525,8 @@ __nodev:
 #define JAR_AUDIO_REPORT_SIZE  233
 #define JAR_AUDIO_FRAME_SIZE   0x3A
 
+#define TS_BUTTON_REPORT_SIZE 19
+
 static int atvr_jarvis_break_events(struct hid_device *hdev,
                                    struct hid_report *report,
                                    u8 *data, int size)
@@ -1567,9 +1602,9 @@ static int atvr_raw_event(struct hid_device *hdev, struct hid_report *report,
 #if (DEBUG_HID_RAW_INPUT == 1)
        pr_info("%s: report->id = 0x%x, size = %d\n",
                __func__, report->id, size);
-       if (size < 22) {
+       if (size <= 22) {
                u32 i;
-               for (i = 1; i < sizeof(i); i++)
+               for (i = 0; i < size; i++)
                        pr_info("data[%d] = 0x%02x\n", i, data[i]);
        }
 #endif
@@ -1636,6 +1671,22 @@ static int atvr_raw_event(struct hid_device *hdev, struct hid_report *report,
                pr_info("Report ID 10: PID: %d, report %s", hdev->product,
                        (char *)debug_info);
                kfree(debug_info);
+       } else if (hdev->product == USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE &&
+                       (report->id == SENSOR_REPORT_ID ||
+                       report->id == SENSOR_REPORT_ID_SYN ||
+                       report->id == SENSOR_REPORT_ID_COMBINED) &&
+                       shdr_dev->snsr_fns && shdr_dev->snsr_fns->recv) {
+               shdr_dev->snsr_fns->recv(shdr_dev->st, data, size);
+               /* TODO: check ret */
+               /* TODO: TEST data goes to hid_report_raw_event */
+               /* combined data report also has button data
+                * call hid_report_raw_event to handle button data
+                */
+               if (report->id == SENSOR_REPORT_ID_COMBINED)
+                       hid_report_raw_event(hdev, 0, data,
+                                            TS_BUTTON_REPORT_SIZE, 0);
+               /* we've handled the event */
+               return 1;
        }
        /* let the event through for regular input processing */
        return 0;
@@ -1647,6 +1698,7 @@ static int atvr_probe(struct hid_device *hdev, const struct hid_device_id *id)
        int ret;
        struct shdr_device *shdr_dev;
        struct snd_card *shdr_card;
+       struct tsfw_icm20628_state *st;
 
        shdr_dev = kzalloc(sizeof(*shdr_dev), GFP_KERNEL);
        if (shdr_dev == NULL) {
@@ -1701,6 +1753,13 @@ static int atvr_probe(struct hid_device *hdev, const struct hid_device_id *id)
        num_remotes++;
 
        mutex_unlock(&snd_cards_lock);
+
+       shdr_dev->snsr_fns = tsfw_icm20628_fns();
+       if (shdr_dev->snsr_fns && shdr_dev->snsr_fns->probe)
+               shdr_dev->snsr_fns->probe(hdev, &st);
+       /* TODO: ret check */
+       shdr_dev->st = st;
+
        return 0;
 err_stop:
        hid_hw_stop(hdev);
@@ -1721,6 +1780,10 @@ static void atvr_remove(struct hid_device *hdev)
        if (shdr_card == NULL)
                return -EIO;
 
+       if (shdr_dev->snsr_fns && shdr_dev->snsr_fns->remove)
+               shdr_dev->snsr_fns->remove(shdr_dev->st);
+       /* TODO: ret check */
+
        mutex_lock(&snd_cards_lock);
        atvr_snd = shdr_card->private_data;
        mutex_lock(&atvr_snd->hdev_lock);
index 9f52dfe6a55f8e5d7a3b55f677f6d9b8eec79c53..b4c5a6b58f5d1d6a042ef8554a3966322972dc2e 100644 (file)
@@ -38,6 +38,7 @@ config KMX61
 
 source "drivers/iio/imu/inv_mpu6050/Kconfig"
 source "drivers/iio/imu/nvi_mpu/Kconfig"
+source "drivers/iio/imu/tsfw_icm20628/Kconfig"
 
 
 config IIO_ADIS_LIB
index 76209a528426b264f8a290fa4eed573728fe7cd2..8e0761c1635e40c24c850a182dbb729c5741da11 100644 (file)
@@ -17,3 +17,4 @@ obj-y += inv_mpu6050/
 obj-$(CONFIG_KMX61) += kmx61.o
 obj-y += inv_mpu/
 obj-y += nvi_mpu/
+obj-y += tsfw_icm20628/
diff --git a/drivers/iio/imu/tsfw_icm20628/Kconfig b/drivers/iio/imu/tsfw_icm20628/Kconfig
new file mode 100644 (file)
index 0000000..f99835d
--- /dev/null
@@ -0,0 +1,5 @@
+config TSFW_ICM
+       bool "Thunderstrike Accel/Gyro/Mag sensors"
+       depends on HID_SHIELD_REMOTE
+       help
+         Enable Accelerometer, Gyro, and Magnetometer sensors on controller.
diff --git a/drivers/iio/imu/tsfw_icm20628/Makefile b/drivers/iio/imu/tsfw_icm20628/Makefile
new file mode 100644 (file)
index 0000000..afa1fea
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_TSFW_ICM) += tsfw_icm20628.o
diff --git a/drivers/iio/imu/tsfw_icm20628/tsfw_icm20628.c b/drivers/iio/imu/tsfw_icm20628/tsfw_icm20628.c
new file mode 100644 (file)
index 0000000..f81f764
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ * drivers/iio/imu/tsfw_icm20628/tsfw_icm20628.c
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION, All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/nvs.h>
+#include <linux/gfp.h>
+#include <linux/hid.h>
+#include <linux/iio/imu/tsfw_icm20628.h>
+#include <linux/module.h>
+
+/* #define DEBUG_TSFW_ICM */
+
+#define TSFW_SENSOR_NAME       "tsfw_icm20628"
+#define TSFW_SENSOR_VENDOR     "Invensense"
+#define TSFW_SENSOR_VERSION    1
+
+#define ID_ACCEL               0
+#define ID_GYRO                        1
+#define ID_MAG                 2
+
+#define AXIS_N                 3 /* 3 channels of data (X, Y, Z) */
+#define SENSOR_REPORT_SYN_MASK 0x80
+
+/* TODO: hacky, declare in .h file of hid */
+extern int atvr_ts_sensor_set(struct hid_device *hdev, bool enable);
+
+/* Define sensor */
+/* TODO: modify delay_us_min|max, implement batch */
+/* TODO: comment delay_us_min|max */
+static struct sensor_cfg tsfw_icm20628_cfg_dflt[] = {
+       {
+               .name                   = "accelerometer",
+               .snsr_id                = SENSOR_TYPE_ACCELEROMETER,
+               .kbuf_sz                = 32,
+               .ch_n                   = AXIS_N,
+               .ch_n_max               = AXIS_N,
+               .ch_sz                  = -2, /* 2 bytes signed per channel */
+               .part                   = TSFW_SENSOR_NAME,
+               .vendor                 = TSFW_SENSOR_VENDOR,
+               .version                = TSFW_SENSOR_VERSION,
+               .delay_us_min           = 14222,
+               .delay_us_max           = 14222,
+               .flags                  = DYNAMIC_SENSOR_MASK,
+               .float_significance     = NVS_FLOAT_NANO,
+               .milliamp               = {
+                       .ival           = 0,
+                       .fval           = 500000000,
+               },
+               .max_range              = {
+                       .ival           = 19,
+                       .fval           = 613300,
+               },
+               .resolution             = {
+                       .ival           = 0,
+                       .fval           = 598550,
+               },
+               .scales[0]              = {
+                       .ival           = 0,
+                       .fval           = 598550,
+               },
+               .scales[1]              = {
+                       .ival           = 0,
+                       .fval           = 598550,
+               },
+               .scales[2]              = {
+                       .ival           = 0,
+                       .fval           = 598550,
+               },
+               .uuid                   = "ts_accel",
+       },
+       {
+               .name                   = "gyroscope",
+               .snsr_id                = SENSOR_TYPE_GYROSCOPE,
+               .kbuf_sz                = 32,
+               .ch_n                   = AXIS_N,
+               .ch_n_max               = AXIS_N,
+               .ch_sz                  = -2, /* 2 bytes signed per channel */
+               .part                   = TSFW_SENSOR_NAME,
+               .vendor                 = TSFW_SENSOR_VENDOR,
+               .version                = TSFW_SENSOR_VERSION,
+               .delay_us_min           = 14222,
+               .delay_us_max           = 14222,
+               .flags                  = DYNAMIC_SENSOR_MASK,
+               .float_significance     = NVS_FLOAT_NANO,
+               .milliamp               = {
+                       .ival           = 3,
+                       .fval           = 700000000,
+               },
+               .max_range              = {
+                       .ival           = 34,
+                       .fval           = 906585040,
+               },
+               .resolution             = {
+                       .ival           = 0,
+                       .fval           = 1064225,
+               },
+               .scales[0]              = {
+                       .ival           = 0,
+                       .fval           = 1064225,
+               },
+               .scales[1]              = {
+                       .ival           = 0,
+                       .fval           = 1064225,
+               },
+               .scales[2]              = {
+                       .ival           = 0,
+                       .fval           = 1064225,
+               },
+               .uuid                   = "ts_gyro",
+       },
+       {
+               .name                   = "magnetic_field",
+               .snsr_id                = SENSOR_TYPE_MAGNETIC_FIELD,
+               .kbuf_sz                = 32,
+               .ch_n                   = AXIS_N,
+               .ch_n_max               = AXIS_N,
+               .ch_sz                  = -2, /* 2 bytes signed per channel */
+               .part                   = TSFW_SENSOR_NAME,
+               .vendor                 = TSFW_SENSOR_VENDOR,
+               .version                = TSFW_SENSOR_VERSION,
+               .delay_us_min           = 14222,
+               .delay_us_max           = 14222,
+               .flags                  = DYNAMIC_SENSOR_MASK,
+               .float_significance     = NVS_FLOAT_NANO,
+               /* TODO: milliamp */
+               .max_range              = {
+                       .ival           = 4912,
+                       .fval           = 0,
+               },
+               .resolution             = {
+                       .ival           = 0,
+                       .fval           = 600000,
+               },
+               .scales[0]              = {
+                       .ival           = 0,
+                       .fval           = 600000,
+               },
+               .scales[1]              = {
+                       .ival           = 0,
+                       .fval           = 600000,
+               },
+               .scales[2]              = {
+                       .ival           = 0,
+                       .fval           = 600000,
+               },
+               .uuid                   = "ts_mag",
+       }
+};
+
+#ifdef DEBUG_TSFW_ICM
+#define pr_debugg(...) pr_info(__VA_ARGS__)
+/* TODO: remove magic numbers */
+static void pr_debugg_buffer(u8 *data, size_t len)
+{
+       char qwerty[136] = { 0 };
+       size_t i, pos = 0;
+       if (len > 43)
+               len = 43;
+       for (i = 0; i < len; i++) {
+               if (i != 0 && i % 8 == 0)
+                       pos += sprintf(qwerty + pos, "\n");
+               pos += sprintf(qwerty + pos, "%02x ", data[i]);
+       }
+       pos += sprintf(qwerty + pos, "\n");
+       pr_info("%s", qwerty);
+}
+#else
+#define pr_debugg(...)         \
+({                             \
+  if (0)                       \
+       pr_info(__VA_ARGS__);   \
+  0;                           \
+})
+static inline void pr_debugg_buffer(u8 *data, size_t len)
+{
+}
+#endif
+
+/* Different types of fragmentation
+ * report_id=5, syn=1, 1 full set (18B)
+ * report_id=5, syn=1, 1 full set + 1 partial set (23B)
+ * report_id=5, syn=0, 1 partial set (1-17B)
+ * report_id=5, syn=0, 1 full set (18B) does this exist?
+ * report_id=5, syn=0, 1 partial set + 1 full set (19-25B)
+ * report_id=5, syn=0, 1 partial set + 1 full set + 1 partial set (20-25B)
+ * report_id=6 (=syn=0), ???
+ * report_id=7 (=syn=1), 1 full set (18B)
+ * report_id=7 (=syn=1), 2 full sets (36B)
+ * report_id=7 (=syn=1), 2 full sets + 1 partial set (41B)
+ */
+static int handle_sensor_data(struct tsfw_icm20628_state *st,
+               u8 report_counter, bool syn, u16 header,
+               u8 *data, size_t len)
+{
+       size_t i;
+       s64 ts;
+       pr_debugg("%s #%d len=%zu syn=%d\n", __func__, report_counter, len, syn);
+       pr_debugg_buffer(data, len);
+       if (syn) {
+               u8 rem, *tmp = data;
+               while (tmp + SENSOR_DATA_SET_SIZE <= data + len) {
+                       u8 t2[SENSOR_DATA_SET_SIZE];
+                       pr_debugg("Passing sensor data to nvs ts=%ld\n", ts);
+                       pr_debugg_buffer(tmp, SENSOR_DATA_SET_SIZE);
+
+                       ts = nvs_timestamp(); /* ns */
+                       /* TODO: st->nvs->nvs_mutex_lock(st->nvs_st); */
+                       for (i = 0; i < 17; i += 2) {
+                               /* Adjust for endianness */
+                               t2[i] = tmp[i+1];
+                               t2[i+1] = tmp[i];
+                       }
+                       st->nvs->handler(st->snsr[ID_ACCEL].nvs_st, t2, ts);
+                       st->nvs->handler(st->snsr[ID_GYRO].nvs_st, t2+6, ts);
+                       st->nvs->handler(st->snsr[ID_MAG].nvs_st, t2+12, ts);
+                       /* TODO: st->nvs->nvs_mutex_unlock(st->nvs_st); */
+                       tmp += SENSOR_DATA_SET_SIZE;
+               }
+               rem = data + len - tmp;
+               pr_debugg("Copying remainder to buffer\n");
+               pr_debugg_buffer(tmp, rem);
+               memcpy(st->buffered_data, tmp, rem);
+               st->buffered_cnt = rem;
+               /* TODO: st->prev_report_counter = report_counter; */
+       } else {
+               /* TODO: need to do something with non-sync packets */
+       }
+       return 0;
+}
+
+/* Report #1a
+ * data[0]: report id = 0x05
+ * data[1]: report counter
+ * data[2-18]: HID button data
+ * data[19]: syn flag (= 0)
+ * data[20]: length of sensor data
+ * data[21-45]: up to 25 bytes of sensor data
+ */
+#define RPT1A_DATA_OFFSET      21
+#define RPT1A_DATA_MAX_LEN     25
+
+/* Report #1b
+ * data[0]: report id = 0x05
+ * data[1]: report counter
+ * data[2-18]: HID button data
+ * data[19]: syn flag (= 1)
+ * data[20]: length of sensor data
+ * data[21-22]: header of which sensors enabled
+ * data[23-45]: up to 23 bytes of sensor data
+ */
+#define RPT1B_DATA_OFFSET      23
+#define RPT1B_DATA_MAX_LEN     23
+
+/* Report #2
+ * data[0]: report id = 0x06 (syn = 0)
+ * data[1]: report counter
+ * data[2]: length of sensor data
+ * data[3-45]: up to 43 bytes of sensor data
+ */
+#define RPT2_DATA_OFFSET       3
+#define RPT2_DATA_MAX_LEN      43
+
+/* Report #3
+ * data[0]: report id = 0x07 (syn = 1)
+ * data[1]: report counter
+ * data[2]: length of sensor data
+ * data[3-4]:  header of which sensors enabled
+ * data[5-45]: up to 41 bytes of sensor data
+ */
+#define RPT3_DATA_OFFSET       5
+#define RPT3_DATA_MAX_LEN      41
+
+static int recv(struct tsfw_icm20628_state *st, u8 *data, size_t size)
+{
+       u8 report_id = data[0];
+       u8 report_counter = data[1];
+       u16 sensor_header = 0;
+       u8 *sensor_data;
+       u8 len;
+       bool syn;
+
+       pr_debugg("%s received %zu bytes\n", __func__, size);
+       pr_debugg_buffer(data, size);
+
+       if (report_id == SENSOR_REPORT_ID_COMBINED) {
+               syn = data[19] & SENSOR_REPORT_SYN_MASK;
+               len = data[20];
+               if (!syn) {
+                       pr_debugg("Detected sensor packet #1a\n");
+                       sensor_data = data + RPT1A_DATA_OFFSET;
+                       if (len > RPT1A_DATA_MAX_LEN)
+                               len = RPT1A_DATA_MAX_LEN;
+               } else {
+                       pr_debugg("Detected sensor packet #1b\n");
+                       /* TODO: might be able to just (u16) data[21] */
+                       sensor_header = (data[22] << 8) | data[21];
+                       sensor_data = data + RPT1B_DATA_OFFSET;
+                       if (len > RPT1B_DATA_MAX_LEN)
+                               len = RPT1B_DATA_MAX_LEN;
+               }
+               handle_sensor_data(st, report_counter, syn, sensor_header,
+                               sensor_data, len);
+       } else if (report_id == SENSOR_REPORT_ID) {
+               pr_debugg("Detected sensor packet #2\n");
+               syn = false;
+               sensor_data = data + RPT2_DATA_OFFSET;
+               len = data[2];
+               if (len > RPT2_DATA_MAX_LEN)
+                       len = RPT2_DATA_MAX_LEN;
+               handle_sensor_data(st, report_counter, syn, sensor_header,
+                               sensor_data, len);
+       } else if (report_id == SENSOR_REPORT_ID_SYN) {
+               pr_debugg("Detected sensor packet #3\n");
+               syn = true;
+               /* TODO: might be able to just (u16) data[3] */
+               sensor_header = (data[4] << 8) | data[3];
+               sensor_data = data + RPT3_DATA_OFFSET;
+               len = data[2];
+               if (len > RPT3_DATA_MAX_LEN)
+                       len = RPT3_DATA_MAX_LEN;
+               handle_sensor_data(st, report_counter, syn, sensor_header,
+                               sensor_data, len);
+       } else {
+               pr_err("unexpected sensor report data\n");
+       }
+       /* TODO: return error code if fail */
+       return 0;
+}
+
+/* TODO: rename, struct device_node *dn needed? */
+static int of_dt(struct tsfw_icm20628_state *st)
+{
+       size_t i;
+       for (i = 0; i < ARRAY_SIZE(tsfw_icm20628_cfg_dflt); i++)
+               memcpy(&st->snsr[i].cfg, &tsfw_icm20628_cfg_dflt[i],
+                               sizeof(st->snsr[i].cfg));
+
+       return 0;
+
+}
+
+static int enable_hw(struct tsfw_icm20628_state *st)
+{
+       int ret;
+       ret = atvr_ts_sensor_set(st->hdev, true);
+       if (ret)
+               pr_err("%s failed to enable ts sensors\n", __func__);
+       return 0;
+}
+
+static int disable_hw(struct tsfw_icm20628_state *st)
+{
+       atvr_ts_sensor_set(st->hdev, false);
+       /* TODO: ret check */
+       return 0;
+}
+
+/* TODO: needed?
+static int set_rate_hw(struct tsfw_icm20628_state *st, unsigned int period)
+{ return 0; }
+static int read_hw(struct tsfw_icm20628_state *st, int32_t data)
+{ return 0; }
+ */
+
+static int enable(void *client, int snsr_id, int enable)
+{
+       struct tsfw_icm20628_state *st = (struct tsfw_icm20628_state *)client;
+       int ret;
+
+       pr_info("%s snsr_id: %d enable: %d\n", __func__, snsr_id, enable);
+       if (enable < 0)
+               return st->enabled;
+
+       /* TODO: make use of snsr_id for enabling/disabling specific sensors
+        * snsr_id will be SENSOR_TYPE_* from nvs.h
+        */
+       if (enable)
+               ret = enable_hw(st);
+       else
+               ret = disable_hw(st);
+       if (!ret)
+               st->enabled = enable;
+       return ret;
+}
+
+static int batch(void *client, int snsr_id, int flags, unsigned int period,
+               unsigned int timeout)
+{
+       pr_info("%s\n", __func__);
+       /* TODO: remove?
+       struct tsfw_icm20628_state *st = (struct tsfw_icm20628_state *)client;
+       return set_rate_hw(st, period);
+       */
+       return 0;
+}
+
+static struct nvs_fn_dev tsfw_icm20628_fn_dev = {
+       .enable = enable,
+       .batch  = batch,
+};
+
+static int probe(struct hid_device *hdev, struct tsfw_icm20628_state **pst)
+{
+       struct tsfw_icm20628_state *st;
+       int ret;
+       size_t i;
+
+       /* TODO: use devm_kzalloc? */
+       *pst = kzalloc(sizeof(struct tsfw_icm20628_state), GFP_KERNEL);
+       st = *pst;
+       if (!st)
+               return -ENOMEM;
+
+       ret = of_dt(st);
+       if (ret) {
+               pr_err("failed to init of_dt\n");
+               goto probe_failed;
+       }
+
+       st->hdev = hdev;
+       tsfw_icm20628_fn_dev.errs = &st->errs;
+       tsfw_icm20628_fn_dev.sts = &st->sts;
+       st->nvs = nvs_iio();
+       if (!st->nvs || !st->nvs->probe) {
+               pr_err("failed to get nvs_iio\n");
+               ret = -ENODEV;
+               goto probe_failed;
+       }
+
+       for (i = 0; i < SNSR_N; i++) {
+               ret = st->nvs->probe(&st->snsr[i].nvs_st, st, &hdev->dev,
+                               &tsfw_icm20628_fn_dev, &st->snsr[i].cfg);
+               if (ret) {
+                       goto nvs_probe_failed;
+               }
+       }
+
+       return 0;
+
+
+nvs_probe_failed:
+       if (st && st->nvs && st->nvs->remove)
+               for (i = 0; i < SNSR_N; i++)
+                       st->nvs->remove(st->snsr[i].nvs_st);
+probe_failed:
+       kfree(st);
+       return ret;
+}
+
+static int remove(struct tsfw_icm20628_state *st)
+{
+       size_t i;
+       if (st && st->nvs && st->nvs->remove)
+               for (i = 0; i < SNSR_N; i++)
+                       st->nvs->remove(st->snsr[i].nvs_st);
+
+       kfree(st);
+       return 0;
+}
+
+static struct tsfw_icm20628_fn_dev fns = {
+       .probe  = probe,
+       .recv   = recv,
+       .remove = remove,
+};
+
+struct tsfw_icm20628_fn_dev *tsfw_icm20628_fns()
+{
+       return &fns;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Thunderstrike FW Accel/Gyro/Mag sensors");
+MODULE_AUTHOR("NVIDIA Corporation");
diff --git a/include/linux/iio/imu/tsfw_icm20628.h b/include/linux/iio/imu/tsfw_icm20628.h
new file mode 100644 (file)
index 0000000..11d075d
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * include/linux/iio/imu/tsfw_icm20628.h
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION, All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __IIO_NVI_TS_MPU_H__
+#define __IIO_NVI_TS_MPU_H__
+
+#include <linux/nvs.h>
+#include <linux/hid.h>
+
+#define SENSOR_REPORT_ID_COMBINED      5
+#define SENSOR_REPORT_ID               6
+#define SENSOR_REPORT_ID_SYN           7
+
+#define SNSR_N                         3
+#define SENSOR_DATA_SET_SIZE           18
+
+struct tsfw_icm20628_snsr {
+       void *nvs_st;
+       struct sensor_cfg cfg;
+};
+
+struct tsfw_icm20628_state {
+       struct nvs_fn_if *nvs;
+       struct hid_device *hdev; /* keep ptr to hid_device to send HOSTCMDs */
+       unsigned int sts;     /* TODO: needed? */
+       unsigned int errs;    /* TODO: needed? */
+       unsigned int enabled; /* TODO: move to tsfw_icm20628_snsr? */
+       struct tsfw_icm20628_snsr snsr[SNSR_N];
+       u8 report_counter;
+       /* sensor data buffer for fragmented packets
+        * guaranteed to be < SENSOR_DATA_SET_SIZE
+        */
+       u8 buffered_data[SENSOR_DATA_SET_SIZE];
+       u8 buffered_cnt;
+};
+
+struct tsfw_icm20628_fn_dev {
+       /* probe
+        * @hdev: hid device associated with sensor
+        * @pst: sensor state data
+        * Returns 0 on success or a negative error code.
+        */
+       int (*probe)(struct hid_device *hdev, struct tsfw_icm20628_state **pst);
+
+       /* recv
+        * @st: sensor state data
+        * @type: report type
+        * @data: sensor data from hid report
+        * @len: length of sensor data
+        * Returns 0 on success or a negative error code.
+        */
+       int (*recv)(struct tsfw_icm20628_state *st, u8 *data, size_t len);
+
+       /* remove
+        * @st: sensor state data
+        * Returns 0 on success or a negative error code.
+        */
+       int (*remove)(struct tsfw_icm20628_state *st);
+};
+
+#ifdef CONFIG_TSFW_ICM
+struct tsfw_icm20628_fn_dev *tsfw_icm20628_fns(void);
+#else
+inline struct tsfw_icm20628_fn_dev *tsfw_icm20628_fns(void) { return NULL; }
+#endif
+
+#endif