|
@@ -12,68 +12,55 @@
|
|
|
#include <linux/sysfs.h>
|
|
#include <linux/sysfs.h>
|
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
|
#include <linux/workqueue.h>
|
|
#include <linux/workqueue.h>
|
|
|
-#include <linux/iio/iio.h>
|
|
|
|
|
-#include <linux/iio/sysfs.h>
|
|
|
|
|
-#include <linux/iio/buffer.h>
|
|
|
|
|
-#include <linux/iio/trigger.h>
|
|
|
|
|
-#include <linux/iio/triggered_buffer.h>
|
|
|
|
|
-#include <linux/iio/trigger_consumer.h>
|
|
|
|
|
-#include <linux/irq.h>
|
|
|
|
|
-
|
|
|
|
|
-#if 0
|
|
|
|
|
|
|
+
|
|
|
extern struct kobject *vfiec_kobj;
|
|
extern struct kobject *vfiec_kobj;
|
|
|
|
|
|
|
|
/* Verifone API Definitions */
|
|
/* Verifone API Definitions */
|
|
|
-enum gsensor_orientation_hex {
|
|
|
|
|
- GSENSOR_ORIENT_UNKNOWN_HEX = 0x00,
|
|
|
|
|
- GSENSOR_ORIENT_PORTRAIT_HEX = 0x14,
|
|
|
|
|
- GSENSOR_ORIENT_LANDSCAPE_HEX = 0x15,
|
|
|
|
|
- GSENSOR_ORIENT_PORTRAIT_FLIP_HEX = 0x16,
|
|
|
|
|
|
|
+enum gsensor_orientation_hex
|
|
|
|
|
+{
|
|
|
|
|
+ GSENSOR_ORIENT_UNKNOWN_HEX = 0x00,
|
|
|
|
|
+ GSENSOR_ORIENT_PORTRAIT_HEX = 0x14,
|
|
|
|
|
+ GSENSOR_ORIENT_LANDSCAPE_HEX = 0x15,
|
|
|
|
|
+ GSENSOR_ORIENT_PORTRAIT_FLIP_HEX = 0x16,
|
|
|
GSENSOR_ORIENT_LANDSCAPE_FLIP_HEX = 0x17,
|
|
GSENSOR_ORIENT_LANDSCAPE_FLIP_HEX = 0x17,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-#define GSENSOR_ORIENT_PORTRAIT_STR "portrait"
|
|
|
|
|
-#define GSENSOR_ORIENT_LANDSCAPE_STR "landscape"
|
|
|
|
|
-#define GSENSOR_ORIENT_PORTRAIT_FLIP_STR "portrait_flip"
|
|
|
|
|
|
|
+#define GSENSOR_ORIENT_PORTRAIT_STR "portrait"
|
|
|
|
|
+#define GSENSOR_ORIENT_LANDSCAPE_STR "landscape"
|
|
|
|
|
+#define GSENSOR_ORIENT_PORTRAIT_FLIP_STR "portrait_flip"
|
|
|
#define GSENSOR_ORIENT_LANDSCAPE_FLIP_STR "landscape_flip"
|
|
#define GSENSOR_ORIENT_LANDSCAPE_FLIP_STR "landscape_flip"
|
|
|
|
|
|
|
|
-#define GSENSOR_MODE_SOFT_RESET BIT(2)
|
|
|
|
|
-#define GSENSOR_MODE_INITIALIZED BIT(1)
|
|
|
|
|
|
|
+#define GSENSOR_MODE_SOFT_RESET BIT(2)
|
|
|
|
|
+#define GSENSOR_MODE_INITIALIZED BIT(1)
|
|
|
|
|
|
|
|
/* Register Definitions */
|
|
/* Register Definitions */
|
|
|
-#define ST_ACCEL_WHO_AM_I_ADDR 0x0F
|
|
|
|
|
-#define ST_ACCEL_CTRL_REG1_ADDR 0x20
|
|
|
|
|
-#define ST_ACCEL_CTRL_REG4_ADDR 0x23
|
|
|
|
|
-#define ST_ACCEL_OUT_X_L_ADDR 0x28
|
|
|
|
|
-#define ST_ACCEL_STATUS_REG_ADDR 0x27
|
|
|
|
|
|
|
+#define ST_ACCEL_WHO_AM_I_ADDR 0x0F
|
|
|
|
|
+#define ST_ACCEL_CTRL_REG1_ADDR 0x20
|
|
|
|
|
+#define ST_ACCEL_CTRL_REG4_ADDR 0x23
|
|
|
|
|
+#define ST_ACCEL_OUT_X_L_ADDR 0x28
|
|
|
|
|
|
|
|
-#define SC7A20_WHO_AM_I_VALUE 0x11
|
|
|
|
|
|
|
+#define SC7A20_WHO_AM_I_VALUE 0x11
|
|
|
|
|
|
|
|
/* Configuration */
|
|
/* Configuration */
|
|
|
-#define GSENSOR_DEBOUNCE_MS 300
|
|
|
|
|
-#define GSENSOR_POLL_INTERVAL_MS 200
|
|
|
|
|
-#define ST_ACCEL_NUMBER_DATA_CHANNELS 3
|
|
|
|
|
|
|
+#define GSENSOR_DEBOUNCE_MS 300
|
|
|
|
|
+#define GSENSOR_POLL_INTERVAL_MS 200
|
|
|
|
|
|
|
|
/* Driver Data Structure */
|
|
/* Driver Data Structure */
|
|
|
-
|
|
|
|
|
-struct gsensor_data {
|
|
|
|
|
|
|
+struct gsensor_data
|
|
|
|
|
+{
|
|
|
struct i2c_client *client;
|
|
struct i2c_client *client;
|
|
|
struct mutex lock;
|
|
struct mutex lock;
|
|
|
struct delayed_work poll_work;
|
|
struct delayed_work poll_work;
|
|
|
struct kobject *gsensor_kobj;
|
|
struct kobject *gsensor_kobj;
|
|
|
- struct iio_dev *indio_dev;
|
|
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
bool enabled;
|
|
bool enabled;
|
|
|
bool initialized;
|
|
bool initialized;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
enum gsensor_orientation_hex orientation_hex;
|
|
enum gsensor_orientation_hex orientation_hex;
|
|
|
enum gsensor_orientation_hex pending_orientation;
|
|
enum gsensor_orientation_hex pending_orientation;
|
|
|
char orientation_str[32];
|
|
char orientation_str[32];
|
|
|
unsigned long last_change_jiffies;
|
|
unsigned long last_change_jiffies;
|
|
|
-
|
|
|
|
|
- /* IIO buffer data */
|
|
|
|
|
- s16 buffer[ST_ACCEL_NUMBER_DATA_CHANNELS + 1];
|
|
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
u8 who_am_i;
|
|
u8 who_am_i;
|
|
|
int irq;
|
|
int irq;
|
|
|
};
|
|
};
|
|
@@ -96,30 +83,34 @@ static int gsensor_write_reg(struct i2c_client *client, u8 reg, u8 value)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Read raw acceleration data (12-bit, right-aligned) */
|
|
/* Read raw acceleration data (12-bit, right-aligned) */
|
|
|
-static int gsensor_read_raw_data(struct gsensor_data *data, int *x, int *y, int *z)
|
|
|
|
|
|
|
+static int gsensor_read_raw_data(struct gsensor_data *data, int *x, int *y,
|
|
|
|
|
+ int *z)
|
|
|
{
|
|
{
|
|
|
u8 xl, xh, yl, yh, zl, zh;
|
|
u8 xl, xh, yl, yh, zl, zh;
|
|
|
s16 raw_x, raw_y, raw_z;
|
|
s16 raw_x, raw_y, raw_z;
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
|
|
- /* Read the X-axis byte by byte */
|
|
|
|
|
ret = gsensor_read_reg(data->client, 0x28, &xl);
|
|
ret = gsensor_read_reg(data->client, 0x28, &xl);
|
|
|
- if (ret < 0) return ret;
|
|
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
|
+ return ret;
|
|
|
ret = gsensor_read_reg(data->client, 0x29, &xh);
|
|
ret = gsensor_read_reg(data->client, 0x29, &xh);
|
|
|
- if (ret < 0) return ret;
|
|
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
|
+ return ret;
|
|
|
|
|
|
|
|
- /* Read the Y-axis byte by byte */
|
|
|
|
|
ret = gsensor_read_reg(data->client, 0x2a, &yl);
|
|
ret = gsensor_read_reg(data->client, 0x2a, &yl);
|
|
|
- if (ret < 0) return ret;
|
|
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
|
+ return ret;
|
|
|
ret = gsensor_read_reg(data->client, 0x2b, &yh);
|
|
ret = gsensor_read_reg(data->client, 0x2b, &yh);
|
|
|
- if (ret < 0) return ret;
|
|
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
|
+ return ret;
|
|
|
|
|
|
|
|
- /* Z-axis */
|
|
|
|
|
ret = gsensor_read_reg(data->client, 0x2c, &zl);
|
|
ret = gsensor_read_reg(data->client, 0x2c, &zl);
|
|
|
- if (ret < 0) return ret;
|
|
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
|
+ return ret;
|
|
|
ret = gsensor_read_reg(data->client, 0x2d, &zh);
|
|
ret = gsensor_read_reg(data->client, 0x2d, &zh);
|
|
|
- if (ret < 0) return ret;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
|
+ return ret;
|
|
|
|
|
+
|
|
|
raw_x = (s16)((xh << 8) | xl);
|
|
raw_x = (s16)((xh << 8) | xl);
|
|
|
raw_y = (s16)((yh << 8) | yl);
|
|
raw_y = (s16)((yh << 8) | yl);
|
|
|
raw_z = (s16)((zh << 8) | zl);
|
|
raw_z = (s16)((zh << 8) | zl);
|
|
@@ -128,7 +119,7 @@ static int gsensor_read_raw_data(struct gsensor_data *data, int *x, int *y, int
|
|
|
*x = (raw_x * 61) / 1000;
|
|
*x = (raw_x * 61) / 1000;
|
|
|
*y = (raw_y * 61) / 1000;
|
|
*y = (raw_y * 61) / 1000;
|
|
|
*z = (raw_z * 61) / 1000;
|
|
*z = (raw_z * 61) / 1000;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -136,79 +127,69 @@ static int gsensor_read_raw_data(struct gsensor_data *data, int *x, int *y, int
|
|
|
static int gsensor_set_enable(struct gsensor_data *data, bool enable)
|
|
static int gsensor_set_enable(struct gsensor_data *data, bool enable)
|
|
|
{
|
|
{
|
|
|
int ret;
|
|
int ret;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (enable) {
|
|
if (enable) {
|
|
|
- /* CTRL_REG1: 0x87 = 100Hz (0x80) + Enable all axes (0x07) */
|
|
|
|
|
ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x87);
|
|
ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x87);
|
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
|
return ret;
|
|
return ret;
|
|
|
msleep(20);
|
|
msleep(20);
|
|
|
} else {
|
|
} else {
|
|
|
- /* CTRL_REG1: 0x00 = Standby mode */
|
|
|
|
|
ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x00);
|
|
ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x00);
|
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
|
return ret;
|
|
return ret;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- return 0;
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-static int gsensor_set_dataready_irq(struct gsensor_data *data, bool enable)
|
|
|
|
|
-{
|
|
|
|
|
- /* For SC7A20, data ready is automatically enabled when sensor is active */
|
|
|
|
|
- /* This function is for compatibility with st_* trigger framework */
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/* Initialize sensor */
|
|
|
static int gsensor_init_sensor(struct gsensor_data *data)
|
|
static int gsensor_init_sensor(struct gsensor_data *data)
|
|
|
{
|
|
{
|
|
|
u8 who_am_i;
|
|
u8 who_am_i;
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
|
|
- /* 1. Verify device */
|
|
|
|
|
ret = gsensor_read_reg(data->client, ST_ACCEL_WHO_AM_I_ADDR, &who_am_i);
|
|
ret = gsensor_read_reg(data->client, ST_ACCEL_WHO_AM_I_ADDR, &who_am_i);
|
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
|
dev_err(&data->client->dev, "Failed to read WHO_AM_I: %d\n", ret);
|
|
dev_err(&data->client->dev, "Failed to read WHO_AM_I: %d\n", ret);
|
|
|
return ret;
|
|
return ret;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
data->who_am_i = who_am_i;
|
|
data->who_am_i = who_am_i;
|
|
|
dev_info(&data->client->dev, "WHO_AM_I = 0x%02x\n", who_am_i);
|
|
dev_info(&data->client->dev, "WHO_AM_I = 0x%02x\n", who_am_i);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (who_am_i != SC7A20_WHO_AM_I_VALUE) {
|
|
if (who_am_i != SC7A20_WHO_AM_I_VALUE) {
|
|
|
dev_err(&data->client->dev, "Invalid WHO_AM_I: 0x%02x\n", who_am_i);
|
|
dev_err(&data->client->dev, "Invalid WHO_AM_I: 0x%02x\n", who_am_i);
|
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /* 2. Setting CTRL_REG4: BDU enable */
|
|
|
|
|
|
|
+ /* Set CTRL_REG4: BDU enable, ±2g */
|
|
|
ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG4_ADDR, 0x80);
|
|
ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG4_ADDR, 0x80);
|
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
|
|
- /* 3. Disable the sensor at startup */
|
|
|
|
|
|
|
+ /* Initial disable */
|
|
|
ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x00);
|
|
ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x00);
|
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
|
return ret;
|
|
return ret;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
data->initialized = true;
|
|
data->initialized = true;
|
|
|
dev_info(&data->client->dev, "Gsensor initialized successfully\n");
|
|
dev_info(&data->client->dev, "Gsensor initialized successfully\n");
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Orientation Calculation */
|
|
/* Orientation Calculation */
|
|
|
-static enum gsensor_orientation_hex gsensor_calc_orientation(int x, int y, int z)
|
|
|
|
|
|
|
+static enum gsensor_orientation_hex gsensor_calc_orientation(int x, int y,
|
|
|
|
|
+ int z)
|
|
|
{
|
|
{
|
|
|
int abs_x = abs(x);
|
|
int abs_x = abs(x);
|
|
|
int abs_y = abs(y);
|
|
int abs_y = abs(y);
|
|
|
int abs_z = abs(z);
|
|
int abs_z = abs(z);
|
|
|
|
|
|
|
|
- /* Device flat - Z axis maximum */
|
|
|
|
|
if (abs_z > 800 && abs_z > abs_x && abs_z > abs_y) {
|
|
if (abs_z > 800 && abs_z > abs_x && abs_z > abs_y) {
|
|
|
return GSENSOR_ORIENT_UNKNOWN_HEX;
|
|
return GSENSOR_ORIENT_UNKNOWN_HEX;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /* Landscape mode: X axis maximum */
|
|
|
|
|
if (abs_x > 800 && abs_x > abs_y) {
|
|
if (abs_x > 800 && abs_x > abs_y) {
|
|
|
if (x > 0)
|
|
if (x > 0)
|
|
|
return GSENSOR_ORIENT_LANDSCAPE_FLIP_HEX;
|
|
return GSENSOR_ORIENT_LANDSCAPE_FLIP_HEX;
|
|
@@ -216,14 +197,13 @@ static enum gsensor_orientation_hex gsensor_calc_orientation(int x, int y, int z
|
|
|
return GSENSOR_ORIENT_LANDSCAPE_HEX;
|
|
return GSENSOR_ORIENT_LANDSCAPE_HEX;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /* Portrait mode: Y axis maximum. */
|
|
|
|
|
if (abs_y > 800 && abs_y > abs_x) {
|
|
if (abs_y > 800 && abs_y > abs_x) {
|
|
|
if (y > 0)
|
|
if (y > 0)
|
|
|
return GSENSOR_ORIENT_PORTRAIT_FLIP_HEX;
|
|
return GSENSOR_ORIENT_PORTRAIT_FLIP_HEX;
|
|
|
else
|
|
else
|
|
|
return GSENSOR_ORIENT_PORTRAIT_HEX;
|
|
return GSENSOR_ORIENT_PORTRAIT_HEX;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return GSENSOR_ORIENT_UNKNOWN_HEX;
|
|
return GSENSOR_ORIENT_UNKNOWN_HEX;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -232,40 +212,46 @@ static void gsensor_update_orientation(struct gsensor_data *data)
|
|
|
int x, y, z;
|
|
int x, y, z;
|
|
|
enum gsensor_orientation_hex new_orient;
|
|
enum gsensor_orientation_hex new_orient;
|
|
|
unsigned long now = jiffies;
|
|
unsigned long now = jiffies;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (!data->enabled)
|
|
if (!data->enabled)
|
|
|
return;
|
|
return;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (gsensor_read_raw_data(data, &x, &y, &z) < 0)
|
|
if (gsensor_read_raw_data(data, &x, &y, &z) < 0)
|
|
|
return;
|
|
return;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
new_orient = gsensor_calc_orientation(x, y, z);
|
|
new_orient = gsensor_calc_orientation(x, y, z);
|
|
|
-
|
|
|
|
|
- if (new_orient != GSENSOR_ORIENT_UNKNOWN_HEX && new_orient != data->orientation_hex) {
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (new_orient != GSENSOR_ORIENT_UNKNOWN_HEX &&
|
|
|
|
|
+ new_orient != data->orientation_hex) {
|
|
|
if (new_orient == data->pending_orientation) {
|
|
if (new_orient == data->pending_orientation) {
|
|
|
- if (time_after(now, data->last_change_jiffies +
|
|
|
|
|
- msecs_to_jiffies(GSENSOR_DEBOUNCE_MS))) {
|
|
|
|
|
- data->orientation_hex = new_orient;
|
|
|
|
|
|
|
+ if (time_after(now, data->last_change_jiffies +
|
|
|
|
|
+ msecs_to_jiffies(GSENSOR_DEBOUNCE_MS))) {
|
|
|
|
|
+ data->orientation_hex = new_orient;
|
|
|
data->pending_orientation = GSENSOR_ORIENT_UNKNOWN_HEX;
|
|
data->pending_orientation = GSENSOR_ORIENT_UNKNOWN_HEX;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
switch (new_orient) {
|
|
switch (new_orient) {
|
|
|
- case GSENSOR_ORIENT_PORTRAIT_HEX:
|
|
|
|
|
- strcpy(data->orientation_str, GSENSOR_ORIENT_PORTRAIT_STR);
|
|
|
|
|
- break;
|
|
|
|
|
- case GSENSOR_ORIENT_LANDSCAPE_HEX:
|
|
|
|
|
- strcpy(data->orientation_str, GSENSOR_ORIENT_LANDSCAPE_STR);
|
|
|
|
|
- break;
|
|
|
|
|
- case GSENSOR_ORIENT_PORTRAIT_FLIP_HEX:
|
|
|
|
|
- strcpy(data->orientation_str, GSENSOR_ORIENT_PORTRAIT_FLIP_STR);
|
|
|
|
|
- break;
|
|
|
|
|
- case GSENSOR_ORIENT_LANDSCAPE_FLIP_HEX:
|
|
|
|
|
- strcpy(data->orientation_str, GSENSOR_ORIENT_LANDSCAPE_FLIP_STR);
|
|
|
|
|
- break;
|
|
|
|
|
- default:
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+ case GSENSOR_ORIENT_PORTRAIT_HEX:
|
|
|
|
|
+ strcpy(data->orientation_str,
|
|
|
|
|
+ GSENSOR_ORIENT_PORTRAIT_STR);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case GSENSOR_ORIENT_LANDSCAPE_HEX:
|
|
|
|
|
+ strcpy(data->orientation_str,
|
|
|
|
|
+ GSENSOR_ORIENT_LANDSCAPE_STR);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case GSENSOR_ORIENT_PORTRAIT_FLIP_HEX:
|
|
|
|
|
+ strcpy(data->orientation_str,
|
|
|
|
|
+ GSENSOR_ORIENT_PORTRAIT_FLIP_STR);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case GSENSOR_ORIENT_LANDSCAPE_FLIP_HEX:
|
|
|
|
|
+ strcpy(data->orientation_str,
|
|
|
|
|
+ GSENSOR_ORIENT_LANDSCAPE_FLIP_STR);
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- dev_info(&data->client->dev, "Orientation: %s [X=%d, Y=%d, Z=%d]\n",
|
|
|
|
|
|
|
+
|
|
|
|
|
+ dev_info(&data->client->dev,
|
|
|
|
|
+ "Orientation: %s [X=%d, Y=%d, Z=%d]\n",
|
|
|
data->orientation_str, x, y, z);
|
|
data->orientation_str, x, y, z);
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
@@ -279,225 +265,22 @@ static void gsensor_update_orientation(struct gsensor_data *data)
|
|
|
|
|
|
|
|
static void gsensor_poll_work(struct work_struct *work)
|
|
static void gsensor_poll_work(struct work_struct *work)
|
|
|
{
|
|
{
|
|
|
- struct gsensor_data *data = container_of(work, struct gsensor_data,
|
|
|
|
|
- poll_work.work);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ struct gsensor_data *data =
|
|
|
|
|
+ container_of(work, struct gsensor_data, poll_work.work);
|
|
|
|
|
+
|
|
|
mutex_lock(&data->lock);
|
|
mutex_lock(&data->lock);
|
|
|
gsensor_update_orientation(data);
|
|
gsensor_update_orientation(data);
|
|
|
mutex_unlock(&data->lock);
|
|
mutex_unlock(&data->lock);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (data->enabled)
|
|
if (data->enabled)
|
|
|
- schedule_delayed_work(&data->poll_work,
|
|
|
|
|
|
|
+ schedule_delayed_work(&data->poll_work,
|
|
|
msecs_to_jiffies(GSENSOR_POLL_INTERVAL_MS));
|
|
msecs_to_jiffies(GSENSOR_POLL_INTERVAL_MS));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* IIO Framework */
|
|
|
|
|
-
|
|
|
|
|
-static int gsensor_read_raw(struct iio_dev *indio_dev,
|
|
|
|
|
- struct iio_chan_spec const *ch,
|
|
|
|
|
- int *val, int *val2, long mask)
|
|
|
|
|
-{
|
|
|
|
|
- struct gsensor_data *data = iio_priv(indio_dev);
|
|
|
|
|
- int x, y, z;
|
|
|
|
|
- int err;
|
|
|
|
|
-
|
|
|
|
|
- switch (mask) {
|
|
|
|
|
- case IIO_CHAN_INFO_RAW:
|
|
|
|
|
- err = gsensor_read_raw_data(data, &x, &y, &z);
|
|
|
|
|
- if (err < 0)
|
|
|
|
|
- return err;
|
|
|
|
|
-
|
|
|
|
|
- switch (ch->channel2) {
|
|
|
|
|
- case IIO_MOD_X:
|
|
|
|
|
- *val = x;
|
|
|
|
|
- return IIO_VAL_INT;
|
|
|
|
|
- case IIO_MOD_Y:
|
|
|
|
|
- *val = y;
|
|
|
|
|
- return IIO_VAL_INT;
|
|
|
|
|
- case IIO_MOD_Z:
|
|
|
|
|
- *val = z;
|
|
|
|
|
- return IIO_VAL_INT;
|
|
|
|
|
- default:
|
|
|
|
|
- return -EINVAL;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- case IIO_CHAN_INFO_SCALE:
|
|
|
|
|
- *val = 0;
|
|
|
|
|
- *val2 = 61035; /* 0.000061035 g */
|
|
|
|
|
- return IIO_VAL_INT_PLUS_NANO;
|
|
|
|
|
-
|
|
|
|
|
- case IIO_CHAN_INFO_SAMP_FREQ:
|
|
|
|
|
- *val = 100;
|
|
|
|
|
- return IIO_VAL_INT;
|
|
|
|
|
-
|
|
|
|
|
- default:
|
|
|
|
|
- return -EINVAL;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static int gsensor_write_raw(struct iio_dev *indio_dev,
|
|
|
|
|
- struct iio_chan_spec const *ch,
|
|
|
|
|
- int val, int val2, long mask)
|
|
|
|
|
-{
|
|
|
|
|
- switch (mask) {
|
|
|
|
|
- case IIO_CHAN_INFO_SAMP_FREQ:
|
|
|
|
|
- if (val == 100)
|
|
|
|
|
- return 0;
|
|
|
|
|
- return -EINVAL;
|
|
|
|
|
- default:
|
|
|
|
|
- return -EINVAL;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* IIO channels */
|
|
|
|
|
-static const struct iio_chan_spec gsensor_channels[] = {
|
|
|
|
|
- {
|
|
|
|
|
- .type = IIO_ACCEL,
|
|
|
|
|
- .modified = 1,
|
|
|
|
|
- .channel2 = IIO_MOD_X,
|
|
|
|
|
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
|
|
|
|
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
|
|
|
|
- BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
|
|
|
|
- .scan_index = 0,
|
|
|
|
|
- .scan_type = {
|
|
|
|
|
- .sign = 's',
|
|
|
|
|
- .realbits = 16,
|
|
|
|
|
- .storagebits = 16,
|
|
|
|
|
- .endianness = IIO_LE,
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- .type = IIO_ACCEL,
|
|
|
|
|
- .modified = 1,
|
|
|
|
|
- .channel2 = IIO_MOD_Y,
|
|
|
|
|
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
|
|
|
|
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
|
|
|
|
- BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
|
|
|
|
- .scan_index = 1,
|
|
|
|
|
- .scan_type = {
|
|
|
|
|
- .sign = 's',
|
|
|
|
|
- .realbits = 16,
|
|
|
|
|
- .storagebits = 16,
|
|
|
|
|
- .endianness = IIO_LE,
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- .type = IIO_ACCEL,
|
|
|
|
|
- .modified = 1,
|
|
|
|
|
- .channel2 = IIO_MOD_Z,
|
|
|
|
|
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
|
|
|
|
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
|
|
|
|
- BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
|
|
|
|
- .scan_index = 2,
|
|
|
|
|
- .scan_type = {
|
|
|
|
|
- .sign = 's',
|
|
|
|
|
- .realbits = 16,
|
|
|
|
|
- .storagebits = 16,
|
|
|
|
|
- .endianness = IIO_LE,
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
- IIO_CHAN_SOFT_TIMESTAMP(3),
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/* Trigger functions */
|
|
|
|
|
-static int gsensor_trig_set_state(struct iio_trigger *trig, bool state)
|
|
|
|
|
-{
|
|
|
|
|
- struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
|
|
|
|
- struct gsensor_data *data = iio_priv(indio_dev);
|
|
|
|
|
-
|
|
|
|
|
- return gsensor_set_dataready_irq(data, state);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static const struct iio_trigger_ops gsensor_trigger_ops = {
|
|
|
|
|
- .set_trigger_state = gsensor_trig_set_state,
|
|
|
|
|
- .validate_device = iio_trigger_validate_own_device,
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/* Buffer functions */
|
|
|
|
|
-static int gsensor_buffer_postenable(struct iio_dev *indio_dev)
|
|
|
|
|
-{
|
|
|
|
|
- struct gsensor_data *data = iio_priv(indio_dev);
|
|
|
|
|
- int err;
|
|
|
|
|
-
|
|
|
|
|
- err = gsensor_set_enable(data, true);
|
|
|
|
|
- if (err < 0)
|
|
|
|
|
- return err;
|
|
|
|
|
-
|
|
|
|
|
- return gsensor_set_dataready_irq(data, true);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static int gsensor_buffer_predisable(struct iio_dev *indio_dev)
|
|
|
|
|
-{
|
|
|
|
|
- struct gsensor_data *data = iio_priv(indio_dev);
|
|
|
|
|
- int err;
|
|
|
|
|
-
|
|
|
|
|
- err = gsensor_set_dataready_irq(data, false);
|
|
|
|
|
- if (err < 0)
|
|
|
|
|
- return err;
|
|
|
|
|
-
|
|
|
|
|
- return gsensor_set_enable(data, false);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static const struct iio_buffer_setup_ops gsensor_buffer_setup_ops = {
|
|
|
|
|
- .postenable = &gsensor_buffer_postenable,
|
|
|
|
|
- .predisable = &gsensor_buffer_predisable,
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/* Trigger handler */
|
|
|
|
|
-static irqreturn_t gsensor_trigger_handler(int irq, void *p)
|
|
|
|
|
-{
|
|
|
|
|
- struct iio_poll_func *pf = p;
|
|
|
|
|
- struct iio_dev *indio_dev = pf->indio_dev;
|
|
|
|
|
- struct gsensor_data *data = iio_priv(indio_dev);
|
|
|
|
|
- int x, y, z;
|
|
|
|
|
-
|
|
|
|
|
- gsensor_read_raw_data(data, &x, &y, &z);
|
|
|
|
|
- data->buffer[0] = x;
|
|
|
|
|
- data->buffer[1] = y;
|
|
|
|
|
- data->buffer[2] = z;
|
|
|
|
|
-
|
|
|
|
|
- iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
|
|
|
|
- iio_get_time_ns(indio_dev));
|
|
|
|
|
- iio_trigger_notify_done(indio_dev->trig);
|
|
|
|
|
-
|
|
|
|
|
- return IRQ_HANDLED;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* IIO attributes */
|
|
|
|
|
-static ssize_t gsensor_show_available_freq(struct device *dev,
|
|
|
|
|
- struct device_attribute *attr,
|
|
|
|
|
- char *buf)
|
|
|
|
|
-{
|
|
|
|
|
- return sprintf(buf, "100\n");
|
|
|
|
|
-}
|
|
|
|
|
-static IIO_DEVICE_ATTR(sampling_frequency_available, 0444,
|
|
|
|
|
- gsensor_show_available_freq, NULL, 0);
|
|
|
|
|
-
|
|
|
|
|
-static struct attribute *gsensor_iio_attrs[] = {
|
|
|
|
|
- &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
|
|
|
|
- NULL,
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-static const struct attribute_group gsensor_iio_attr_group = {
|
|
|
|
|
- .attrs = gsensor_iio_attrs,
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-static const struct iio_info gsensor_iio_info = {
|
|
|
|
|
- .read_raw = gsensor_read_raw,
|
|
|
|
|
- .write_raw = gsensor_write_raw,
|
|
|
|
|
- .attrs = &gsensor_iio_attr_group,
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/* Allocate ring buffer and trigger */
|
|
|
|
|
-static int gsensor_allocate_ring(struct iio_dev *indio_dev)
|
|
|
|
|
-{
|
|
|
|
|
- return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
|
|
|
|
|
- NULL, &gsensor_trigger_handler, &gsensor_buffer_setup_ops);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
/* ==================== Verifone Sysfs Interface ==================== */
|
|
/* ==================== Verifone Sysfs Interface ==================== */
|
|
|
|
|
|
|
|
-static ssize_t enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
|
|
|
|
|
|
+static ssize_t enable_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
|
|
|
+ char *buf)
|
|
|
{
|
|
{
|
|
|
return sprintf(buf, "%d\n", g_data ? g_data->enabled : 0);
|
|
return sprintf(buf, "%d\n", g_data ? g_data->enabled : 0);
|
|
|
}
|
|
}
|
|
@@ -508,16 +291,16 @@ static ssize_t enable_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
|
struct gsensor_data *data = g_data;
|
|
struct gsensor_data *data = g_data;
|
|
|
unsigned long val;
|
|
unsigned long val;
|
|
|
int ret;
|
|
int ret;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (!data)
|
|
if (!data)
|
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
ret = kstrtoul(buf, 0, &val);
|
|
ret = kstrtoul(buf, 0, &val);
|
|
|
if (ret)
|
|
if (ret)
|
|
|
return ret;
|
|
return ret;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
mutex_lock(&data->lock);
|
|
mutex_lock(&data->lock);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (val && !data->enabled) {
|
|
if (val && !data->enabled) {
|
|
|
ret = gsensor_set_enable(data, true);
|
|
ret = gsensor_set_enable(data, true);
|
|
|
if (ret == 0) {
|
|
if (ret == 0) {
|
|
@@ -531,7 +314,7 @@ static ssize_t enable_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
|
if (ret == 0)
|
|
if (ret == 0)
|
|
|
data->enabled = false;
|
|
data->enabled = false;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
mutex_unlock(&data->lock);
|
|
mutex_unlock(&data->lock);
|
|
|
return ret < 0 ? ret : count;
|
|
return ret < 0 ? ret : count;
|
|
|
}
|
|
}
|
|
@@ -542,16 +325,20 @@ static ssize_t screen_orientation_show(struct kobject *kobj,
|
|
|
{
|
|
{
|
|
|
return sprintf(buf, "%s\n", g_data ? g_data->orientation_str : "unknown");
|
|
return sprintf(buf, "%s\n", g_data ? g_data->orientation_str : "unknown");
|
|
|
}
|
|
}
|
|
|
-static struct kobj_attribute screen_orientation_attr = __ATTR_RO(screen_orientation);
|
|
|
|
|
|
|
+static struct kobj_attribute screen_orientation_attr =
|
|
|
|
|
+ __ATTR_RO(screen_orientation);
|
|
|
|
|
|
|
|
static ssize_t instantaneous_orientation_show(struct kobject *kobj,
|
|
static ssize_t instantaneous_orientation_show(struct kobject *kobj,
|
|
|
- struct kobj_attribute *attr, char *buf)
|
|
|
|
|
|
|
+ struct kobj_attribute *attr,
|
|
|
|
|
+ char *buf)
|
|
|
{
|
|
{
|
|
|
return sprintf(buf, "0x%02x\n", g_data ? g_data->orientation_hex : 0);
|
|
return sprintf(buf, "0x%02x\n", g_data ? g_data->orientation_hex : 0);
|
|
|
}
|
|
}
|
|
|
-static struct kobj_attribute instantaneous_orientation_attr = __ATTR_RO(instantaneous_orientation);
|
|
|
|
|
|
|
+static struct kobj_attribute instantaneous_orientation_attr =
|
|
|
|
|
+ __ATTR_RO(instantaneous_orientation);
|
|
|
|
|
|
|
|
-static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
|
|
|
|
|
|
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
|
|
|
+ char *buf)
|
|
|
{
|
|
{
|
|
|
u8 mode = 0;
|
|
u8 mode = 0;
|
|
|
if (g_data && g_data->initialized)
|
|
if (g_data && g_data->initialized)
|
|
@@ -564,13 +351,13 @@ static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
|
{
|
|
{
|
|
|
struct gsensor_data *data = g_data;
|
|
struct gsensor_data *data = g_data;
|
|
|
unsigned long val;
|
|
unsigned long val;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (!data)
|
|
if (!data)
|
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (kstrtoul(buf, 0, &val))
|
|
if (kstrtoul(buf, 0, &val))
|
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (val & GSENSOR_MODE_SOFT_RESET) {
|
|
if (val & GSENSOR_MODE_SOFT_RESET) {
|
|
|
mutex_lock(&data->lock);
|
|
mutex_lock(&data->lock);
|
|
|
gsensor_init_sensor(data);
|
|
gsensor_init_sensor(data);
|
|
@@ -578,12 +365,13 @@ static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
|
strcpy(data->orientation_str, GSENSOR_ORIENT_LANDSCAPE_STR);
|
|
strcpy(data->orientation_str, GSENSOR_ORIENT_LANDSCAPE_STR);
|
|
|
mutex_unlock(&data->lock);
|
|
mutex_unlock(&data->lock);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return count;
|
|
return count;
|
|
|
}
|
|
}
|
|
|
static struct kobj_attribute mode_attr = __ATTR_RW(mode);
|
|
static struct kobj_attribute mode_attr = __ATTR_RW(mode);
|
|
|
|
|
|
|
|
-static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
|
|
|
|
|
|
+static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
|
|
|
+ char *buf)
|
|
|
{
|
|
{
|
|
|
u8 state = 0;
|
|
u8 state = 0;
|
|
|
if (g_data && g_data->enabled)
|
|
if (g_data && g_data->enabled)
|
|
@@ -592,16 +380,17 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, cha
|
|
|
}
|
|
}
|
|
|
static struct kobj_attribute state_attr = __ATTR_RO(state);
|
|
static struct kobj_attribute state_attr = __ATTR_RO(state);
|
|
|
|
|
|
|
|
-static ssize_t raw_data_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
|
|
|
|
|
|
+static ssize_t raw_data_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
|
|
|
+ char *buf)
|
|
|
{
|
|
{
|
|
|
int x, y, z;
|
|
int x, y, z;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (!g_data)
|
|
if (!g_data)
|
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (gsensor_read_raw_data(g_data, &x, &y, &z) < 0)
|
|
if (gsensor_read_raw_data(g_data, &x, &y, &z) < 0)
|
|
|
return -EIO;
|
|
return -EIO;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return sprintf(buf, "%d %d %d\n", x, y, z);
|
|
return sprintf(buf, "%d %d %d\n", x, y, z);
|
|
|
}
|
|
}
|
|
|
static struct kobj_attribute raw_data_attr = __ATTR_RO(raw_data);
|
|
static struct kobj_attribute raw_data_attr = __ATTR_RO(raw_data);
|
|
@@ -620,21 +409,21 @@ static const struct attribute_group gsensor_attr_group = {
|
|
|
.attrs = gsensor_attrs,
|
|
.attrs = gsensor_attrs,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-/* I2C Probe/Remove */
|
|
|
|
|
|
|
+/* ==================== I2C Probe/Remove ==================== */
|
|
|
|
|
+
|
|
|
static int gsensor_probe(struct i2c_client *client)
|
|
static int gsensor_probe(struct i2c_client *client)
|
|
|
{
|
|
{
|
|
|
- struct iio_dev *indio_dev;
|
|
|
|
|
struct gsensor_data *data;
|
|
struct gsensor_data *data;
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
|
|
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
|
|
|
|
- if (!indio_dev)
|
|
|
|
|
|
|
+ dev_info(&client->dev, "Gsensor probe\n");
|
|
|
|
|
+
|
|
|
|
|
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
|
|
|
|
|
+ if (!data)
|
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
- data = iio_priv(indio_dev);
|
|
|
|
|
- data->client = client;
|
|
|
|
|
- data->indio_dev = indio_dev;
|
|
|
|
|
- data->irq = client->irq;
|
|
|
|
|
|
|
+ data->client = client;
|
|
|
|
|
+ data->irq = client->irq;
|
|
|
|
|
|
|
|
mutex_init(&data->lock);
|
|
mutex_init(&data->lock);
|
|
|
INIT_DELAYED_WORK(&data->poll_work, gsensor_poll_work);
|
|
INIT_DELAYED_WORK(&data->poll_work, gsensor_poll_work);
|
|
@@ -643,49 +432,12 @@ static int gsensor_probe(struct i2c_client *client)
|
|
|
data->pending_orientation = GSENSOR_ORIENT_UNKNOWN_HEX;
|
|
data->pending_orientation = GSENSOR_ORIENT_UNKNOWN_HEX;
|
|
|
data->last_change_jiffies = jiffies;
|
|
data->last_change_jiffies = jiffies;
|
|
|
|
|
|
|
|
- i2c_set_clientdata(client, indio_dev);
|
|
|
|
|
|
|
+ i2c_set_clientdata(client, data);
|
|
|
|
|
|
|
|
ret = gsensor_init_sensor(data);
|
|
ret = gsensor_init_sensor(data);
|
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
|
dev_err(&client->dev, "Init failed: %d\n", ret);
|
|
dev_err(&client->dev, "Init failed: %d\n", ret);
|
|
|
- return ret;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- indio_dev->modes = INDIO_DIRECT_MODE;
|
|
|
|
|
- indio_dev->info = &gsensor_iio_info;
|
|
|
|
|
- indio_dev->channels = gsensor_channels;
|
|
|
|
|
- indio_dev->num_channels = ARRAY_SIZE(gsensor_channels);
|
|
|
|
|
-
|
|
|
|
|
- ret = gsensor_allocate_ring(indio_dev);
|
|
|
|
|
- if (ret < 0) {
|
|
|
|
|
- dev_err(&client->dev, "Failed to allocate ring buffer: %d\n", ret);
|
|
|
|
|
- return ret;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (data->irq > 0) {
|
|
|
|
|
- struct iio_trigger *trig;
|
|
|
|
|
-
|
|
|
|
|
- trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name,
|
|
|
|
|
- iio_device_id(indio_dev));
|
|
|
|
|
- if (!trig) {
|
|
|
|
|
- ret = -ENOMEM;
|
|
|
|
|
- goto error;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- trig->ops = &gsensor_trigger_ops;
|
|
|
|
|
- iio_trigger_set_drvdata(trig, indio_dev);
|
|
|
|
|
-
|
|
|
|
|
- ret = devm_iio_trigger_register(&client->dev, trig);
|
|
|
|
|
- if (ret < 0)
|
|
|
|
|
- goto error;
|
|
|
|
|
-
|
|
|
|
|
- indio_dev->trig = iio_trigger_get(trig);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- ret = iio_device_register(indio_dev);
|
|
|
|
|
- if (ret < 0) {
|
|
|
|
|
- dev_err(&client->dev, "Failed to register IIO device: %d\n", ret);
|
|
|
|
|
- goto error;
|
|
|
|
|
|
|
+ goto err_free;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
g_data = data;
|
|
g_data = data;
|
|
@@ -697,18 +449,18 @@ static int gsensor_probe(struct i2c_client *client)
|
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
|
dev_err(&client->dev, "Failed to create sysfs group\n");
|
|
dev_err(&client->dev, "Failed to create sysfs group\n");
|
|
|
kobject_put(data->gsensor_kobj);
|
|
kobject_put(data->gsensor_kobj);
|
|
|
- goto error_unregister;
|
|
|
|
|
|
|
+ goto err_free;
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
dev_err(&client->dev, "Failed to create gsensor kobject\n");
|
|
dev_err(&client->dev, "Failed to create gsensor kobject\n");
|
|
|
- goto error_unregister;
|
|
|
|
|
|
|
+ goto err_free;
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
dev_err(&client->dev, "vfiec_kobj not available\n");
|
|
dev_err(&client->dev, "vfiec_kobj not available\n");
|
|
|
- goto error_unregister;
|
|
|
|
|
|
|
+ goto err_free;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /* ========== auto enable the sensor========== */
|
|
|
|
|
|
|
+ /* 自动启用传感器 */
|
|
|
ret = gsensor_set_enable(data, true);
|
|
ret = gsensor_set_enable(data, true);
|
|
|
if (ret == 0) {
|
|
if (ret == 0) {
|
|
|
data->enabled = true;
|
|
data->enabled = true;
|
|
@@ -718,24 +470,23 @@ static int gsensor_probe(struct i2c_client *client)
|
|
|
} else {
|
|
} else {
|
|
|
dev_warn(&client->dev, "Failed to auto-enable sensor: %d\n", ret);
|
|
dev_warn(&client->dev, "Failed to auto-enable sensor: %d\n", ret);
|
|
|
}
|
|
}
|
|
|
- /* ================================= */
|
|
|
|
|
|
|
|
|
|
dev_info(&client->dev, "Gsensor driver loaded (WHO_AM_I=0x%02x)\n",
|
|
dev_info(&client->dev, "Gsensor driver loaded (WHO_AM_I=0x%02x)\n",
|
|
|
data->who_am_i);
|
|
data->who_am_i);
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
-error_unregister:
|
|
|
|
|
- iio_device_unregister(indio_dev);
|
|
|
|
|
-error:
|
|
|
|
|
|
|
+err_free:
|
|
|
|
|
+ kfree(data);
|
|
|
|
|
+ g_data = NULL;
|
|
|
return ret;
|
|
return ret;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int gsensor_remove(struct i2c_client *client)
|
|
static int gsensor_remove(struct i2c_client *client)
|
|
|
{
|
|
{
|
|
|
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
|
|
|
|
- struct gsensor_data *data = iio_priv(indio_dev);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ struct gsensor_data *data = g_data;
|
|
|
|
|
+
|
|
|
if (data) {
|
|
if (data) {
|
|
|
|
|
+ dev_info(&client->dev, "Removing Gsensor driver...\n");
|
|
|
cancel_delayed_work_sync(&data->poll_work);
|
|
cancel_delayed_work_sync(&data->poll_work);
|
|
|
if (data->enabled)
|
|
if (data->enabled)
|
|
|
gsensor_set_enable(data, false);
|
|
gsensor_set_enable(data, false);
|
|
@@ -743,8 +494,8 @@ static int gsensor_remove(struct i2c_client *client)
|
|
|
sysfs_remove_group(data->gsensor_kobj, &gsensor_attr_group);
|
|
sysfs_remove_group(data->gsensor_kobj, &gsensor_attr_group);
|
|
|
kobject_put(data->gsensor_kobj);
|
|
kobject_put(data->gsensor_kobj);
|
|
|
}
|
|
}
|
|
|
- iio_device_unregister(indio_dev);
|
|
|
|
|
mutex_destroy(&data->lock);
|
|
mutex_destroy(&data->lock);
|
|
|
|
|
+ kfree(data);
|
|
|
g_data = NULL;
|
|
g_data = NULL;
|
|
|
}
|
|
}
|
|
|
return 0;
|
|
return 0;
|
|
@@ -753,27 +504,62 @@ static int gsensor_remove(struct i2c_client *client)
|
|
|
/* ==================== I2C Driver ==================== */
|
|
/* ==================== I2C Driver ==================== */
|
|
|
|
|
|
|
|
static const struct i2c_device_id gsensor_id_table[] = {
|
|
static const struct i2c_device_id gsensor_id_table[] = {
|
|
|
- { "sc7a20", 0 },
|
|
|
|
|
- { "gsensor", 0 },
|
|
|
|
|
- {}
|
|
|
|
|
-};
|
|
|
|
|
|
|
+ {"sc7a20", 0}, {"gsensor", 0}, {}};
|
|
|
MODULE_DEVICE_TABLE(i2c, gsensor_id_table);
|
|
MODULE_DEVICE_TABLE(i2c, gsensor_id_table);
|
|
|
|
|
|
|
|
static struct i2c_driver gsensor_driver = {
|
|
static struct i2c_driver gsensor_driver = {
|
|
|
- .driver = {
|
|
|
|
|
- .name = "verifone-gsensor",
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ .driver =
|
|
|
|
|
+ {
|
|
|
|
|
+ .name = "verifone-gsensor",
|
|
|
|
|
+ },
|
|
|
.probe_new = gsensor_probe,
|
|
.probe_new = gsensor_probe,
|
|
|
- .remove = gsensor_remove,
|
|
|
|
|
- .id_table = gsensor_id_table,
|
|
|
|
|
|
|
+ .remove = gsensor_remove,
|
|
|
|
|
+ .id_table = gsensor_id_table,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-/* Init/Exit */
|
|
|
|
|
|
|
+/* ==================== Init/Exit ==================== */
|
|
|
|
|
+static void gsensor_auto_create_i2c_device(void)
|
|
|
|
|
+{
|
|
|
|
|
+ struct i2c_adapter *adapter;
|
|
|
|
|
+ struct i2c_board_info info = {
|
|
|
|
|
+ I2C_BOARD_INFO("sc7a20", 0x19),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ printk(KERN_INFO "gsensor: auto_create_i2c_device START\n");
|
|
|
|
|
+
|
|
|
|
|
+ adapter = i2c_get_adapter(0);
|
|
|
|
|
+ if (adapter) {
|
|
|
|
|
+ printk(KERN_INFO "gsensor: got adapter for bus 0\n");
|
|
|
|
|
+ i2c_new_client_device(adapter, &info);
|
|
|
|
|
+ i2c_put_adapter(adapter);
|
|
|
|
|
+ printk(KERN_INFO "gsensor: created device at 0x19 on bus 0\n");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ printk(KERN_INFO "gsensor: no adapter for bus 0\n");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ printk(KERN_INFO "gsensor: auto_create_i2c_device END\n");
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
int gsensor_init_main(void)
|
|
int gsensor_init_main(void)
|
|
|
{
|
|
{
|
|
|
- printk(KERN_INFO "gsensor: Registering driver\n");
|
|
|
|
|
- return i2c_add_driver(&gsensor_driver);
|
|
|
|
|
|
|
+ int ret;
|
|
|
|
|
+
|
|
|
|
|
+ printk(KERN_INFO "gsensor: Registering driver START\n");
|
|
|
|
|
+
|
|
|
|
|
+ ret = i2c_add_driver(&gsensor_driver);
|
|
|
|
|
+ if (ret < 0) {
|
|
|
|
|
+ printk(KERN_ERR "gsensor: Failed to register I2C driver: %d\n", ret);
|
|
|
|
|
+ return ret;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ printk(KERN_INFO "gsensor: i2c_add_driver done\n");
|
|
|
|
|
+
|
|
|
|
|
+ gsensor_auto_create_i2c_device();
|
|
|
|
|
+
|
|
|
|
|
+ printk(KERN_INFO "gsensor: Driver initialized\n");
|
|
|
|
|
+ return 0;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
EXPORT_SYMBOL(gsensor_init_main);
|
|
EXPORT_SYMBOL(gsensor_init_main);
|
|
|
|
|
|
|
|
void gsensor_exit_main(void)
|
|
void gsensor_exit_main(void)
|
|
@@ -787,14 +573,3 @@ MODULE_LICENSE("GPL v2");
|
|
|
MODULE_AUTHOR("Verifone, Inc.");
|
|
MODULE_AUTHOR("Verifone, Inc.");
|
|
|
MODULE_DESCRIPTION("Verifone Gsensor driver for screen orientation");
|
|
MODULE_DESCRIPTION("Verifone Gsensor driver for screen orientation");
|
|
|
|
|
|
|
|
-#endif
|
|
|
|
|
-
|
|
|
|
|
-void gsensor_exit_main(void)
|
|
|
|
|
-{
|
|
|
|
|
-
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-int gsensor_init_main(void)
|
|
|
|
|
-{
|
|
|
|
|
- return 0;
|
|
|
|
|
-}
|
|
|