Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2011 Kionix, Inc. * Written by Chris Hudson <chudson@kionix.com> */
#include <linux/delay.h>#include <linux/i2c.h>#include <linux/input.h>#include <linux/interrupt.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/input/kxtj9.h>
#define NAME "kxtj9"#define G_MAX 8000/* OUTPUT REGISTERS */#define XOUT_L 0x06#define WHO_AM_I 0x0F/* CONTROL REGISTERS */#define INT_REL 0x1A#define CTRL_REG1 0x1B#define INT_CTRL1 0x1E#define DATA_CTRL 0x21/* CONTROL REGISTER 1 BITS */#define PC1_OFF 0x7F#define PC1_ON (1 << 7)/* Data ready function enable bit: set during probe if using irq mode */#define DRDYE (1 << 5)/* DATA CONTROL REGISTER BITS */#define ODR12_5F 0#define ODR25F 1#define ODR50F 2#define ODR100F 3#define ODR200F 4#define ODR400F 5#define ODR800F 6/* INTERRUPT CONTROL REGISTER 1 BITS *//* Set these during probe if using irq mode */#define KXTJ9_IEL (1 << 3)#define KXTJ9_IEA (1 << 4)#define KXTJ9_IEN (1 << 5)/* INPUT_ABS CONSTANTS */#define FUZZ 3#define FLAT 3/* RESUME STATE INDICES */#define RES_DATA_CTRL 0#define RES_CTRL_REG1 1#define RES_INT_CTRL1 2#define RESUME_ENTRIES 3
/* * The following table lists the maximum appropriate poll interval for each * available output data rate. */static const struct { unsigned int cutoff; u8 mask;} kxtj9_odr_table[] = { { 3, ODR800F }, { 5, ODR400F }, { 10, ODR200F }, { 20, ODR100F }, { 40, ODR50F }, { 80, ODR25F }, { 0, ODR12_5F},};
struct kxtj9_data { struct i2c_client *client; struct kxtj9_platform_data pdata; struct input_dev *input_dev; unsigned int last_poll_interval; u8 shift; u8 ctrl_reg1; u8 data_ctrl; u8 int_ctrl;};
static int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len){ struct i2c_msg msgs[] = { { .addr = tj9->client->addr, .flags = tj9->client->flags, .len = 1, .buf = &addr, }, { .addr = tj9->client->addr, .flags = tj9->client->flags | I2C_M_RD, .len = len, .buf = data, }, };
return i2c_transfer(tj9->client->adapter, msgs, 2);}
static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9){ s16 acc_data[3]; /* Data bytes from hardware xL, xH, yL, yH, zL, zH */ s16 x, y, z; int err;
err = kxtj9_i2c_read(tj9, XOUT_L, (u8 *)acc_data, 6); if (err < 0) dev_err(&tj9->client->dev, "accelerometer data read failed\n");
x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]); y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]); z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]);
x >>= tj9->shift; y >>= tj9->shift; z >>= tj9->shift;
input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x); input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y); input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_z ? -z : z); input_sync(tj9->input_dev);}
static irqreturn_t kxtj9_isr(int irq, void *dev){ struct kxtj9_data *tj9 = dev; int err;
/* data ready is the only possible interrupt type */ kxtj9_report_acceleration_data(tj9);
err = i2c_smbus_read_byte_data(tj9->client, INT_REL); if (err < 0) dev_err(&tj9->client->dev, "error clearing interrupt status: %d\n", err);
return IRQ_HANDLED;}
static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range){ switch (new_g_range) { case KXTJ9_G_2G: tj9->shift = 4; break; case KXTJ9_G_4G: tj9->shift = 3; break; case KXTJ9_G_8G: tj9->shift = 2; break; default: return -EINVAL; }
tj9->ctrl_reg1 &= 0xe7; tj9->ctrl_reg1 |= new_g_range;
return 0;}
static int kxtj9_update_odr(struct kxtj9_data *tj9, unsigned int poll_interval){ int err; int i;
/* Use the lowest ODR that can support the requested poll interval */ for (i = 0; i < ARRAY_SIZE(kxtj9_odr_table); i++) { tj9->data_ctrl = kxtj9_odr_table[i].mask; if (poll_interval < kxtj9_odr_table[i].cutoff) break; }
err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0); if (err < 0) return err;
err = i2c_smbus_write_byte_data(tj9->client, DATA_CTRL, tj9->data_ctrl); if (err < 0) return err;
err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1); if (err < 0) return err;
return 0;}
static int kxtj9_device_power_on(struct kxtj9_data *tj9){ if (tj9->pdata.power_on) return tj9->pdata.power_on();
return 0;}
static void kxtj9_device_power_off(struct kxtj9_data *tj9){ int err;
tj9->ctrl_reg1 &= PC1_OFF; err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1); if (err < 0) dev_err(&tj9->client->dev, "soft power off failed\n");
if (tj9->pdata.power_off) tj9->pdata.power_off();}
static int kxtj9_enable(struct kxtj9_data *tj9){ int err;
err = kxtj9_device_power_on(tj9); if (err < 0) return err;
/* ensure that PC1 is cleared before updating control registers */ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0); if (err < 0) return err;
/* only write INT_CTRL_REG1 if in irq mode */ if (tj9->client->irq) { err = i2c_smbus_write_byte_data(tj9->client, INT_CTRL1, tj9->int_ctrl); if (err < 0) return err; }
err = kxtj9_update_g_range(tj9, tj9->pdata.g_range); if (err < 0) return err;
/* turn on outputs */ tj9->ctrl_reg1 |= PC1_ON; err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1); if (err < 0) return err;
err = kxtj9_update_odr(tj9, tj9->last_poll_interval); if (err < 0) return err;
/* clear initial interrupt if in irq mode */ if (tj9->client->irq) { err = i2c_smbus_read_byte_data(tj9->client, INT_REL); if (err < 0) { dev_err(&tj9->client->dev, "error clearing interrupt: %d\n", err); goto fail; } }
return 0;
fail: kxtj9_device_power_off(tj9); return err;}
static void kxtj9_disable(struct kxtj9_data *tj9){ kxtj9_device_power_off(tj9);}
static int kxtj9_input_open(struct input_dev *input){ struct kxtj9_data *tj9 = input_get_drvdata(input);
return kxtj9_enable(tj9);}
static void kxtj9_input_close(struct input_dev *dev){ struct kxtj9_data *tj9 = input_get_drvdata(dev);
kxtj9_disable(tj9);}
/* * When IRQ mode is selected, we need to provide an interface to allow the user * to change the output data rate of the part. For consistency, we are using * the set_poll method, which accepts a poll interval in milliseconds, and then * calls update_odr() while passing this value as an argument. In IRQ mode, the * data outputs will not be read AT the requested poll interval, rather, the * lowest ODR that can support the requested interval. The client application * will be responsible for retrieving data from the input node at the desired * interval. */
/* Returns currently selected poll interval (in ms) */static ssize_t kxtj9_get_poll(struct device *dev, struct device_attribute *attr, char *buf){ struct i2c_client *client = to_i2c_client(dev); struct kxtj9_data *tj9 = i2c_get_clientdata(client);
return sprintf(buf, "%d\n", tj9->last_poll_interval);}
/* Allow users to select a new poll interval (in ms) */static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct i2c_client *client = to_i2c_client(dev); struct kxtj9_data *tj9 = i2c_get_clientdata(client); struct input_dev *input_dev = tj9->input_dev; unsigned int interval; int error;
error = kstrtouint(buf, 10, &interval); if (error < 0) return error;
/* Lock the device to prevent races with open/close (and itself) */ guard(mutex)(&input_dev->mutex); guard(disable_irq)(&client->irq);
/* * Set current interval to the greater of the minimum interval or * the requested interval */ tj9->last_poll_interval = max(interval, tj9->pdata.min_interval);
kxtj9_update_odr(tj9, tj9->last_poll_interval);
return count;}
static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
static struct attribute *kxtj9_attrs[] = { &dev_attr_poll.attr, NULL};
static umode_t kxtj9_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n){ struct device *dev = kobj_to_dev(kobj); struct i2c_client *client = to_i2c_client(dev);
return client->irq ? attr->mode : 0;}
static struct attribute_group kxtj9_group = { .attrs = kxtj9_attrs, .is_visible = kxtj9_attr_is_visible,};__ATTRIBUTE_GROUPS(kxtj9);
static void kxtj9_poll(struct input_dev *input){ struct kxtj9_data *tj9 = input_get_drvdata(input); unsigned int poll_interval = input_get_poll_interval(input);
kxtj9_report_acceleration_data(tj9);
if (poll_interval != tj9->last_poll_interval) { kxtj9_update_odr(tj9, poll_interval); tj9->last_poll_interval = poll_interval; }}
static void kxtj9_platform_exit(void *data){ struct kxtj9_data *tj9 = data;
if (tj9->pdata.exit) tj9->pdata.exit();}
static int kxtj9_verify(struct kxtj9_data *tj9){ int retval;
retval = kxtj9_device_power_on(tj9); if (retval < 0) return retval;
retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I); if (retval < 0) { dev_err(&tj9->client->dev, "read err int source\n"); goto out; }
retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0;
out: kxtj9_device_power_off(tj9); return retval;}
static int kxtj9_probe(struct i2c_client *client){ const struct kxtj9_platform_data *pdata = dev_get_platdata(&client->dev); struct kxtj9_data *tj9; struct input_dev *input_dev; int err;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, "client is not i2c capable\n"); return -ENXIO; }
if (!pdata) { dev_err(&client->dev, "platform data is NULL; exiting\n"); return -EINVAL; }
tj9 = devm_kzalloc(&client->dev, sizeof(*tj9), GFP_KERNEL); if (!tj9) { dev_err(&client->dev, "failed to allocate memory for module data\n"); return -ENOMEM; }
tj9->client = client; tj9->pdata = *pdata;
if (pdata->init) { err = pdata->init(); if (err < 0) return err; }
err = devm_add_action_or_reset(&client->dev, kxtj9_platform_exit, tj9); if (err) return err;
err = kxtj9_verify(tj9); if (err < 0) { dev_err(&client->dev, "device not recognized\n"); return err; }
i2c_set_clientdata(client, tj9);
tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range; tj9->last_poll_interval = tj9->pdata.init_interval;
input_dev = devm_input_allocate_device(&client->dev); if (!input_dev) { dev_err(&client->dev, "input device allocate failed\n"); return -ENOMEM; }
input_set_drvdata(input_dev, tj9); tj9->input_dev = input_dev;
input_dev->name = "kxtj9_accel"; input_dev->id.bustype = BUS_I2C;
input_dev->open = kxtj9_input_open; input_dev->close = kxtj9_input_close;
input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT); input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT); input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
if (client->irq <= 0) { err = input_setup_polling(input_dev, kxtj9_poll); if (err) return err; }
err = input_register_device(input_dev); if (err) { dev_err(&client->dev, "unable to register input polled device %s: %d\n", input_dev->name, err); return err; }
if (client->irq) { /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */ tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL; tj9->ctrl_reg1 |= DRDYE;
err = devm_request_threaded_irq(&client->dev, client->irq, NULL, kxtj9_isr, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "kxtj9-irq", tj9); if (err) { dev_err(&client->dev, "request irq failed: %d\n", err); return err; } }
return 0;}
static int kxtj9_suspend(struct device *dev){ struct i2c_client *client = to_i2c_client(dev); struct kxtj9_data *tj9 = i2c_get_clientdata(client); struct input_dev *input_dev = tj9->input_dev;
guard(mutex)(&input_dev->mutex);
if (input_device_enabled(input_dev)) kxtj9_disable(tj9);
return 0;}
static int kxtj9_resume(struct device *dev){ struct i2c_client *client = to_i2c_client(dev); struct kxtj9_data *tj9 = i2c_get_clientdata(client); struct input_dev *input_dev = tj9->input_dev;
guard(mutex)(&input_dev->mutex);
if (input_device_enabled(input_dev)) kxtj9_enable(tj9);
return 0;}
static DEFINE_SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
static const struct i2c_device_id kxtj9_id[] = { { .name = NAME }, { }};
MODULE_DEVICE_TABLE(i2c, kxtj9_id);
static struct i2c_driver kxtj9_driver = { .driver = { .name = NAME, .dev_groups = kxtj9_groups, .pm = pm_sleep_ptr(&kxtj9_pm_ops), }, .probe = kxtj9_probe, .id_table = kxtj9_id,};
module_i2c_driver(kxtj9_driver);
MODULE_DESCRIPTION("KXTJ9 accelerometer driver");MODULE_AUTHOR("Chris Hudson <chudson@kionix.com>");MODULE_LICENSE("GPL");