| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788 |
- /* SPDX-License-Identifier: GPL-2.0-only
- *
- * Verifone Gsensor driver for screen orientation
- */
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/i2c.h>
- #include <linux/delay.h>
- #include <linux/mutex.h>
- #include <linux/sysfs.h>
- #include <linux/slab.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>
- extern struct kobject *vfiec_kobj;
- /* 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,
- 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_LANDSCAPE_FLIP_STR "landscape_flip"
- #define GSENSOR_MODE_SOFT_RESET BIT(2)
- #define GSENSOR_MODE_INITIALIZED BIT(1)
- /* 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 SC7A20_WHO_AM_I_VALUE 0x11
- /* Configuration */
- #define GSENSOR_DEBOUNCE_MS 300
- #define GSENSOR_POLL_INTERVAL_MS 200
- #define ST_ACCEL_NUMBER_DATA_CHANNELS 3
- /* Driver Data Structure */
- struct gsensor_data {
- struct i2c_client *client;
- struct mutex lock;
- struct delayed_work poll_work;
- struct kobject *gsensor_kobj;
- struct iio_dev *indio_dev;
-
- bool enabled;
- bool initialized;
-
- enum gsensor_orientation_hex orientation_hex;
- enum gsensor_orientation_hex pending_orientation;
- char orientation_str[32];
- unsigned long last_change_jiffies;
-
- /* IIO buffer data */
- s16 buffer[ST_ACCEL_NUMBER_DATA_CHANNELS + 1];
-
- u8 who_am_i;
- int irq;
- };
- static struct gsensor_data *g_data = NULL;
- /* I2C Helper Functions */
- static int gsensor_read_reg(struct i2c_client *client, u8 reg, u8 *data)
- {
- int ret = i2c_smbus_read_byte_data(client, reg);
- if (ret < 0)
- return ret;
- *data = ret;
- return 0;
- }
- static int gsensor_write_reg(struct i2c_client *client, u8 reg, u8 value)
- {
- return i2c_smbus_write_byte_data(client, reg, value);
- }
- /* Read raw acceleration data (12-bit, right-aligned) */
- static int gsensor_read_raw_data(struct gsensor_data *data, int *x, int *y, int *z)
- {
- u8 xl, xh, yl, yh, zl, zh;
- s16 raw_x, raw_y, raw_z;
- int ret;
- /* Read the X-axis byte by byte */
- ret = gsensor_read_reg(data->client, 0x28, &xl);
- if (ret < 0) return ret;
- ret = gsensor_read_reg(data->client, 0x29, &xh);
- if (ret < 0) return ret;
- /* Read the Y-axis byte by byte */
- ret = gsensor_read_reg(data->client, 0x2a, &yl);
- if (ret < 0) return ret;
- ret = gsensor_read_reg(data->client, 0x2b, &yh);
- if (ret < 0) return ret;
- /* Z-axis */
- ret = gsensor_read_reg(data->client, 0x2c, &zl);
- if (ret < 0) return ret;
- ret = gsensor_read_reg(data->client, 0x2d, &zh);
- if (ret < 0) return ret;
-
- raw_x = (s16)((xh << 8) | xl);
- raw_y = (s16)((yh << 8) | yl);
- raw_z = (s16)((zh << 8) | zl);
- /* ±2g Mode: 1 LSB = 0.06103515625 mg */
- *x = (raw_x * 61) / 1000;
- *y = (raw_y * 61) / 1000;
- *z = (raw_z * 61) / 1000;
-
- return 0;
- }
- /* Enable/Disable sensor */
- static int gsensor_set_enable(struct gsensor_data *data, bool enable)
- {
- int ret;
-
- if (enable) {
- /* CTRL_REG1: 0x87 = 100Hz (0x80) + Enable all axes (0x07) */
- ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x87);
- if (ret < 0)
- return ret;
- msleep(20);
- } else {
- /* CTRL_REG1: 0x00 = Standby mode */
- ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x00);
- if (ret < 0)
- 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;
- }
- static int gsensor_init_sensor(struct gsensor_data *data)
- {
- u8 who_am_i;
- int ret;
- /* 1. Verify device */
- ret = gsensor_read_reg(data->client, ST_ACCEL_WHO_AM_I_ADDR, &who_am_i);
- if (ret < 0) {
- dev_err(&data->client->dev, "Failed to read WHO_AM_I: %d\n", ret);
- return ret;
- }
-
- data->who_am_i = 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) {
- dev_err(&data->client->dev, "Invalid WHO_AM_I: 0x%02x\n", who_am_i);
- return -ENODEV;
- }
- /* 2. Setting CTRL_REG4: BDU enable */
- ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG4_ADDR, 0x80);
- if (ret < 0)
- return ret;
- /* 3. Disable the sensor at startup */
- ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x00);
- if (ret < 0)
- return ret;
-
- data->initialized = true;
- dev_info(&data->client->dev, "Gsensor initialized successfully\n");
-
- return 0;
- }
- /* Orientation Calculation */
- static enum gsensor_orientation_hex gsensor_calc_orientation(int x, int y, int z)
- {
- int abs_x = abs(x);
- int abs_y = abs(y);
- int abs_z = abs(z);
- /* Device flat - Z axis maximum */
- if (abs_z > 800 && abs_z > abs_x && abs_z > abs_y) {
- return GSENSOR_ORIENT_UNKNOWN_HEX;
- }
- /* Landscape mode: X axis maximum */
- if (abs_x > 800 && abs_x > abs_y) {
- if (x > 0)
- return GSENSOR_ORIENT_LANDSCAPE_FLIP_HEX;
- else
- return GSENSOR_ORIENT_LANDSCAPE_HEX;
- }
- /* Portrait mode: Y axis maximum. */
- if (abs_y > 800 && abs_y > abs_x) {
- if (y > 0)
- return GSENSOR_ORIENT_PORTRAIT_FLIP_HEX;
- else
- return GSENSOR_ORIENT_PORTRAIT_HEX;
- }
-
- return GSENSOR_ORIENT_UNKNOWN_HEX;
- }
- static void gsensor_update_orientation(struct gsensor_data *data)
- {
- int x, y, z;
- enum gsensor_orientation_hex new_orient;
- unsigned long now = jiffies;
-
- if (!data->enabled)
- return;
-
- if (gsensor_read_raw_data(data, &x, &y, &z) < 0)
- return;
-
- new_orient = gsensor_calc_orientation(x, y, z);
-
- if (new_orient != GSENSOR_ORIENT_UNKNOWN_HEX && new_orient != data->orientation_hex) {
- 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;
- data->pending_orientation = GSENSOR_ORIENT_UNKNOWN_HEX;
-
- 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;
- }
-
- dev_info(&data->client->dev, "Orientation: %s [X=%d, Y=%d, Z=%d]\n",
- data->orientation_str, x, y, z);
- }
- } else {
- data->pending_orientation = new_orient;
- data->last_change_jiffies = now;
- }
- } else if (new_orient == data->orientation_hex) {
- data->pending_orientation = GSENSOR_ORIENT_UNKNOWN_HEX;
- }
- }
- static void gsensor_poll_work(struct work_struct *work)
- {
- struct gsensor_data *data = container_of(work, struct gsensor_data,
- poll_work.work);
-
- mutex_lock(&data->lock);
- gsensor_update_orientation(data);
- mutex_unlock(&data->lock);
-
- if (data->enabled)
- schedule_delayed_work(&data->poll_work,
- 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 ==================== */
- 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);
- }
- static ssize_t enable_store(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t count)
- {
- struct gsensor_data *data = g_data;
- unsigned long val;
- int ret;
-
- if (!data)
- return -ENODEV;
-
- ret = kstrtoul(buf, 0, &val);
- if (ret)
- return ret;
-
- mutex_lock(&data->lock);
-
- if (val && !data->enabled) {
- ret = gsensor_set_enable(data, true);
- if (ret == 0) {
- data->enabled = true;
- schedule_delayed_work(&data->poll_work, 0);
- gsensor_update_orientation(data);
- }
- } else if (!val && data->enabled) {
- cancel_delayed_work_sync(&data->poll_work);
- ret = gsensor_set_enable(data, false);
- if (ret == 0)
- data->enabled = false;
- }
-
- mutex_unlock(&data->lock);
- return ret < 0 ? ret : count;
- }
- static struct kobj_attribute enable_attr = __ATTR_RW(enable);
- static ssize_t screen_orientation_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- return sprintf(buf, "%s\n", g_data ? g_data->orientation_str : "unknown");
- }
- static struct kobj_attribute screen_orientation_attr = __ATTR_RO(screen_orientation);
- static ssize_t instantaneous_orientation_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- 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 ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
- {
- u8 mode = 0;
- if (g_data && g_data->initialized)
- mode |= GSENSOR_MODE_INITIALIZED;
- return sprintf(buf, "0x%02x\n", mode);
- }
- static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t count)
- {
- struct gsensor_data *data = g_data;
- unsigned long val;
-
- if (!data)
- return -ENODEV;
-
- if (kstrtoul(buf, 0, &val))
- return -EINVAL;
-
- if (val & GSENSOR_MODE_SOFT_RESET) {
- mutex_lock(&data->lock);
- gsensor_init_sensor(data);
- data->orientation_hex = GSENSOR_ORIENT_LANDSCAPE_HEX;
- strcpy(data->orientation_str, GSENSOR_ORIENT_LANDSCAPE_STR);
- mutex_unlock(&data->lock);
- }
-
- return count;
- }
- static struct kobj_attribute mode_attr = __ATTR_RW(mode);
- static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
- {
- u8 state = 0;
- if (g_data && g_data->enabled)
- state |= 0x01;
- return sprintf(buf, "0x%02x\n", 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)
- {
- int x, y, z;
-
- if (!g_data)
- return -ENODEV;
-
- if (gsensor_read_raw_data(g_data, &x, &y, &z) < 0)
- return -EIO;
-
- return sprintf(buf, "%d %d %d\n", x, y, z);
- }
- static struct kobj_attribute raw_data_attr = __ATTR_RO(raw_data);
- static struct attribute *gsensor_attrs[] = {
- &enable_attr.attr,
- &screen_orientation_attr.attr,
- &instantaneous_orientation_attr.attr,
- &mode_attr.attr,
- &state_attr.attr,
- &raw_data_attr.attr,
- NULL,
- };
- static const struct attribute_group gsensor_attr_group = {
- .attrs = gsensor_attrs,
- };
- /* I2C Probe/Remove */
- static int gsensor_probe(struct i2c_client *client)
- {
- struct iio_dev *indio_dev;
- struct gsensor_data *data;
- int ret;
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
- if (!indio_dev)
- return -ENOMEM;
- data = iio_priv(indio_dev);
- data->client = client;
- data->indio_dev = indio_dev;
- data->irq = client->irq;
- mutex_init(&data->lock);
- INIT_DELAYED_WORK(&data->poll_work, gsensor_poll_work);
- data->orientation_hex = GSENSOR_ORIENT_LANDSCAPE_HEX;
- strcpy(data->orientation_str, GSENSOR_ORIENT_LANDSCAPE_STR);
- data->pending_orientation = GSENSOR_ORIENT_UNKNOWN_HEX;
- data->last_change_jiffies = jiffies;
- i2c_set_clientdata(client, indio_dev);
- ret = gsensor_init_sensor(data);
- if (ret < 0) {
- 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;
- }
- g_data = data;
- if (vfiec_kobj) {
- data->gsensor_kobj = kobject_create_and_add("gsensor", vfiec_kobj);
- if (data->gsensor_kobj) {
- ret = sysfs_create_group(data->gsensor_kobj, &gsensor_attr_group);
- if (ret < 0) {
- dev_err(&client->dev, "Failed to create sysfs group\n");
- kobject_put(data->gsensor_kobj);
- goto error_unregister;
- }
- } else {
- dev_err(&client->dev, "Failed to create gsensor kobject\n");
- goto error_unregister;
- }
- } else {
- dev_err(&client->dev, "vfiec_kobj not available\n");
- goto error_unregister;
- }
- /* ========== auto enable the sensor========== */
- ret = gsensor_set_enable(data, true);
- if (ret == 0) {
- data->enabled = true;
- schedule_delayed_work(&data->poll_work, 0);
- gsensor_update_orientation(data);
- dev_info(&client->dev, "Gsensor auto-enabled on load\n");
- } else {
- 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",
- data->who_am_i);
- return 0;
- error_unregister:
- iio_device_unregister(indio_dev);
- error:
- return ret;
- }
- 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);
-
- if (data) {
- cancel_delayed_work_sync(&data->poll_work);
- if (data->enabled)
- gsensor_set_enable(data, false);
- if (data->gsensor_kobj) {
- sysfs_remove_group(data->gsensor_kobj, &gsensor_attr_group);
- kobject_put(data->gsensor_kobj);
- }
- iio_device_unregister(indio_dev);
- mutex_destroy(&data->lock);
- g_data = NULL;
- }
- return 0;
- }
- /* ==================== I2C Driver ==================== */
- static const struct i2c_device_id gsensor_id_table[] = {
- { "sc7a20", 0 },
- { "gsensor", 0 },
- {}
- };
- MODULE_DEVICE_TABLE(i2c, gsensor_id_table);
- static struct i2c_driver gsensor_driver = {
- .driver = {
- .name = "verifone-gsensor",
- },
- .probe_new = gsensor_probe,
- .remove = gsensor_remove,
- .id_table = gsensor_id_table,
- };
- /* Init/Exit */
- int gsensor_init_main(void)
- {
- printk(KERN_INFO "gsensor: Registering driver\n");
- return i2c_add_driver(&gsensor_driver);
- }
- EXPORT_SYMBOL(gsensor_init_main);
- void gsensor_exit_main(void)
- {
- printk(KERN_INFO "gsensor: Unregistering driver\n");
- i2c_del_driver(&gsensor_driver);
- }
- EXPORT_SYMBOL(gsensor_exit_main);
- MODULE_LICENSE("GPL v2");
- MODULE_AUTHOR("Verifone, Inc.");
- MODULE_DESCRIPTION("Verifone Gsensor driver for screen orientation");
|