diff options
Diffstat (limited to 'drivers/sensor/yas_mag_kernel_driver.c')
-rw-r--r-- | drivers/sensor/yas_mag_kernel_driver.c | 2192 |
1 files changed, 2192 insertions, 0 deletions
diff --git a/drivers/sensor/yas_mag_kernel_driver.c b/drivers/sensor/yas_mag_kernel_driver.c new file mode 100644 index 0000000..0106958 --- /dev/null +++ b/drivers/sensor/yas_mag_kernel_driver.c @@ -0,0 +1,2192 @@ +/* + * Copyright (c) 2010-2011 Yamaha Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/atomic.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/workqueue.h> + +#define __LINUX_KERNEL_DRIVER__ +#include <linux/sensor/yas.h> +#include <linux/sensor/sensors_core.h> +#include "yas_mag_driver.c" + +#define SYSFS_PCBTEST +#ifdef SYSFS_PCBTEST +#include "yas_pcb_test.h" +#include "yas_pcb_test.c" +#endif + +#define GEOMAGNETIC_I2C_DEVICE_NAME "yas532" +#define GEOMAGNETIC_INPUT_NAME "geomagnetic" +#define GEOMAGNETIC_INPUT_RAW_NAME "geomagnetic_raw" +#undef GEOMAGNETIC_PLATFORM_API + +#define ABS_STATUS (ABS_BRAKE) +#define ABS_WAKE (ABS_MISC) + +#define ABS_RAW_DISTORTION (ABS_THROTTLE) +#define ABS_RAW_THRESHOLD (ABS_RUDDER) +#define ABS_RAW_SHAPE (ABS_WHEEL) +#define ABS_RAW_MODE (ABS_HAT0X) +#define ABS_RAW_REPORT (ABS_GAS) + +struct geomagnetic_data { + struct input_dev *input_data; + struct input_dev *input_raw; + struct delayed_work work; + struct semaphore driver_lock; + struct semaphore multi_lock; + atomic_t last_data[3]; + atomic_t last_status; + atomic_t enable; + int filter_enable; + int filter_len; + int32_t filter_noise[3]; + int32_t filter_threshold; + int delay; + int32_t threshold; + int32_t distortion[3]; + int32_t shape; + int32_t ellipsoid_mode; + struct yas_mag_offset driver_offset; +#if DEBUG + int suspend; +#endif +#ifdef YAS_MAG_MANUAL_OFFSET + struct yas_vector manual_offset; +#endif + struct yas_matrix static_matrix; + struct yas_matrix dynamic_matrix; +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct list_head devfile_list; + struct list_head raw_devfile_list; +#endif + struct device *magnetic_sensor_device; + struct mag_platform_data *mag_pdata; + uint8_t dev_id; + int noise_test_init; +}; + +static struct i2c_client *this_client; + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + +#include <linux/miscdevice.h> +#define MAX_COUNT (64) +#define SENSOR_NAME "geomagnetic" +#define SENSOR_RAW_NAME "geomagnetic_raw" + +struct sensor_device { + struct list_head list; + struct mutex lock; + wait_queue_head_t waitq; + struct input_event events[MAX_COUNT]; + int head, num_event; +}; + +static void get_time_stamp(struct timeval *tv) +{ + struct timespec ts; + ktime_get_ts(&ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; +} + +static void make_event(struct input_event *ev, int type, int code, int value) +{ + struct timeval tv; + get_time_stamp(&tv); + ev->type = type; + ev->code = code; + ev->value = value; + ev->time = tv; +} + +static void +make_event_w_time(struct input_event *ev, int type, int code, int value, + struct timeval *tv) +{ + ev->type = type; + ev->code = code; + ev->value = value; + ev->time = *tv; +} + +static void sensor_enq(struct sensor_device *kdev, struct input_event *ev) +{ + int idx; + + idx = kdev->head + kdev->num_event; + if (MAX_COUNT <= idx) + idx -= MAX_COUNT; + kdev->events[idx] = *ev; + kdev->num_event++; + if (MAX_COUNT < kdev->num_event) { + kdev->num_event = MAX_COUNT; + kdev->head++; + if (MAX_COUNT <= kdev->head) + kdev->head -= MAX_COUNT; + } +} + +static int sensor_deq(struct sensor_device *kdev, struct input_event *ev) +{ + if (kdev->num_event == 0) + return 0; + + *ev = kdev->events[kdev->head]; + kdev->num_event--; + kdev->head++; + if (MAX_COUNT <= kdev->head) + kdev->head -= MAX_COUNT; + return 1; +} + +static void +sensor_event(struct list_head *devlist, struct input_event *ev, int num) +{ + struct sensor_device *kdev; + int i; + + list_for_each_entry(kdev, devlist, list) { + mutex_lock(&kdev->lock); + for (i = 0; i < num; i++) + sensor_enq(kdev, &ev[i]); + mutex_unlock(&kdev->lock); + wake_up_interruptible(&kdev->waitq); + } +} + +static ssize_t +sensor_write(struct file *f, const char __user *buf, size_t count, + loff_t *pos) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + struct sensor_device *kdev; + struct input_event ev[MAX_COUNT]; + int num, i; + + if (count < sizeof(struct input_event)) + return -EINVAL; + num = count / sizeof(struct input_event); + if (MAX_COUNT < num) + num = MAX_COUNT; + + if (copy_from_user(ev, buf, num * sizeof(struct input_event))) + return -EFAULT; + + list_for_each_entry(kdev, &data->devfile_list, list) { + mutex_lock(&kdev->lock); + for (i = 0; i < num; i++) + sensor_enq(kdev, &ev[i]); + mutex_unlock(&kdev->lock); + wake_up_interruptible(&kdev->waitq); + } + + return count; +} + +static ssize_t +sensor_read(struct file *f, char __user *buf, size_t count, loff_t *pos) +{ + struct sensor_device *kdev = f->private_data; + int rt, num; + struct input_event ev[MAX_COUNT]; + + if (count < sizeof(struct input_event)) + return -EINVAL; + + rt = wait_event_interruptible(kdev->waitq, kdev->num_event != 0); + if (rt) + return rt; + + mutex_lock(&kdev->lock); + for (num = 0; num < count / sizeof(struct input_event); num++) { + if (!sensor_deq(kdev, &ev[num])) + break; + } + mutex_unlock(&kdev->lock); + + if (copy_to_user(buf, ev, num * sizeof(struct input_event))) + return -EFAULT; + + return num * sizeof(struct input_event); +} + +static unsigned int sensor_poll(struct file *f, struct poll_table_struct *wait) +{ + struct sensor_device *kdev = f->private_data; + + poll_wait(f, &kdev->waitq, wait); + if (kdev->num_event != 0) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int sensor_open(struct inode *inode, struct file *f) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + struct sensor_device *kdev; + + kdev = kzalloc(sizeof(struct sensor_device), GFP_KERNEL); + if (!kdev) + return -ENOMEM; + + mutex_init(&kdev->lock); + init_waitqueue_head(&kdev->waitq); + f->private_data = kdev; + kdev->head = 0; + kdev->num_event = 0; + list_add(&kdev->list, &data->devfile_list); + + return 0; +} + +static int sensor_release(struct inode *inode, struct file *f) +{ + struct sensor_device *kdev = f->private_data; + + list_del(&kdev->list); + kfree(kdev); + + return 0; +} + +static int sensor_raw_open(struct inode *inode, struct file *f) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + struct sensor_device *kdev; + + kdev = kzalloc(sizeof(struct sensor_device), GFP_KERNEL); + if (!kdev) + return -ENOMEM; + + mutex_init(&kdev->lock); + init_waitqueue_head(&kdev->waitq); + f->private_data = kdev; + kdev->head = 0; + kdev->num_event = 0; + list_add(&kdev->list, &data->raw_devfile_list); + + return 0; +} + +const struct file_operations sensor_fops = { + .owner = THIS_MODULE, + .open = sensor_open, + .release = sensor_release, + .write = sensor_write, + .read = sensor_read, + .poll = sensor_poll, +}; + +static struct miscdevice sensor_devfile = { + .name = SENSOR_NAME, + .fops = &sensor_fops, + .minor = MISC_DYNAMIC_MINOR, +}; + +const struct file_operations sensor_raw_fops = { + .owner = THIS_MODULE, + .open = sensor_raw_open, + .release = sensor_release, + .write = sensor_write, + .read = sensor_read, + .poll = sensor_poll, +}; + +static struct miscdevice sensor_raw_devfile = { + .name = SENSOR_RAW_NAME, + .fops = &sensor_raw_fops, + .minor = MISC_DYNAMIC_MINOR, +}; + +#endif + +static int geomagnetic_i2c_open(void) +{ + return 0; +} + +static int geomagnetic_i2c_close(void) +{ + return 0; +} + +#if YAS_MAG_DRIVER == YAS_MAG_DRIVER_YAS529 +static int geomagnetic_i2c_write(const uint8_t *buf, int len) +{ + if (i2c_master_send(this_client, buf, len) < 0) + return -1; +#if DEBUG + YLOGD(("[W] [%02x]\n", buf[0])); +#endif + + return 0; +} + +static int geomagnetic_i2c_read(uint8_t *buf, int len) +{ + if (i2c_master_recv(this_client, buf, len) < 0) + return -1; + return 0; +} + +#else + +static int geomagnetic_i2c_write(uint8_t addr, const uint8_t *buf, int len) +{ + uint8_t tmp[16]; + + if (sizeof(tmp) - 1 < len) + return -1; + + tmp[0] = addr; + memcpy(&tmp[1], buf, len); + + if (i2c_master_send(this_client, tmp, len + 1) < 0) + return -1; +#if DEBUG + YLOGD(("[W] addr[%02x] [%02x]\n", addr, buf[0])); +#endif + + return 0; +} + +static int geomagnetic_i2c_read(uint8_t addr, uint8_t *buf, int len) +{ + struct i2c_msg msg[2]; + int err; + + msg[0].addr = this_client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &addr; + msg[1].addr = this_client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = buf; + + err = i2c_transfer(this_client->adapter, msg, 2); + if (err != 2) { + dev_err(&this_client->dev, + "i2c_transfer() read error: slave_addr=%02x, reg_addr=%02x, err=%d\n", + this_client->addr, addr, err); + return err; + } + + return 0; +} + +#endif + +static int geomagnetic_lock(void) +{ + struct geomagnetic_data *data = NULL; + int rt; + + if (this_client == NULL) + return -1; + + data = i2c_get_clientdata(this_client); + rt = down_interruptible(&data->driver_lock); + if (rt < 0) + up(&data->driver_lock); + return rt; +} + +static int geomagnetic_unlock(void) +{ + struct geomagnetic_data *data = NULL; + + if (this_client == NULL) + return -1; + + data = i2c_get_clientdata(this_client); + up(&data->driver_lock); + return 0; +} + +static void geomagnetic_msleep(int ms) +{ + usleep_range(ms * 999, ms * 1000); +} + +static void geomagnetic_current_time(int32_t *sec, int32_t *msec) +{ + struct timeval tv; + + do_gettimeofday(&tv); + + *sec = tv.tv_sec; + *msec = tv.tv_usec / 1000; +} + +static struct yas_mag_driver hwdep_driver = { + .callback = { + .lock = geomagnetic_lock, + .unlock = geomagnetic_unlock, + .device_open = geomagnetic_i2c_open, + .device_close = geomagnetic_i2c_close, + .device_read = geomagnetic_i2c_read, + .device_write = geomagnetic_i2c_write, + .msleep = geomagnetic_msleep, + .current_time = geomagnetic_current_time, + }, +}; + +static int geomagnetic_multi_lock(void) +{ + struct geomagnetic_data *data = NULL; + int rt; + + if (this_client == NULL) + return -1; + + data = i2c_get_clientdata(this_client); + rt = down_interruptible(&data->multi_lock); + if (rt < 0) + up(&data->multi_lock); + return rt; +} + +static int geomagnetic_multi_unlock(void) +{ + struct geomagnetic_data *data = NULL; + + if (this_client == NULL) + return -1; + + data = i2c_get_clientdata(this_client); + up(&data->multi_lock); + return 0; +} + +static int geomagnetic_enable(struct geomagnetic_data *data) +{ + if (!atomic_cmpxchg(&data->enable, 0, 1)) + schedule_delayed_work(&data->work, 0); + + return 0; +} + +static int geomagnetic_disable(struct geomagnetic_data *data) +{ + if (atomic_cmpxchg(&data->enable, 1, 0)) + cancel_delayed_work_sync(&data->work); + + return 0; +} + +/* Sysfs interface */ +static ssize_t +geomagnetic_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int delay; + + geomagnetic_multi_lock(); + + delay = data->delay; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", delay); +} + +static ssize_t +geomagnetic_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + if (hwdep_driver.set_delay == NULL) + return -ENOTTY; + + geomagnetic_multi_lock(); + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + if (hwdep_driver.set_delay(value) == 0) + data->delay = value; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + + return sprintf(buf, "%d\n", atomic_read(&data->enable)); +} + +static ssize_t +geomagnetic_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + value = !(!value); + + if (hwdep_driver.set_enable == NULL) + return -ENOTTY; + + if (geomagnetic_multi_lock() < 0) + return count; + + if (hwdep_driver.set_enable(value) == 0) { + if (value) + geomagnetic_enable(data); + else + geomagnetic_disable(data); + } + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_filter_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int filter_enable; + + geomagnetic_multi_lock(); + + filter_enable = data->filter_enable; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", filter_enable); +} + +static ssize_t +geomagnetic_filter_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + unsigned long value; + int error; + + if (hwdep_driver.set_filter_enable == NULL) + return -ENOTTY; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + if (geomagnetic_multi_lock() < 0) + return count; + + if (hwdep_driver.set_filter_enable(value) == 0) + data->filter_enable = !!value; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_filter_len_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int filter_len; + + geomagnetic_multi_lock(); + + filter_len = data->filter_len; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", filter_len); +} + +static ssize_t +geomagnetic_filter_len_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + struct yas_mag_filter filter; + unsigned long value; + int error; + + if (hwdep_driver.get_filter == NULL || hwdep_driver.set_filter == NULL) + return -ENOTTY; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + if (geomagnetic_multi_lock() < 0) + return count; + + hwdep_driver.get_filter(&filter); + filter.len = value; + if (hwdep_driver.set_filter(&filter) == 0) + data->filter_len = value; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_filter_noise_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int rt; + + geomagnetic_multi_lock(); + + rt = sprintf(buf, "%d %d %d\n", + data->filter_noise[0], + data->filter_noise[1], data->filter_noise[2]); + + geomagnetic_multi_unlock(); + + return rt; +} + +static ssize_t +geomagnetic_filter_noise_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct yas_mag_filter filter; + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int32_t filter_noise[3]; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d", + &filter_noise[0], &filter_noise[1], &filter_noise[2]); + hwdep_driver.get_filter(&filter); + memcpy(filter.noise, filter_noise, sizeof(filter.noise)); + if (hwdep_driver.set_filter(&filter) == 0) + memcpy(data->filter_noise, filter_noise, + sizeof(data->filter_noise)); + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_filter_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int32_t filter_threshold; + + geomagnetic_multi_lock(); + + filter_threshold = data->filter_threshold; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", filter_threshold); +} + +static ssize_t +geomagnetic_filter_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + struct yas_mag_filter filter; + unsigned long value; + int error; + + if (hwdep_driver.get_filter == NULL || hwdep_driver.set_filter == NULL) + return -ENOTTY; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + if (geomagnetic_multi_lock() < 0) + return count; + + hwdep_driver.get_filter(&filter); + filter.threshold = value; + if (hwdep_driver.set_filter(&filter) == 0) + data->filter_threshold = value; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (hwdep_driver.get_position == NULL) + return -ENOTTY; + return sprintf(buf, "%d\n", hwdep_driver.get_position()); +} + +static ssize_t +geomagnetic_position_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + if (hwdep_driver.set_position == NULL) + return -ENOTTY; + hwdep_driver.set_position(value); + + return count; +} + +static ssize_t +geomagnetic_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int rt; + + rt = sprintf(buf, "%d %d %d\n", + atomic_read(&data->last_data[0]), + atomic_read(&data->last_data[1]), + atomic_read(&data->last_data[2])); + + return rt; +} + +static ssize_t +geomagnetic_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int rt; + + rt = sprintf(buf, "%d\n", atomic_read(&data->last_status)); + + return rt; +} + +static ssize_t +geomagnetic_status_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + static int16_t cnt = 1; +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev; +#endif + int accuracy = 0; + int code = 0; + int value = 0; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d", &accuracy); + if (0 <= accuracy && accuracy <= 3) + atomic_set(&data->last_status, accuracy); + code |= YAS_REPORT_CALIB_OFFSET_CHANGED; + value = (cnt++ << 16) | (code); + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + mkev(&ev, EV_ABS, ABS_RAW_REPORT, value); + sensor_event(&data->raw_devfile_list, &ev, 1); +#else + input_report_abs(data->input_raw, ABS_RAW_REPORT, value); + input_sync(data->input_raw); +#endif + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_wake_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + static int16_t cnt = 1; +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; + make_event(ev, EV_ABS, ABS_WAKE, cnt++); + sensor_event(&data->devfile_list, ev, 1); +#else + input_report_abs(data->input_data, ABS_WAKE, cnt++); + input_sync(data->input_data); +#endif + + return count; +} + +#if DEBUG + +static int geomagnetic_suspend(struct i2c_client *client, pm_message_t mesg); +static int geomagnetic_resume(struct i2c_client *client); + +static ssize_t +geomagnetic_debug_suspend_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input); + + return sprintf(buf, "%d\n", data->suspend); +} + +static ssize_t +geomagnetic_debug_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long suspend; + int error; + + error = strict_strtoul(buf, 10, &suspend); + if (unlikely(error)) + return error; + + if (suspend) { + pm_message_t msg; + memset(&msg, 0, sizeof(msg)); + geomagnetic_suspend(this_client, msg); + } else + geomagnetic_resume(this_client); + + return count; +} + +#endif /* DEBUG */ + +static DEVICE_ATTR(delay, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_delay_show, geomagnetic_delay_store); +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_enable_show, geomagnetic_enable_store); +static DEVICE_ATTR(filter_enable, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_filter_enable_show, + geomagnetic_filter_enable_store); +static DEVICE_ATTR(filter_len, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_filter_len_show, geomagnetic_filter_len_store); +static DEVICE_ATTR(filter_threshold, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_filter_threshold_show, + geomagnetic_filter_threshold_store); +static DEVICE_ATTR(filter_noise, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_filter_noise_show, + geomagnetic_filter_noise_store); +static DEVICE_ATTR(data, S_IRUGO, geomagnetic_data_show, NULL); +static DEVICE_ATTR(status, S_IRUGO|S_IWUSR|S_IWGRP, geomagnetic_status_show, + geomagnetic_status_store); +static DEVICE_ATTR(wake, S_IWUSR | S_IWGRP, NULL, geomagnetic_wake_store); +static DEVICE_ATTR(position, S_IRUGO | S_IWUSR, + geomagnetic_position_show, geomagnetic_position_store); +#if DEBUG +static DEVICE_ATTR(debug_suspend, S_IRUGO | S_IWUSR, + geomagnetic_debug_suspend_show, + geomagnetic_debug_suspend_store); +#endif /* DEBUG */ + +static struct attribute *geomagnetic_attributes[] = { + &dev_attr_delay.attr, + &dev_attr_enable.attr, + &dev_attr_filter_enable.attr, + &dev_attr_filter_len.attr, + &dev_attr_filter_threshold.attr, + &dev_attr_filter_noise.attr, + &dev_attr_data.attr, + &dev_attr_status.attr, + &dev_attr_wake.attr, + &dev_attr_position.attr, +#if DEBUG + &dev_attr_debug_suspend.attr, +#endif /* DEBUG */ + NULL +}; + +static struct attribute_group geomagnetic_attribute_group = { + .attrs = geomagnetic_attributes +}; + +static ssize_t +geomagnetic_raw_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int threshold; + + geomagnetic_multi_lock(); + + threshold = data->threshold; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", threshold); +} + +static ssize_t +geomagnetic_raw_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + geomagnetic_multi_lock(); + + if (0 <= value && value <= 2) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; + make_event(ev, EV_ABS, ABS_RAW_THRESHOLD, value); + sensor_event(&data->raw_devfile_list, ev, 1); +#endif + data->threshold = value; +#ifndef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + input_report_abs(data->input_raw, ABS_RAW_THRESHOLD, value); + input_sync(data->input_raw); +#endif + } + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_raw_distortion_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int rt; + + geomagnetic_multi_lock(); + + rt = sprintf(buf, "%d %d %d\n", + data->distortion[0], + data->distortion[1], data->distortion[2]); + + geomagnetic_multi_unlock(); + + return rt; +} + +static ssize_t +geomagnetic_raw_distortion_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int32_t distortion[3]; + static int32_t val = 1; + int i; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d", &distortion[0], &distortion[1], &distortion[2]); + if (distortion[0] > 0 && distortion[1] > 0 && distortion[2] > 0) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; + make_event(ev, EV_ABS, ABS_RAW_DISTORTION, val++); + sensor_event(&data->raw_devfile_list, ev, 1); +#endif + for (i = 0; i < 3; i++) + data->distortion[i] = distortion[i]; +#ifndef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + input_report_abs(data->input_raw, ABS_RAW_DISTORTION, val++); + input_sync(data->input_raw); +#endif + } + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_raw_shape_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int shape; + + geomagnetic_multi_lock(); + + shape = data->shape; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", shape); +} + +static ssize_t +geomagnetic_raw_shape_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + geomagnetic_multi_lock(); + + if (0 == value || value == 1) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; + make_event(ev, EV_ABS, ABS_RAW_SHAPE, value); + sensor_event(&data->raw_devfile_list, ev, 1); +#endif + data->shape = value; +#ifndef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + input_report_abs(data->input_raw, ABS_RAW_SHAPE, value); + input_sync(data->input_raw); +#endif + } + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_raw_offsets_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_mag_offset offset; + int accuracy; + + geomagnetic_multi_lock(); + + offset = data->driver_offset; + accuracy = atomic_read(&data->last_status); + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d %d %d %d %d %d %d\n", + offset.hard_offset[0], + offset.hard_offset[1], + offset.hard_offset[2], + offset.calib_offset.v[0], + offset.calib_offset.v[1], + offset.calib_offset.v[2], accuracy); +} + +static ssize_t +geomagnetic_raw_offsets_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_mag_offset offset; + int32_t hard_offset[3]; + int i, accuracy; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d %d %d %d %d", + &hard_offset[0], + &hard_offset[1], + &hard_offset[2], + &offset.calib_offset.v[0], + &offset.calib_offset.v[1], &offset.calib_offset.v[2], &accuracy); + if (0 <= accuracy && accuracy <= 3) { + for (i = 0; i < 3; i++) + offset.hard_offset[i] = (int8_t) hard_offset[i]; + + if (hwdep_driver.set_offset(&offset) == 0) { + atomic_set(&data->last_status, accuracy); + data->driver_offset = offset; + } + } + + geomagnetic_multi_unlock(); + + return count; +} + +#ifdef YAS_MAG_MANUAL_OFFSET +static ssize_t +geomagnetic_raw_manual_offsets_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_vector offset; + + geomagnetic_multi_lock(); + + offset = data->manual_offset; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d %d %d\n", offset.v[0], offset.v[1], + offset.v[2]); +} + +static ssize_t +geomagnetic_raw_manual_offsets_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_vector offset; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d", &offset.v[0], &offset.v[1], &offset.v[2]); + if (hwdep_driver.set_manual_offset(&offset) == 0) + data->manual_offset = offset; + + geomagnetic_multi_unlock(); + + return count; +} +#endif + +static ssize_t +geomagnetic_raw_static_matrix_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_matrix matrix; + + geomagnetic_multi_lock(); + + matrix = data->static_matrix; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d %d %d %d %d %d %d %d %d\n", + matrix.matrix[0], matrix.matrix[1], matrix.matrix[2], + matrix.matrix[3], matrix.matrix[4], matrix.matrix[5], + matrix.matrix[6], matrix.matrix[7], matrix.matrix[8]); +} + +static ssize_t +geomagnetic_raw_static_matrix_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_matrix matrix; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d %d %d %d %d %d %d", + &matrix.matrix[0], &matrix.matrix[1], &matrix.matrix[2], + &matrix.matrix[3], &matrix.matrix[4], &matrix.matrix[5], + &matrix.matrix[6], &matrix.matrix[7], &matrix.matrix[8]); + if (hwdep_driver.set_static_matrix(&matrix) == 0) + data->static_matrix = matrix; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_raw_dynamic_matrix_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_matrix matrix; + + geomagnetic_multi_lock(); + + matrix = data->dynamic_matrix; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d %d %d %d %d %d %d %d %d\n", + matrix.matrix[0], matrix.matrix[1], matrix.matrix[2], + matrix.matrix[3], matrix.matrix[4], matrix.matrix[5], + matrix.matrix[6], matrix.matrix[7], matrix.matrix[8]); +} + +static ssize_t +geomagnetic_raw_dynamic_matrix_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_matrix matrix; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d %d %d %d %d %d %d", + &matrix.matrix[0], &matrix.matrix[1], &matrix.matrix[2], + &matrix.matrix[3], &matrix.matrix[4], &matrix.matrix[5], + &matrix.matrix[6], &matrix.matrix[7], &matrix.matrix[8]); + if (hwdep_driver.set_dynamic_matrix(&matrix) == 0) + data->dynamic_matrix = matrix; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_raw_ellipsoid_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int ellipsoid_mode; + + geomagnetic_multi_lock(); + + ellipsoid_mode = data->ellipsoid_mode; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", ellipsoid_mode); +} + +static ssize_t +geomagnetic_raw_ellipsoid_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + value = !(!value); +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; +#endif + + geomagnetic_multi_lock(); + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + make_event(ev, EV_ABS, ABS_RAW_MODE, value); + sensor_event(&data->raw_devfile_list, ev, 1); +#endif + data->ellipsoid_mode = value; +#ifndef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + input_report_abs(data->input_raw, ABS_RAW_MODE, value); + input_sync(data->input_raw); +#endif + + geomagnetic_multi_unlock(); + + return count; +} + +#ifdef SYSFS_PCBTEST + +static int +pcbtest_i2c_write(uint8_t slave, uint8_t addr, const uint8_t *buf, int len) +{ + return geomagnetic_i2c_write(addr, buf, len); +} + +static int +pcbtest_i2c_read(uint8_t slave, uint8_t addr, uint8_t *buf, int len) +{ + return geomagnetic_i2c_read(addr, buf, len); +} + +static struct yas_pcb_test pcbtest = { + .callback = { + .power_on = NULL, + .power_off = NULL, + .i2c_open = geomagnetic_i2c_open, + .i2c_close = geomagnetic_i2c_close, + .i2c_read = pcbtest_i2c_read, + .i2c_write = pcbtest_i2c_write, + .msleep = geomagnetic_msleep, + .read_intpin = NULL, + }, +}; + + +static ssize_t +geomagnetic_raw_self_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + int id, x, y1, y2, dir, sx, sy, ohx, ohy, ohz; + int err1, err2, err3, err4, err5, err6, err7; + + if (data->noise_test_init) + pcbtest.power_off(); + err1 = pcbtest.power_on_and_device_check(&id); + err3 = pcbtest.initialization(); + err4 = + pcbtest.offset_control_measurement_and_set_offset_register( + &x, &y1, &y2); + err5 = pcbtest.direction_measurement(&dir); + err6 = + pcbtest.sensitivity_measurement_of_magnetic_sensor_by_test_coil( + &sx, &sy); + err7 = pcbtest.magnetic_field_level_check(&ohx, &ohy, &ohz); + err2 = pcbtest.power_off(); + data->noise_test_init = 0; + if (unlikely(id != 0x2)) + err1 = -1; + if (unlikely(x < -30 || x > 30)) + err4 = -1; + if (unlikely(y1 < -30 || y1 > 30)) + err4 = -1; + if (unlikely(y2 < -30 || y2 > 30)) + err4 = -1; + if (unlikely(sx < 17 || sy < 22)) + err6 = -1; + if (unlikely(ohx < -600 || ohx > 600)) + err7 = -1; + if (unlikely(ohy < -600 || ohy > 600)) + err7 = -1; + if (unlikely(ohz < -600 || ohz > 600)) + err7 = -1; + + pr_info("%s\n" + "Test1 - err = %d, id = %d\n" + "Test3 - err = %d\n" + "Test4 - err = %d, offset = %d,%d,%d\n" + "Test5 - err = %d, direction = %d\n" + "Test6 - err = %d, sensitivity = %d,%d\n" + "Test7 - err = %d, offset = %d,%d,%d\n" + "Test2 - err = %d\n", __func__, + err1, id, err3, err4, x, y1, y2, err5, dir, err6, sx, sy, + err7, ohx, ohy, ohz, err2); + + return sprintf(buf, + "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + err1, id, err3, err4, x, y1, y2, err5, dir, err6, sx, + sy, err7, ohx, ohy, ohz, err2); +} + +static ssize_t +geomagnetic_raw_self_test_noise_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + int id, x, y1, y2, dir, hx0, hy0, hz0; + int err8; +#if CONFIG_MACH_KONA_SENSOR + pcbtest.power_on_and_device_check(&id); + pcbtest.initialization(); + pcbtest.offset_control_measurement_and_set_offset_register( + &x, &y1, &y2); +#else + if (!data->noise_test_init) { + pcbtest.power_on_and_device_check(&id); + pcbtest.initialization(); + pcbtest.offset_control_measurement_and_set_offset_register( + &x, &y1, &y2); + } +#endif + pcbtest.direction_measurement(&dir); + err8 = pcbtest.noise_level_check(&hx0, &hy0, &hz0); + if (err8 < 0) { + pr_err("%s: err8=%d\n", __func__, err8); + hx0 = 0; + hy0 = 0; + hz0 = 0; + } + usleep_range(3000, 3100); + data->noise_test_init = 1; + pr_debug("%s: %d, %d, %d\n", __func__, hx0, hy0, hz0); + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", hx0, hy0, hz0); +} + +static DEVICE_ATTR(self_test, S_IRUSR, geomagnetic_raw_self_test_show, NULL); +static DEVICE_ATTR(self_test_noise, S_IRUSR, + geomagnetic_raw_self_test_noise_show, NULL); + +#endif + + +static DEVICE_ATTR(threshold, S_IRUGO | S_IWUSR, + geomagnetic_raw_threshold_show, + geomagnetic_raw_threshold_store); +static DEVICE_ATTR(distortion, S_IRUGO | S_IWUSR, + geomagnetic_raw_distortion_show, + geomagnetic_raw_distortion_store); +static DEVICE_ATTR(shape, S_IRUGO | S_IWUSR, geomagnetic_raw_shape_show, + geomagnetic_raw_shape_store); +static DEVICE_ATTR(offsets, S_IRUGO | S_IWUSR, geomagnetic_raw_offsets_show, + geomagnetic_raw_offsets_store); +#ifdef YAS_MAG_MANUAL_OFFSET +static DEVICE_ATTR(manual_offsets, S_IRUGO | S_IWUSR, + geomagnetic_raw_manual_offsets_show, + geomagnetic_raw_manual_offsets_store); +#endif +static DEVICE_ATTR(static_matrix, S_IRUGO | S_IWUSR, + geomagnetic_raw_static_matrix_show, + geomagnetic_raw_static_matrix_store); +static DEVICE_ATTR(dynamic_matrix, S_IRUGO | S_IWUSR, + geomagnetic_raw_dynamic_matrix_show, + geomagnetic_raw_dynamic_matrix_store); +static DEVICE_ATTR(ellipsoid_mode, S_IRUGO | S_IWUSR, + geomagnetic_raw_ellipsoid_mode_show, + geomagnetic_raw_ellipsoid_mode_store); +static struct attribute *geomagnetic_raw_attributes[] = { +#ifdef SYSFS_PCBTEST + &dev_attr_self_test.attr, + &dev_attr_self_test_noise.attr, +#endif + &dev_attr_threshold.attr, + &dev_attr_distortion.attr, + &dev_attr_shape.attr, + &dev_attr_offsets.attr, +#ifdef YAS_MAG_MANUAL_OFFSET + &dev_attr_manual_offsets.attr, +#endif + &dev_attr_static_matrix.attr, + &dev_attr_dynamic_matrix.attr, + &dev_attr_ellipsoid_mode.attr, + NULL +}; + +static struct attribute_group geomagnetic_raw_attribute_group = { + .attrs = geomagnetic_raw_attributes +}; +static struct device_attribute dev_attr_magnetic_sensor_selftest = + __ATTR(selftest, S_IRUSR | S_IRGRP, + geomagnetic_raw_self_test_show, NULL); + +static struct device_attribute dev_attr_magnetic_sensor_raw_data = + __ATTR(raw_data, S_IRUSR | S_IRGRP, + geomagnetic_raw_self_test_noise_show, NULL); + +static ssize_t magnetic_vendor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", "YAMAHA"); +} +static struct device_attribute dev_attr_magnetic_sensor_vendor = + __ATTR(vendor, S_IRUSR | S_IRGRP, magnetic_vendor_show, NULL); + +static ssize_t magnetic_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + int ret; + if (data->dev_id == YAS_YAS532_DEVICE_ID) + ret = sprintf(buf, "%s\n", "YAS532"); + else + ret = sprintf(buf, "%s\n", "YAS530C"); + return ret; +} +static struct device_attribute dev_attr_magnetic_sensor_name = + __ATTR(name, S_IRUSR | S_IRGRP, magnetic_name_show, NULL); + +static struct device_attribute *magnetic_sensor_attrs[] = { + &dev_attr_magnetic_sensor_selftest, + &dev_attr_magnetic_sensor_raw_data, + &dev_attr_magnetic_sensor_vendor, + &dev_attr_magnetic_sensor_name, + NULL, +}; + + +/* Interface Functions for Lower Layer */ +#ifdef YAS_MAG_MANUAL_OFFSET +void geomagnetic_manual_offset(void) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + struct yas_vector offset; + if (data->mag_pdata->chg_status == CABLE_TYPE_NONE) { + offset = data->mag_pdata->full_offset; + if (hwdep_driver.set_manual_offset(&offset) == 0) + data->manual_offset = offset; + } else if (data->mag_pdata->chg_status == CABLE_TYPE_USB) { + offset = data->mag_pdata->usb_offset; + if (hwdep_driver.set_manual_offset(&offset) == 0) + data->manual_offset = offset; + } else if (data->mag_pdata->chg_status == CABLE_TYPE_AC) { + offset = data->mag_pdata->ta_offset; + if (hwdep_driver.set_manual_offset(&offset) == 0) + data->manual_offset = offset; + } else { + offset = data->mag_pdata->full_offset; + if (hwdep_driver.set_manual_offset(&offset) == 0) + data->manual_offset = offset; + } + data->mag_pdata->offset_enable = 0; +} +#endif + +static int geomagnetic_work(struct yas_mag_data *magdata) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + uint32_t time_delay_ms = 100; + static int cnt; + int rt, i, accuracy; +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[5]; + struct timeval tv; +#endif + +#ifdef YAS_MAG_MANUAL_OFFSET + if (data->mag_pdata) { + if (data->mag_pdata->offset_enable) + geomagnetic_manual_offset(); + } +#endif + + if (hwdep_driver.measure == NULL || hwdep_driver.get_offset == NULL) + return time_delay_ms; + + rt = hwdep_driver.measure(magdata, &time_delay_ms); + if (rt < 0) + YLOGE(("measure failed[%d]\n", rt)); + YLOGD(("xy1y2 [%d][%d][%d] raw[%d][%d][%d]\n", + magdata->xy1y2.v[0], magdata->xy1y2.v[1], magdata->xy1y2.v[2], + magdata->xyz.v[0], magdata->xyz.v[1], magdata->xyz.v[2])); + + if (rt >= 0) { + accuracy = atomic_read(&data->last_status); + + if ((rt & YAS_REPORT_OVERFLOW_OCCURED) + || (rt & YAS_REPORT_HARD_OFFSET_CHANGED) + || (rt & YAS_REPORT_CALIB_OFFSET_CHANGED)) { + static uint16_t count = 1; + int code = 0; + int value = 0; + + hwdep_driver.get_offset(&data->driver_offset); + if (rt & YAS_REPORT_OVERFLOW_OCCURED) { + atomic_set(&data->last_status, 0); + accuracy = 0; + } + + /* report event */ + code |= (rt & YAS_REPORT_OVERFLOW_OCCURED); + code |= (rt & YAS_REPORT_HARD_OFFSET_CHANGED); + code |= (rt & YAS_REPORT_CALIB_OFFSET_CHANGED); + value = (count++ << 16) | (code); +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + make_event(ev, EV_ABS, ABS_RAW_REPORT, value); + sensor_event(&data->raw_devfile_list, ev, 1); +#else + input_report_abs(data->input_raw, ABS_RAW_REPORT, + value); + input_sync(data->input_raw); +#endif + } + + if (rt & YAS_REPORT_DATA) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + get_time_stamp(&tv); + make_event_w_time(&ev[0], EV_ABS, ABS_X, + magdata->xyz.v[0], &tv); + make_event_w_time(&ev[1], EV_ABS, ABS_Y, + magdata->xyz.v[1], &tv); + make_event_w_time(&ev[2], EV_ABS, ABS_Z, + magdata->xyz.v[2], &tv); + make_event_w_time(&ev[3], EV_ABS, ABS_STATUS, accuracy, + &tv); + make_event_w_time(&ev[4], EV_SYN, 0, 0, &tv); + sensor_event(&data->devfile_list, ev, 5); +#else + /* report magnetic data in [nT] */ + input_report_abs(data->input_data, ABS_X, + magdata->xyz.v[0]); + input_report_abs(data->input_data, ABS_Y, + magdata->xyz.v[1]); + input_report_abs(data->input_data, ABS_Z, + magdata->xyz.v[2]); + if (atomic_read(&data->last_data[0]) == + magdata->xyz.v[0] + && atomic_read(&data->last_data[1]) == + magdata->xyz.v[1] + && atomic_read(&data->last_data[2]) == + magdata->xyz.v[2]) { + input_report_abs(data->input_data, ABS_RUDDER, + cnt++); + } + input_report_abs(data->input_data, ABS_STATUS, + accuracy); + input_sync(data->input_data); +#endif + + for (i = 0; i < 3; i++) + atomic_set(&data->last_data[i], + magdata->xyz.v[i]); + } + + if (rt & YAS_REPORT_CALIB) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + get_time_stamp(&tv); + make_event_w_time(&ev[0], EV_ABS, ABS_X, + magdata->raw.v[0], &tv); + make_event_w_time(&ev[1], EV_ABS, ABS_Y, + magdata->raw.v[1], &tv); + make_event_w_time(&ev[2], EV_ABS, ABS_Z, + magdata->raw.v[2], &tv); + make_event_w_time(&ev[3], EV_SYN, 0, 0, &tv); + sensor_event(&data->raw_devfile_list, ev, 4); +#else + /* report raw magnetic data */ + input_report_abs(data->input_raw, ABS_X, + magdata->raw.v[0]); + input_report_abs(data->input_raw, ABS_Y, + magdata->raw.v[1]); + input_report_abs(data->input_raw, ABS_Z, + magdata->raw.v[2]); + input_sync(data->input_raw); +#endif + } + } else { + time_delay_ms = 100; + } + + return time_delay_ms; + +} + +static void geomagnetic_input_work_func(struct work_struct *work) +{ + struct geomagnetic_data *data = + container_of((struct delayed_work *)work, + struct geomagnetic_data, work); + uint32_t time_delay_ms; + struct yas_mag_data magdata; + + time_delay_ms = geomagnetic_work(&magdata); + + if (time_delay_ms > 60) + schedule_delayed_work(&data->work, + msecs_to_jiffies(time_delay_ms)); + else { + if (time_delay_ms > 0) + usleep_range(time_delay_ms * 1000, + time_delay_ms * 1100); + schedule_delayed_work(&data->work, 0); + } +} + +static int geomagnetic_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct geomagnetic_data *data = i2c_get_clientdata(client); + + if (atomic_read(&data->enable)) + cancel_delayed_work_sync(&data->work); +#if DEBUG + data->suspend = 1; +#endif + + return 0; +} + +static int geomagnetic_resume(struct i2c_client *client) +{ + struct geomagnetic_data *data = i2c_get_clientdata(client); + + if (atomic_read(&data->enable)) + schedule_delayed_work(&data->work, 0); +#if DEBUG + data->suspend = 0; +#endif + + return 0; +} + +static int +geomagnetic_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct geomagnetic_data *data = NULL; + struct input_dev *input_data = NULL, *input_raw = NULL; + int rt, sysfs_created = 0, sysfs_raw_created = 0; + int data_registered = 0, raw_registered = 0, i; + struct yas_mag_filter filter; + struct mag_platform_data *pdata; + + pr_err("%s, is called\n", __func__); + + i2c_set_clientdata(client, NULL); + data = kzalloc(sizeof(struct geomagnetic_data), GFP_KERNEL); + if (data == NULL) { + rt = -ENOMEM; + goto err; + } + + pdata = (struct mag_platform_data *) client->dev.platform_data; + data->mag_pdata = pdata; + + if (pdata) { + if (pdata->power_on) + pdata->power_on(true); + } + data->threshold = YAS_DEFAULT_MAGCALIB_THRESHOLD; + for (i = 0; i < 3; i++) + data->distortion[i] = YAS_DEFAULT_MAGCALIB_DISTORTION; + data->shape = 0; + atomic_set(&data->enable, 0); + for (i = 0; i < 3; i++) + atomic_set(&data->last_data[i], 0); + atomic_set(&data->last_status, 0); + INIT_DELAYED_WORK(&data->work, geomagnetic_input_work_func); + sema_init(&data->driver_lock, 1); + sema_init(&data->multi_lock, 1); + + input_data = input_allocate_device(); + if (input_data == NULL) { + rt = -ENOMEM; + YLOGE(("mag Failed to allocate input_data device\n")); + goto err; + } + + input_data->name = GEOMAGNETIC_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + set_bit(EV_ABS, input_data->evbit); + input_set_abs_params(input_data, ABS_X, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_data, ABS_Y, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_data, ABS_Z, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_data, ABS_RUDDER, 0x80000000, 0x7fffffff, 0, + 0); + input_set_abs_params(input_data, ABS_STATUS, 0, 3, 0, 0); + input_set_abs_params(input_data, ABS_WAKE, 0x80000000, 0x7fffffff, 0, + 0); + input_data->dev.parent = &client->dev; + + rt = input_register_device(input_data); + if (rt) { + YLOGE(("mag: Unable to reg input_data %s\n", input_data->name)); + goto err; + } + data_registered = 1; + + rt = sysfs_create_group(&input_data->dev.kobj, + &geomagnetic_attribute_group); + if (rt) { + YLOGE(("mag sysfs_create failed[%s]\n", input_data->name)); + goto err; + } + sysfs_created = 1; + + input_raw = input_allocate_device(); + if (input_raw == NULL) { + rt = -ENOMEM; + YLOGE(("mag Failed to alloc input_raw dev\n")); + goto err; + } + + input_raw->name = GEOMAGNETIC_INPUT_RAW_NAME; + input_raw->id.bustype = BUS_I2C; + set_bit(EV_ABS, input_raw->evbit); + input_set_abs_params(input_raw, ABS_X, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_raw, ABS_Y, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_raw, ABS_Z, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_raw, ABS_RAW_DISTORTION, 0, 0x7fffffff, 0, + 0); + input_set_abs_params(input_raw, ABS_RAW_THRESHOLD, 0, 2, 0, 0); + input_set_abs_params(input_raw, ABS_RAW_SHAPE, 0, 1, 0, 0); + input_set_abs_params(input_raw, ABS_RAW_MODE, 0, 1, 0, 0); + input_set_abs_params(input_raw, ABS_RAW_REPORT, 0x80000000, 0x7fffffff, + 0, 0); + input_raw->dev.parent = &client->dev; + + rt = input_register_device(input_raw); + if (rt) { + YLOGE(("mag: Unable to reg input_raw dev\n")); + goto err; + } + raw_registered = 1; + + rt = sysfs_create_group(&input_raw->dev.kobj, + &geomagnetic_raw_attribute_group); + if (rt) { + YLOGE(("geomagnetic_probe: sysfs_create_group failed[%s]\n", + input_data->name)); + goto err; + } + sysfs_raw_created = 1; + + this_client = client; + data->input_raw = input_raw; + data->input_data = input_data; + input_set_drvdata(input_data, data); + input_set_drvdata(input_raw, data); + i2c_set_clientdata(client, data); + rt = yas_mag_driver_init(&hwdep_driver); + if (rt < 0) { + YLOGE(("yas_mag_driver_init failed[%d]\n", rt)); + goto err; + } + if (hwdep_driver.init != NULL) { + rt = hwdep_driver.init(); + if (rt < 0) { + YLOGE(("hwdep_driver.init() failed[%d]\n", rt)); + goto err; + } + } + if (hwdep_driver.set_position != NULL) { + if (pdata) { + if (pdata->orientation) { + pr_info("%s: set from board file.\n", __func__); + if (hwdep_driver. + set_position(pdata->orientation + - YAS532_POSITION_OFFSET) < 0) { + pr_err("set_position failed %d\n", rt); + goto err; + } + } else { + pr_info("%s: set from defconfig.\n", __func__); + if (hwdep_driver. + set_position( + CONFIG_INPUT_YAS_MAGNETOMETER_POSITION) + < 0) { + pr_err("set_position failed %d\n", rt); + goto err; + } + } + } else { + if (hwdep_driver. + set_position( + CONFIG_INPUT_YAS_MAGNETOMETER_POSITION) + < 0) { + pr_err("set_position() failed[%d]\n", rt); + goto err; + } + } + pr_info("%s: yas magnetic position is %d\n", __func__, + hwdep_driver.get_position()); + } + if (hwdep_driver.get_offset != NULL) { + if (hwdep_driver.get_offset(&data->driver_offset) < 0) { + YLOGE(("hwdep_driver get_driver_state failed\n")); + goto err; + } + } + if (hwdep_driver.get_delay != NULL) + data->delay = hwdep_driver.get_delay(); + + if (hwdep_driver.set_filter_enable != NULL) { + /* default to enable */ + if (hwdep_driver.set_filter_enable(1) == 0) + data->filter_enable = 1; + } + if (hwdep_driver.get_filter != NULL) { + if (hwdep_driver.get_filter(&filter) < 0) { + YLOGE(("hwdep_driver get_filter failed\n")); + goto err; + } + data->filter_len = filter.len; + for (i = 0; i < 3; i++) + data->filter_noise[i] = filter.noise[i]; + data->filter_threshold = filter.threshold; + } + if (hwdep_driver.get_static_matrix != NULL) + hwdep_driver.get_static_matrix(&data->static_matrix); + if (hwdep_driver.get_dynamic_matrix != NULL) + hwdep_driver.get_dynamic_matrix(&data->dynamic_matrix); +#ifdef SYSFS_PCBTEST + rt = yas_pcb_test_init(&pcbtest); + if (rt < 0) { + YLOGE(("yas_pcb_test_init failed[%d]\n", rt)); + goto err; + } +#endif +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + INIT_LIST_HEAD(&data->devfile_list); + INIT_LIST_HEAD(&data->raw_devfile_list); + if (misc_register(&sensor_devfile) < 0) + goto err; + if (misc_register(&sensor_raw_devfile) < 0) { + misc_deregister(&sensor_devfile); + goto err; + } +#endif + + rt = + geomagnetic_i2c_read(YAS_REGADDR_DEVICE_ID, &data->dev_id, 1); + if (rt) { + pr_err("%s: cound not read device id(%d).\n", + __func__, rt); + goto err; + } else + pr_info("%s: yamaha magnetic sensor id = %x\n", + __func__, data->dev_id); + + rt = sensors_register(data->magnetic_sensor_device, + NULL, magnetic_sensor_attrs, + "magnetic_sensor"); + if (rt) { + pr_err("%s: cound not register magnetic sensor device(%d).\n", + __func__, rt); + goto out_sensor_register_failed; + } + + return 0; + +out_sensor_register_failed: +err: + if (data != NULL) { + if (input_raw != NULL) { + if (sysfs_raw_created) + sysfs_remove_group(&input_raw->dev.kobj, + &geomagnetic_raw_attribute_group); + + if (raw_registered) + input_unregister_device(input_raw); + else + input_free_device(input_raw); + } + if (input_data != NULL) { + if (sysfs_created) + sysfs_remove_group(&input_data->dev.kobj, + &geomagnetic_attribute_group); + if (data_registered) + input_unregister_device(input_data); + else + input_free_device(input_data); + } + if (pdata) { + if (pdata->power_on) + pdata->power_on(false); + } + kfree(data); + } + + return rt; +} + +static int geomagnetic_remove(struct i2c_client *client) +{ + struct geomagnetic_data *data = i2c_get_clientdata(client); + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + misc_deregister(&sensor_devfile); + misc_deregister(&sensor_raw_devfile); +#endif + if (data != NULL) { + geomagnetic_disable(data); + if (hwdep_driver.term != NULL) + hwdep_driver.term(); + + input_unregister_device(data->input_raw); + sysfs_remove_group(&data->input_data->dev.kobj, + &geomagnetic_attribute_group); + sysfs_remove_group(&data->input_raw->dev.kobj, + &geomagnetic_raw_attribute_group); + input_unregister_device(data->input_data); + kfree(data); + } + + return 0; +} + +static void geomagnetic_shutdown(struct i2c_client *client) +{ + struct geomagnetic_data *data = i2c_get_clientdata(client); + + if (data != NULL) { + geomagnetic_disable(data); + if (data->mag_pdata) { + if (data->mag_pdata->power_on) + data->mag_pdata->power_on(false); + } + } +} + +/* I2C Device Driver */ +static struct i2c_device_id geomagnetic_idtable[] = { + {GEOMAGNETIC_I2C_DEVICE_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, geomagnetic_idtable); + +static struct i2c_driver geomagnetic_i2c_driver = { + .driver = { + .name = GEOMAGNETIC_I2C_DEVICE_NAME, + .owner = THIS_MODULE, + }, + + .id_table = geomagnetic_idtable, + .probe = geomagnetic_probe, + .remove = geomagnetic_remove, + .suspend = geomagnetic_suspend, + .resume = geomagnetic_resume, + .shutdown = geomagnetic_shutdown, +}; + +static int __init geomagnetic_init(void) +{ + return i2c_add_driver(&geomagnetic_i2c_driver); +} + +static void __exit geomagnetic_term(void) +{ + i2c_del_driver(&geomagnetic_i2c_driver); +} + +#ifdef GEOMAGNETIC_PLATFORM_API +static int geomagnetic_api_enable(int enable) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + int rt; + + if (geomagnetic_multi_lock() < 0) + return -1; + enable = !!enable; + rt = hwdep_driver.set_enable(enable); + if (rt == 0) { + atomic_set(&data->enable, enable); + if (enable) + rt = hwdep_driver.set_delay(20); + } + + geomagnetic_multi_unlock(); + + return rt; +} + +int geomagnetic_api_resume(void) +{ + return geomagnetic_api_enable(1); +} +EXPORT_SYMBOL(geomagnetic_api_resume); + +int geomagnetic_api_suspend(void) +{ + return geomagnetic_api_enable(0); +} +EXPORT_SYMBOL(geomagnetic_api_suspend); + +int geomagnetic_api_read(int *xyz, int *raw, int *xy1y2, int *accuracy) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + struct yas_mag_data magdata; + int i; + + geomagnetic_work(&magdata); + if (xyz != NULL) { + for (i = 0; i < 3; i++) + xyz[i] = magdata.xyz.v[i]; + } + if (raw != NULL) { + for (i = 0; i < 3; i++) + raw[i] = magdata.raw.v[i]; + } + if (xy1y2 != NULL) { + for (i = 0; i < 3; i++) + xy1y2[i] = magdata.xy1y2.v[i]; + } + if (accuracy != NULL) + *accuracy = atomic_read(&data->last_status); + + return 0; +} +EXPORT_SYMBOL(geomagnetic_api_read); +#endif + +module_init(geomagnetic_init); +module_exit(geomagnetic_term); + +MODULE_AUTHOR("Yamaha Corporation"); +#if YAS_MAG_DRIVER == YAS_MAG_DRIVER_YAS529 +MODULE_DESCRIPTION("YAS529 Geomagnetic Sensor Driver"); +#elif YAS_MAG_DRIVER == YAS_MAG_DRIVER_YAS530 +MODULE_DESCRIPTION("YAS530 Geomagnetic Sensor Driver"); +#elif CONFIG_SENSORS_YAS532 +MODULE_DESCRIPTION("YAS532 Geomagnetic Sensor Driver"); +#endif +MODULE_LICENSE("GPL"); +MODULE_VERSION("4.4.702a"); |