|  | /* | 
|  | *  mma8451.c - Linux kernel modules for 3-Axis Orientation/Motion | 
|  | *  Detection Sensor | 
|  | * | 
|  | *  Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reserved. | 
|  | * | 
|  | *  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., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | */ | 
|  |  | 
|  | #include <linux/module.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/i2c.h> | 
|  | #include <linux/pm.h> | 
|  | #include <linux/mutex.h> | 
|  | #include <linux/delay.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/irq.h> | 
|  | #include <linux/hwmon-sysfs.h> | 
|  | #include <linux/err.h> | 
|  | #include <linux/hwmon.h> | 
|  | #include <linux/input-polldev.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/regulator/consumer.h> | 
|  |  | 
|  | #define MMA8451_I2C_ADDR	0x1C | 
|  | #define MMA8451_ID		0x1A | 
|  | #define MMA8452_ID		0x2A | 
|  | #define MMA8453_ID		0x3A | 
|  |  | 
|  | #define POLL_INTERVAL_MIN	1 | 
|  | #define POLL_INTERVAL_MAX	500 | 
|  | #define POLL_INTERVAL		100	/* msecs */ | 
|  | #define INPUT_FUZZ		32 | 
|  | #define INPUT_FLAT		32 | 
|  | #define MODE_CHANGE_DELAY_MS	100 | 
|  |  | 
|  | #define MMA8451_STATUS_ZYXDR	0x08 | 
|  | #define MMA8451_BUF_SIZE	7 | 
|  | #define DEFAULT_POSITION	0 | 
|  |  | 
|  | /* register enum for mma8451 registers */ | 
|  | enum { | 
|  | MMA8451_STATUS = 0x00, | 
|  | MMA8451_OUT_X_MSB, | 
|  | MMA8451_OUT_X_LSB, | 
|  | MMA8451_OUT_Y_MSB, | 
|  | MMA8451_OUT_Y_LSB, | 
|  | MMA8451_OUT_Z_MSB, | 
|  | MMA8451_OUT_Z_LSB, | 
|  |  | 
|  | MMA8451_F_SETUP = 0x09, | 
|  | MMA8451_TRIG_CFG, | 
|  | MMA8451_SYSMOD, | 
|  | MMA8451_INT_SOURCE, | 
|  | MMA8451_WHO_AM_I, | 
|  | MMA8451_XYZ_DATA_CFG, | 
|  | MMA8451_HP_FILTER_CUTOFF, | 
|  |  | 
|  | MMA8451_PL_STATUS, | 
|  | MMA8451_PL_CFG, | 
|  | MMA8451_PL_COUNT, | 
|  | MMA8451_PL_BF_ZCOMP, | 
|  | MMA8451_P_L_THS_REG, | 
|  |  | 
|  | MMA8451_FF_MT_CFG, | 
|  | MMA8451_FF_MT_SRC, | 
|  | MMA8451_FF_MT_THS, | 
|  | MMA8451_FF_MT_COUNT, | 
|  |  | 
|  | MMA8451_TRANSIENT_CFG = 0x1D, | 
|  | MMA8451_TRANSIENT_SRC, | 
|  | MMA8451_TRANSIENT_THS, | 
|  | MMA8451_TRANSIENT_COUNT, | 
|  |  | 
|  | MMA8451_PULSE_CFG, | 
|  | MMA8451_PULSE_SRC, | 
|  | MMA8451_PULSE_THSX, | 
|  | MMA8451_PULSE_THSY, | 
|  | MMA8451_PULSE_THSZ, | 
|  | MMA8451_PULSE_TMLT, | 
|  | MMA8451_PULSE_LTCY, | 
|  | MMA8451_PULSE_WIND, | 
|  |  | 
|  | MMA8451_ASLP_COUNT, | 
|  | MMA8451_CTRL_REG1, | 
|  | MMA8451_CTRL_REG2, | 
|  | MMA8451_CTRL_REG3, | 
|  | MMA8451_CTRL_REG4, | 
|  | MMA8451_CTRL_REG5, | 
|  |  | 
|  | MMA8451_OFF_X, | 
|  | MMA8451_OFF_Y, | 
|  | MMA8451_OFF_Z, | 
|  |  | 
|  | MMA8451_REG_END, | 
|  | }; | 
|  |  | 
|  | /* The sensitivity is represented in counts/g. In 2g mode the | 
|  | sensitivity is 1024 counts/g. In 4g mode the sensitivity is 512 | 
|  | counts/g and in 8g mode the sensitivity is 256 counts/g. | 
|  | */ | 
|  | enum { | 
|  | MODE_2G = 0, | 
|  | MODE_4G, | 
|  | MODE_8G, | 
|  | }; | 
|  |  | 
|  | enum { | 
|  | MMA_STANDBY = 0, | 
|  | MMA_ACTIVED, | 
|  | }; | 
|  |  | 
|  | /* mma8451 status */ | 
|  | struct mma8451_status { | 
|  | u8 mode; | 
|  | u8 ctl_reg1; | 
|  | int active; | 
|  | int position; | 
|  | }; | 
|  |  | 
|  | static struct mma8451_status mma_status; | 
|  | static struct input_polled_dev *mma8451_idev; | 
|  | static struct device *hwmon_dev; | 
|  | static struct i2c_client *mma8451_i2c_client; | 
|  |  | 
|  | static int senstive_mode = MODE_2G; | 
|  | static int ACCHAL[8][3][3] = { | 
|  | { {0, -1, 0}, {1, 0, 0}, {0, 0, 1} }, | 
|  | { {-1, 0, 0}, {0, -1, 0}, {0, 0, 1} }, | 
|  | { {0, 1, 0}, {-1, 0, 0}, {0, 0, 1} }, | 
|  | { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} }, | 
|  |  | 
|  | { {0, -1, 0}, {-1, 0, 0}, {0, 0, -1} }, | 
|  | { {-1, 0, 0}, {0, 1, 0}, {0, 0, -1} }, | 
|  | { {0, 1, 0}, {1, 0, 0}, {0, 0, -1} }, | 
|  | { {1, 0, 0}, {0, -1, 0}, {0, 0, -1} }, | 
|  | }; | 
|  |  | 
|  | static DEFINE_MUTEX(mma8451_lock); | 
|  | static int mma8451_adjust_position(short *x, short *y, short *z) | 
|  | { | 
|  | short rawdata[3], data[3]; | 
|  | int i, j; | 
|  | int position = mma_status.position; | 
|  | if (position < 0 || position > 7) | 
|  | position = 0; | 
|  | rawdata[0] = *x; | 
|  | rawdata[1] = *y; | 
|  | rawdata[2] = *z; | 
|  | for (i = 0; i < 3; i++) { | 
|  | data[i] = 0; | 
|  | for (j = 0; j < 3; j++) | 
|  | data[i] += rawdata[j] * ACCHAL[position][i][j]; | 
|  | } | 
|  | *x = data[0]; | 
|  | *y = data[1]; | 
|  | *z = data[2]; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int mma8451_change_mode(struct i2c_client *client, int mode) | 
|  | { | 
|  | int result; | 
|  |  | 
|  | mma_status.ctl_reg1 = 0; | 
|  | result = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, 0); | 
|  | if (result < 0) | 
|  | goto out; | 
|  | mma_status.active = MMA_STANDBY; | 
|  |  | 
|  | result = i2c_smbus_write_byte_data(client, MMA8451_XYZ_DATA_CFG, | 
|  | mode); | 
|  | if (result < 0) | 
|  | goto out; | 
|  | mdelay(MODE_CHANGE_DELAY_MS); | 
|  | mma_status.mode = mode; | 
|  |  | 
|  | return 0; | 
|  | out: | 
|  | dev_err(&client->dev, "error when init mma8451:(%d)", result); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static int mma8451_read_data(short *x, short *y, short *z) | 
|  | { | 
|  | u8 tmp_data[MMA8451_BUF_SIZE]; | 
|  | int ret; | 
|  |  | 
|  | ret = i2c_smbus_read_i2c_block_data(mma8451_i2c_client, | 
|  | MMA8451_OUT_X_MSB, 7, tmp_data); | 
|  | if (ret < MMA8451_BUF_SIZE) { | 
|  | dev_err(&mma8451_i2c_client->dev, "i2c block read failed\n"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | *x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1]; | 
|  | *y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3]; | 
|  | *z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5]; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void report_abs(void) | 
|  | { | 
|  | short x, y, z; | 
|  | int result; | 
|  | int retry = 3; | 
|  |  | 
|  | mutex_lock(&mma8451_lock); | 
|  | if (mma_status.active == MMA_STANDBY) | 
|  | goto out; | 
|  | /* wait for the data ready */ | 
|  | do { | 
|  | result = i2c_smbus_read_byte_data(mma8451_i2c_client, | 
|  | MMA8451_STATUS); | 
|  | retry--; | 
|  | msleep(1); | 
|  | } while (!(result & MMA8451_STATUS_ZYXDR) && retry > 0); | 
|  | if (retry == 0) | 
|  | goto out; | 
|  | if (mma8451_read_data(&x, &y, &z) != 0) | 
|  | goto out; | 
|  | mma8451_adjust_position(&x, &y, &z); | 
|  | input_report_abs(mma8451_idev->input, ABS_X, x); | 
|  | input_report_abs(mma8451_idev->input, ABS_Y, y); | 
|  | input_report_abs(mma8451_idev->input, ABS_Z, z); | 
|  | input_sync(mma8451_idev->input); | 
|  | out: | 
|  | mutex_unlock(&mma8451_lock); | 
|  | } | 
|  |  | 
|  | static void mma8451_dev_poll(struct input_polled_dev *dev) | 
|  | { | 
|  | report_abs(); | 
|  | } | 
|  |  | 
|  | static ssize_t mma8451_enable_show(struct device *dev, | 
|  | struct device_attribute *attr, char *buf) | 
|  | { | 
|  | struct i2c_client *client; | 
|  | u8 val; | 
|  | int enable; | 
|  |  | 
|  | mutex_lock(&mma8451_lock); | 
|  | client = mma8451_i2c_client; | 
|  | val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); | 
|  | if ((val & 0x01) && mma_status.active == MMA_ACTIVED) | 
|  | enable = 1; | 
|  | else | 
|  | enable = 0; | 
|  | mutex_unlock(&mma8451_lock); | 
|  | return sprintf(buf, "%d\n", enable); | 
|  | } | 
|  |  | 
|  | static ssize_t mma8451_enable_store(struct device *dev, | 
|  | struct device_attribute *attr, | 
|  | const char *buf, size_t count) | 
|  | { | 
|  | struct i2c_client *client; | 
|  | int ret; | 
|  | unsigned long enable; | 
|  | u8 val = 0; | 
|  |  | 
|  | ret = strict_strtoul(buf, 10, &enable); | 
|  | if (ret) { | 
|  | dev_err(dev, "string transform error\n"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | mutex_lock(&mma8451_lock); | 
|  | client = mma8451_i2c_client; | 
|  | enable = (enable > 0) ? 1 : 0; | 
|  | if (enable && mma_status.active == MMA_STANDBY) { | 
|  | val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); | 
|  | ret = | 
|  | i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, | 
|  | val | 0x01); | 
|  | if (!ret) | 
|  | mma_status.active = MMA_ACTIVED; | 
|  |  | 
|  | } else if (enable == 0 && mma_status.active == MMA_ACTIVED) { | 
|  | val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); | 
|  | ret = | 
|  | i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, | 
|  | val & 0xFE); | 
|  | if (!ret) | 
|  | mma_status.active = MMA_STANDBY; | 
|  |  | 
|  | } | 
|  | mutex_unlock(&mma8451_lock); | 
|  | return count; | 
|  | } | 
|  |  | 
|  | static ssize_t mma8451_position_show(struct device *dev, | 
|  | struct device_attribute *attr, char *buf) | 
|  | { | 
|  | int position = 0; | 
|  | mutex_lock(&mma8451_lock); | 
|  | position = mma_status.position; | 
|  | mutex_unlock(&mma8451_lock); | 
|  | return sprintf(buf, "%d\n", position); | 
|  | } | 
|  |  | 
|  | static ssize_t mma8451_position_store(struct device *dev, | 
|  | struct device_attribute *attr, | 
|  | const char *buf, size_t count) | 
|  | { | 
|  | unsigned long  position; | 
|  | int ret; | 
|  | ret = strict_strtoul(buf, 10, &position); | 
|  | if (ret) { | 
|  | dev_err(dev, "string transform error\n"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | mutex_lock(&mma8451_lock); | 
|  | mma_status.position = (int)position; | 
|  | mutex_unlock(&mma8451_lock); | 
|  | return count; | 
|  | } | 
|  |  | 
|  | static ssize_t mma8451_scalemode_show(struct device *dev, | 
|  | struct device_attribute *attr, | 
|  | char *buf) | 
|  | { | 
|  | int mode = 0; | 
|  | mutex_lock(&mma8451_lock); | 
|  | mode = (int)mma_status.mode; | 
|  | mutex_unlock(&mma8451_lock); | 
|  |  | 
|  | return sprintf(buf, "%d\n", mode); | 
|  | } | 
|  |  | 
|  | static ssize_t mma8451_scalemode_store(struct device *dev, | 
|  | struct device_attribute *attr, | 
|  | const char *buf, size_t count) | 
|  | { | 
|  | unsigned long  mode; | 
|  | int ret, active_save; | 
|  | struct i2c_client *client = mma8451_i2c_client; | 
|  |  | 
|  | ret = strict_strtoul(buf, 10, &mode); | 
|  | if (ret) { | 
|  | dev_err(dev, "string transform error\n"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (mode > MODE_8G) { | 
|  | dev_warn(dev, "not supported mode\n"); | 
|  | ret = count; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | mutex_lock(&mma8451_lock); | 
|  | if (mode == mma_status.mode) { | 
|  | ret = count; | 
|  | goto out_unlock; | 
|  | } | 
|  |  | 
|  | active_save = mma_status.active; | 
|  | ret = mma8451_change_mode(client, mode); | 
|  | if (ret) | 
|  | goto out_unlock; | 
|  |  | 
|  | if (active_save == MMA_ACTIVED) { | 
|  | ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, 1); | 
|  |  | 
|  | if (ret) | 
|  | goto out_unlock; | 
|  | mma_status.active = active_save; | 
|  | } | 
|  |  | 
|  | out_unlock: | 
|  | mutex_unlock(&mma8451_lock); | 
|  | out: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, | 
|  | mma8451_enable_show, mma8451_enable_store); | 
|  | static DEVICE_ATTR(position, S_IWUSR | S_IRUGO, | 
|  | mma8451_position_show, mma8451_position_store); | 
|  | static DEVICE_ATTR(scalemode, S_IWUSR | S_IRUGO, | 
|  | mma8451_scalemode_show, mma8451_scalemode_store); | 
|  |  | 
|  | static struct attribute *mma8451_attributes[] = { | 
|  | &dev_attr_enable.attr, | 
|  | &dev_attr_position.attr, | 
|  | &dev_attr_scalemode.attr, | 
|  | NULL | 
|  | }; | 
|  |  | 
|  | static const struct attribute_group mma8451_attr_group = { | 
|  | .attrs = mma8451_attributes, | 
|  | }; | 
|  |  | 
|  | static int mma8451_probe(struct i2c_client *client, | 
|  | const struct i2c_device_id *id) | 
|  | { | 
|  | int result, client_id; | 
|  | struct input_dev *idev; | 
|  | struct i2c_adapter *adapter; | 
|  | u32 pos; | 
|  | struct device_node *of_node = client->dev.of_node; | 
|  | struct regulator *vdd, *vdd_io; | 
|  |  | 
|  | mma8451_i2c_client = client; | 
|  |  | 
|  | vdd = devm_regulator_get(&client->dev, "vdd"); | 
|  | if (!IS_ERR(vdd)) { | 
|  | result = regulator_enable(vdd); | 
|  | if (result) { | 
|  | dev_err(&client->dev, "vdd set voltage error\n"); | 
|  | return result; | 
|  | } | 
|  | } | 
|  |  | 
|  | vdd_io = devm_regulator_get(&client->dev, "vddio"); | 
|  | if (!IS_ERR(vdd_io)) { | 
|  | result = regulator_enable(vdd_io); | 
|  | if (result) { | 
|  | dev_err(&client->dev, "vddio set voltage error\n"); | 
|  | return result; | 
|  | } | 
|  | } | 
|  |  | 
|  | adapter = to_i2c_adapter(client->dev.parent); | 
|  | result = i2c_check_functionality(adapter, | 
|  | I2C_FUNC_SMBUS_BYTE | | 
|  | I2C_FUNC_SMBUS_BYTE_DATA); | 
|  | if (!result) | 
|  | goto err_out; | 
|  |  | 
|  | client_id = i2c_smbus_read_byte_data(client, MMA8451_WHO_AM_I); | 
|  | if (client_id != MMA8451_ID && client_id != MMA8452_ID | 
|  | && client_id != MMA8453_ID) { | 
|  | dev_err(&client->dev, | 
|  | "read chip ID 0x%x is not equal to 0x%x or 0x%x!\n", | 
|  | result, MMA8451_ID, MMA8452_ID); | 
|  | result = -EINVAL; | 
|  | goto err_out; | 
|  | } | 
|  |  | 
|  | /* Initialize the MMA8451 chip */ | 
|  | result = mma8451_change_mode(client, senstive_mode); | 
|  | if (result) { | 
|  | dev_err(&client->dev, | 
|  | "error when init mma8451 chip:(%d)\n", result); | 
|  | goto err_out; | 
|  | } | 
|  |  | 
|  | hwmon_dev = hwmon_device_register(&client->dev); | 
|  | if (!hwmon_dev) { | 
|  | result = -ENOMEM; | 
|  | dev_err(&client->dev, "error when register hwmon device\n"); | 
|  | goto err_out; | 
|  | } | 
|  |  | 
|  | mma8451_idev = input_allocate_polled_device(); | 
|  | if (!mma8451_idev) { | 
|  | result = -ENOMEM; | 
|  | dev_err(&client->dev, "alloc poll device failed!\n"); | 
|  | goto err_alloc_poll_device; | 
|  | } | 
|  | mma8451_idev->poll = mma8451_dev_poll; | 
|  | mma8451_idev->poll_interval = POLL_INTERVAL; | 
|  | mma8451_idev->poll_interval_min = POLL_INTERVAL_MIN; | 
|  | mma8451_idev->poll_interval_max = POLL_INTERVAL_MAX; | 
|  | idev = mma8451_idev->input; | 
|  | idev->name = "mma845x"; | 
|  | idev->id.bustype = BUS_I2C; | 
|  | idev->evbit[0] = BIT_MASK(EV_ABS); | 
|  |  | 
|  | input_set_abs_params(idev, ABS_X, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); | 
|  | input_set_abs_params(idev, ABS_Y, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); | 
|  | input_set_abs_params(idev, ABS_Z, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); | 
|  |  | 
|  | result = input_register_polled_device(mma8451_idev); | 
|  | if (result) { | 
|  | dev_err(&client->dev, "register poll device failed!\n"); | 
|  | goto err_register_polled_device; | 
|  | } | 
|  | result = sysfs_create_group(&idev->dev.kobj, &mma8451_attr_group); | 
|  | if (result) { | 
|  | dev_err(&client->dev, "create device file failed!\n"); | 
|  | result = -EINVAL; | 
|  | goto err_register_polled_device; | 
|  | } | 
|  |  | 
|  | result = of_property_read_u32(of_node, "position", &pos); | 
|  | if (result) | 
|  | pos = DEFAULT_POSITION; | 
|  | mma_status.position = (int)pos; | 
|  |  | 
|  | return 0; | 
|  | err_register_polled_device: | 
|  | input_free_polled_device(mma8451_idev); | 
|  | err_alloc_poll_device: | 
|  | hwmon_device_unregister(&client->dev); | 
|  | err_out: | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static int mma8451_stop_chip(struct i2c_client *client) | 
|  | { | 
|  | int ret = 0; | 
|  | if (mma_status.active == MMA_ACTIVED) { | 
|  | mma_status.ctl_reg1 = i2c_smbus_read_byte_data(client, | 
|  | MMA8451_CTRL_REG1); | 
|  | ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, | 
|  | mma_status.ctl_reg1 & 0xFE); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int mma8451_remove(struct i2c_client *client) | 
|  | { | 
|  | int ret; | 
|  | ret = mma8451_stop_chip(client); | 
|  | hwmon_device_unregister(hwmon_dev); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_PM_SLEEP | 
|  | static int mma8451_suspend(struct device *dev) | 
|  | { | 
|  | struct i2c_client *client = to_i2c_client(dev); | 
|  |  | 
|  | return mma8451_stop_chip(client); | 
|  | } | 
|  |  | 
|  | static int mma8451_resume(struct device *dev) | 
|  | { | 
|  | int ret = 0; | 
|  | struct i2c_client *client = to_i2c_client(dev); | 
|  | if (mma_status.active == MMA_ACTIVED) | 
|  | ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, | 
|  | mma_status.ctl_reg1); | 
|  | return ret; | 
|  |  | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static const struct i2c_device_id mma8451_id[] = { | 
|  | {"mma8451", 0}, | 
|  | }; | 
|  |  | 
|  | MODULE_DEVICE_TABLE(i2c, mma8451_id); | 
|  |  | 
|  | static SIMPLE_DEV_PM_OPS(mma8451_pm_ops, mma8451_suspend, mma8451_resume); | 
|  | static struct i2c_driver mma8451_driver = { | 
|  | .driver = { | 
|  | .name = "mma8451", | 
|  | .owner = THIS_MODULE, | 
|  | .pm = &mma8451_pm_ops, | 
|  | }, | 
|  | .probe = mma8451_probe, | 
|  | .remove = mma8451_remove, | 
|  | .id_table = mma8451_id, | 
|  | }; | 
|  |  | 
|  | static int __init mma8451_init(void) | 
|  | { | 
|  | /* register driver */ | 
|  | int res; | 
|  |  | 
|  | res = i2c_add_driver(&mma8451_driver); | 
|  | if (res < 0) { | 
|  | printk(KERN_INFO "add mma8451 i2c driver failed\n"); | 
|  | return -ENODEV; | 
|  | } | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static void __exit mma8451_exit(void) | 
|  | { | 
|  | i2c_del_driver(&mma8451_driver); | 
|  | } | 
|  |  | 
|  | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | 
|  | MODULE_DESCRIPTION("MMA8451 3-Axis Orientation/Motion Detection Sensor driver"); | 
|  | MODULE_LICENSE("GPL"); | 
|  |  | 
|  | module_init(mma8451_init); | 
|  | module_exit(mma8451_exit); |