All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] bma023: Support for Bosch BMA023 and SMB380 accelerometers
@ 2011-01-25 16:05 Alan Cox
  2011-01-26 11:11 ` Jonathan Cameron
  0 siblings, 1 reply; 5+ messages in thread
From: Alan Cox @ 2011-01-25 16:05 UTC (permalink / raw)
  To: linux-input

From: Joseph Lai <joseph_lai@wistron.com>

This patch supports driver for BOSCH BMA023 and SMB380 which are accelerometers.

We expose the data from this device to uusers through sysfs interface in
the same manner as the lis drivers so that the data can be used for one off
uses, and also as as an input device for joystick type uses.

The driver allows the setting and reading of the device's properties via
sysfs attributes.

Based on an original driver
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

[Extensively rewritten]

Signed-off-by: Joseph Lai <joseph_lai@wistron.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 drivers/input/misc/Kconfig  |   10 
 drivers/input/misc/Makefile |    1 
 drivers/input/misc/bma023.c | 1306 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c/bma023.h  |   44 +
 4 files changed, 1361 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/misc/bma023.c
 create mode 100644 include/linux/i2c/bma023.h


diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index b0c6772..3b595b8 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -454,4 +454,14 @@ config INPUT_CMA3000_I2C
 	  To compile this driver as a module, choose M here: the
 	  module will be called cma3000_d0x_i2c.
 
+config INPUT_BMA023
+	tristate "BMA023/SMB380 Triaxial acceleration sensor"
+	depends on I2C
+	help
+	  Say Y here if you want to support Bosch BMA023/SMB380
+	  connected via an I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bma023.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 9b47971..79b3f66 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_INPUT_PWM_BEEPER)		+= pwm-beeper.o
 obj-$(CONFIG_INPUT_RB532_BUTTON)	+= rb532_button.o
 obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)	+= rotary_encoder.o
 obj-$(CONFIG_INPUT_SGI_BTNS)		+= sgi_btns.o
+obj-$(CONFIG_INPUT_BMA023)			+= bma023.o
 obj-$(CONFIG_INPUT_SPARCSPKR)		+= sparcspkr.o
 obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON)	+= twl4030-pwrbutton.o
 obj-$(CONFIG_INPUT_TWL4030_VIBRA)	+= twl4030-vibra.o
diff --git a/drivers/input/misc/bma023.c b/drivers/input/misc/bma023.c
new file mode 100644
index 0000000..489f363
--- /dev/null
+++ b/drivers/input/misc/bma023.c
@@ -0,0 +1,1306 @@
+/*
+ * bma023.c - BMA023 Tri-axis accelerometer driver
+ *
+ * Copyright (C) 2010 Samsung Eletronics Co.Ltd
+ * Kim Kyuwon <q1.kim@samsung.com>
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ * Donggeun Kim <dg77.kim@samsung.com>
+ *
+ * Copyright (C) 2011 Wistron Co.Ltd
+ * Joseph Lai <joseph_lai@wistron.com>
+ *
+ * Copyright (c) 2011 Intel Corporation
+ * Alan Cox <alan@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * 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.
+ *
+ * TODO (After)
+ *	Investigate if the IRQ disable is really needed (needs that data
+ *		sheet reviewing in more detail)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/i2c/bma023.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+#define BMA023_DEFAULT_AUTO_DELAY	250	/* mS */
+
+#define BMA023_CHIP_ID_REG	0x00
+#define BMA023_X_LSB_REG	0x02
+#define BMA023_X_MSB_REG	0x03
+#define BMA023_Y_LSB_REG	0x04
+#define BMA023_Y_MSB_REG	0x05
+#define BMA023_Z_LSB_REG	0x06
+#define BMA023_Z_MSB_REG	0x07
+#define BMA023_TEMP_REG		0x08 /*  An output of 0 equals -30C,
+					 1 LSB equals 0.5C */
+#define BMA023_CTRL1_REG	0x0a
+#define BMA023_CTRL2_REG	0x0b
+#define BMA023_SETTINGS1_REG	0x0c
+#define BMA023_SETTINGS2_REG	0x0d
+#define BMA023_SETTINGS3_REG	0x0e
+#define BMA023_SETTINGS4_REG	0x0f
+#define BMA023_SETTINGS5_REG	0x10
+#define BMA023_SETTINGS6_REG	0x11
+#define BMA023_RANGE_BW_REG	0x14
+#define BMA023_CONF2_REG	0x15
+
+#define BMA023_CHIP_ID		0x02
+
+#define BMA023_NEW_DATA_INT_SHIFT	5
+#define BMA023_NEW_DATA_INT_MASK	(0x1 << 5)
+
+#define BMA023_RANGE_SHIFT		3
+#define BMA023_RANGE_MASK		(0x3 << 3)
+#define BMA023_BANDWIDTH_SHIFT		0
+#define BMA023_BANDWIDTH_MASK		(0x7)
+
+#define BMA023_HG_HYST_SHIFT		3
+#define BMA023_HG_HYST_MASK		(0x7 << 3)
+#define BMA023_LG_HYST_SHIFT		0
+#define BMA023_LG_HYST_MASK		(0x7)
+
+#define BMA023_HG_DUR_SHIFT		(0x0)
+#define BMA023_HG_DUR_MASK		(0xff)
+#define BMA023_HG_THRES_SHIFT		(0x0)
+#define BMA023_HG_THRES_MASK		(0xff)
+#define BMA023_LG_DUR_SHIFT		(0x0)
+#define BMA023_LG_DUR_MASK		(0xff)
+#define BMA023_LG_THRES_SHIFT		(0x0)
+#define BMA023_LG_THRES_MASK		(0xff)
+
+#define BMA023_ENABLE_HG_SHIFT		1
+#define BMA023_ENABLE_HG_MASK		(0x1 << 1)
+#define BMA023_ENABLE_LG_SHIFT		0
+#define BMA023_ENABLE_LG_MASK		(0x1)
+
+#define BMA023_SLEEP_SHIFT		0
+#define BMA023_SLEEP_MASK		(0x1)
+
+#define BMA023_ACCEL_BITS		10
+#define BMA023_MAX_VALUE		((1 << ((BMA023_ACCEL_BITS) - 1)) - 1)
+#define BMA023_MIN_VALUE		(-(1 << ((BMA023_ACCEL_BITS) - 1)))
+
+#define BMA023_DEFAULT_RANGE		BMA023_RANGE_2G
+#define BMA023_DEFAULT_BANDWIDTH	BMA023_BW_190HZ
+#define BMA023_DEFAULT_NEW_DATA_INT	0
+#define BMA023_DEFAULT_HG_INT		0
+#define BMA023_DEFAULT_LG_INT		0
+#define BMA023_DEFAULT_HG_DURATION	0x05
+#define BMA023_DEFAULT_HG_THRESHOLD	0xa0
+#define BMA023_DEFAULT_HG_HYST		0
+#define BMA023_DEFAULT_LG_DURATION	0x05
+#define BMA023_DEFAULT_LG_THRESHOLD	0x14
+#define BMA023_DEFAULT_LG_HYST		0
+
+#define BMA023_SUSPEND	0
+#define BMA023_RESUME	1
+
+#define BMA023_CUSTOMER_REG	0x12
+#define BMA023_INSPECT_VAL	0xBB
+#define BMA023_CHECK_SHIFT	0
+#define BMA023_CHECK_MASK	0xff
+
+struct bma023_data {
+	s16 x;
+	s16 y;
+	s16 z;
+};
+
+struct bma023_sensor {
+	struct i2c_client *client;
+	struct device *dev;
+	struct input_dev *idev;
+	struct mutex lock;
+	struct bma023_data data;
+	int auto_delay;
+	int soft_power;
+
+	u8 range;
+	u8 bandwidth;
+	u8 new_data_int;
+	u8 hg_int;
+	u8 lg_int;
+	u8 lg_dur;
+	u8 lg_thres;
+	u8 lg_hyst;
+	u8 hg_dur;
+	u8 hg_thres;
+	u8 hg_hyst;
+	u8 power_mode;
+};
+
+/**
+ *	bma023_write_reg	-	write a BMA023 register via I2C
+ *	@reg: register being updated
+ *	@val: value to write
+ *
+ *	Updates the register or returns an error if the write fails.
+ */
+static int bma023_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+	int ret;
+	/*
+	 * According to the datasheet, the interrupt should be deactivated
+	 * on the host side when write sequences operate.
+	 */
+	disable_irq_nosync(client->irq);
+	ret = i2c_smbus_write_byte_data(client, reg, val);
+	enable_irq(client->irq);
+	if (ret < 0)
+		dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
+			__func__, reg, val, ret);
+	return ret;
+}
+
+/**
+ *	bma023_read_reg	-	read a BMA023 register via I2C
+ *	@reg: register being updated
+ *	@val: value to write
+ *
+ *	Reads the register or returns a negative error code on failure.
+ */
+static int bma023_read_reg(struct i2c_client *client, u8 reg)
+{
+	int ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0)
+		dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
+			__func__, reg, ret);
+	return ret;
+}
+
+/**
+ *	bma023_xyz_read_reg	-	read the axes values
+ *	@reg: register being updated
+ *	@val: value to write
+ *
+ *	Reads the register values in one transaction or returns a negative
+ *	error code on failure/
+ */
+static int bma023_xyz_read_reg(struct i2c_client *client,
+			       u8 *buffer, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = 1,
+			.buf = buffer,
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = length,
+			.buf = buffer,
+		},
+	};
+	return i2c_transfer(client->adapter, msg, 2);
+}
+
+/**
+ *	bma023_set_reg_bits	-	update a subset of register bits
+ *	@client: i2c client for the sensor
+ *	@val: value to write
+ *	@shift: bit offset of field
+ *	@mask: bit mask of field to update
+ *	@reg: register number
+ *
+ *	Update one of the many bitfield registers on the BMA023 by reading
+ *	and writing back bits. The caller *MUST* hold the sensor lock when
+ *	using this function in order to prevent overlapping updates corrupting
+ *	the register.
+ *
+ *	Returns success or an error code
+ */
+static int bma023_set_reg_bits(struct i2c_client *client,
+					int val, int shift, u8 mask, u8 reg)
+{
+	int data = bma023_read_reg(client, reg);
+
+	if (data < 0)
+		return data;
+
+	data = (data & ~mask) | ((val << shift) & mask);
+	return bma023_write_reg(client, reg, data);
+}
+
+/**
+ *	bma023_get_reg_bits	-	read register bitfield
+ *	@client: i2c client for the sensor
+ *	@shift: bit offset of field
+ *	@mask: bit mask of field to update
+ *	@reg: register number
+ *
+ *	Read a bit field from the sensor and return the value of the field. The
+ *	lock is not needed in this case
+ */
+static int bma023_get_reg_bits(struct i2c_client *client, int shift,
+					u8 mask, u8 reg)
+{
+	int data = bma023_read_reg(client, reg);
+	if (data < 0)
+		return data;
+	data = (data & mask) >> shift;
+	return data;
+}
+
+/*
+ *	Functions for handling all the configurable fields
+ */
+
+/**
+ *	bma023_set_check_reg	-	set an inspective value
+ *	@client: i2c client for the sensor
+ *	@check: update the inspective value
+ *
+ *	Set an inspective value to customer's register, which can record
+ *	any value from user. The inspective value will keep until the chip
+ *	off or shutdown. User can recognize the chip needs re-initialize
+ *	or not by this register.
+ */
+static int bma023_set_check_reg(struct i2c_client *client, u8 check)
+{
+	return bma023_set_reg_bits(client, check, BMA023_CHECK_SHIFT,
+				   BMA023_CHECK_MASK, BMA023_CUSTOMER_REG);
+}
+
+static int bma023_get_check_reg(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_CHECK_SHIFT,
+				   BMA023_CHECK_MASK, BMA023_CUSTOMER_REG);
+}
+
+/**
+ *	bma023_set_range	-	set the full scale acceleration range
+ *	@client: i2c client for the sensor
+ *	@range: new range setting
+ *
+ *	The range of bma023 will affect the chip to change the sensitivity 
+ *	of gravitation. The value of range includes +/- 2g, 4g and 8g.
+ *
+ *	The change of range will respond to 3 axes directly.
+ */
+static int bma023_set_range(struct i2c_client *client, u8 range)
+{
+	return bma023_set_reg_bits(client, range, BMA023_RANGE_SHIFT,
+				   BMA023_RANGE_MASK, BMA023_RANGE_BW_REG);
+}
+
+static int bma023_get_range(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_RANGE_SHIFT,
+				   BMA023_RANGE_MASK, BMA023_RANGE_BW_REG);
+}
+
+/**
+ *	bma023_set_bandwidth	-	set the digital filtering of ADC output data
+ *	@client: i2c client for the sensor
+ *	@bw: new bandwidth setting
+ *
+ *	The bandwidth affect the ability of noise filter. The value of bandwidth
+ *	includes 25Hz, 50Hz, 100Hz, 190Hz, 375Hz, 750Hz and 1500Hz.
+ *
+ *	The change of bandwidth will respond to 3 axes directly.
+ */
+static int bma023_set_bandwidth(struct i2c_client *client, u8 bw)
+{
+	return bma023_set_reg_bits(client, bw, BMA023_BANDWIDTH_SHIFT,
+				   BMA023_BANDWIDTH_MASK, BMA023_RANGE_BW_REG);
+}
+
+static int bma023_get_bandwidth(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_BANDWIDTH_SHIFT,
+				   BMA023_BANDWIDTH_MASK, BMA023_RANGE_BW_REG);
+}
+
+/**
+ *	bma023_set_new_data_int	-	switch on/off of new data interrupt
+ *	@client: i2c client for the sensor
+ *	@val: value to switch on/off, 1: on, 0: off
+ *
+ *	To turn on this interrupt will generate an interrupt when all three
+ *	axes acceleration values are new.
+ */
+static int bma023_set_new_data_int(struct i2c_client *client, u8 val)
+{
+	return bma023_set_reg_bits(client, val, BMA023_NEW_DATA_INT_SHIFT,
+				   BMA023_NEW_DATA_INT_MASK, BMA023_CONF2_REG);
+}
+
+static int bma023_get_new_data_int(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_NEW_DATA_INT_SHIFT,
+				   BMA023_NEW_DATA_INT_MASK, BMA023_CONF2_REG);
+}
+
+/**
+ *	bma023_set_hg_int	-	switch on/off of high-g interrupt
+ *	@client: i2c client for the sensor
+ *	@val: value to switch on/off, 1: on, 0: off
+ *
+ *	To turn on this interrupt will generate an interrupt when the
+ *	oscillatory criteria is satisfied by hg_dur and hg_thres.
+ *
+ *	Usually, it is used to debounce the high-g criteria.
+ */
+static int bma023_set_hg_int(struct i2c_client *client, u8 val)
+{
+	return bma023_set_reg_bits(client, val, BMA023_ENABLE_HG_SHIFT,
+				   BMA023_ENABLE_HG_MASK, BMA023_CTRL2_REG);
+}
+
+static int bma023_get_hg_int(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_ENABLE_HG_SHIFT,
+				   BMA023_ENABLE_HG_MASK, BMA023_CTRL2_REG);
+}
+
+/**
+ *	bma023_set_lg_int	-	switch on/off of low-g interrupt
+ *	@client: i2c client for the sensor
+ *	@val: value to switch on/off, 1: on, 0: off
+ *
+ *	To turn on this setting will generate an interrupt when the
+ *	oscillatory criteria is satisfied by lg_dur and lg_thres.
+ *
+ *	Usually, it is used to detect a free fall.
+ */
+static int bma023_set_lg_int(struct i2c_client *client, u8 val)
+{
+	return bma023_set_reg_bits(client, val,	BMA023_ENABLE_LG_SHIFT,
+				   BMA023_ENABLE_LG_MASK, BMA023_CTRL2_REG);
+}
+
+static int bma023_get_lg_int(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_ENABLE_LG_SHIFT,
+				   BMA023_ENABLE_LG_MASK, BMA023_CTRL2_REG);
+}
+
+/**
+ *	bma023_set_lg_dur	-	set low-g duration
+ *	@client: i2c client for the sensor
+ *	@dur: duration value, 0-255
+ *	
+ *	Generate an interrupt when the gravitation is detected in low-g
+ *	criterion for long enough duration.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_lg_dur(struct i2c_client *client, u8 dur)
+{
+	return bma023_set_reg_bits(client, dur,	BMA023_LG_DUR_SHIFT,
+				   BMA023_LG_DUR_MASK, BMA023_SETTINGS2_REG);
+}
+
+static int bma023_get_lg_dur(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_LG_DUR_SHIFT,
+				   BMA023_LG_DUR_MASK, BMA023_SETTINGS2_REG);
+}
+
+/**
+ *	bma023_set_lg_thres	-	set low-g threshold
+ *	@client: i2c client for the sensor
+ *	@thres: threshold value, 0-255
+ *	
+ *	Filter 3 axes detection and limit to trigger an interrupt signal
+ *	when the gravitation is detected in low-g threshold.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_lg_thres(struct i2c_client *client, u8 thres)
+{
+	return bma023_set_reg_bits(client, thres, BMA023_LG_THRES_SHIFT,
+				   BMA023_LG_THRES_MASK, BMA023_SETTINGS1_REG);
+}
+
+static int bma023_get_lg_thres(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_LG_THRES_SHIFT,
+				   BMA023_LG_THRES_MASK, BMA023_SETTINGS1_REG);
+}
+
+/**
+ *	bma023_set_lg_hyst	-	set low-g hysteresis
+ *	@client: i2c client for the sensor
+ *	@hyst: hysteresis value, 0-7
+ *	
+ *	Expand the range of detectable gravitation to reset an interrupt.
+ *	An interrupt will be latched until outside of low-g criterion with 
+ *	hysteresis.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_lg_hyst(struct i2c_client *client, u8 hyst)
+{
+	return bma023_set_reg_bits(client, hyst, BMA023_LG_HYST_SHIFT,
+				   BMA023_LG_HYST_MASK, BMA023_SETTINGS6_REG);
+}
+
+static int bma023_get_lg_hyst(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_LG_HYST_SHIFT,
+				   BMA023_LG_HYST_MASK,	BMA023_SETTINGS6_REG);
+}
+
+/**
+ *	bma023_set_hg_dur	-	set high-g duration
+ *	@client: i2c client for the sensor
+ *	@dur: duration value, 0-255
+ *	
+ *	Generate an interrupt when the gravitation is detected in high-g
+ *	criterion for long enough duration.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_hg_dur(struct i2c_client *client, u8 dur)
+{
+	return bma023_set_reg_bits(client, dur,	BMA023_HG_DUR_SHIFT,
+				   BMA023_HG_DUR_MASK, BMA023_SETTINGS4_REG);
+}
+
+static int bma023_get_hg_dur(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_HG_DUR_SHIFT,
+				   BMA023_HG_DUR_MASK, BMA023_SETTINGS4_REG);
+}
+
+/**
+ *	bma023_set_hg_thres	-	set high-g threshold
+ *	@client: i2c client for the sensor
+ *	@thres: threshold value, 0-255
+ *	
+ *	Filter 3 axes detection and limit to trigger an interrupt signal
+ *	when the gravitation is detected in high-g threshold.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_hg_thres(struct i2c_client *client, u8 thres)
+{
+	return bma023_set_reg_bits(client, thres, BMA023_HG_THRES_SHIFT,
+				   BMA023_HG_THRES_MASK, BMA023_SETTINGS3_REG);
+}
+
+static int bma023_get_hg_thres(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_HG_THRES_SHIFT,
+				   BMA023_HG_THRES_MASK, BMA023_SETTINGS3_REG);
+}
+
+/**
+ *	bma023_set_hg_hyst	-	set low-g hysteresis
+ *	@client: i2c client for the sensor
+ *	@hyst: hysteresis value, 0-7
+ *	
+ *	Expand the range of detectable gravitation to reset an interrupt.
+ *	An interrupt will be latched until outside of high-g criterion with 
+ *	hysteresis.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_hg_hyst(struct i2c_client *client, u8 hyst)
+{
+	return bma023_set_reg_bits(client, hyst, BMA023_HG_HYST_SHIFT,
+				   BMA023_HG_HYST_MASK,	BMA023_SETTINGS6_REG);
+}
+
+static int bma023_get_hg_hyst(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_HG_HYST_SHIFT,
+				   BMA023_HG_HYST_MASK,	BMA023_SETTINGS6_REG);
+}
+
+/**
+ *	bma023_power_mode	-	set the power mode
+ *	@client: i2c client for the sensor
+ *	@val: value to switch on/off of power, 1: normal power, 0: low power
+ *
+ *	Put device to normal-power mode or low-power mode.
+ */
+static int bma023_set_power_mode(struct i2c_client *client, u8 val)
+{
+	return bma023_set_reg_bits(client, ~val, BMA023_SLEEP_SHIFT,
+				   BMA023_SLEEP_MASK, BMA023_CTRL1_REG);
+}
+
+static int bma023_get_power_mode(struct i2c_client *client)
+{
+	return !bma023_get_reg_bits(client, BMA023_SLEEP_SHIFT,
+				   BMA023_SLEEP_MASK, BMA023_CTRL1_REG);
+}
+
+/**
+ *	bma023_correct_accel_sign	-	convert to expected format
+ *	@val: twos complement value
+ *
+ *	The description of the digital signals x, y and z is "2' complement".
+ *	So we need to correct the sign of data read by i2c.
+ */
+static s16 bma023_correct_accel_sign(s16 val)
+{
+	val <<= (sizeof(s16) * BITS_PER_BYTE - BMA023_ACCEL_BITS);
+	val >>= (sizeof(s16) * BITS_PER_BYTE - BMA023_ACCEL_BITS);
+	return val;
+}
+
+/**
+ *	bma023_merge_register_values	-	turn into co-ordinate
+ *	@lsb: low bits from sensor
+ *	@msb: high bits from sensor
+ *
+ *	Turns a sensor register reading into a Linux input layer
+ *	value and returns it.
+ */
+static s16 bma023_merge_register_values(u8 lsb, u8 msb)
+{
+	s16 val = (msb << 2) | (lsb >> 6);
+	return bma023_correct_accel_sign(val);
+}
+
+/**
+ *	bma023_read_xyz		-	get co-ordinates from device
+ *	@client: i2c address of sensor
+ *	@coords: co-ordinates to update
+ *
+ *	Return the converted X Y and Z co-ordinates from the sensor device
+ */
+static void bma023_read_xyz(struct i2c_client *client,
+				struct bma023_data *coords)
+{
+	u8 buffer[6];
+	buffer[0] = BMA023_X_LSB_REG;
+	bma023_xyz_read_reg(client, buffer, 6);
+	coords->x = bma023_merge_register_values(buffer[0], buffer[1]);
+	coords->y = bma023_merge_register_values(buffer[2], buffer[3]);
+	coords->z = bma023_merge_register_values(buffer[4], buffer[5]);
+	dev_dbg(&client->dev, "%s: x %d, y %d, z %d\n", __func__,
+					coords->x, coords->y, coords->z);
+}
+
+/**
+ *	bma023_show_xyz		-	show co-ordinate readings
+ *	@dev: device of sensor
+ *	@attr: device attributes of sysfs node
+ *	@buf: buffer for output
+ *
+ *	Perform a one off read of the sensor data. In non-interrupt mode
+ *	the sensor can be used for one off reads of the axes rather than
+ *	as an input device.
+ */
+static ssize_t bma023_show_xyz(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&sensor->lock);
+	bma023_read_xyz(sensor->client, &sensor->data);
+	mutex_unlock(&sensor->lock);
+
+	pm_runtime_put(dev);
+
+	return sprintf(buf, "(%d,%d,%d)\n",
+		sensor->data.x, sensor->data.y, sensor->data.z);
+}
+static DEVICE_ATTR(accel_data, S_IRUGO, bma023_show_xyz, NULL);
+
+/**
+ *	bma023_set_attr	-	set an attribute on the sensor
+ *	@sensor sensor to update
+ *	@buf: buffer to parse
+ *	@count: length of buffer written
+ *	@get: read function to use
+ *	@set: write function to use
+ *	@data: where to store cached result
+ *
+ *	Helper function for setting attributes of the sensor. This does
+ *	all the parsing and locking in one place calling out to the helper
+ *	functions provided.
+ */
+static ssize_t bma023_set_attr(struct bma023_sensor *sensor,
+				const char *buf, size_t count,
+				int (*get)(struct i2c_client *),
+				int (*set)(struct i2c_client *, u8 val),
+				u8 *data)
+{
+	unsigned long val;
+	int ret;
+
+	if (!count)
+		return count;
+
+	ret = strict_strtoul(buf, 10, &val);
+	if (!ret) {
+		/* We don't need the device to be fully powered up for this
+		   but we do need to be sure that we force the i²c controller
+		   to be awake */
+		pm_runtime_get_sync(sensor->dev);
+		mutex_lock(&sensor->lock);
+		ret = set(sensor->client, val);
+		if (ret >= 0) {
+			ret = get(sensor->client);
+			if (ret >= 0)
+				*data = ret;
+		}
+		mutex_unlock(&sensor->lock);
+		pm_runtime_put(sensor->dev);
+		if (ret >= 0)
+			return count;
+	}
+	return ret;
+}
+
+/* Methods for the sysfs attributes */
+static ssize_t bma023_show_power_mode(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", bma023_get_power_mode(sensor->client));
+}
+
+static ssize_t bma023_store_power_mode(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	unsigned long val;
+	int ret = strict_strtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&sensor->lock);
+	if (val & !sensor->soft_power)
+		pm_runtime_get(dev);
+	else if (!val && sensor->soft_power)
+		pm_runtime_put(dev);
+	sensor->soft_power = val;
+	mutex_unlock(&sensor->lock);
+
+	return count;
+}
+
+static DEVICE_ATTR(power_mode, S_IRUGO | S_IWUSR,
+		bma023_show_power_mode, bma023_store_power_mode);
+
+static ssize_t bma023_show_range(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->range);
+}
+
+static ssize_t bma023_store_range(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_range,
+			bma023_set_range,
+			&sensor->range);
+}
+
+static DEVICE_ATTR(range, S_IRUGO | S_IWUSR,
+		bma023_show_range, bma023_store_range);
+
+static ssize_t bma023_show_bandwidth(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->bandwidth);
+}
+
+static ssize_t bma023_store_bandwidth(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_bandwidth,
+			bma023_set_bandwidth,
+			&sensor->bandwidth);
+}
+
+static DEVICE_ATTR(bandwidth, S_IRUGO | S_IWUSR,
+		bma023_show_bandwidth, bma023_store_bandwidth);
+
+static ssize_t bma023_show_new_data_int(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->new_data_int);
+}
+
+static ssize_t bma023_store_new_data_int(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_new_data_int,
+			bma023_set_new_data_int,
+			&sensor->new_data_int);
+}
+
+static DEVICE_ATTR(new_data_int, S_IRUGO | S_IWUSR,
+		bma023_show_new_data_int, bma023_store_new_data_int);
+
+static ssize_t bma023_show_hg_int(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->hg_int);
+}
+
+static ssize_t bma023_store_hg_int(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_hg_int,
+			bma023_set_hg_int,
+			&sensor->hg_int);
+}
+
+static DEVICE_ATTR(hg_int, S_IRUGO | S_IWUSR,
+		bma023_show_hg_int, bma023_store_hg_int);
+
+static ssize_t bma023_show_lg_int(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->lg_int);
+}
+
+static ssize_t bma023_store_lg_int(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_lg_int,
+			bma023_set_lg_int,
+			&sensor->lg_int);
+}
+
+static DEVICE_ATTR(lg_int, S_IRUGO | S_IWUSR,
+		bma023_show_lg_int, bma023_store_lg_int);
+
+static ssize_t bma023_show_lg_dur(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->lg_dur);
+}
+
+static ssize_t bma023_store_lg_dur(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_lg_dur,
+			bma023_set_lg_dur,
+			&sensor->lg_dur);
+}
+
+static DEVICE_ATTR(lg_dur, S_IRUGO | S_IWUSR,
+		bma023_show_lg_dur, bma023_store_lg_dur);
+
+static ssize_t bma023_show_lg_thres(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->lg_thres);
+}
+
+static ssize_t bma023_store_lg_thres(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_lg_thres,
+			bma023_set_lg_thres,
+			&sensor->lg_thres);
+}
+
+static DEVICE_ATTR(lg_thres, S_IRUGO | S_IWUSR,
+		bma023_show_lg_thres, bma023_store_lg_thres);
+
+static ssize_t bma023_show_lg_hyst(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->lg_hyst);
+}
+
+static ssize_t bma023_store_lg_hyst(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_lg_hyst,
+			bma023_set_lg_hyst,
+			&sensor->lg_hyst);
+}
+
+static DEVICE_ATTR(lg_hyst, S_IRUGO | S_IWUSR,
+		bma023_show_lg_hyst, bma023_store_lg_hyst);
+
+static ssize_t bma023_show_hg_dur(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->hg_dur);
+}
+
+static ssize_t bma023_store_hg_dur(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_hg_dur,
+			bma023_set_hg_dur,
+			&sensor->hg_dur);
+}
+
+static DEVICE_ATTR(hg_dur, S_IRUGO | S_IWUSR,
+		bma023_show_hg_dur, bma023_store_hg_dur);
+
+static ssize_t bma023_show_hg_thres(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->hg_thres);
+}
+
+static ssize_t bma023_store_hg_thres(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_hg_thres,
+			bma023_set_hg_thres,
+			&sensor->hg_thres);
+}
+
+static DEVICE_ATTR(hg_thres, S_IRUGO | S_IWUSR,
+		bma023_show_hg_thres, bma023_store_hg_thres);
+
+static ssize_t bma023_show_hg_hyst(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->hg_hyst);
+}
+
+static ssize_t bma023_store_hg_hyst(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_hg_hyst,
+			bma023_set_hg_hyst,
+			&sensor->hg_hyst);
+}
+
+static DEVICE_ATTR(hg_hyst, S_IRUGO | S_IWUSR,
+		bma023_show_hg_hyst, bma023_store_hg_hyst);
+
+static struct attribute *bma023_attributes[] = {
+	&dev_attr_accel_data.attr,
+	&dev_attr_power_mode.attr,
+	&dev_attr_range.attr,
+	&dev_attr_bandwidth.attr,
+	&dev_attr_new_data_int.attr,
+	&dev_attr_hg_int.attr,
+	&dev_attr_lg_int.attr,
+	&dev_attr_lg_dur.attr,
+	&dev_attr_lg_thres.attr,
+	&dev_attr_lg_hyst.attr,
+	&dev_attr_hg_dur.attr,
+	&dev_attr_hg_thres.attr,
+	&dev_attr_hg_hyst.attr,
+	NULL
+};
+
+static const struct attribute_group bma023_group = {
+	.attrs	= bma023_attributes,
+};
+
+/**
+ *	bma023_interrupt_thread	-	handle an IRQ
+ *	@irq: interrupt numner
+ *	@data: the sensor
+ *
+ *	Called by the kernel single threaded after an interrupt occurs. Read
+ *	the sensor data and generate an input event for it.
+ */
+static irqreturn_t bma023_interrupt_thread(int irq, void *data)
+{
+	struct bma023_sensor *sensor = data;
+
+	mutex_lock(&sensor->lock);
+	bma023_read_xyz(sensor->client, &sensor->data);
+	mutex_unlock(&sensor->lock);
+
+	input_report_abs(sensor->idev, ABS_X, sensor->data.x);
+	input_report_abs(sensor->idev, ABS_Y, sensor->data.y);
+	input_report_abs(sensor->idev, ABS_Z, sensor->data.z);
+	input_sync(sensor->idev);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ *	bma023_initialize	-	set sensor parameters
+ *	@sensor: sensor to configure
+ *
+ *	Load the actual hardware with the sensor values that were provided
+ */
+static void bma023_initialize(struct bma023_sensor *sensor)
+{
+	bma023_set_range(sensor->client, sensor->range);
+	bma023_set_bandwidth(sensor->client, sensor->bandwidth);
+	bma023_set_new_data_int(sensor->client, sensor->new_data_int);
+	bma023_set_hg_dur(sensor->client, sensor->hg_dur);
+	bma023_set_hg_thres(sensor->client, sensor->hg_thres);
+	bma023_set_hg_hyst(sensor->client, sensor->hg_hyst);
+	bma023_set_lg_dur(sensor->client, sensor->lg_dur);
+	bma023_set_lg_thres(sensor->client, sensor->lg_thres);
+	bma023_set_lg_hyst(sensor->client, sensor->lg_hyst);
+	bma023_set_hg_int(sensor->client, sensor->hg_int);
+	bma023_set_lg_int(sensor->client, sensor->lg_int);
+	bma023_set_power_mode(sensor->client, sensor->power_mode);
+	bma023_set_check_reg(sensor->client, BMA023_INSPECT_VAL);
+}
+
+/**
+ *	bma023_input_open	-	called on input event open
+ *	@input: input dev of opened device
+ *
+ *	The input layer calls this function when input event is opened. The
+ *	function will push the device to resume. Then, the device is ready
+ *	to provide data.
+ */
+static int bma023_input_open(struct input_dev *input)
+{
+	struct bma023_sensor *sensor = input_get_drvdata(input);
+	pm_runtime_get(sensor->dev);
+	return 0;
+}
+
+/**
+ *	bma023_input_close	-	called on input event close
+ *	@input: input dev of closed device
+ *
+ *	The input layer calls this function when input event is closed. The
+ *	function will push the device to suspend.
+ */
+static void bma023_input_close(struct input_dev *input)
+{
+	struct bma023_sensor *sensor = input_get_drvdata(input);
+	pm_runtime_put(sensor->dev);
+}
+
+/**
+ *	bma023_unregister_input_device	-	remove input dev
+ *	@sensor: sensor to remove from input
+ *
+ *	Free the interrupt and input device for the sensor. We must free
+ *	the interrupt first
+ */
+static void bma023_unregister_input_device(struct bma023_sensor *sensor)
+{
+	struct i2c_client *client = sensor->client;
+	if (client->irq > 0)
+		free_irq(client->irq, sensor);
+	input_unregister_device(sensor->idev);
+	sensor->idev = NULL;
+}
+
+/**
+ *	bma023_register_input_device	-	remove input dev
+ *	@sensor: sensor to remove from input
+ *
+ *	Add an input device to the sensor. This will be used to report
+ *	events from the sensor itself.
+ */
+static int bma023_register_input_device(struct bma023_sensor *sensor)
+{
+	struct i2c_client *client = sensor->client;
+	struct input_dev *idev;
+	int ret;
+	sensor->idev = input_allocate_device();
+	idev = sensor->idev;
+	if (!idev) {
+		dev_err(&client->dev, "failed to allocate input device\n");
+		ret = -ENOMEM;
+		goto failed_alloc;
+	}
+	idev->name = "BMA023 Sensor";
+	idev->open = bma023_input_open;
+	idev->close = bma023_input_close;
+	idev->id.bustype = BUS_I2C;
+	idev->dev.parent = &client->dev;
+	idev->evbit[0] = BIT_MASK(EV_ABS);
+	input_set_abs_params(idev, ABS_X, BMA023_MIN_VALUE,
+			BMA023_MAX_VALUE, 0, 0);
+	input_set_abs_params(idev, ABS_Y, BMA023_MIN_VALUE,
+			BMA023_MAX_VALUE, 0, 0);
+	input_set_abs_params(idev, ABS_Z, BMA023_MIN_VALUE,
+			BMA023_MAX_VALUE, 0, 0);
+	input_set_drvdata(idev, sensor);
+	ret = input_register_device(idev);
+	if (ret) {
+		dev_err(&client->dev, "failed to register input device\n");
+		goto failed_reg;
+	}
+	if (client->irq > 0) {
+		ret = request_threaded_irq(client->irq, NULL,
+				bma023_interrupt_thread, IRQF_TRIGGER_RISING,
+					"bma023_int", sensor);
+		if (ret) {
+			dev_err(&client->dev, "can't get IRQ %d, ret %d\n",
+					client->irq, ret);
+			goto failed_irq;
+		}
+	}
+	return 0;
+failed_irq:
+	input_unregister_device(idev);
+	return ret;
+
+failed_reg:
+	if (idev)
+		input_free_device(idev);
+failed_alloc:
+	return ret;
+}
+
+/**
+ *	bma023_probe	-	device detection callback
+ *	@client: i2c client of found device
+ *	@id: id match information
+ *
+ *	The I2C layer calls us when it believes a sensor is present at this
+ *	address. Probe to see if this is correct and to validate the device.
+ *
+ *	If present install the relevant sysfs interfaces and input device.
+ */
+static int __devinit bma023_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct bma023_sensor *sensor;
+	struct bma023_platform_data *pdata;
+	int ret;
+	sensor = kzalloc(sizeof(struct bma023_sensor), GFP_KERNEL);
+	if (!sensor) {
+		dev_err(&client->dev, "failed to allocate driver data\n");
+		return -ENOMEM;
+	}
+	pdata = client->dev.platform_data;
+	sensor->dev = &client->dev;
+
+	sensor->client = client;
+	i2c_set_clientdata(client, sensor);
+
+	ret = bma023_read_reg(client, BMA023_CHIP_ID_REG);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to detect device\n");
+		goto failed_free;
+	}
+	if (ret != BMA023_CHIP_ID) {
+		dev_err(&client->dev, "unsupported chip id\n");
+		goto failed_free;
+	}
+
+	mutex_init(&sensor->lock);
+
+	ret = sysfs_create_group(&client->dev.kobj, &bma023_group);
+	if (ret) {
+		dev_err(&client->dev, "failed to create attribute group\n");
+		goto failed_free;
+	}
+
+	pm_runtime_set_active(&client->dev);
+
+	ret = bma023_register_input_device(sensor);
+	if (ret)
+		dev_err(&client->dev, "only provide sysfs\n");
+
+	if (pdata) {
+		sensor->range = pdata->range;
+		sensor->bandwidth = pdata->bandwidth;
+		sensor->new_data_int = pdata->new_data_int;
+		sensor->hg_int = pdata->hg_int;
+		sensor->lg_int = pdata->lg_int;
+		sensor->hg_dur = pdata->hg_dur;
+		sensor->hg_thres = pdata->hg_thres;
+		sensor->hg_hyst = pdata->hg_hyst;
+		sensor->lg_dur = pdata->lg_dur;
+		sensor->lg_thres = pdata->lg_thres;
+		sensor->lg_hyst = pdata->lg_hyst;
+		sensor->auto_delay = pdata->auto_delay;
+	} else {
+		sensor->range = BMA023_DEFAULT_RANGE;
+		sensor->bandwidth = BMA023_DEFAULT_BANDWIDTH;
+		sensor->new_data_int = BMA023_DEFAULT_NEW_DATA_INT;
+		sensor->hg_int = BMA023_DEFAULT_HG_INT;
+		sensor->lg_int = BMA023_DEFAULT_LG_INT;
+		sensor->hg_dur = BMA023_DEFAULT_HG_DURATION;
+		sensor->hg_thres = BMA023_DEFAULT_HG_THRESHOLD;
+		sensor->hg_hyst = BMA023_DEFAULT_HG_HYST;
+		sensor->lg_dur = BMA023_DEFAULT_LG_DURATION;
+		sensor->lg_thres = BMA023_DEFAULT_LG_THRESHOLD;
+		sensor->lg_hyst = BMA023_DEFAULT_LG_HYST;
+		sensor->auto_delay = BMA023_DEFAULT_AUTO_DELAY;
+	}
+	sensor->power_mode = BMA023_SUSPEND;
+
+	bma023_initialize(sensor);
+
+	pm_runtime_enable(&client->dev);
+	pm_runtime_set_autosuspend_delay(&client->dev, sensor->auto_delay);
+
+	dev_info(&client->dev, "%s registered\n", id->name);
+	return 0;
+
+failed_free:
+	kfree(sensor);
+	return ret;
+}
+
+/**
+ *	bam023_remove	-	remove a sensor
+ *	@client: i2c client of sensor being removed
+ *
+ *	Our sensor is going away, clean up the resources.
+ */
+static int __devexit bma023_remove(struct i2c_client *client)
+{
+	struct bma023_sensor *sensor = i2c_get_clientdata(client);
+
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+
+	if (sensor->idev)
+		bma023_unregister_input_device(sensor);
+	sysfs_remove_group(&client->dev.kobj, &bma023_group);
+	kfree(sensor);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+/**
+ *	bma023_suspend		-	called on device suspend
+ *	@client: i2c client of sensor
+ *	@mesg: actual suspend type
+ *
+ *	Put the device into sleep mode before we suspend the machine.
+ */
+static int bma023_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	bma023_set_power_mode(client, BMA023_SUSPEND);
+	return 0;
+}
+
+/**
+ *	bma023_resume		-	called on device resume
+ *	@client: i2c client of sensor
+ *
+ *	Put the device into powered mode on resume.
+ */
+static int bma023_resume(struct i2c_client *client)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(&client->dev);
+	if (bma023_get_check_reg(client) != BMA023_INSPECT_VAL)
+		bma023_initialize(sensor);
+	bma023_set_power_mode(client, sensor->power_mode);
+	msleep(4);  /* wait for accel chip resume */
+	return 0;
+}
+
+#else
+#define bma023_suspend NULL
+#define bma023_resume NULL
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int bma023_runtime_suspend(struct device *dev)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	bma023_set_power_mode(sensor->client, BMA023_SUSPEND);
+	return 0;
+}
+
+static int bma023_runtime_resume(struct device *dev)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	bma023_set_power_mode(sensor->client, BMA023_RESUME);
+	msleep(4);  /* wait for accel chip resume */
+	return 0;
+}
+
+static const struct dev_pm_ops bma023_pm = {
+	.runtime_suspend = bma023_runtime_suspend,
+	.runtime_resume  = bma023_runtime_resume,
+};
+#endif
+
+static const struct i2c_device_id bma023_ids[] = {
+	{ "bma023", 0 },
+	{ "smb380", 1 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, bma023_ids);
+
+static struct i2c_driver bma023_i2c_driver = {
+	.driver	= {
+		.name	= "bma023",
+#ifdef CONFIG_PM_RUNTIME
+		.pm		= &bma023_pm,
+#endif		
+	},
+	.probe		= bma023_probe,
+	.remove		= __devexit_p(bma023_remove),
+	.suspend	= bma023_suspend,
+	.resume		= bma023_resume,
+	.id_table	= bma023_ids,
+};
+
+static int __init bma023_init(void)
+{
+	return i2c_add_driver(&bma023_i2c_driver);
+}
+module_init(bma023_init);
+
+static void __exit bma023_exit(void)
+{
+	i2c_del_driver(&bma023_i2c_driver);
+}
+module_exit(bma023_exit);
+
+MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>, Wistron Corp.");
+MODULE_DESCRIPTION("BMA023/SMB380 Tri-axis accelerometer driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/i2c/bma023.h b/include/linux/i2c/bma023.h
new file mode 100644
index 0000000..52a0df5
--- /dev/null
+++ b/include/linux/i2c/bma023.h
@@ -0,0 +1,44 @@
+/*
+ * bma023.h - BMA023 Tri-axis accelerometer driver
+ *
+ * Copyright (c) 2010 Samsung Eletronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _BMA023_H_
+#define _BMA023_H_
+
+#define	BMA023_RANGE_2G		0
+#define	BMA023_RANGE_4G		1
+#define	BMA023_RANGE_8G		2
+
+/* Used to setup the digital filtering bandwidth of ADC output */
+#define	BMA023_BW_25HZ		0
+#define	BMA023_BW_50HZ		1
+#define	BMA023_BW_100HZ		2
+#define	BMA023_BW_190HZ		3
+#define	BMA023_BW_375HZ		4
+#define	BMA023_BW_750HZ		5
+#define	BMA023_BW_1500HZ	6
+
+struct bma023_platform_data {
+	u8 range;
+	u8 bandwidth;
+	u8 new_data_int;
+	u8 hg_int;
+	u8 lg_int;
+	u8 lg_dur;
+	u8 lg_thres;
+	u8 lg_hyst;
+	u8 hg_dur;
+	u8 hg_thres;
+	u8 hg_hyst;
+	int auto_delay;		/* mS */
+};
+
+#endif /* _BMA023_H_ */

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] bma023: Support for Bosch BMA023 and SMB380 accelerometers
  2011-01-25 16:05 [PATCH] bma023: Support for Bosch BMA023 and SMB380 accelerometers Alan Cox
@ 2011-01-26 11:11 ` Jonathan Cameron
  2011-01-26 11:42   ` Alan Cox
  0 siblings, 1 reply; 5+ messages in thread
From: Jonathan Cameron @ 2011-01-26 11:11 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-input, Eric Piel, Hemanth V

On 01/25/11 16:05, Alan Cox wrote:
> From: Joseph Lai <joseph_lai@wistron.com>
> 
> This patch supports driver for BOSCH BMA023 and SMB380 which are accelerometers.

Hi Alan,

Good to see this driver moving forward.

I've cc'd Eric and Hemanth as I think they may wish to review this driver
or take part in any interface discussions.

A few general queries inline. (Note I review from the end so some comments
may seem backwards ;)

To sumarize

* Looks like a number of error codes that could usefully be passed on are
being eaten in the power management functions.

* Big need for documentation. We have a whole host of new attributes. Some
have come from other drivers, but still could do with documenting here.

* Lots of discussion on sysfs attributes needed, documentation will obviously
make this easier!  I think there are some magic numbers in here still in
no particular units? Difficult to tell without the datasheet.
> 
> We expose the data from this device to uusers through sysfs interface in
> the same manner as the lis drivers 

This is a somewhat misleading comment. Unless a lot of new sysfs changes to
lis3 drivers are set in a tree that hasn't merged, I'm seeing 3 attributes
in that driver (selftest, position and rate).  None of them match with any
in this driver. (obviously you don't actually say they are the same, but
the my initial assumption is that they would be so others may think this!)

If equivalents of these are sat in a tree somewhere a pointer to that tree
would be helpful.

Whilst several discussions of sysfs interfaces for these devices have bounced
around, no particularly firm decisions have been reached and perhaps we need
futher discussion.  We do have a fairly extensive set of equivalents in IIO
but are still open to other suggestions. (please see our docs in
staging/iio/Documentation) They are reasonaly comprehensive, though a few
things have snuck into the drivers without proper documentation in place.


Anyhow, few minors + interface issues.

Thanks for working on this one, it would be good to finally see it merge.

Jonathan
> so that the data can be used for one off
> uses, and also as as an input device for joystick type uses.



> 
> The driver allows the setting and reading of the device's properties via
> sysfs attributes.
> 
> Based on an original driver
> Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> 
> [Extensively rewritten]
> 
> Signed-off-by: Joseph Lai <joseph_lai@wistron.com>
> Signed-off-by: Alan Cox <alan@linux.intel.com>
> ---
> 
>  drivers/input/misc/Kconfig  |   10 
>  drivers/input/misc/Makefile |    1 
>  drivers/input/misc/bma023.c | 1306 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/i2c/bma023.h  |   44 +
>  4 files changed, 1361 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/input/misc/bma023.c
>  create mode 100644 include/linux/i2c/bma023.h
> 
> 
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index b0c6772..3b595b8 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -454,4 +454,14 @@ config INPUT_CMA3000_I2C
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called cma3000_d0x_i2c.
>  
> +config INPUT_BMA023
> +	tristate "BMA023/SMB380 Triaxial acceleration sensor"
> +	depends on I2C
> +	help
> +	  Say Y here if you want to support Bosch BMA023/SMB380
> +	  connected via an I2C bus.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called bma023.
> +
>  endif
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 9b47971..79b3f66 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -36,6 +36,7 @@ obj-$(CONFIG_INPUT_PWM_BEEPER)		+= pwm-beeper.o
>  obj-$(CONFIG_INPUT_RB532_BUTTON)	+= rb532_button.o
>  obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)	+= rotary_encoder.o
>  obj-$(CONFIG_INPUT_SGI_BTNS)		+= sgi_btns.o
> +obj-$(CONFIG_INPUT_BMA023)			+= bma023.o
>  obj-$(CONFIG_INPUT_SPARCSPKR)		+= sparcspkr.o
>  obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON)	+= twl4030-pwrbutton.o
>  obj-$(CONFIG_INPUT_TWL4030_VIBRA)	+= twl4030-vibra.o
> diff --git a/drivers/input/misc/bma023.c b/drivers/input/misc/bma023.c
> new file mode 100644
> index 0000000..489f363
> --- /dev/null
> +++ b/drivers/input/misc/bma023.c
> @@ -0,0 +1,1306 @@
> +/*
> + * bma023.c - BMA023 Tri-axis accelerometer driver
> + *
> + * Copyright (C) 2010 Samsung Eletronics Co.Ltd
> + * Kim Kyuwon <q1.kim@samsung.com>
> + * Kyungmin Park <kyungmin.park@samsung.com>
> + * Donggeun Kim <dg77.kim@samsung.com>
> + *
> + * Copyright (C) 2011 Wistron Co.Ltd
> + * Joseph Lai <joseph_lai@wistron.com>
> + *
> + * Copyright (c) 2011 Intel Corporation
> + * Alan Cox <alan@linux.intel.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + *
> + * 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.
> + *
> + * TODO (After)
> + *	Investigate if the IRQ disable is really needed (needs that data
> + *		sheet reviewing in more detail)
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/mutex.h>
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/input.h>
> +#include <linux/i2c/bma023.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/pm_runtime.h>
> +
> +#define BMA023_DEFAULT_AUTO_DELAY	250	/* mS */
> +
> +#define BMA023_CHIP_ID_REG	0x00
> +#define BMA023_X_LSB_REG	0x02
> +#define BMA023_X_MSB_REG	0x03
> +#define BMA023_Y_LSB_REG	0x04
> +#define BMA023_Y_MSB_REG	0x05
> +#define BMA023_Z_LSB_REG	0x06
> +#define BMA023_Z_MSB_REG	0x07
> +#define BMA023_TEMP_REG		0x08 /*  An output of 0 equals -30C,
> +					 1 LSB equals 0.5C */
> +#define BMA023_CTRL1_REG	0x0a
> +#define BMA023_CTRL2_REG	0x0b
> +#define BMA023_SETTINGS1_REG	0x0c
> +#define BMA023_SETTINGS2_REG	0x0d
> +#define BMA023_SETTINGS3_REG	0x0e
> +#define BMA023_SETTINGS4_REG	0x0f
> +#define BMA023_SETTINGS5_REG	0x10
> +#define BMA023_SETTINGS6_REG	0x11
> +#define BMA023_RANGE_BW_REG	0x14
> +#define BMA023_CONF2_REG	0x15
> +
> +#define BMA023_CHIP_ID		0x02
> +
> +#define BMA023_NEW_DATA_INT_SHIFT	5
> +#define BMA023_NEW_DATA_INT_MASK	(0x1 << 5)
> +
> +#define BMA023_RANGE_SHIFT		3
> +#define BMA023_RANGE_MASK		(0x3 << 3)
> +#define BMA023_BANDWIDTH_SHIFT		0
> +#define BMA023_BANDWIDTH_MASK		(0x7)
> +
> +#define BMA023_HG_HYST_SHIFT		3
> +#define BMA023_HG_HYST_MASK		(0x7 << 3)
> +#define BMA023_LG_HYST_SHIFT		0
> +#define BMA023_LG_HYST_MASK		(0x7)
> +
> +#define BMA023_HG_DUR_SHIFT		(0x0)
> +#define BMA023_HG_DUR_MASK		(0xff)
> +#define BMA023_HG_THRES_SHIFT		(0x0)
> +#define BMA023_HG_THRES_MASK		(0xff)
> +#define BMA023_LG_DUR_SHIFT		(0x0)
> +#define BMA023_LG_DUR_MASK		(0xff)
> +#define BMA023_LG_THRES_SHIFT		(0x0)
> +#define BMA023_LG_THRES_MASK		(0xff)
> +
> +#define BMA023_ENABLE_HG_SHIFT		1
> +#define BMA023_ENABLE_HG_MASK		(0x1 << 1)
> +#define BMA023_ENABLE_LG_SHIFT		0
> +#define BMA023_ENABLE_LG_MASK		(0x1)
> +
> +#define BMA023_SLEEP_SHIFT		0
> +#define BMA023_SLEEP_MASK		(0x1)
> +
> +#define BMA023_ACCEL_BITS		10
> +#define BMA023_MAX_VALUE		((1 << ((BMA023_ACCEL_BITS) - 1)) - 1)
> +#define BMA023_MIN_VALUE		(-(1 << ((BMA023_ACCEL_BITS) - 1)))
> +
> +#define BMA023_DEFAULT_RANGE		BMA023_RANGE_2G
> +#define BMA023_DEFAULT_BANDWIDTH	BMA023_BW_190HZ
> +#define BMA023_DEFAULT_NEW_DATA_INT	0
> +#define BMA023_DEFAULT_HG_INT		0
> +#define BMA023_DEFAULT_LG_INT		0
> +#define BMA023_DEFAULT_HG_DURATION	0x05
> +#define BMA023_DEFAULT_HG_THRESHOLD	0xa0
> +#define BMA023_DEFAULT_HG_HYST		0
> +#define BMA023_DEFAULT_LG_DURATION	0x05
> +#define BMA023_DEFAULT_LG_THRESHOLD	0x14
> +#define BMA023_DEFAULT_LG_HYST		0
> +
> +#define BMA023_SUSPEND	0
> +#define BMA023_RESUME	1
> +
> +#define BMA023_CUSTOMER_REG	0x12
> +#define BMA023_INSPECT_VAL	0xBB
> +#define BMA023_CHECK_SHIFT	0
> +#define BMA023_CHECK_MASK	0xff
> +
> +struct bma023_data {
> +	s16 x;
> +	s16 y;
> +	s16 z;
> +};
> +
> +struct bma023_sensor {
> +	struct i2c_client *client;
> +	struct device *dev;
> +	struct input_dev *idev;
> +	struct mutex lock;
> +	struct bma023_data data;
> +	int auto_delay;
I for one have no idea what auto_delay and soft_power might be...
Perhaps some documentation?
> +	int soft_power;
> +
> +	u8 range;
> +	u8 bandwidth;
> +	u8 new_data_int;
> +	u8 hg_int;
> +	u8 lg_int;
> +	u8 lg_dur;
> +	u8 lg_thres;
> +	u8 lg_hyst;
> +	u8 hg_dur;
> +	u8 hg_thres;
> +	u8 hg_hyst;
> +	u8 power_mode;
> +};
> +
> +/**
> + *	bma023_write_reg	-	write a BMA023 register via I2C
> + *	@reg: register being updated
> + *	@val: value to write
> + *
> + *	Updates the register or returns an error if the write fails.
> + */
> +static int bma023_write_reg(struct i2c_client *client, u8 reg, u8 val)
> +{
> +	int ret;
> +	/*
> +	 * According to the datasheet, the interrupt should be deactivated
> +	 * on the host side when write sequences operate.
> +	 */
> +	disable_irq_nosync(client->irq);
> +	ret = i2c_smbus_write_byte_data(client, reg, val);
> +	enable_irq(client->irq);
> +	if (ret < 0)
> +		dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
> +			__func__, reg, val, ret);
> +	return ret;
> +}
> +
> +/**
> + *	bma023_read_reg	-	read a BMA023 register via I2C
> + *	@reg: register being updated
> + *	@val: value to write
> + *
> + *	Reads the register or returns a negative error code on failure.
> + */
> +static int bma023_read_reg(struct i2c_client *client, u8 reg)
> +{
> +	int ret = i2c_smbus_read_byte_data(client, reg);
> +	if (ret < 0)
> +		dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
> +			__func__, reg, ret);

Do we need this wrapper? It does pretty print any errors, but do we
care assuming they are correctly passed on?
> +	return ret;
> +}
> +
> +/**
> + *	bma023_xyz_read_reg	-	read the axes values
> + *	@reg: register being updated
> + *	@val: value to write
> + *
> + *	Reads the register values in one transaction or returns a negative
> + *	error code on failure/
> + */
> +static int bma023_xyz_read_reg(struct i2c_client *client,
> +			       u8 *buffer, int length)
> +{
> +	struct i2c_msg msg[] = {
> +		{
> +			.addr = client->addr,
> +			.flags = 0,
> +			.len = 1,
> +			.buf = buffer,
> +		},
}, { ?
> +		{
> +			.addr = client->addr,
> +			.flags = I2C_M_RD,
> +			.len = length,
> +			.buf = buffer,
> +		},
> +	};
> +	return i2c_transfer(client->adapter, msg, 2);
> +}
> +
> +/**
> + *	bma023_set_reg_bits	-	update a subset of register bits
> + *	@client: i2c client for the sensor
> + *	@val: value to write
> + *	@shift: bit offset of field
> + *	@mask: bit mask of field to update
> + *	@reg: register number
> + *
> + *	Update one of the many bitfield registers on the BMA023 by reading
> + *	and writing back bits. The caller *MUST* hold the sensor lock when
> + *	using this function in order to prevent overlapping updates corrupting
> + *	the register.
> + *
> + *	Returns success or an error code
> + */
> +static int bma023_set_reg_bits(struct i2c_client *client,
> +					int val, int shift, u8 mask, u8 reg)
> +{
> +	int data = bma023_read_reg(client, reg);
> +
> +	if (data < 0)
> +		return data;
> +
> +	data = (data & ~mask) | ((val << shift) & mask);
> +	return bma023_write_reg(client, reg, data);
> +}
> +
> +/**
> + *	bma023_get_reg_bits	-	read register bitfield
> + *	@client: i2c client for the sensor
> + *	@shift: bit offset of field
> + *	@mask: bit mask of field to update
> + *	@reg: register number
> + *
> + *	Read a bit field from the sensor and return the value of the field. The
> + *	lock is not needed in this case
> + */
> +static int bma023_get_reg_bits(struct i2c_client *client, int shift,
> +					u8 mask, u8 reg)
> +{
> +	int data = bma023_read_reg(client, reg);
> +	if (data < 0)
> +		return data;
> +	data = (data & mask) >> shift;
> +	return data;
> +}
> +
I can't help feeling that much of the following could be combined
into 2 functions with the right macro foo and container structures,
much the way it is done in hwmon an others.
> +/*
> + *	Functions for handling all the configurable fields
> + */
> +
> +/**
> + *	bma023_set_check_reg	-	set an inspective value
> + *	@client: i2c client for the sensor
> + *	@check: update the inspective value
> + *
> + *	Set an inspective value to customer's register, which can record
> + *	any value from user. The inspective value will keep until the chip
> + *	off or shutdown. User can recognize the chip needs re-initialize
> + *	or not by this register.
> + */
> +static int bma023_set_check_reg(struct i2c_client *client, u8 check)
> +{
> +	return bma023_set_reg_bits(client, check, BMA023_CHECK_SHIFT,
> +				   BMA023_CHECK_MASK, BMA023_CUSTOMER_REG);
> +}
> +
> +static int bma023_get_check_reg(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_CHECK_SHIFT,
> +				   BMA023_CHECK_MASK, BMA023_CUSTOMER_REG);
> +}
> +
> +/**
> + *	bma023_set_range	-	set the full scale acceleration range
> + *	@client: i2c client for the sensor
> + *	@range: new range setting
> + *
> + *	The range of bma023 will affect the chip to change the sensitivity 
> + *	of gravitation. The value of range includes +/- 2g, 4g and 8g.
> + *
> + *	The change of range will respond to 3 axes directly.
> + */
> +static int bma023_set_range(struct i2c_client *client, u8 range)
> +{
> +	return bma023_set_reg_bits(client, range, BMA023_RANGE_SHIFT,
> +				   BMA023_RANGE_MASK, BMA023_RANGE_BW_REG);
> +}
> +
> +static int bma023_get_range(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_RANGE_SHIFT,
> +				   BMA023_RANGE_MASK, BMA023_RANGE_BW_REG);
> +}
> +
> +/**
> + *	bma023_set_bandwidth	-	set the digital filtering of ADC output data
> + *	@client: i2c client for the sensor
> + *	@bw: new bandwidth setting
> + *
> + *	The bandwidth affect the ability of noise filter. The value of bandwidth
> + *	includes 25Hz, 50Hz, 100Hz, 190Hz, 375Hz, 750Hz and 1500Hz.
> + *
> + *	The change of bandwidth will respond to 3 axes directly.
> + */
> +static int bma023_set_bandwidth(struct i2c_client *client, u8 bw)
> +{
> +	return bma023_set_reg_bits(client, bw, BMA023_BANDWIDTH_SHIFT,
> +				   BMA023_BANDWIDTH_MASK, BMA023_RANGE_BW_REG);
> +}
> +
> +static int bma023_get_bandwidth(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_BANDWIDTH_SHIFT,
> +				   BMA023_BANDWIDTH_MASK, BMA023_RANGE_BW_REG);
> +}
> +
> +/**
> + *	bma023_set_new_data_int	-	switch on/off of new data interrupt
> + *	@client: i2c client for the sensor
> + *	@val: value to switch on/off, 1: on, 0: off
> + *
> + *	To turn on this interrupt will generate an interrupt when all three
> + *	axes acceleration values are new.
> + */
> +static int bma023_set_new_data_int(struct i2c_client *client, u8 val)
> +{
> +	return bma023_set_reg_bits(client, val, BMA023_NEW_DATA_INT_SHIFT,
> +				   BMA023_NEW_DATA_INT_MASK, BMA023_CONF2_REG);
> +}
> +
> +static int bma023_get_new_data_int(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_NEW_DATA_INT_SHIFT,
> +				   BMA023_NEW_DATA_INT_MASK, BMA023_CONF2_REG);
> +}
> +
> +/**
> + *	bma023_set_hg_int	-	switch on/off of high-g interrupt
> + *	@client: i2c client for the sensor
> + *	@val: value to switch on/off, 1: on, 0: off
> + *
> + *	To turn on this interrupt will generate an interrupt when the
> + *	oscillatory criteria is satisfied by hg_dur and hg_thres.
> + *
> + *	Usually, it is used to debounce the high-g criteria.
> + */
> +static int bma023_set_hg_int(struct i2c_client *client, u8 val)
> +{
> +	return bma023_set_reg_bits(client, val, BMA023_ENABLE_HG_SHIFT,
> +				   BMA023_ENABLE_HG_MASK, BMA023_CTRL2_REG);
> +}
> +
> +static int bma023_get_hg_int(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_ENABLE_HG_SHIFT,
> +				   BMA023_ENABLE_HG_MASK, BMA023_CTRL2_REG);
> +}
> +
> +/**
> + *	bma023_set_lg_int	-	switch on/off of low-g interrupt
> + *	@client: i2c client for the sensor
> + *	@val: value to switch on/off, 1: on, 0: off
> + *
> + *	To turn on this setting will generate an interrupt when the
> + *	oscillatory criteria is satisfied by lg_dur and lg_thres.
> + *
> + *	Usually, it is used to detect a free fall.
> + */
> +static int bma023_set_lg_int(struct i2c_client *client, u8 val)
> +{
> +	return bma023_set_reg_bits(client, val,	BMA023_ENABLE_LG_SHIFT,
> +				   BMA023_ENABLE_LG_MASK, BMA023_CTRL2_REG);
> +}
> +
> +static int bma023_get_lg_int(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_ENABLE_LG_SHIFT,
> +				   BMA023_ENABLE_LG_MASK, BMA023_CTRL2_REG);
> +}
> +
> +/**
> + *	bma023_set_lg_dur	-	set low-g duration
> + *	@client: i2c client for the sensor
> + *	@dur: duration value, 0-255
> + *	
> + *	Generate an interrupt when the gravitation is detected in low-g
> + *	criterion for long enough duration.
> + *
> + *	This setting is used to interrupt detection.
> + */
> +static int bma023_set_lg_dur(struct i2c_client *client, u8 dur)
> +{
> +	return bma023_set_reg_bits(client, dur,	BMA023_LG_DUR_SHIFT,
> +				   BMA023_LG_DUR_MASK, BMA023_SETTINGS2_REG);
> +}
> +
> +static int bma023_get_lg_dur(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_LG_DUR_SHIFT,
> +				   BMA023_LG_DUR_MASK, BMA023_SETTINGS2_REG);
> +}
> +
> +/**
> + *	bma023_set_lg_thres	-	set low-g threshold
> + *	@client: i2c client for the sensor
> + *	@thres: threshold value, 0-255
> + *	
> + *	Filter 3 axes detection and limit to trigger an interrupt signal
> + *	when the gravitation is detected in low-g threshold.
> + *
> + *	This setting is used to interrupt detection.
> + */
> +static int bma023_set_lg_thres(struct i2c_client *client, u8 thres)
> +{
> +	return bma023_set_reg_bits(client, thres, BMA023_LG_THRES_SHIFT,
> +				   BMA023_LG_THRES_MASK, BMA023_SETTINGS1_REG);
> +}
> +
> +static int bma023_get_lg_thres(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_LG_THRES_SHIFT,
> +				   BMA023_LG_THRES_MASK, BMA023_SETTINGS1_REG);
> +}
> +
> +/**
> + *	bma023_set_lg_hyst	-	set low-g hysteresis
> + *	@client: i2c client for the sensor
> + *	@hyst: hysteresis value, 0-7
> + *	
> + *	Expand the range of detectable gravitation to reset an interrupt.
> + *	An interrupt will be latched until outside of low-g criterion with 
> + *	hysteresis.
> + *
> + *	This setting is used to interrupt detection.
> + */
> +static int bma023_set_lg_hyst(struct i2c_client *client, u8 hyst)
> +{
> +	return bma023_set_reg_bits(client, hyst, BMA023_LG_HYST_SHIFT,
> +				   BMA023_LG_HYST_MASK, BMA023_SETTINGS6_REG);
> +}
> +
> +static int bma023_get_lg_hyst(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_LG_HYST_SHIFT,
> +				   BMA023_LG_HYST_MASK,	BMA023_SETTINGS6_REG);
> +}
> +
> +/**
> + *	bma023_set_hg_dur	-	set high-g duration
> + *	@client: i2c client for the sensor
> + *	@dur: duration value, 0-255
Again, units are?  We really need this stuff to be clear.
> + *	
> + *	Generate an interrupt when the gravitation is detected in high-g
> + *	criterion for long enough duration.
> + *
> + *	This setting is used to interrupt detection.
> + */
> +static int bma023_set_hg_dur(struct i2c_client *client, u8 dur)
> +{
> +	return bma023_set_reg_bits(client, dur,	BMA023_HG_DUR_SHIFT,
> +				   BMA023_HG_DUR_MASK, BMA023_SETTINGS4_REG);
> +}
> +
> +static int bma023_get_hg_dur(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_HG_DUR_SHIFT,
> +				   BMA023_HG_DUR_MASK, BMA023_SETTINGS4_REG);
> +}
> +
> +/**
> + *	bma023_set_hg_thres	-	set high-g threshold
> + *	@client: i2c client for the sensor
> + *	@thres: threshold value, 0-255
Units are?  
> + *	
> + *	Filter 3 axes detection and limit to trigger an interrupt signal
> + *	when the gravitation is detected in high-g threshold.
> + *
> + *	This setting is used to interrupt detection.
> + */
> +static int bma023_set_hg_thres(struct i2c_client *client, u8 thres)
> +{
> +	return bma023_set_reg_bits(client, thres, BMA023_HG_THRES_SHIFT,
> +				   BMA023_HG_THRES_MASK, BMA023_SETTINGS3_REG);
> +}
> +
> +static int bma023_get_hg_thres(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_HG_THRES_SHIFT,
> +				   BMA023_HG_THRES_MASK, BMA023_SETTINGS3_REG);
> +}
> +
> +/**
> + *	bma023_set_hg_hyst	-	set low-g hysteresis
> + *	@client: i2c client for the sensor
> + *	@hyst: hysteresis value, 0-7
What units?  This is passed straight up to userspace, so unit free isn't
a generalizable option.
> + *	
> + *	Expand the range of detectable gravitation to reset an interrupt.
> + *	An interrupt will be latched until outside of high-g criterion with 
> + *	hysteresis.
> + *
> + *	This setting is used to interrupt detection.
> + */
> +static int bma023_set_hg_hyst(struct i2c_client *client, u8 hyst)
> +{
> +	return bma023_set_reg_bits(client, hyst, BMA023_HG_HYST_SHIFT,
> +				   BMA023_HG_HYST_MASK,	BMA023_SETTINGS6_REG);
> +}
> +
> +static int bma023_get_hg_hyst(struct i2c_client *client)
> +{
> +	return bma023_get_reg_bits(client, BMA023_HG_HYST_SHIFT,
> +				   BMA023_HG_HYST_MASK,	BMA023_SETTINGS6_REG);
> +}
> +
> +/**
> + *	bma023_power_mode	-	set the power mode
> + *	@client: i2c client for the sensor
> + *	@val: value to switch on/off of power, 1: normal power, 0: low power
> + *
> + *	Put device to normal-power mode or low-power mode.
> + */
> +static int bma023_set_power_mode(struct i2c_client *client, u8 val)
> +{
> +	return bma023_set_reg_bits(client, ~val, BMA023_SLEEP_SHIFT,
> +				   BMA023_SLEEP_MASK, BMA023_CTRL1_REG);
> +}
> +
> +static int bma023_get_power_mode(struct i2c_client *client)
> +{
> +	return !bma023_get_reg_bits(client, BMA023_SLEEP_SHIFT,
> +				   BMA023_SLEEP_MASK, BMA023_CTRL1_REG);
> +}
> +
> +/**
> + *	bma023_correct_accel_sign	-	convert to expected format
> + *	@val: twos complement value
> + *
> + *	The description of the digital signals x, y and z is "2' complement".
> + *	So we need to correct the sign of data read by i2c.
> + */
I keep thinking this sort of sign extension really ought to be in a standard
header somewhere. It turns up in so many drivers...
> +static s16 bma023_correct_accel_sign(s16 val)
> +{
> +	val <<= (sizeof(s16) * BITS_PER_BYTE - BMA023_ACCEL_BITS);
> +	val >>= (sizeof(s16) * BITS_PER_BYTE - BMA023_ACCEL_BITS);
> +	return val;
> +}
> +
> +/**
> + *	bma023_merge_register_values	-	turn into co-ordinate
> + *	@lsb: low bits from sensor
> + *	@msb: high bits from sensor
> + *
> + *	Turns a sensor register reading into a Linux input layer
> + *	value and returns it.
> + */
> +static s16 bma023_merge_register_values(u8 lsb, u8 msb)
> +{
> +	s16 val = (msb << 2) | (lsb >> 6);
> +	return bma023_correct_accel_sign(val);
> +}
> +
> +/**
> + *	bma023_read_xyz		-	get co-ordinates from device
> + *	@client: i2c address of sensor
> + *	@coords: co-ordinates to update
> + *
> + *	Return the converted X Y and Z co-ordinates from the sensor device
> + */
> +static void bma023_read_xyz(struct i2c_client *client,
> +				struct bma023_data *coords)
> +{
> +	u8 buffer[6];
> +	buffer[0] = BMA023_X_LSB_REG;
> +	bma023_xyz_read_reg(client, buffer, 6);
> +	coords->x = bma023_merge_register_values(buffer[0], buffer[1]);
> +	coords->y = bma023_merge_register_values(buffer[2], buffer[3]);
> +	coords->z = bma023_merge_register_values(buffer[4], buffer[5]);
> +	dev_dbg(&client->dev, "%s: x %d, y %d, z %d\n", __func__,
> +					coords->x, coords->y, coords->z);
> +}
> +
> +/**
> + *	bma023_show_xyz		-	show co-ordinate readings
> + *	@dev: device of sensor
> + *	@attr: device attributes of sysfs node
> + *	@buf: buffer for output
> + *
> + *	Perform a one off read of the sensor data. In non-interrupt mode
> + *	the sensor can be used for one off reads of the axes rather than
> + *	as an input device.
> + */
> +static ssize_t bma023_show_xyz(struct device *dev,
> +			       struct device_attribute *attr, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +
> +	pm_runtime_get_sync(dev);
> +
> +	mutex_lock(&sensor->lock);
> +	bma023_read_xyz(sensor->client, &sensor->data);
> +	mutex_unlock(&sensor->lock);
> +
> +	pm_runtime_put(dev);
> +
These look to be raw adc values. If you want to do that, please provide
in sysfs the numbers needed to convert them to SI units.
> +	return sprintf(buf, "(%d,%d,%d)\n",
> +		sensor->data.x, sensor->data.y, sensor->data.z);
> +}
> +static DEVICE_ATTR(accel_data, S_IRUGO, bma023_show_xyz, NULL);
> +
> +/**
> + *	bma023_set_attr	-	set an attribute on the sensor
> + *	@sensor sensor to update
> + *	@buf: buffer to parse
> + *	@count: length of buffer written
> + *	@get: read function to use
> + *	@set: write function to use
> + *	@data: where to store cached result
> + *
> + *	Helper function for setting attributes of the sensor. This does
> + *	all the parsing and locking in one place calling out to the helper
> + *	functions provided.
> + */
> +static ssize_t bma023_set_attr(struct bma023_sensor *sensor,
> +				const char *buf, size_t count,
> +				int (*get)(struct i2c_client *),
> +				int (*set)(struct i2c_client *, u8 val),
> +				u8 *data)
> +{
> +	unsigned long val;
> +	int ret;
> +
> +	if (!count)
> +		return count;
> +
> +	ret = strict_strtoul(buf, 10, &val);
> +	if (!ret) {
> +		/* We don't need the device to be fully powered up for this
> +		   but we do need to be sure that we force the i²c controller
> +		   to be awake */
> +		pm_runtime_get_sync(sensor->dev);
> +		mutex_lock(&sensor->lock);
> +		ret = set(sensor->client, val);
> +		if (ret >= 0) {
> +			ret = get(sensor->client);
> +			if (ret >= 0)
> +				*data = ret;
> +		}
> +		mutex_unlock(&sensor->lock);
> +		pm_runtime_put(sensor->dev);
> +		if (ret >= 0)
> +			return count;
> +	}
> +	return ret;
> +}
> +
> +/* Methods for the sysfs attributes */
> +static ssize_t bma023_show_power_mode(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", bma023_get_power_mode(sensor->client));
> +}
> +
> +static ssize_t bma023_store_power_mode(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	unsigned long val;
> +	int ret = strict_strtoul(buf, 10, &val);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&sensor->lock);
> +	if (val & !sensor->soft_power)
> +		pm_runtime_get(dev);
> +	else if (!val && sensor->soft_power)
> +		pm_runtime_put(dev);
> +	sensor->soft_power = val;
> +	mutex_unlock(&sensor->lock);
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(power_mode, S_IRUGO | S_IWUSR,
> +		bma023_show_power_mode, bma023_store_power_mode);
> +
> +static ssize_t bma023_show_range(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", sensor->range);
> +}
> +
> +static ssize_t bma023_store_range(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return bma023_set_attr(sensor, buf, count,
> +			bma023_get_range,
> +			bma023_set_range,
> +			&sensor->range);
> +}
> +
> +static DEVICE_ATTR(range, S_IRUGO | S_IWUSR,
> +		bma023_show_range, bma023_store_range);
> +
> +static ssize_t bma023_show_bandwidth(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", sensor->bandwidth);
> +}
> +
> +static ssize_t bma023_store_bandwidth(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return bma023_set_attr(sensor, buf, count,
> +			bma023_get_bandwidth,
> +			bma023_set_bandwidth,
> +			&sensor->bandwidth);
> +}
> +
> +static DEVICE_ATTR(bandwidth, S_IRUGO | S_IWUSR,
> +		bma023_show_bandwidth, bma023_store_bandwidth);
> +
> +static ssize_t bma023_show_new_data_int(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", sensor->new_data_int);
> +}
> +
> +static ssize_t bma023_store_new_data_int(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return bma023_set_attr(sensor, buf, count,
> +			bma023_get_new_data_int,
> +			bma023_set_new_data_int,
> +			&sensor->new_data_int);
> +}
> +
> +static DEVICE_ATTR(new_data_int, S_IRUGO | S_IWUSR,
> +		bma023_show_new_data_int, bma023_store_new_data_int);
> +
> +static ssize_t bma023_show_hg_int(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", sensor->hg_int);
> +}
> +
> +static ssize_t bma023_store_hg_int(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return bma023_set_attr(sensor, buf, count,
> +			bma023_get_hg_int,
> +			bma023_set_hg_int,
> +			&sensor->hg_int);
> +}
> +
> +static DEVICE_ATTR(hg_int, S_IRUGO | S_IWUSR,
> +		bma023_show_hg_int, bma023_store_hg_int);
> +
> +static ssize_t bma023_show_lg_int(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", sensor->lg_int);
> +}
> +
> +static ssize_t bma023_store_lg_int(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return bma023_set_attr(sensor, buf, count,
> +			bma023_get_lg_int,
> +			bma023_set_lg_int,
> +			&sensor->lg_int);
> +}
> +
> +static DEVICE_ATTR(lg_int, S_IRUGO | S_IWUSR,
> +		bma023_show_lg_int, bma023_store_lg_int);
> +
> +static ssize_t bma023_show_lg_dur(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", sensor->lg_dur);
> +}
> +
> +static ssize_t bma023_store_lg_dur(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return bma023_set_attr(sensor, buf, count,
> +			bma023_get_lg_dur,
> +			bma023_set_lg_dur,
> +			&sensor->lg_dur);
> +}
> +
> +static DEVICE_ATTR(lg_dur, S_IRUGO | S_IWUSR,
> +		bma023_show_lg_dur, bma023_store_lg_dur);
> +
> +static ssize_t bma023_show_lg_thres(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", sensor->lg_thres);
> +}
> +
> +static ssize_t bma023_store_lg_thres(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return bma023_set_attr(sensor, buf, count,
> +			bma023_get_lg_thres,
> +			bma023_set_lg_thres,
> +			&sensor->lg_thres);
> +}
> +
> +static DEVICE_ATTR(lg_thres, S_IRUGO | S_IWUSR,
> +		bma023_show_lg_thres, bma023_store_lg_thres);
> +
> +static ssize_t bma023_show_lg_hyst(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", sensor->lg_hyst);
> +}
> +
> +static ssize_t bma023_store_lg_hyst(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return bma023_set_attr(sensor, buf, count,
> +			bma023_get_lg_hyst,
> +			bma023_set_lg_hyst,
> +			&sensor->lg_hyst);
> +}
> +
> +static DEVICE_ATTR(lg_hyst, S_IRUGO | S_IWUSR,
> +		bma023_show_lg_hyst, bma023_store_lg_hyst);
> +
> +static ssize_t bma023_show_hg_dur(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", sensor->hg_dur);
> +}
> +
> +static ssize_t bma023_store_hg_dur(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return bma023_set_attr(sensor, buf, count,
> +			bma023_get_hg_dur,
> +			bma023_set_hg_dur,
> +			&sensor->hg_dur);
> +}
> +
> +static DEVICE_ATTR(hg_dur, S_IRUGO | S_IWUSR,
> +		bma023_show_hg_dur, bma023_store_hg_dur);
> +
> +static ssize_t bma023_show_hg_thres(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", sensor->hg_thres);
> +}
> +
> +static ssize_t bma023_store_hg_thres(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return bma023_set_attr(sensor, buf, count,
> +			bma023_get_hg_thres,
> +			bma023_set_hg_thres,
> +			&sensor->hg_thres);
> +}
> +
> +static DEVICE_ATTR(hg_thres, S_IRUGO | S_IWUSR,
> +		bma023_show_hg_thres, bma023_store_hg_thres);
> +
> +static ssize_t bma023_show_hg_hyst(struct device *dev,
> +		struct device_attribute *att, char *buf)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return sprintf(buf, "%d\n", sensor->hg_hyst);
> +}
> +
> +static ssize_t bma023_store_hg_hyst(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	return bma023_set_attr(sensor, buf, count,
> +			bma023_get_hg_hyst,
> +			bma023_set_hg_hyst,
> +			&sensor->hg_hyst);
> +}
> +
> +static DEVICE_ATTR(hg_hyst, S_IRUGO | S_IWUSR,
> +		bma023_show_hg_hyst, bma023_store_hg_hyst);
> +
> +static struct attribute *bma023_attributes[] = {
> +	&dev_attr_accel_data.attr,
These are raw values I think?  Again, we allow this in IIO, but insist
on conversion factors to m/s^2 also being provided in the sysfs interface
so userspace knows what it is getting.

> +	&dev_attr_power_mode.attr,
Perhaps rename this low_power_mode and invert the value as then
it won't be a pair of magic numbers.

> +	&dev_attr_range.attr,
Lots of discussion on this one has occured in the past. Personally I think
having an attribute for the gain is clearer (came out of ALS type devices
where the concept of 'range' is very dependent on what the input light spectrum
is). Still don't really care about that one!

> +	&dev_attr_bandwidth.attr,
I'm guessing this controls a filter?  If so it is one area I don't think
anyone has come up with a convention for yet in sysfs.  One to discuss
(quite a few of Analog's sensors have complex filtering options, but I
don't think any drivers support them yet).

> +	&dev_attr_new_data_int.attr,
(is this enabling and disabling the interrupt? Surely that should be handled
by the input code and doesn't really want to be exposed?
> +	&dev_attr_hg_int.attr,
I'm definitely against the terms hg and lg.  High g is a silly
name even if Bosch have used it on the sensor.  Also, these really
don't generalize well (again I'm thinking of other sensor types).
(as an aside, I'd reorganise this list so either the equivalent
parameters are together or all the lg and hg ones are grouped).

For reference, the IIO equivalents may seem long winded, but they
are clear, consistent and generalizable. I'm guessing a bit on
what the bosch chip is doing based on other parts.

accel_x&y&z_mag_falling_*
(for low threshold detectors it tends to be all are below a level)
accel_x|y|z_mag_rising_*
(for high mag, they tend to detect if any channel is above a level).
Otherwise, the naming difference are minor. We went with period instead
of dur, _en instead of _int and hyst as you have
(though looks like that one is missing docs). 

> +	&dev_attr_lg_int.attr,
> +	&dev_attr_lg_dur.attr,
> +	&dev_attr_lg_thres.attr,
> +	&dev_attr_lg_hyst.attr,
> +	&dev_attr_hg_dur.attr,
> +	&dev_attr_hg_thres.attr,
> +	&dev_attr_hg_hyst.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group bma023_group = {
> +	.attrs	= bma023_attributes,
> +};
> +
> +/**
> + *	bma023_interrupt_thread	-	handle an IRQ
> + *	@irq: interrupt numner
typo
> + *	@data: the sensor
> + *
> + *	Called by the kernel single threaded after an interrupt occurs. Read
> + *	the sensor data and generate an input event for it.
> + */
> +static irqreturn_t bma023_interrupt_thread(int irq, void *data)
> +{
> +	struct bma023_sensor *sensor = data;
> +
> +	mutex_lock(&sensor->lock);
> +	bma023_read_xyz(sensor->client, &sensor->data);
> +	mutex_unlock(&sensor->lock);
> +
> +	input_report_abs(sensor->idev, ABS_X, sensor->data.x);
> +	input_report_abs(sensor->idev, ABS_Y, sensor->data.y);
> +	input_report_abs(sensor->idev, ABS_Z, sensor->data.z);
> +	input_sync(sensor->idev);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + *	bma023_initialize	-	set sensor parameters
> + *	@sensor: sensor to configure
> + *
> + *	Load the actual hardware with the sensor values that were provided
> + */
> +static void bma023_initialize(struct bma023_sensor *sensor)
> +{
> +	bma023_set_range(sensor->client, sensor->range);
> +	bma023_set_bandwidth(sensor->client, sensor->bandwidth);
> +	bma023_set_new_data_int(sensor->client, sensor->new_data_int);
> +	bma023_set_hg_dur(sensor->client, sensor->hg_dur);
> +	bma023_set_hg_thres(sensor->client, sensor->hg_thres);
> +	bma023_set_hg_hyst(sensor->client, sensor->hg_hyst);
> +	bma023_set_lg_dur(sensor->client, sensor->lg_dur);
> +	bma023_set_lg_thres(sensor->client, sensor->lg_thres);
> +	bma023_set_lg_hyst(sensor->client, sensor->lg_hyst);
> +	bma023_set_hg_int(sensor->client, sensor->hg_int);
> +	bma023_set_lg_int(sensor->client, sensor->lg_int);
> +	bma023_set_power_mode(sensor->client, sensor->power_mode);
> +	bma023_set_check_reg(sensor->client, BMA023_INSPECT_VAL);
> +}
> +
> +/**
> + *	bma023_input_open	-	called on input event open
> + *	@input: input dev of opened device
> + *
> + *	The input layer calls this function when input event is opened. The
> + *	function will push the device to resume. Then, the device is ready
> + *	to provide data.
> + */
> +static int bma023_input_open(struct input_dev *input)
> +{
> +	struct bma023_sensor *sensor = input_get_drvdata(input);
> +	pm_runtime_get(sensor->dev);
> +	return 0;
> +}
> +
> +/**
> + *	bma023_input_close	-	called on input event close
> + *	@input: input dev of closed device
> + *
> + *	The input layer calls this function when input event is closed. The
> + *	function will push the device to suspend.
> + */
> +static void bma023_input_close(struct input_dev *input)
> +{
> +	struct bma023_sensor *sensor = input_get_drvdata(input);
> +	pm_runtime_put(sensor->dev);
> +}
> +
> +/**
> + *	bma023_unregister_input_device	-	remove input dev
> + *	@sensor: sensor to remove from input
> + *
> + *	Free the interrupt and input device for the sensor. We must free
> + *	the interrupt first
> + */
> +static void bma023_unregister_input_device(struct bma023_sensor *sensor)
> +{
> +	struct i2c_client *client = sensor->client;
> +	if (client->irq > 0)
> +		free_irq(client->irq, sensor);
> +	input_unregister_device(sensor->idev);
> +	sensor->idev = NULL;
> +}
> +
> +/**
> + *	bma023_register_input_device	-	remove input dev
> + *	@sensor: sensor to remove from input
> + *
> + *	Add an input device to the sensor. This will be used to report
> + *	events from the sensor itself.
> + */
> +static int bma023_register_input_device(struct bma023_sensor *sensor)
> +{
> +	struct i2c_client *client = sensor->client;
> +	struct input_dev *idev;
> +	int ret;
> +	sensor->idev = input_allocate_device();
> +	idev = sensor->idev;
> +	if (!idev) {
> +		dev_err(&client->dev, "failed to allocate input device\n");
> +		ret = -ENOMEM;
> +		goto failed_alloc;
> +	}
> +	idev->name = "BMA023 Sensor";
> +	idev->open = bma023_input_open;
> +	idev->close = bma023_input_close;
> +	idev->id.bustype = BUS_I2C;
> +	idev->dev.parent = &client->dev;
> +	idev->evbit[0] = BIT_MASK(EV_ABS);
> +	input_set_abs_params(idev, ABS_X, BMA023_MIN_VALUE,
> +			BMA023_MAX_VALUE, 0, 0);
> +	input_set_abs_params(idev, ABS_Y, BMA023_MIN_VALUE,
> +			BMA023_MAX_VALUE, 0, 0);
> +	input_set_abs_params(idev, ABS_Z, BMA023_MIN_VALUE,
> +			BMA023_MAX_VALUE, 0, 0);
> +	input_set_drvdata(idev, sensor);
> +	ret = input_register_device(idev);
> +	if (ret) {
> +		dev_err(&client->dev, "failed to register input device\n");
> +		goto failed_reg;
> +	}
> +	if (client->irq > 0) {
> +		ret = request_threaded_irq(client->irq, NULL,
> +				bma023_interrupt_thread, IRQF_TRIGGER_RISING,
> +					"bma023_int", sensor);
Is there any reason to have the _int postfix?  It is clearly an interrupt and
I don't think the device has any others.
> +		if (ret) {
> +			dev_err(&client->dev, "can't get IRQ %d, ret %d\n",
> +					client->irq, ret);
> +			goto failed_irq;
> +		}
> +	}
> +	return 0;
> +failed_irq:
> +	input_unregister_device(idev);
> +	return ret;
> +
> +failed_reg:
> +	if (idev)
> +		input_free_device(idev);
> +failed_alloc:
> +	return ret;
> +}
> +
> +/**
> + *	bma023_probe	-	device detection callback
> + *	@client: i2c client of found device
> + *	@id: id match information
> + *
> + *	The I2C layer calls us when it believes a sensor is present at this
> + *	address. Probe to see if this is correct and to validate the device.
> + *
> + *	If present install the relevant sysfs interfaces and input device.
> + */
> +static int __devinit bma023_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct bma023_sensor *sensor;
> +	struct bma023_platform_data *pdata;
> +	int ret;
> +	sensor = kzalloc(sizeof(struct bma023_sensor), GFP_KERNEL);
> +	if (!sensor) {
> +		dev_err(&client->dev, "failed to allocate driver data\n");
> +		return -ENOMEM;
> +	}
> +	pdata = client->dev.platform_data;
> +	sensor->dev = &client->dev;
> +
> +	sensor->client = client;
> +	i2c_set_clientdata(client, sensor);
> +
> +	ret = bma023_read_reg(client, BMA023_CHIP_ID_REG);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "failed to detect device\n");
> +		goto failed_free;
> +	}
> +	if (ret != BMA023_CHIP_ID) {
> +		dev_err(&client->dev, "unsupported chip id\n");
> +		goto failed_free;
> +	}
> +
> +	mutex_init(&sensor->lock);
> +
> +	ret = sysfs_create_group(&client->dev.kobj, &bma023_group);
> +	if (ret) {
> +		dev_err(&client->dev, "failed to create attribute group\n");
> +		goto failed_free;
> +	}
> +
> +	pm_runtime_set_active(&client->dev);
> +
> +	ret = bma023_register_input_device(sensor);
> +	if (ret)
> +		dev_err(&client->dev, "only provide sysfs\n");
> +
> +	if (pdata) {
> +		sensor->range = pdata->range;
> +		sensor->bandwidth = pdata->bandwidth;
> +		sensor->new_data_int = pdata->new_data_int;
> +		sensor->hg_int = pdata->hg_int;
> +		sensor->lg_int = pdata->lg_int;
> +		sensor->hg_dur = pdata->hg_dur;
> +		sensor->hg_thres = pdata->hg_thres;
> +		sensor->hg_hyst = pdata->hg_hyst;
> +		sensor->lg_dur = pdata->lg_dur;
> +		sensor->lg_thres = pdata->lg_thres;
> +		sensor->lg_hyst = pdata->lg_hyst;
> +		sensor->auto_delay = pdata->auto_delay;
> +	} else {
> +		sensor->range = BMA023_DEFAULT_RANGE;
> +		sensor->bandwidth = BMA023_DEFAULT_BANDWIDTH;
> +		sensor->new_data_int = BMA023_DEFAULT_NEW_DATA_INT;
> +		sensor->hg_int = BMA023_DEFAULT_HG_INT;
> +		sensor->lg_int = BMA023_DEFAULT_LG_INT;
> +		sensor->hg_dur = BMA023_DEFAULT_HG_DURATION;
> +		sensor->hg_thres = BMA023_DEFAULT_HG_THRESHOLD;
> +		sensor->hg_hyst = BMA023_DEFAULT_HG_HYST;
> +		sensor->lg_dur = BMA023_DEFAULT_LG_DURATION;
> +		sensor->lg_thres = BMA023_DEFAULT_LG_THRESHOLD;
> +		sensor->lg_hyst = BMA023_DEFAULT_LG_HYST;
> +		sensor->auto_delay = BMA023_DEFAULT_AUTO_DELAY;
> +	}
> +	sensor->power_mode = BMA023_SUSPEND;
> +
> +	bma023_initialize(sensor);
> +
> +	pm_runtime_enable(&client->dev);

Why store auto_delay, it is only used here...
> +	pm_runtime_set_autosuspend_delay(&client->dev, sensor->auto_delay);
> +
> +	dev_info(&client->dev, "%s registered\n", id->name);
> +	return 0;
> +
> +failed_free:
> +	kfree(sensor);
> +	return ret;
> +}
> +
> +/**
> + *	bam023_remove	-	remove a sensor
> + *	@client: i2c client of sensor being removed
> + *
> + *	Our sensor is going away, clean up the resources.
> + */
> +static int __devexit bma023_remove(struct i2c_client *client)
> +{
> +	struct bma023_sensor *sensor = i2c_get_clientdata(client);
> +
> +	pm_runtime_disable(&client->dev);
> +	pm_runtime_set_suspended(&client->dev);
> +
> +	if (sensor->idev)
> +		bma023_unregister_input_device(sensor);
> +	sysfs_remove_group(&client->dev.kobj, &bma023_group);
> +	kfree(sensor);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +
> +/**
> + *	bma023_suspend		-	called on device suspend
> + *	@client: i2c client of sensor
> + *	@mesg: actual suspend type
> + *
> + *	Put the device into sleep mode before we suspend the machine.
> + */
> +static int bma023_suspend(struct i2c_client *client, pm_message_t mesg)
> +{
> +	bma023_set_power_mode(client, BMA023_SUSPEND);
> +	return 0;
> +}
> +
> +/**
> + *	bma023_resume		-	called on device resume
> + *	@client: i2c client of sensor
> + *
> + *	Put the device into powered mode on resume.
> + */
> +static int bma023_resume(struct i2c_client *client)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(&client->dev);
> +	if (bma023_get_check_reg(client) != BMA023_INSPECT_VAL)
> +		bma023_initialize(sensor);
> +	bma023_set_power_mode(client, sensor->power_mode);
> +	msleep(4);  /* wait for accel chip resume */
> +	return 0;
> +}
> +
> +#else
> +#define bma023_suspend NULL
> +#define bma023_resume NULL
> +#endif
> +
> +#ifdef CONFIG_PM_RUNTIME
> +
> +static int bma023_runtime_suspend(struct device *dev)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	bma023_set_power_mode(sensor->client, BMA023_SUSPEND);
On these could we usefully pass on the return value from
bma023_set_power_mode?

> +	return 0;
> +}
> +
> +static int bma023_runtime_resume(struct device *dev)
> +{
> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> +	bma023_set_power_mode(sensor->client, BMA023_RESUME);
> +	msleep(4);  /* wait for accel chip resume */
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops bma023_pm = {
> +	.runtime_suspend = bma023_runtime_suspend,
> +	.runtime_resume  = bma023_runtime_resume,
> +};
> +#endif
> +
> +static const struct i2c_device_id bma023_ids[] = {
> +	{ "bma023", 0 },
> +	{ "smb380", 1 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, bma023_ids);
> +
> +static struct i2c_driver bma023_i2c_driver = {
> +	.driver	= {
> +		.name	= "bma023",
> +#ifdef CONFIG_PM_RUNTIME
> +		.pm		= &bma023_pm,
> +#endif		
> +	},
> +	.probe		= bma023_probe,
> +	.remove		= __devexit_p(bma023_remove),
> +	.suspend	= bma023_suspend,
> +	.resume		= bma023_resume,
> +	.id_table	= bma023_ids,
> +};
> +
> +static int __init bma023_init(void)
> +{
> +	return i2c_add_driver(&bma023_i2c_driver);
> +}
> +module_init(bma023_init);
> +
> +static void __exit bma023_exit(void)
> +{
> +	i2c_del_driver(&bma023_i2c_driver);
> +}
> +module_exit(bma023_exit);
> +
> +MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>, Wistron Corp.");
> +MODULE_DESCRIPTION("BMA023/SMB380 Tri-axis accelerometer driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/i2c/bma023.h b/include/linux/i2c/bma023.h
> new file mode 100644
> index 0000000..52a0df5
> --- /dev/null
> +++ b/include/linux/i2c/bma023.h
> @@ -0,0 +1,44 @@
> +/*
> + * bma023.h - BMA023 Tri-axis accelerometer driver
> + *
> + * Copyright (c) 2010 Samsung Eletronics
> + * Kyungmin Park <kyungmin.park@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#ifndef _BMA023_H_
> +#define _BMA023_H_
> +
> +#define	BMA023_RANGE_2G		0
> +#define	BMA023_RANGE_4G		1
> +#define	BMA023_RANGE_8G		2
> +
> +/* Used to setup the digital filtering bandwidth of ADC output */
> +#define	BMA023_BW_25HZ		0
> +#define	BMA023_BW_50HZ		1
> +#define	BMA023_BW_100HZ		2
> +#define	BMA023_BW_190HZ		3
> +#define	BMA023_BW_375HZ		4
> +#define	BMA023_BW_750HZ		5
> +#define	BMA023_BW_1500HZ	6
> +
Documentation of this structure would probably be a useful addition.
> +struct bma023_platform_data {
> +	u8 range;
> +	u8 bandwidth;
> +	u8 new_data_int;
> +	u8 hg_int;
> +	u8 lg_int;
> +	u8 lg_dur;
> +	u8 lg_thres;
> +	u8 lg_hyst;
> +	u8 hg_dur;
> +	u8 hg_thres;
> +	u8 hg_hyst;
> +	int auto_delay;		/* mS */
> +};
> +
> +#endif /* _BMA023_H_ */
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] bma023: Support for Bosch BMA023 and SMB380 accelerometers
  2011-01-26 11:11 ` Jonathan Cameron
@ 2011-01-26 11:42   ` Alan Cox
  2011-01-26 12:08     ` Jonathan Cameron
  0 siblings, 1 reply; 5+ messages in thread
From: Alan Cox @ 2011-01-26 11:42 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-input, Eric Piel, Hemanth V

> * Lots of discussion on sysfs attributes needed, documentation will obviously
> make this easier!  I think there are some magic numbers in here still in
> no particular units? Difficult to tell without the datasheet.

I'll double check on that. I don't believe the data sheet is available
publically.

> > We expose the data from this device to uusers through sysfs interface in
> > the same manner as the lis drivers 
> 
> This is a somewhat misleading comment. Unless a lot of new sysfs changes to

I meant the XYZ are exposed in the same format but do need some scaling
info yes.

> Whilst several discussions of sysfs interfaces for these devices have bounced
> around, no particularly firm decisions have been reached and perhaps we need
> futher discussion.  We do have a fairly extensive set of equivalents in IIO
> but are still open to other suggestions. (please see our docs in
> staging/iio/Documentation) They are reasonaly comprehensive, though a few
> things have snuck into the drivers without proper documentation in place.

IIO is something that unfortunately isn't within our timescales -
especially with no certainty it'll ever go upstream. That's a hint btw -
I think IIO needs throwing at Linus ;)

Once IIO was upstream then the rest would follow (and we've got a few
other driver bits in misc and in internal trees that would then be able
to follow nicely)


> > +struct bma023_sensor {
> > +	struct i2c_client *client;
> > +	struct device *dev;
> > +	struct input_dev *idev;
> > +	struct mutex lock;
> > +	struct bma023_data data;
> > +	int auto_delay;
> I for one have no idea what auto_delay and soft_power might be...
> Perhaps some documentation?

Power management delay and our soft power state - will comment
accordingly.


> > +static int bma023_read_reg(struct i2c_client *client, u8 reg)
> > +{
> > +	int ret = i2c_smbus_read_byte_data(client, reg);
> > +	if (ret < 0)
> > +		dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
> > +			__func__, reg, ret);
> 
> Do we need this wrapper? It does pretty print any errors, but do we
> care assuming they are correctly passed on?

At this point I'd certainly like to keep it but long term it may well not
be needed.


> I can't help feeling that much of the following could be combined
> into 2 functions with the right macro foo and container structures,
> much the way it is done in hwmon an others.

Some of it was originally macro foo, including some truely 'elegant' use
of the pre-processor. When I expanded it by hand it was several times
larger than the code now in its place. gcc is pretty good at its job so
letting gcc work out what it wants inlined seems best.

> > + *	bma023_set_hg_dur	-	set high-g duration
> > + *	@client: i2c client for the sensor
> > + *	@dur: duration value, 0-255
> Again, units are?  We really need this stuff to be clear.

> > + *	bma023_set_hg_thres	-	set high-g threshold
> > + *	@client: i2c client for the sensor
> > + *	@thres: threshold value, 0-255
> Units are?  

> > +	mutex_lock(&sensor->lock);
> > +	bma023_read_xyz(sensor->client, &sensor->data);
> > +	mutex_unlock(&sensor->lock);
> > +
> > +	pm_runtime_put(dev);
> > +
> These look to be raw adc values. If you want to do that, please provide
> in sysfs the numbers needed to convert them to SI units.


> > +static struct attribute *bma023_attributes[] = {
> > +	&dev_attr_accel_data.attr,
> These are raw values I think?  Again, we allow this in IIO, but insist
> on conversion factors to m/s^2 also being provided in the sysfs interface
> so userspace knows what it is getting.

This isn't an IIO driver. As and when IIO isn't in staging then we can
worry about this a bit more IMHO.

> > +	&dev_attr_power_mode.attr,
> Perhaps rename this low_power_mode and invert the value as then
> it won't be a pair of magic numbers.

I'll take a look - ultimately I'd like to kill it off and just rely on
runtime PM but not everyone builds runtime PM systems yet.

> > +	&dev_attr_new_data_int.attr,
> (is this enabling and disabling the interrupt? Surely that should be handled
> by the input code and doesn't really want to be exposed?

It depends what you are doing with the device what events you want to be
flagged. In almost all cases the platform would know and provide the
right defaults.

> For reference, the IIO equivalents may seem long winded, but they
> are clear, consistent and generalizable. I'm guessing a bit on
> what the bosch chip is doing based on other parts.

Again when/if IIO is upstream it will make sense to follow IIO.


> > +		ret = request_threaded_irq(client->irq, NULL,
> > +				bma023_interrupt_thread, IRQF_TRIGGER_RISING,
> > +					"bma023_int", sensor);
> Is there any reason to have the _int postfix?  It is clearly an interrupt and
> I don't think the device has any others.

Good point - killed

> > +	pm_runtime_enable(&client->dev);
> 
> Why store auto_delay, it is only used here...

Already fixed

> > +static int bma023_runtime_suspend(struct device *dev)
> > +{
> > +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
> > +	bma023_set_power_mode(sensor->client, BMA023_SUSPEND);
> On these could we usefully pass on the return value from
> bma023_set_power_mode?

In the suspend case it is pretty meaningless - on the resume case I will
do so.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] bma023: Support for Bosch BMA023 and SMB380 accelerometers
  2011-01-26 11:42   ` Alan Cox
@ 2011-01-26 12:08     ` Jonathan Cameron
  0 siblings, 0 replies; 5+ messages in thread
From: Jonathan Cameron @ 2011-01-26 12:08 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-input, Eric Piel, Hemanth V

On 01/26/11 11:42, Alan Cox wrote:
>> * Lots of discussion on sysfs attributes needed, documentation will obviously
>> make this easier!  I think there are some magic numbers in here still in
>> no particular units? Difficult to tell without the datasheet.
> 
> I'll double check on that. I don't believe the data sheet is available
> publically.
Always worth kicking them manufacturers a bit more on this. One day they
will learn...
> 
>>> We expose the data from this device to uusers through sysfs interface in
>>> the same manner as the lis drivers 
>>
>> This is a somewhat misleading comment. Unless a lot of new sysfs changes to
> 
> I meant the XYZ are exposed in the same format but do need some scaling
> info yes.
> 
>> Whilst several discussions of sysfs interfaces for these devices have bounced
>> around, no particularly firm decisions have been reached and perhaps we need
>> futher discussion.  We do have a fairly extensive set of equivalents in IIO
>> but are still open to other suggestions. (please see our docs in
>> staging/iio/Documentation) They are reasonaly comprehensive, though a few
>> things have snuck into the drivers without proper documentation in place.
> 
> IIO is something that unfortunately isn't within our timescales -
> especially with no certainty it'll ever go upstream. That's a hint btw -
> I think IIO needs throwing at Linus ;)
Hmm.. I admit to being somewhat nervous about moving out of staging, the ALS fun
and games certainly didn't help.  Sadly we aren't ready quite
yet - too many ugly corners that will just confuse the debate and a lot of
ugly drivers that ideally would be cleaned up first as well.  We made an exception
for the huge dump from Analog, but I don't think we'll take any in that state
again. It just diverts attention from the core work.

I have no problem with drivers going elsewhere (particularly 3d accelerometer
drivers that are mainly used for input anyway).  I'm glad Dmitry has started
taking these.  The IIO/input bridge may work, but it does add a userspace burden
which would be nice to avoid in common cases!

I'm also in favour of other drivers going in misc for now.

Still having said that, there is always unifying work that can be done
on interfaces, wherever the drivers actually are and that is why I'm pushing
back on drivers like these.
> 
> Once IIO was upstream then the rest would follow (and we've got a few
> other driver bits in misc and in internal trees that would then be able
> to follow nicely)
> 
> 
>>> +struct bma023_sensor {
>>> +	struct i2c_client *client;
>>> +	struct device *dev;
>>> +	struct input_dev *idev;
>>> +	struct mutex lock;
>>> +	struct bma023_data data;
>>> +	int auto_delay;
>> I for one have no idea what auto_delay and soft_power might be...
>> Perhaps some documentation?
> 
> Power management delay and our soft power state - will comment
> accordingly.
> 
> 
>>> +static int bma023_read_reg(struct i2c_client *client, u8 reg)
>>> +{
>>> +	int ret = i2c_smbus_read_byte_data(client, reg);
>>> +	if (ret < 0)
>>> +		dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
>>> +			__func__, reg, ret);
>>
>> Do we need this wrapper? It does pretty print any errors, but do we
>> care assuming they are correctly passed on?
> 
> At this point I'd certainly like to keep it but long term it may well not
> be needed.
Fair enough. I suspect it will stay for ever, but it is a trivial point
anyway so no real problem.
> 
> 
>> I can't help feeling that much of the following could be combined
>> into 2 functions with the right macro foo and container structures,
>> much the way it is done in hwmon an others.
> 
> Some of it was originally macro foo, including some truely 'elegant' use
> of the pre-processor. When I expanded it by hand it was several times
> larger than the code now in its place. gcc is pretty good at its job so
> letting gcc work out what it wants inlined seems best.
Fair enough. I guess this sort of macro foo doesn't really belong
in drivers but rather subsystems where it can be well tested and shared
across a lot of drivers.
> 
>>> + *	bma023_set_hg_dur	-	set high-g duration
>>> + *	@client: i2c client for the sensor
>>> + *	@dur: duration value, 0-255
>> Again, units are?  We really need this stuff to be clear.
> 
>>> + *	bma023_set_hg_thres	-	set high-g threshold
>>> + *	@client: i2c client for the sensor
>>> + *	@thres: threshold value, 0-255
>> Units are?  
> 
>>> +	mutex_lock(&sensor->lock);
>>> +	bma023_read_xyz(sensor->client, &sensor->data);
>>> +	mutex_unlock(&sensor->lock);
>>> +
>>> +	pm_runtime_put(dev);
>>> +
>> These look to be raw adc values. If you want to do that, please provide
>> in sysfs the numbers needed to convert them to SI units.
> 
> 
>>> +static struct attribute *bma023_attributes[] = {
>>> +	&dev_attr_accel_data.attr,
>> These are raw values I think?  Again, we allow this in IIO, but insist
>> on conversion factors to m/s^2 also being provided in the sysfs interface
>> so userspace knows what it is getting.
> 
> This isn't an IIO driver. As and when IIO isn't in staging then we can
> worry about this a bit more IMHO.
Was only using IIO elements as an examples to avoid reiterating whole
discussion about how to handle this stuff; take the actual examples with
a pinch of salt as you say.
> 
>>> +	&dev_attr_power_mode.attr,
>> Perhaps rename this low_power_mode and invert the value as then
>> it won't be a pair of magic numbers.
> 
> I'll take a look - ultimately I'd like to kill it off and just rely on
> runtime PM but not everyone builds runtime PM systems yet.
Sure
> 
>>> +	&dev_attr_new_data_int.attr,
>> (is this enabling and disabling the interrupt? Surely that should be handled
>> by the input code and doesn't really want to be exposed?
> 
> It depends what you are doing with the device what events you want to be
> flagged. In almost all cases the platform would know and provide the
> right defaults.
> 
>> For reference, the IIO equivalents may seem long winded, but they
>> are clear, consistent and generalizable. I'm guessing a bit on
>> what the bosch chip is doing based on other parts.
> 
> Again when/if IIO is upstream it will make sense to follow IIO.
Consistency matters whether we are dealing with the particular form
we picked or another one...
> 
> 
>>> +		ret = request_threaded_irq(client->irq, NULL,
>>> +				bma023_interrupt_thread, IRQF_TRIGGER_RISING,
>>> +					"bma023_int", sensor);
>> Is there any reason to have the _int postfix?  It is clearly an interrupt and
>> I don't think the device has any others.
> 
> Good point - killed
> 
>>> +	pm_runtime_enable(&client->dev);
>>
>> Why store auto_delay, it is only used here...
> 
> Already fixed
> 
>>> +static int bma023_runtime_suspend(struct device *dev)
>>> +{
>>> +	struct bma023_sensor *sensor = dev_get_drvdata(dev);
>>> +	bma023_set_power_mode(sensor->client, BMA023_SUSPEND);
>> On these could we usefully pass on the return value from
>> bma023_set_power_mode?
> 
> In the suspend case it is pretty meaningless - on the resume case I will
> do so.
Fair enough.

Jonathan


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH] bma023: Support for Bosch BMA023 and SMB380 accelerometers
@ 2011-02-09  9:59 Alan Cox
  0 siblings, 0 replies; 5+ messages in thread
From: Alan Cox @ 2011-02-09  9:59 UTC (permalink / raw)
  To: linux-input

Updated patch to add documentation for the interfaces provided. Note that
this is an input driver, it's not a driver for the staging IIO work, nor is
it intended to be.

--

From: Joseph Lai <joseph_lai@wistron.com>

This patch supports driver for BOSCH BMA023 and SMB380 which are accelerometers.

We also expose some data from this device to uusers through sysfs interface in
a similar manner as the lis drivers so that the data can be used for one off
uses, and also as as an input device for joystick type uses.

The driver allows the setting and reading of the device's properties via
sysfs attributes.

Based on an original driver
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

[Extensively rewritten]

Signed-off-by: Joseph Lai <joseph_lai@wistron.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 Documentation/ABI/testing/sysfs-i2c-bma023 |   93 ++
 drivers/input/misc/Kconfig                 |   10 
 drivers/input/misc/Makefile                |    1 
 drivers/input/misc/bma023.c                | 1308 ++++++++++++++++++++++++++++
 include/linux/i2c/bma023.h                 |   44 +
 5 files changed, 1456 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-i2c-bma023
 create mode 100644 drivers/input/misc/bma023.c
 create mode 100644 include/linux/i2c/bma023.h


diff --git a/Documentation/ABI/testing/sysfs-i2c-bma023 b/Documentation/ABI/testing/sysfs-i2c-bma023
new file mode 100644
index 0000000..04a293c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-i2c-bma023
@@ -0,0 +1,93 @@
+*Note*: It is anticpated that if IIO ever makes it upstream that the
+preferred general purpose non-input interface to this hardware will become IIO.
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/range
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	Configure the sensing range of the device
+		0: +/- 2G
+		1: +/- 4G
+		2: +/- 8G
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/bandwidth
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	Digital filtering on ADC output data
+		0: 25Hz
+		1: 50Hz
+		2: 100Hz
+		3: 190Hz
+		4: 375Hz
+		5: 750Hz
+		6: 1500Hz
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/new_data_int
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	Generate an interrupt when the axes values are changed
+		0: disable
+		1: enable
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/hg_int
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	Generate an interrupt when the high-g threshold is met
+		0: disable
+		1: enable
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/lg_int
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	Generate an interrupt when the low-g threshold is met
+		0: disable
+		1: enable
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/hg_dur
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	High G duration required to trigger.
+		(See the equations in the data sheet)
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/hg_hyst
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	High G duration hysteresis
+		(See the equations in the data sheet)
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/hg_thres
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	High G threshold
+		(See the equations in the data sheet)
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/lg_dur
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	Low G duration required to trigger
+		(See the equations in the data sheet)
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/lg_hyst
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	Low G duration hysteresis
+		(See the equations in the data sheet)
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/lg_thres
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	Low G threshold
+		(See the equations in the data sheet)
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/power_mode
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	Power mode
+		0: Use runtime PM
+		1: Forced on
+
+What:		/sys/bus/i2c/devices/<busnum>-<devaddr>/accel_data
+Date:		February 2011
+Contact:	Alan Cox <alan@linux.intel.com>
+Description:	Raw XYZ co-ordinate data, each ranges from -512 to +511,
+		formatted as (x,y,z) to match existing similar interfaces
+
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index b0c6772..3b595b8 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -454,4 +454,14 @@ config INPUT_CMA3000_I2C
 	  To compile this driver as a module, choose M here: the
 	  module will be called cma3000_d0x_i2c.
 
+config INPUT_BMA023
+	tristate "BMA023/SMB380 Triaxial acceleration sensor"
+	depends on I2C
+	help
+	  Say Y here if you want to support Bosch BMA023/SMB380
+	  connected via an I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bma023.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 9b47971..79b3f66 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_INPUT_PWM_BEEPER)		+= pwm-beeper.o
 obj-$(CONFIG_INPUT_RB532_BUTTON)	+= rb532_button.o
 obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)	+= rotary_encoder.o
 obj-$(CONFIG_INPUT_SGI_BTNS)		+= sgi_btns.o
+obj-$(CONFIG_INPUT_BMA023)			+= bma023.o
 obj-$(CONFIG_INPUT_SPARCSPKR)		+= sparcspkr.o
 obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON)	+= twl4030-pwrbutton.o
 obj-$(CONFIG_INPUT_TWL4030_VIBRA)	+= twl4030-vibra.o
diff --git a/drivers/input/misc/bma023.c b/drivers/input/misc/bma023.c
new file mode 100644
index 0000000..d7abccd
--- /dev/null
+++ b/drivers/input/misc/bma023.c
@@ -0,0 +1,1308 @@
+/*
+ * bma023.c - BMA023 Tri-axis accelerometer driver
+ *
+ * Copyright (C) 2010 Samsung Eletronics Co.Ltd
+ * Kim Kyuwon <q1.kim@samsung.com>
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ * Donggeun Kim <dg77.kim@samsung.com>
+ *
+ * Copyright (C) 2011 Wistron Co.Ltd
+ * Joseph Lai <joseph_lai@wistron.com>
+ *
+ * Copyright (c) 2011 Intel Corporation
+ * Alan Cox <alan@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * 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.
+ *
+ * TODO (After)
+ *	Investigate if the IRQ disable is really needed (needs that data
+ *		sheet reviewing in more detail)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/i2c/bma023.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+#define BMA023_DEFAULT_AUTO_DELAY	250	/* mS */
+
+#define BMA023_CHIP_ID_REG	0x00
+#define BMA023_X_LSB_REG	0x02
+#define BMA023_X_MSB_REG	0x03
+#define BMA023_Y_LSB_REG	0x04
+#define BMA023_Y_MSB_REG	0x05
+#define BMA023_Z_LSB_REG	0x06
+#define BMA023_Z_MSB_REG	0x07
+#define BMA023_TEMP_REG		0x08 /*  An output of 0 equals -30C,
+					 1 LSB equals 0.5C */
+#define BMA023_CTRL1_REG	0x0a
+#define BMA023_CTRL2_REG	0x0b
+#define BMA023_SETTINGS1_REG	0x0c
+#define BMA023_SETTINGS2_REG	0x0d
+#define BMA023_SETTINGS3_REG	0x0e
+#define BMA023_SETTINGS4_REG	0x0f
+#define BMA023_SETTINGS5_REG	0x10
+#define BMA023_SETTINGS6_REG	0x11
+#define BMA023_RANGE_BW_REG	0x14
+#define BMA023_CONF2_REG	0x15
+
+#define BMA023_CHIP_ID		0x02
+
+#define BMA023_NEW_DATA_INT_SHIFT	5
+#define BMA023_NEW_DATA_INT_MASK	(0x1 << 5)
+
+#define BMA023_RANGE_SHIFT		3
+#define BMA023_RANGE_MASK		(0x3 << 3)
+#define BMA023_BANDWIDTH_SHIFT		0
+#define BMA023_BANDWIDTH_MASK		(0x7)
+
+#define BMA023_HG_HYST_SHIFT		3
+#define BMA023_HG_HYST_MASK		(0x7 << 3)
+#define BMA023_LG_HYST_SHIFT		0
+#define BMA023_LG_HYST_MASK		(0x7)
+
+#define BMA023_HG_DUR_SHIFT		(0x0)
+#define BMA023_HG_DUR_MASK		(0xff)
+#define BMA023_HG_THRES_SHIFT		(0x0)
+#define BMA023_HG_THRES_MASK		(0xff)
+#define BMA023_LG_DUR_SHIFT		(0x0)
+#define BMA023_LG_DUR_MASK		(0xff)
+#define BMA023_LG_THRES_SHIFT		(0x0)
+#define BMA023_LG_THRES_MASK		(0xff)
+
+#define BMA023_ENABLE_HG_SHIFT		1
+#define BMA023_ENABLE_HG_MASK		(0x1 << 1)
+#define BMA023_ENABLE_LG_SHIFT		0
+#define BMA023_ENABLE_LG_MASK		(0x1)
+
+#define BMA023_SLEEP_SHIFT		0
+#define BMA023_SLEEP_MASK		(0x1)
+
+#define BMA023_ACCEL_BITS		10
+#define BMA023_MAX_VALUE		((1 << ((BMA023_ACCEL_BITS) - 1)) - 1)
+#define BMA023_MIN_VALUE		(-(1 << ((BMA023_ACCEL_BITS) - 1)))
+
+#define BMA023_DEFAULT_RANGE		BMA023_RANGE_2G
+#define BMA023_DEFAULT_BANDWIDTH	BMA023_BW_190HZ
+#define BMA023_DEFAULT_NEW_DATA_INT	0
+#define BMA023_DEFAULT_HG_INT		0
+#define BMA023_DEFAULT_LG_INT		0
+#define BMA023_DEFAULT_HG_DURATION	0x05
+#define BMA023_DEFAULT_HG_THRESHOLD	0xa0
+#define BMA023_DEFAULT_HG_HYST		0
+#define BMA023_DEFAULT_LG_DURATION	0x05
+#define BMA023_DEFAULT_LG_THRESHOLD	0x14
+#define BMA023_DEFAULT_LG_HYST		0
+
+#define BMA023_SUSPEND	0
+#define BMA023_RESUME	1
+
+#define BMA023_CUSTOMER_REG	0x12
+#define BMA023_INSPECT_VAL	0xBB
+#define BMA023_CHECK_SHIFT	0
+#define BMA023_CHECK_MASK	0xff
+
+struct bma023_data {
+	s16 x;
+	s16 y;
+	s16 z;
+};
+
+struct bma023_sensor {
+	struct i2c_client *client;
+	struct device *dev;
+	struct input_dev *idev;
+	struct mutex lock;
+	struct bma023_data data;
+	int soft_power;		/* Our sysfs requested power status as
+				   opposed to the actual one which is
+				   a combination of this and pm runtime */
+	u8 range;
+	u8 bandwidth;
+	u8 new_data_int;
+	u8 hg_int;
+	u8 lg_int;
+	u8 lg_dur;
+	u8 lg_thres;
+	u8 lg_hyst;
+	u8 hg_dur;
+	u8 hg_thres;
+	u8 hg_hyst;
+	u8 power_mode;
+};
+
+/**
+ *	bma023_write_reg	-	write a BMA023 register via I2C
+ *	@reg: register being updated
+ *	@val: value to write
+ *
+ *	Updates the register or returns an error if the write fails.
+ */
+static int bma023_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+	int ret;
+	/*
+	 * According to the datasheet, the interrupt should be deactivated
+	 * on the host side when write sequences operate.
+	 */
+	disable_irq_nosync(client->irq);
+	ret = i2c_smbus_write_byte_data(client, reg, val);
+	enable_irq(client->irq);
+	if (ret < 0)
+		dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
+			__func__, reg, val, ret);
+	return ret;
+}
+
+/**
+ *	bma023_read_reg	-	read a BMA023 register via I2C
+ *	@reg: register being updated
+ *	@val: value to write
+ *
+ *	Reads the register or returns a negative error code on failure.
+ */
+static int bma023_read_reg(struct i2c_client *client, u8 reg)
+{
+	int ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0)
+		dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
+			__func__, reg, ret);
+	return ret;
+}
+
+/**
+ *	bma023_xyz_read_reg	-	read the axes values
+ *	@reg: register being updated
+ *	@val: value to write
+ *
+ *	Reads the register values in one transaction or returns a negative
+ *	error code on failure/
+ */
+static int bma023_xyz_read_reg(struct i2c_client *client,
+			       u8 *buffer, int length)
+{
+	struct i2c_msg msg[] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = 1,
+			.buf = buffer,
+		}, {
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = length,
+			.buf = buffer,
+		},
+	};
+	return i2c_transfer(client->adapter, msg, 2);
+}
+
+/**
+ *	bma023_set_reg_bits	-	update a subset of register bits
+ *	@client: i2c client for the sensor
+ *	@val: value to write
+ *	@shift: bit offset of field
+ *	@mask: bit mask of field to update
+ *	@reg: register number
+ *
+ *	Update one of the many bitfield registers on the BMA023 by reading
+ *	and writing back bits. The caller *MUST* hold the sensor lock when
+ *	using this function in order to prevent overlapping updates corrupting
+ *	the register.
+ *
+ *	Returns success or an error code
+ */
+static int bma023_set_reg_bits(struct i2c_client *client,
+					int val, int shift, u8 mask, u8 reg)
+{
+	int data = bma023_read_reg(client, reg);
+
+	if (data < 0)
+		return data;
+
+	data = (data & ~mask) | ((val << shift) & mask);
+	return bma023_write_reg(client, reg, data);
+}
+
+/**
+ *	bma023_get_reg_bits	-	read register bitfield
+ *	@client: i2c client for the sensor
+ *	@shift: bit offset of field
+ *	@mask: bit mask of field to update
+ *	@reg: register number
+ *
+ *	Read a bit field from the sensor and return the value of the field. The
+ *	lock is not needed in this case
+ */
+static int bma023_get_reg_bits(struct i2c_client *client, int shift,
+					u8 mask, u8 reg)
+{
+	int data = bma023_read_reg(client, reg);
+	if (data < 0)
+		return data;
+	data = (data & mask) >> shift;
+	return data;
+}
+
+/*
+ *	Functions for handling all the configurable fields
+ */
+
+/**
+ *	bma023_set_check_reg	-	set an inspective value
+ *	@client: i2c client for the sensor
+ *	@check: update the inspective value
+ *
+ *	Set an inspective value to customer's register, which can record
+ *	any value from user. The inspective value will keep until the chip
+ *	off or shutdown. User can recognize the chip needs re-initialize
+ *	or not by this register.
+ */
+static int bma023_set_check_reg(struct i2c_client *client, u8 check)
+{
+	return bma023_set_reg_bits(client, check, BMA023_CHECK_SHIFT,
+				   BMA023_CHECK_MASK, BMA023_CUSTOMER_REG);
+}
+
+static int bma023_get_check_reg(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_CHECK_SHIFT,
+				   BMA023_CHECK_MASK, BMA023_CUSTOMER_REG);
+}
+
+/**
+ *	bma023_set_range	-	set the full scale acceleration range
+ *	@client: i2c client for the sensor
+ *	@range: new range setting
+ *
+ *	The range of bma023 will affect the chip to change the sensitivity 
+ *	of gravitation. The value of range includes +/- 2g, 4g and 8g.
+ *
+ *	The change of range will respond to 3 axes directly.
+ */
+static int bma023_set_range(struct i2c_client *client, u8 range)
+{
+	return bma023_set_reg_bits(client, range, BMA023_RANGE_SHIFT,
+				   BMA023_RANGE_MASK, BMA023_RANGE_BW_REG);
+}
+
+static int bma023_get_range(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_RANGE_SHIFT,
+				   BMA023_RANGE_MASK, BMA023_RANGE_BW_REG);
+}
+
+/**
+ *	bma023_set_bandwidth	-	set the digital filtering of ADC output data
+ *	@client: i2c client for the sensor
+ *	@bw: new bandwidth setting
+ *
+ *	The bandwidth affect the ability of noise filter. The value of bandwidth
+ *	includes 25Hz, 50Hz, 100Hz, 190Hz, 375Hz, 750Hz and 1500Hz.
+ *
+ *	The change of bandwidth will respond to 3 axes directly.
+ */
+static int bma023_set_bandwidth(struct i2c_client *client, u8 bw)
+{
+	return bma023_set_reg_bits(client, bw, BMA023_BANDWIDTH_SHIFT,
+				   BMA023_BANDWIDTH_MASK, BMA023_RANGE_BW_REG);
+}
+
+static int bma023_get_bandwidth(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_BANDWIDTH_SHIFT,
+				   BMA023_BANDWIDTH_MASK, BMA023_RANGE_BW_REG);
+}
+
+/**
+ *	bma023_set_new_data_int	-	switch on/off of new data interrupt
+ *	@client: i2c client for the sensor
+ *	@val: value to switch on/off, 1: on, 0: off
+ *
+ *	To turn on this interrupt will generate an interrupt when all three
+ *	axes acceleration values are new.
+ */
+static int bma023_set_new_data_int(struct i2c_client *client, u8 val)
+{
+	return bma023_set_reg_bits(client, val, BMA023_NEW_DATA_INT_SHIFT,
+				   BMA023_NEW_DATA_INT_MASK, BMA023_CONF2_REG);
+}
+
+static int bma023_get_new_data_int(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_NEW_DATA_INT_SHIFT,
+				   BMA023_NEW_DATA_INT_MASK, BMA023_CONF2_REG);
+}
+
+/**
+ *	bma023_set_hg_int	-	switch on/off of high-g interrupt
+ *	@client: i2c client for the sensor
+ *	@val: value to switch on/off, 1: on, 0: off
+ *
+ *	To turn on this interrupt will generate an interrupt when the
+ *	oscillatory criteria is satisfied by hg_dur and hg_thres.
+ *
+ *	Usually, it is used to debounce the high-g criteria.
+ */
+static int bma023_set_hg_int(struct i2c_client *client, u8 val)
+{
+	return bma023_set_reg_bits(client, val, BMA023_ENABLE_HG_SHIFT,
+				   BMA023_ENABLE_HG_MASK, BMA023_CTRL2_REG);
+}
+
+static int bma023_get_hg_int(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_ENABLE_HG_SHIFT,
+				   BMA023_ENABLE_HG_MASK, BMA023_CTRL2_REG);
+}
+
+/**
+ *	bma023_set_lg_int	-	switch on/off of low-g interrupt
+ *	@client: i2c client for the sensor
+ *	@val: value to switch on/off, 1: on, 0: off
+ *
+ *	To turn on this setting will generate an interrupt when the
+ *	oscillatory criteria is satisfied by lg_dur and lg_thres.
+ *
+ *	Usually, it is used to detect a free fall.
+ */
+static int bma023_set_lg_int(struct i2c_client *client, u8 val)
+{
+	return bma023_set_reg_bits(client, val,	BMA023_ENABLE_LG_SHIFT,
+				   BMA023_ENABLE_LG_MASK, BMA023_CTRL2_REG);
+}
+
+static int bma023_get_lg_int(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_ENABLE_LG_SHIFT,
+				   BMA023_ENABLE_LG_MASK, BMA023_CTRL2_REG);
+}
+
+/**
+ *	bma023_set_lg_dur	-	set low-g duration
+ *	@client: i2c client for the sensor
+ *	@dur: duration value, 0-255
+ *	
+ *	Generate an interrupt when the gravitation is detected in low-g
+ *	criterion for long enough duration.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_lg_dur(struct i2c_client *client, u8 dur)
+{
+	return bma023_set_reg_bits(client, dur,	BMA023_LG_DUR_SHIFT,
+				   BMA023_LG_DUR_MASK, BMA023_SETTINGS2_REG);
+}
+
+static int bma023_get_lg_dur(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_LG_DUR_SHIFT,
+				   BMA023_LG_DUR_MASK, BMA023_SETTINGS2_REG);
+}
+
+/**
+ *	bma023_set_lg_thres	-	set low-g threshold
+ *	@client: i2c client for the sensor
+ *	@thres: threshold value, 0-255
+ *	
+ *	Filter 3 axes detection and limit to trigger an interrupt signal
+ *	when the gravitation is detected in low-g threshold.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_lg_thres(struct i2c_client *client, u8 thres)
+{
+	return bma023_set_reg_bits(client, thres, BMA023_LG_THRES_SHIFT,
+				   BMA023_LG_THRES_MASK, BMA023_SETTINGS1_REG);
+}
+
+static int bma023_get_lg_thres(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_LG_THRES_SHIFT,
+				   BMA023_LG_THRES_MASK, BMA023_SETTINGS1_REG);
+}
+
+/**
+ *	bma023_set_lg_hyst	-	set low-g hysteresis
+ *	@client: i2c client for the sensor
+ *	@hyst: hysteresis value, 0-7
+ *	
+ *	Expand the range of detectable gravitation to reset an interrupt.
+ *	An interrupt will be latched until outside of low-g criterion with 
+ *	hysteresis.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_lg_hyst(struct i2c_client *client, u8 hyst)
+{
+	return bma023_set_reg_bits(client, hyst, BMA023_LG_HYST_SHIFT,
+				   BMA023_LG_HYST_MASK, BMA023_SETTINGS6_REG);
+}
+
+static int bma023_get_lg_hyst(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_LG_HYST_SHIFT,
+				   BMA023_LG_HYST_MASK,	BMA023_SETTINGS6_REG);
+}
+
+/**
+ *	bma023_set_hg_dur	-	set high-g duration
+ *	@client: i2c client for the sensor
+ *	@dur: duration value, 0-255
+ *	
+ *	Generate an interrupt when the gravitation is detected in high-g
+ *	criterion for long enough duration.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_hg_dur(struct i2c_client *client, u8 dur)
+{
+	return bma023_set_reg_bits(client, dur,	BMA023_HG_DUR_SHIFT,
+				   BMA023_HG_DUR_MASK, BMA023_SETTINGS4_REG);
+}
+
+static int bma023_get_hg_dur(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_HG_DUR_SHIFT,
+				   BMA023_HG_DUR_MASK, BMA023_SETTINGS4_REG);
+}
+
+/**
+ *	bma023_set_hg_thres	-	set high-g threshold
+ *	@client: i2c client for the sensor
+ *	@thres: threshold value, 0-255
+ *	
+ *	Filter 3 axes detection and limit to trigger an interrupt signal
+ *	when the gravitation is detected in high-g threshold.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_hg_thres(struct i2c_client *client, u8 thres)
+{
+	return bma023_set_reg_bits(client, thres, BMA023_HG_THRES_SHIFT,
+				   BMA023_HG_THRES_MASK, BMA023_SETTINGS3_REG);
+}
+
+static int bma023_get_hg_thres(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_HG_THRES_SHIFT,
+				   BMA023_HG_THRES_MASK, BMA023_SETTINGS3_REG);
+}
+
+/**
+ *	bma023_set_hg_hyst	-	set low-g hysteresis
+ *	@client: i2c client for the sensor
+ *	@hyst: hysteresis value, 0-7
+ *	
+ *	Expand the range of detectable gravitation to reset an interrupt.
+ *	An interrupt will be latched until outside of high-g criterion with 
+ *	hysteresis.
+ *
+ *	This setting is used to interrupt detection.
+ */
+static int bma023_set_hg_hyst(struct i2c_client *client, u8 hyst)
+{
+	return bma023_set_reg_bits(client, hyst, BMA023_HG_HYST_SHIFT,
+				   BMA023_HG_HYST_MASK,	BMA023_SETTINGS6_REG);
+}
+
+static int bma023_get_hg_hyst(struct i2c_client *client)
+{
+	return bma023_get_reg_bits(client, BMA023_HG_HYST_SHIFT,
+				   BMA023_HG_HYST_MASK,	BMA023_SETTINGS6_REG);
+}
+
+/**
+ *	bma023_power_mode	-	set the power mode
+ *	@client: i2c client for the sensor
+ *	@val: value to switch on/off of power, 1: normal power, 0: low power
+ *
+ *	Put device to normal-power mode or low-power mode.
+ */
+static int bma023_set_power_mode(struct i2c_client *client, u8 val)
+{
+	return bma023_set_reg_bits(client, ~val, BMA023_SLEEP_SHIFT,
+				   BMA023_SLEEP_MASK, BMA023_CTRL1_REG);
+}
+
+static int bma023_get_power_mode(struct i2c_client *client)
+{
+	return !bma023_get_reg_bits(client, BMA023_SLEEP_SHIFT,
+				   BMA023_SLEEP_MASK, BMA023_CTRL1_REG);
+}
+
+/**
+ *	bma023_correct_accel_sign	-	convert to expected format
+ *	@val: twos complement value
+ *
+ *	The description of the digital signals x, y and z is "2' complement".
+ *	So we need to correct the sign of data read by i2c.
+ */
+static s16 bma023_correct_accel_sign(s16 val)
+{
+	val <<= (sizeof(s16) * BITS_PER_BYTE - BMA023_ACCEL_BITS);
+	val >>= (sizeof(s16) * BITS_PER_BYTE - BMA023_ACCEL_BITS);
+	return val;
+}
+
+/**
+ *	bma023_merge_register_values	-	turn into co-ordinate
+ *	@lsb: low bits from sensor
+ *	@msb: high bits from sensor
+ *
+ *	Turns a sensor register reading into a Linux input layer
+ *	value and returns it.
+ */
+static s16 bma023_merge_register_values(u8 lsb, u8 msb)
+{
+	s16 val = (msb << 2) | (lsb >> 6);
+	return bma023_correct_accel_sign(val);
+}
+
+/**
+ *	bma023_read_xyz		-	get co-ordinates from device
+ *	@client: i2c address of sensor
+ *	@coords: co-ordinates to update
+ *
+ *	Return the converted X Y and Z co-ordinates from the sensor device
+ */
+static void bma023_read_xyz(struct i2c_client *client,
+				struct bma023_data *coords)
+{
+	u8 buffer[6];
+	buffer[0] = BMA023_X_LSB_REG;
+	bma023_xyz_read_reg(client, buffer, 6);
+	coords->x = bma023_merge_register_values(buffer[0], buffer[1]);
+	coords->y = bma023_merge_register_values(buffer[2], buffer[3]);
+	coords->z = bma023_merge_register_values(buffer[4], buffer[5]);
+	dev_dbg(&client->dev, "%s: x %d, y %d, z %d\n", __func__,
+					coords->x, coords->y, coords->z);
+}
+
+/**
+ *	bma023_show_xyz		-	show co-ordinate readings
+ *	@dev: device of sensor
+ *	@attr: device attributes of sysfs node
+ *	@buf: buffer for output
+ *
+ *	Perform a one off read of the sensor data. In non-interrupt mode
+ *	the sensor can be used for one off reads of the axes rather than
+ *	as an input device.
+ */
+static ssize_t bma023_show_xyz(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&sensor->lock);
+	bma023_read_xyz(sensor->client, &sensor->data);
+	mutex_unlock(&sensor->lock);
+
+	pm_runtime_put(dev);
+
+	return sprintf(buf, "(%d,%d,%d)\n",
+		sensor->data.x, sensor->data.y, sensor->data.z);
+}
+static DEVICE_ATTR(accel_data, S_IRUGO, bma023_show_xyz, NULL);
+
+/**
+ *	bma023_set_attr	-	set an attribute on the sensor
+ *	@sensor sensor to update
+ *	@buf: buffer to parse
+ *	@count: length of buffer written
+ *	@get: read function to use
+ *	@set: write function to use
+ *	@data: where to store cached result
+ *
+ *	Helper function for setting attributes of the sensor. This does
+ *	all the parsing and locking in one place calling out to the helper
+ *	functions provided.
+ */
+static ssize_t bma023_set_attr(struct bma023_sensor *sensor,
+				const char *buf, size_t count,
+				int (*get)(struct i2c_client *),
+				int (*set)(struct i2c_client *, u8 val),
+				u8 *data)
+{
+	unsigned long val;
+	int ret;
+
+	if (!count)
+		return count;
+
+	ret = strict_strtoul(buf, 10, &val);
+	if (!ret) {
+		/* We don't need the device to be fully powered up for this
+		   but we do need to be sure that we force the i²c controller
+		   to be awake */
+		pm_runtime_get_sync(sensor->dev);
+		mutex_lock(&sensor->lock);
+		ret = set(sensor->client, val);
+		if (ret >= 0) {
+			ret = get(sensor->client);
+			if (ret >= 0)
+				*data = ret;
+		}
+		mutex_unlock(&sensor->lock);
+		pm_runtime_put(sensor->dev);
+		if (ret >= 0)
+			return count;
+	}
+	return ret;
+}
+
+/* Methods for the sysfs attributes */
+static ssize_t bma023_show_power_mode(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", bma023_get_power_mode(sensor->client));
+}
+
+static ssize_t bma023_store_power_mode(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	unsigned long val;
+	int ret = strict_strtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&sensor->lock);
+	if (val & !sensor->soft_power)
+		pm_runtime_get(dev);
+	else if (!val && sensor->soft_power)
+		pm_runtime_put(dev);
+	sensor->soft_power = val;
+	mutex_unlock(&sensor->lock);
+
+	return count;
+}
+
+static DEVICE_ATTR(power_mode, S_IRUGO | S_IWUSR,
+		bma023_show_power_mode, bma023_store_power_mode);
+
+static ssize_t bma023_show_range(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->range);
+}
+
+static ssize_t bma023_store_range(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_range,
+			bma023_set_range,
+			&sensor->range);
+}
+
+static DEVICE_ATTR(range, S_IRUGO | S_IWUSR,
+		bma023_show_range, bma023_store_range);
+
+static ssize_t bma023_show_bandwidth(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->bandwidth);
+}
+
+static ssize_t bma023_store_bandwidth(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_bandwidth,
+			bma023_set_bandwidth,
+			&sensor->bandwidth);
+}
+
+static DEVICE_ATTR(bandwidth, S_IRUGO | S_IWUSR,
+		bma023_show_bandwidth, bma023_store_bandwidth);
+
+static ssize_t bma023_show_new_data_int(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->new_data_int);
+}
+
+static ssize_t bma023_store_new_data_int(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_new_data_int,
+			bma023_set_new_data_int,
+			&sensor->new_data_int);
+}
+
+static DEVICE_ATTR(new_data_int, S_IRUGO | S_IWUSR,
+		bma023_show_new_data_int, bma023_store_new_data_int);
+
+static ssize_t bma023_show_hg_int(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->hg_int);
+}
+
+static ssize_t bma023_store_hg_int(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_hg_int,
+			bma023_set_hg_int,
+			&sensor->hg_int);
+}
+
+static DEVICE_ATTR(hg_int, S_IRUGO | S_IWUSR,
+		bma023_show_hg_int, bma023_store_hg_int);
+
+static ssize_t bma023_show_lg_int(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->lg_int);
+}
+
+static ssize_t bma023_store_lg_int(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_lg_int,
+			bma023_set_lg_int,
+			&sensor->lg_int);
+}
+
+static DEVICE_ATTR(lg_int, S_IRUGO | S_IWUSR,
+		bma023_show_lg_int, bma023_store_lg_int);
+
+static ssize_t bma023_show_lg_dur(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->lg_dur);
+}
+
+static ssize_t bma023_store_lg_dur(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_lg_dur,
+			bma023_set_lg_dur,
+			&sensor->lg_dur);
+}
+
+static DEVICE_ATTR(lg_dur, S_IRUGO | S_IWUSR,
+		bma023_show_lg_dur, bma023_store_lg_dur);
+
+static ssize_t bma023_show_lg_thres(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->lg_thres);
+}
+
+static ssize_t bma023_store_lg_thres(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_lg_thres,
+			bma023_set_lg_thres,
+			&sensor->lg_thres);
+}
+
+static DEVICE_ATTR(lg_thres, S_IRUGO | S_IWUSR,
+		bma023_show_lg_thres, bma023_store_lg_thres);
+
+static ssize_t bma023_show_lg_hyst(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->lg_hyst);
+}
+
+static ssize_t bma023_store_lg_hyst(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_lg_hyst,
+			bma023_set_lg_hyst,
+			&sensor->lg_hyst);
+}
+
+static DEVICE_ATTR(lg_hyst, S_IRUGO | S_IWUSR,
+		bma023_show_lg_hyst, bma023_store_lg_hyst);
+
+static ssize_t bma023_show_hg_dur(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->hg_dur);
+}
+
+static ssize_t bma023_store_hg_dur(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_hg_dur,
+			bma023_set_hg_dur,
+			&sensor->hg_dur);
+}
+
+static DEVICE_ATTR(hg_dur, S_IRUGO | S_IWUSR,
+		bma023_show_hg_dur, bma023_store_hg_dur);
+
+static ssize_t bma023_show_hg_thres(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->hg_thres);
+}
+
+static ssize_t bma023_store_hg_thres(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_hg_thres,
+			bma023_set_hg_thres,
+			&sensor->hg_thres);
+}
+
+static DEVICE_ATTR(hg_thres, S_IRUGO | S_IWUSR,
+		bma023_show_hg_thres, bma023_store_hg_thres);
+
+static ssize_t bma023_show_hg_hyst(struct device *dev,
+		struct device_attribute *att, char *buf)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", sensor->hg_hyst);
+}
+
+static ssize_t bma023_store_hg_hyst(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	return bma023_set_attr(sensor, buf, count,
+			bma023_get_hg_hyst,
+			bma023_set_hg_hyst,
+			&sensor->hg_hyst);
+}
+
+static DEVICE_ATTR(hg_hyst, S_IRUGO | S_IWUSR,
+		bma023_show_hg_hyst, bma023_store_hg_hyst);
+
+static struct attribute *bma023_attributes[] = {
+	&dev_attr_accel_data.attr,
+	&dev_attr_power_mode.attr,
+	&dev_attr_range.attr,
+	&dev_attr_bandwidth.attr,
+	&dev_attr_new_data_int.attr,
+	&dev_attr_hg_int.attr,
+	&dev_attr_lg_int.attr,
+	&dev_attr_lg_dur.attr,
+	&dev_attr_lg_thres.attr,
+	&dev_attr_lg_hyst.attr,
+	&dev_attr_hg_dur.attr,
+	&dev_attr_hg_thres.attr,
+	&dev_attr_hg_hyst.attr,
+	NULL
+};
+
+static const struct attribute_group bma023_group = {
+	.attrs	= bma023_attributes,
+};
+
+/**
+ *	bma023_interrupt_thread	-	handle an IRQ
+ *	@irq: interrupt numner
+ *	@data: the sensor
+ *
+ *	Called by the kernel single threaded after an interrupt occurs. Read
+ *	the sensor data and generate an input event for it.
+ */
+static irqreturn_t bma023_interrupt_thread(int irq, void *data)
+{
+	struct bma023_sensor *sensor = data;
+
+	mutex_lock(&sensor->lock);
+	bma023_read_xyz(sensor->client, &sensor->data);
+	mutex_unlock(&sensor->lock);
+
+	input_report_abs(sensor->idev, ABS_X, sensor->data.x);
+	input_report_abs(sensor->idev, ABS_Y, sensor->data.y);
+	input_report_abs(sensor->idev, ABS_Z, sensor->data.z);
+	input_sync(sensor->idev);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ *	bma023_initialize	-	set sensor parameters
+ *	@sensor: sensor to configure
+ *
+ *	Load the actual hardware with the sensor values that were provided
+ */
+static void bma023_initialize(struct bma023_sensor *sensor)
+{
+	bma023_set_range(sensor->client, sensor->range);
+	bma023_set_bandwidth(sensor->client, sensor->bandwidth);
+	bma023_set_new_data_int(sensor->client, sensor->new_data_int);
+	bma023_set_hg_dur(sensor->client, sensor->hg_dur);
+	bma023_set_hg_thres(sensor->client, sensor->hg_thres);
+	bma023_set_hg_hyst(sensor->client, sensor->hg_hyst);
+	bma023_set_lg_dur(sensor->client, sensor->lg_dur);
+	bma023_set_lg_thres(sensor->client, sensor->lg_thres);
+	bma023_set_lg_hyst(sensor->client, sensor->lg_hyst);
+	bma023_set_hg_int(sensor->client, sensor->hg_int);
+	bma023_set_lg_int(sensor->client, sensor->lg_int);
+	bma023_set_power_mode(sensor->client, sensor->power_mode);
+	bma023_set_check_reg(sensor->client, BMA023_INSPECT_VAL);
+}
+
+/**
+ *	bma023_input_open	-	called on input event open
+ *	@input: input dev of opened device
+ *
+ *	The input layer calls this function when input event is opened. The
+ *	function will push the device to resume. Then, the device is ready
+ *	to provide data.
+ */
+static int bma023_input_open(struct input_dev *input)
+{
+	struct bma023_sensor *sensor = input_get_drvdata(input);
+	pm_runtime_get(sensor->dev);
+	return 0;
+}
+
+/**
+ *	bma023_input_close	-	called on input event close
+ *	@input: input dev of closed device
+ *
+ *	The input layer calls this function when input event is closed. The
+ *	function will push the device to suspend.
+ */
+static void bma023_input_close(struct input_dev *input)
+{
+	struct bma023_sensor *sensor = input_get_drvdata(input);
+	pm_runtime_put(sensor->dev);
+}
+
+/**
+ *	bma023_unregister_input_device	-	remove input dev
+ *	@sensor: sensor to remove from input
+ *
+ *	Free the interrupt and input device for the sensor. We must free
+ *	the interrupt first
+ */
+static void bma023_unregister_input_device(struct bma023_sensor *sensor)
+{
+	struct i2c_client *client = sensor->client;
+	if (client->irq > 0)
+		free_irq(client->irq, sensor);
+	input_unregister_device(sensor->idev);
+	sensor->idev = NULL;
+}
+
+/**
+ *	bma023_register_input_device	-	remove input dev
+ *	@sensor: sensor to remove from input
+ *
+ *	Add an input device to the sensor. This will be used to report
+ *	events from the sensor itself.
+ */
+static int bma023_register_input_device(struct bma023_sensor *sensor)
+{
+	struct i2c_client *client = sensor->client;
+	struct input_dev *idev;
+	int ret;
+	sensor->idev = input_allocate_device();
+	idev = sensor->idev;
+	if (!idev) {
+		dev_err(&client->dev, "failed to allocate input device\n");
+		ret = -ENOMEM;
+		goto failed_alloc;
+	}
+	idev->name = "BMA023 Sensor";
+	idev->open = bma023_input_open;
+	idev->close = bma023_input_close;
+	idev->id.bustype = BUS_I2C;
+	idev->dev.parent = &client->dev;
+	idev->evbit[0] = BIT_MASK(EV_ABS);
+	input_set_abs_params(idev, ABS_X, BMA023_MIN_VALUE,
+			BMA023_MAX_VALUE, 0, 0);
+	input_set_abs_params(idev, ABS_Y, BMA023_MIN_VALUE,
+			BMA023_MAX_VALUE, 0, 0);
+	input_set_abs_params(idev, ABS_Z, BMA023_MIN_VALUE,
+			BMA023_MAX_VALUE, 0, 0);
+	input_set_drvdata(idev, sensor);
+	ret = input_register_device(idev);
+	if (ret) {
+		dev_err(&client->dev, "failed to register input device\n");
+		goto failed_reg;
+	}
+	if (client->irq > 0) {
+		ret = request_threaded_irq(client->irq, NULL,
+				bma023_interrupt_thread, IRQF_TRIGGER_RISING,
+					"bma023", sensor);
+		if (ret) {
+			dev_err(&client->dev, "can't get IRQ %d, ret %d\n",
+					client->irq, ret);
+			goto failed_irq;
+		}
+	}
+	return 0;
+failed_irq:
+	input_unregister_device(idev);
+	return ret;
+
+failed_reg:
+	if (idev)
+		input_free_device(idev);
+failed_alloc:
+	return ret;
+}
+
+/**
+ *	bma023_probe	-	device detection callback
+ *	@client: i2c client of found device
+ *	@id: id match information
+ *
+ *	The I2C layer calls us when it believes a sensor is present at this
+ *	address. Probe to see if this is correct and to validate the device.
+ *
+ *	If present install the relevant sysfs interfaces and input device.
+ */
+static int __devinit bma023_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct bma023_sensor *sensor;
+	struct bma023_platform_data *pdata;
+	int ret;
+	int auto_delay;		/* PM timeout */
+
+	sensor = kzalloc(sizeof(struct bma023_sensor), GFP_KERNEL);
+	if (!sensor) {
+		dev_err(&client->dev, "failed to allocate driver data\n");
+		return -ENOMEM;
+	}
+	pdata = client->dev.platform_data;
+	sensor->dev = &client->dev;
+
+	sensor->client = client;
+	i2c_set_clientdata(client, sensor);
+
+	ret = bma023_read_reg(client, BMA023_CHIP_ID_REG);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to detect device\n");
+		goto failed_free;
+	}
+	if (ret != BMA023_CHIP_ID) {
+		dev_err(&client->dev, "unsupported chip id\n");
+		goto failed_free;
+	}
+
+	mutex_init(&sensor->lock);
+
+	ret = sysfs_create_group(&client->dev.kobj, &bma023_group);
+	if (ret) {
+		dev_err(&client->dev, "failed to create attribute group\n");
+		goto failed_free;
+	}
+
+	pm_runtime_set_active(&client->dev);
+
+	ret = bma023_register_input_device(sensor);
+	if (ret)
+		dev_err(&client->dev, "only provide sysfs\n");
+
+	if (pdata) {
+		sensor->range = pdata->range;
+		sensor->bandwidth = pdata->bandwidth;
+		sensor->new_data_int = pdata->new_data_int;
+		sensor->hg_int = pdata->hg_int;
+		sensor->lg_int = pdata->lg_int;
+		sensor->hg_dur = pdata->hg_dur;
+		sensor->hg_thres = pdata->hg_thres;
+		sensor->hg_hyst = pdata->hg_hyst;
+		sensor->lg_dur = pdata->lg_dur;
+		sensor->lg_thres = pdata->lg_thres;
+		sensor->lg_hyst = pdata->lg_hyst;
+		auto_delay = pdata->auto_delay;
+	} else {
+		sensor->range = BMA023_DEFAULT_RANGE;
+		sensor->bandwidth = BMA023_DEFAULT_BANDWIDTH;
+		sensor->new_data_int = BMA023_DEFAULT_NEW_DATA_INT;
+		sensor->hg_int = BMA023_DEFAULT_HG_INT;
+		sensor->lg_int = BMA023_DEFAULT_LG_INT;
+		sensor->hg_dur = BMA023_DEFAULT_HG_DURATION;
+		sensor->hg_thres = BMA023_DEFAULT_HG_THRESHOLD;
+		sensor->hg_hyst = BMA023_DEFAULT_HG_HYST;
+		sensor->lg_dur = BMA023_DEFAULT_LG_DURATION;
+		sensor->lg_thres = BMA023_DEFAULT_LG_THRESHOLD;
+		sensor->lg_hyst = BMA023_DEFAULT_LG_HYST;
+		auto_delay = BMA023_DEFAULT_AUTO_DELAY;
+	}
+	sensor->power_mode = BMA023_SUSPEND;
+
+	bma023_initialize(sensor);
+
+	pm_runtime_enable(&client->dev);
+	pm_runtime_set_autosuspend_delay(&client->dev, auto_delay);
+
+	dev_info(&client->dev, "%s registered\n", id->name);
+	return 0;
+
+failed_free:
+	kfree(sensor);
+	return ret;
+}
+
+/**
+ *	bam023_remove	-	remove a sensor
+ *	@client: i2c client of sensor being removed
+ *
+ *	Our sensor is going away, clean up the resources.
+ */
+static int __devexit bma023_remove(struct i2c_client *client)
+{
+	struct bma023_sensor *sensor = i2c_get_clientdata(client);
+
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+
+	if (sensor->idev)
+		bma023_unregister_input_device(sensor);
+	sysfs_remove_group(&client->dev.kobj, &bma023_group);
+	kfree(sensor);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+/**
+ *	bma023_suspend		-	called on device suspend
+ *	@client: i2c client of sensor
+ *	@mesg: actual suspend type
+ *
+ *	Put the device into sleep mode before we suspend the machine.
+ */
+static int bma023_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	bma023_set_power_mode(client, BMA023_SUSPEND);
+	return 0;
+}
+
+/**
+ *	bma023_resume		-	called on device resume
+ *	@client: i2c client of sensor
+ *
+ *	Put the device into powered mode on resume.
+ */
+static int bma023_resume(struct i2c_client *client)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(&client->dev);
+	if (bma023_get_check_reg(client) != BMA023_INSPECT_VAL)
+		bma023_initialize(sensor);
+	bma023_set_power_mode(client, sensor->power_mode);
+	msleep(4);  /* wait for accel chip resume */
+	return 0;
+}
+
+#else
+#define bma023_suspend NULL
+#define bma023_resume NULL
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int bma023_runtime_suspend(struct device *dev)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	bma023_set_power_mode(sensor->client, BMA023_SUSPEND);
+	return 0;
+}
+
+static int bma023_runtime_resume(struct device *dev)
+{
+	struct bma023_sensor *sensor = dev_get_drvdata(dev);
+	int ret = bma023_set_power_mode(sensor->client, BMA023_RESUME);
+	if (ret == 0)
+		msleep(4);  /* wait for accel chip resume */
+	return ret;
+}
+
+static const struct dev_pm_ops bma023_pm = {
+	.runtime_suspend = bma023_runtime_suspend,
+	.runtime_resume  = bma023_runtime_resume,
+};
+#endif
+
+static const struct i2c_device_id bma023_ids[] = {
+	{ "bma023", 0 },
+	{ "smb380", 1 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, bma023_ids);
+
+static struct i2c_driver bma023_i2c_driver = {
+	.driver	= {
+		.name	= "bma023",
+#ifdef CONFIG_PM_RUNTIME
+		.pm		= &bma023_pm,
+#endif		
+	},
+	.probe		= bma023_probe,
+	.remove		= __devexit_p(bma023_remove),
+	.suspend	= bma023_suspend,
+	.resume		= bma023_resume,
+	.id_table	= bma023_ids,
+};
+
+static int __init bma023_init(void)
+{
+	return i2c_add_driver(&bma023_i2c_driver);
+}
+module_init(bma023_init);
+
+static void __exit bma023_exit(void)
+{
+	i2c_del_driver(&bma023_i2c_driver);
+}
+module_exit(bma023_exit);
+
+MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>, Wistron Corp.");
+MODULE_DESCRIPTION("BMA023/SMB380 Tri-axis accelerometer driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/i2c/bma023.h b/include/linux/i2c/bma023.h
new file mode 100644
index 0000000..7c6e890
--- /dev/null
+++ b/include/linux/i2c/bma023.h
@@ -0,0 +1,44 @@
+/*
+ * bma023.h - BMA023 Tri-axis accelerometer driver
+ *
+ * Copyright (c) 2010 Samsung Eletronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _BMA023_H_
+#define _BMA023_H_
+
+#define	BMA023_RANGE_2G		0
+#define	BMA023_RANGE_4G		1
+#define	BMA023_RANGE_8G		2
+
+/* Used to setup the digital filtering bandwidth of ADC output */
+#define	BMA023_BW_25HZ		0
+#define	BMA023_BW_50HZ		1
+#define	BMA023_BW_100HZ		2
+#define	BMA023_BW_190HZ		3
+#define	BMA023_BW_375HZ		4
+#define	BMA023_BW_750HZ		5
+#define BMA023_BW_1500HZ	6
+
+struct bma023_platform_data {
+	u8 range;		/* BMA023_RANGE_xxx */
+	u8 bandwidth;		/* BMA023_BW_xxx */
+	u8 new_data_int;	/* Set to enable new data interrupt */
+	u8 hg_int;		/* Set to enable high G interrupt */
+	u8 lg_int;		/* Set to enable low G interrupt */
+	u8 lg_dur;		/* Low G duration */
+	u8 lg_thres;		/* Low G threshold */
+	u8 lg_hyst;		/* Low G Hysterisis */
+	u8 hg_dur;		/* Ditto for High G */
+	u8 hg_thres;
+	u8 hg_hyst;
+	int auto_delay;		/* Runtime PM keeps active on use for mS */
+};
+
+#endif /* _BMA023_H_ */

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-02-09 10:28 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-25 16:05 [PATCH] bma023: Support for Bosch BMA023 and SMB380 accelerometers Alan Cox
2011-01-26 11:11 ` Jonathan Cameron
2011-01-26 11:42   ` Alan Cox
2011-01-26 12:08     ` Jonathan Cameron
2011-02-09  9:59 Alan Cox

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.