Staging drivers patches for 3.20-rc1

Here's the big staging driver tree update for 3.20-rc1.  Lots of little
 things in here, adding up to lots of overall cleanups.  The IIO driver
 updates are also in here as they cross the staging tree boundry a lot.
 I2O has moved into staging as well, as a plan to drop it from the tree
 eventually as that's a dead subsystem.
 
 All of this has been in linux-next with no reported issues for a while.
 
 Signed-off-by: Greg Kroah-Hartman <[email protected]>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iEYEABECAAYFAlTgtVQACgkQMUfUDdst+yk4mACgshYZ1fOQDoPR+BXd+QD1HXfh
 GosAoICXkSjDQjwVo13W6QHIVMsUezY+
 =4jHr
 -----END PGP SIGNATURE-----

Merge tag 'staging-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging drivers patches from Greg KH:
 "Here's the big staging driver tree update for 3.20-rc1.

  Lots of little things in here, adding up to lots of overall cleanups.
  The IIO driver updates are also in here as they cross the staging tree
  boundry a lot.  I2O has moved into staging as well, as a plan to drop
  it from the tree eventually as that's a dead subsystem.

  All of this has been in linux-next with no reported issues for a
  while"

* tag 'staging-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (740 commits)
  staging: lustre: lustre: libcfs: define symbols as static
  staging: rtl8712: Do coding style cleanup
  staging: lustre: make obd_updatemax_lock static
  staging: rtl8188eu: core: switch with redundant cases
  staging: rtl8188eu: odm: conditional setting with no effect
  staging: rtl8188eu: odm: condition with no effect
  staging: ft1000: fix braces warning
  staging: sm7xxfb: fix remaining CamelCase
  staging: sm7xxfb: fix CamelCase
  staging: rtl8723au: multiple condition with no effect - if identical to else
  staging: sm7xxfb: make smtc_scr_info static
  staging/lustre/mdc: Initialize req in mdc_enqueue for !it case
  staging/lustre/clio: Do not allow group locks with gid 0
  staging/lustre/llite: don't add to page cache upon failure
  staging/lustre/llite: Add exception entry check after radix_tree
  staging/lustre/libcfs: protect kkuc_groups from write access
  staging/lustre/fld: refer to MDT0 for fld lookup in some cases
  staging/lustre/llite: Solve a race to access lli_has_smd in read case
  staging/lustre/ptlrpc: hold rq_lock when modify rq_flags
  staging/lustre/lnet: portal spreading rotor should be unsigned
  ...
This commit is contained in:
Linus Torvalds 2015-02-15 11:30:39 -08:00
commit 46f7b63556
525 changed files with 31906 additions and 15103 deletions

View file

@ -27,7 +27,6 @@ boolean "IIO callback buffer used for push in-kernel interfaces"
usage. That is, those where the data is pushed to the consumer.
config IIO_KFIFO_BUF
select IIO_TRIGGER
tristate "Industrial I/O buffering based on kfifo"
help
A simple fifo based on kfifo. Note that this currently provides

View file

@ -43,6 +43,9 @@ config HID_SENSOR_ACCEL_3D
Say yes here to build support for the HID SENSOR
accelerometers 3D.
To compile this driver as a module, choose M here: the
module will be called hid-sensor-accel-3d.
config IIO_ST_ACCEL_3AXIS
tristate "STMicroelectronics accelerometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
@ -80,6 +83,9 @@ config KXSD9
Say yes here to build support for the Kionix KXSD9 accelerometer.
Currently this only supports the device via an SPI interface.
To compile this driver as a module, choose M here: the module
will be called kxsd9.
config MMA8452
tristate "Freescale MMA8452Q Accelerometer Driver"
depends on I2C
@ -105,4 +111,29 @@ config KXCJK1013
To compile this driver as a module, choose M here: the module will
be called kxcjk-1013.
config MMA9551_CORE
tristate
config MMA9551
tristate "Freescale MMA9551L Intelligent Motion-Sensing Platform Driver"
depends on I2C
select MMA9551_CORE
help
Say yes here to build support for the Freescale MMA9551L
Intelligent Motion-Sensing Platform Driver.
To compile this driver as a module, choose M here: the module
will be called mma9551.
config MMA9553
tristate "Freescale MMA9553L Intelligent Pedometer Platform Driver"
depends on I2C
select MMA9551_CORE
help
Say yes here to build support for the Freescale MMA9553L
Intelligent Pedometer Platform Driver.
To compile this driver as a module, choose M here: the module
will be called mma9553.
endmenu

View file

@ -10,6 +10,12 @@ obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o
obj-$(CONFIG_MMA8452) += mma8452.o
obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
obj-$(CONFIG_MMA9551) += mma9551.o
obj-$(CONFIG_MMA9553) += mma9553.o
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o
obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
st_accel-y := st_accel_core.o
st_accel-$(CONFIG_IIO_BUFFER) += st_accel_buffer.o

View file

@ -111,19 +111,12 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
switch (mask) {
case 0:
poll_value = hid_sensor_read_poll_value(
&accel_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&accel_state->common_attributes, true);
msleep_interruptible(poll_value * 2);
report_id = accel_state->accel[chan->scan_index].report_id;
address = accel_3d_addresses[chan->scan_index];
if (report_id >= 0)
@ -419,6 +412,7 @@ static struct platform_driver hid_accel_3d_platform_driver = {
.id_table = hid_accel_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_accel_3d_probe,
.remove = hid_accel_3d_remove,

View file

@ -108,6 +108,7 @@ struct kxcjk1013_data {
bool motion_trigger_on;
int64_t timestamp;
enum kx_chipset chipset;
bool is_smo8500_device;
};
enum kxcjk1013_axis {
@ -377,6 +378,7 @@ static int kxcjk1013_get_startup_times(struct kxcjk1013_data *data)
static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
{
#ifdef CONFIG_PM
int ret;
if (on)
@ -388,8 +390,11 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
if (ret < 0) {
dev_err(&data->client->dev,
"Failed: kxcjk1013_set_power_state for %d\n", on);
if (on)
pm_runtime_put_noidle(&data->client->dev);
return ret;
}
#endif
return 0;
}
@ -858,6 +863,8 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev,
ret = kxcjk1013_setup_any_motion_interrupt(data, state);
if (ret < 0) {
kxcjk1013_set_power_state(data, false);
data->ev_enable_state = 0;
mutex_unlock(&data->mutex);
return ret;
}
@ -1008,6 +1015,7 @@ static int kxcjk1013_data_rdy_trigger_set_state(struct iio_trigger *trig,
else
ret = kxcjk1013_setup_new_data_interrupt(data, state);
if (ret < 0) {
kxcjk1013_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
@ -1131,12 +1139,16 @@ static irqreturn_t kxcjk1013_data_rdy_trig_poll(int irq, void *private)
}
static const char *kxcjk1013_match_acpi_device(struct device *dev,
enum kx_chipset *chipset)
enum kx_chipset *chipset,
bool *is_smo8500_device)
{
const struct acpi_device_id *id;
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return NULL;
if (strcmp(id->id, "SMO8500") == 0)
*is_smo8500_device = true;
*chipset = (enum kx_chipset)id->driver_data;
return dev_name(dev);
@ -1151,6 +1163,8 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client,
if (!client)
return -EINVAL;
if (data->is_smo8500_device)
return -ENOTSUPP;
dev = &client->dev;
@ -1200,7 +1214,8 @@ static int kxcjk1013_probe(struct i2c_client *client,
name = id->name;
} else if (ACPI_HANDLE(&client->dev)) {
name = kxcjk1013_match_acpi_device(&client->dev,
&data->chipset);
&data->chipset,
&data->is_smo8500_device);
} else
return -ENODEV;
@ -1228,21 +1243,25 @@ static int kxcjk1013_probe(struct i2c_client *client,
KXCJK1013_IRQ_NAME,
indio_dev);
if (ret)
return ret;
goto err_poweroff;
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d",
indio_dev->name,
indio_dev->id);
if (!data->dready_trig)
return -ENOMEM;
if (!data->dready_trig) {
ret = -ENOMEM;
goto err_poweroff;
}
data->motion_trig = devm_iio_trigger_alloc(&client->dev,
"%s-any-motion-dev%d",
indio_dev->name,
indio_dev->id);
if (!data->motion_trig)
return -ENOMEM;
if (!data->motion_trig) {
ret = -ENOMEM;
goto err_poweroff;
}
data->dready_trig->dev.parent = &client->dev;
data->dready_trig->ops = &kxcjk1013_trigger_ops;
@ -1251,7 +1270,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
iio_trigger_get(indio_dev->trig);
ret = iio_trigger_register(data->dready_trig);
if (ret)
return ret;
goto err_poweroff;
data->motion_trig->dev.parent = &client->dev;
data->motion_trig->ops = &kxcjk1013_trigger_ops;
@ -1300,6 +1319,8 @@ err_trigger_unregister:
iio_trigger_unregister(data->dready_trig);
if (data->motion_trig)
iio_trigger_unregister(data->motion_trig);
err_poweroff:
kxcjk1013_set_mode(data, STANDBY);
return ret;
}
@ -1349,10 +1370,7 @@ static int kxcjk1013_resume(struct device *dev)
int ret = 0;
mutex_lock(&data->mutex);
/* Check, if the suspend occured while active */
if (data->dready_trigger_on || data->motion_trigger_on ||
data->ev_enable_state)
ret = kxcjk1013_set_mode(data, OPERATION);
ret = kxcjk1013_set_mode(data, OPERATION);
mutex_unlock(&data->mutex);
return ret;
@ -1364,8 +1382,14 @@ static int kxcjk1013_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct kxcjk1013_data *data = iio_priv(indio_dev);
int ret;
return kxcjk1013_set_mode(data, STANDBY);
ret = kxcjk1013_set_mode(data, STANDBY);
if (ret < 0) {
dev_err(&data->client->dev, "powering off device failed\n");
return -EAGAIN;
}
return 0;
}
static int kxcjk1013_runtime_resume(struct device *dev)
@ -1399,6 +1423,7 @@ static const struct acpi_device_id kx_acpi_match[] = {
{"KXCJ1013", KXCJK1013},
{"KXCJ1008", KXCJ91008},
{"KXTJ1009", KXTJ21009},
{"SMO8500", KXCJ91008},
{ },
};
MODULE_DEVICE_TABLE(acpi, kx_acpi_match);
@ -1407,6 +1432,7 @@ static const struct i2c_device_id kxcjk1013_id[] = {
{"kxcjk1013", KXCJK1013},
{"kxcj91008", KXCJ91008},
{"kxtj21009", KXTJ21009},
{"SMO8500", KXCJ91008},
{}
};

View file

@ -111,7 +111,7 @@ static const int mma8452_samp_freq[8][2] = {
{6, 250000}, {1, 560000}
};
/*
/*
* Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048
* The userspace interface uses m/s^2 and we declare micro units
* So scale factor is given by:

637
drivers/iio/accel/mma9551.c Normal file
View file

@ -0,0 +1,637 @@
/*
* Freescale MMA9551L Intelligent Motion-Sensing Platform driver
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/pm_runtime.h>
#include "mma9551_core.h"
#define MMA9551_DRV_NAME "mma9551"
#define MMA9551_IRQ_NAME "mma9551_event"
#define MMA9551_GPIO_NAME "mma9551_int"
#define MMA9551_GPIO_COUNT 4
/* Tilt application (inclination in IIO terms). */
#define MMA9551_TILT_XZ_ANG_REG 0x00
#define MMA9551_TILT_YZ_ANG_REG 0x01
#define MMA9551_TILT_XY_ANG_REG 0x02
#define MMA9551_TILT_ANGFLG BIT(7)
#define MMA9551_TILT_QUAD_REG 0x03
#define MMA9551_TILT_XY_QUAD_SHIFT 0
#define MMA9551_TILT_YZ_QUAD_SHIFT 2
#define MMA9551_TILT_XZ_QUAD_SHIFT 4
#define MMA9551_TILT_CFG_REG 0x01
#define MMA9551_TILT_ANG_THRESH_MASK GENMASK(3, 0)
#define MMA9551_DEFAULT_SAMPLE_RATE 122 /* Hz */
/* Tilt events are mapped to the first three GPIO pins. */
enum mma9551_tilt_axis {
mma9551_x = 0,
mma9551_y,
mma9551_z,
};
struct mma9551_data {
struct i2c_client *client;
struct mutex mutex;
int event_enabled[3];
int irqs[MMA9551_GPIO_COUNT];
};
static int mma9551_read_incli_chan(struct i2c_client *client,
const struct iio_chan_spec *chan,
int *val)
{
u8 quad_shift, angle, quadrant;
u16 reg_addr;
int ret;
switch (chan->channel2) {
case IIO_MOD_X:
reg_addr = MMA9551_TILT_YZ_ANG_REG;
quad_shift = MMA9551_TILT_YZ_QUAD_SHIFT;
break;
case IIO_MOD_Y:
reg_addr = MMA9551_TILT_XZ_ANG_REG;
quad_shift = MMA9551_TILT_XZ_QUAD_SHIFT;
break;
case IIO_MOD_Z:
reg_addr = MMA9551_TILT_XY_ANG_REG;
quad_shift = MMA9551_TILT_XY_QUAD_SHIFT;
break;
default:
return -EINVAL;
}
ret = mma9551_set_power_state(client, true);
if (ret < 0)
return ret;
ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
reg_addr, &angle);
if (ret < 0)
goto out_poweroff;
ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
MMA9551_TILT_QUAD_REG, &quadrant);
if (ret < 0)
goto out_poweroff;
angle &= ~MMA9551_TILT_ANGFLG;
quadrant = (quadrant >> quad_shift) & 0x03;
if (quadrant == 1 || quadrant == 3)
*val = 90 * (quadrant + 1) - angle;
else
*val = angle + 90 * quadrant;
ret = IIO_VAL_INT;
out_poweroff:
mma9551_set_power_state(client, false);
return ret;
}
static int mma9551_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_INCLI:
mutex_lock(&data->mutex);
ret = mma9551_read_incli_chan(data->client, chan, val);
mutex_unlock(&data->mutex);
return ret;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_ACCEL:
mutex_lock(&data->mutex);
ret = mma9551_read_accel_chan(data->client,
chan, val, val2);
mutex_unlock(&data->mutex);
return ret;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ACCEL:
return mma9551_read_accel_scale(val, val2);
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int mma9551_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
struct mma9551_data *data = iio_priv(indio_dev);
switch (chan->type) {
case IIO_INCLI:
/* IIO counts axes from 1, because IIO_NO_MOD is 0. */
return data->event_enabled[chan->channel2 - 1];
default:
return -EINVAL;
}
}
static int mma9551_config_incli_event(struct iio_dev *indio_dev,
enum iio_modifier axis,
int state)
{
struct mma9551_data *data = iio_priv(indio_dev);
enum mma9551_tilt_axis mma_axis;
int ret;
/* IIO counts axes from 1, because IIO_NO_MOD is 0. */
mma_axis = axis - 1;
if (data->event_enabled[mma_axis] == state)
return 0;
if (state == 0) {
ret = mma9551_gpio_config(data->client,
(enum mma9551_gpio_pin)mma_axis,
MMA9551_APPID_NONE, 0, 0);
if (ret < 0)
return ret;
ret = mma9551_set_power_state(data->client, false);
if (ret < 0)
return ret;
} else {
int bitnum;
/* Bit 7 of each angle register holds the angle flag. */
switch (axis) {
case IIO_MOD_X:
bitnum = 7 + 8 * MMA9551_TILT_YZ_ANG_REG;
break;
case IIO_MOD_Y:
bitnum = 7 + 8 * MMA9551_TILT_XZ_ANG_REG;
break;
case IIO_MOD_Z:
bitnum = 7 + 8 * MMA9551_TILT_XY_ANG_REG;
break;
default:
return -EINVAL;
}
ret = mma9551_set_power_state(data->client, true);
if (ret < 0)
return ret;
ret = mma9551_gpio_config(data->client,
(enum mma9551_gpio_pin)mma_axis,
MMA9551_APPID_TILT, bitnum, 0);
if (ret < 0) {
mma9551_set_power_state(data->client, false);
return ret;
}
}
data->event_enabled[mma_axis] = state;
return ret;
}
static int mma9551_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
int state)
{
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
switch (chan->type) {
case IIO_INCLI:
mutex_lock(&data->mutex);
ret = mma9551_config_incli_event(indio_dev,
chan->channel2, state);
mutex_unlock(&data->mutex);
return ret;
default:
return -EINVAL;
}
}
static int mma9551_write_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int val, int val2)
{
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
switch (chan->type) {
case IIO_INCLI:
if (val2 != 0 || val < 1 || val > 10)
return -EINVAL;
mutex_lock(&data->mutex);
ret = mma9551_update_config_bits(data->client,
MMA9551_APPID_TILT,
MMA9551_TILT_CFG_REG,
MMA9551_TILT_ANG_THRESH_MASK,
val);
mutex_unlock(&data->mutex);
return ret;
default:
return -EINVAL;
}
}
static int mma9551_read_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int *val, int *val2)
{
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
u8 tmp;
switch (chan->type) {
case IIO_INCLI:
mutex_lock(&data->mutex);
ret = mma9551_read_config_byte(data->client,
MMA9551_APPID_TILT,
MMA9551_TILT_CFG_REG, &tmp);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
*val = tmp & MMA9551_TILT_ANG_THRESH_MASK;
*val2 = 0;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static const struct iio_event_spec mma9551_incli_event = {
.type = IIO_EV_TYPE_ROC,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
};
#define MMA9551_INCLI_CHANNEL(axis) { \
.type = IIO_INCLI, \
.modified = 1, \
.channel2 = axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
.event_spec = &mma9551_incli_event, \
.num_event_specs = 1, \
}
static const struct iio_chan_spec mma9551_channels[] = {
MMA9551_ACCEL_CHANNEL(IIO_MOD_X),
MMA9551_ACCEL_CHANNEL(IIO_MOD_Y),
MMA9551_ACCEL_CHANNEL(IIO_MOD_Z),
MMA9551_INCLI_CHANNEL(IIO_MOD_X),
MMA9551_INCLI_CHANNEL(IIO_MOD_Y),
MMA9551_INCLI_CHANNEL(IIO_MOD_Z),
};
static const struct iio_info mma9551_info = {
.driver_module = THIS_MODULE,
.read_raw = mma9551_read_raw,
.read_event_config = mma9551_read_event_config,
.write_event_config = mma9551_write_event_config,
.read_event_value = mma9551_read_event_value,
.write_event_value = mma9551_write_event_value,
};
static irqreturn_t mma9551_event_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct mma9551_data *data = iio_priv(indio_dev);
int i, ret, mma_axis = -1;
u16 reg;
u8 val;
mutex_lock(&data->mutex);
for (i = 0; i < 3; i++)
if (irq == data->irqs[i]) {
mma_axis = i;
break;
}
if (mma_axis == -1) {
/* IRQ was triggered on 4th line, which we don't use. */
dev_warn(&data->client->dev,
"irq triggered on unused line %d\n", data->irqs[3]);
goto out;
}
switch (mma_axis) {
case mma9551_x:
reg = MMA9551_TILT_YZ_ANG_REG;
break;
case mma9551_y:
reg = MMA9551_TILT_XZ_ANG_REG;
break;
case mma9551_z:
reg = MMA9551_TILT_XY_ANG_REG;
break;
}
/*
* Read the angle even though we don't use it, otherwise we
* won't get any further interrupts.
*/
ret = mma9551_read_status_byte(data->client, MMA9551_APPID_TILT,
reg, &val);
if (ret < 0) {
dev_err(&data->client->dev,
"error %d reading tilt register in IRQ\n", ret);
goto out;
}
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_INCLI, 0, (mma_axis + 1),
IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING),
iio_get_time_ns());
out:
mutex_unlock(&data->mutex);
return IRQ_HANDLED;
}
static int mma9551_init(struct mma9551_data *data)
{
int ret;
ret = mma9551_read_version(data->client);
if (ret)
return ret;
return mma9551_set_device_state(data->client, true);
}
static int mma9551_gpio_probe(struct iio_dev *indio_dev)
{
struct gpio_desc *gpio;
int i, ret;
struct mma9551_data *data = iio_priv(indio_dev);
struct device *dev = &data->client->dev;
for (i = 0; i < MMA9551_GPIO_COUNT; i++) {
gpio = devm_gpiod_get_index(dev, MMA9551_GPIO_NAME, i);
if (IS_ERR(gpio)) {
dev_err(dev, "acpi gpio get index failed\n");
return PTR_ERR(gpio);
}
ret = gpiod_direction_input(gpio);
if (ret)
return ret;
data->irqs[i] = gpiod_to_irq(gpio);
ret = devm_request_threaded_irq(dev, data->irqs[i],
NULL, mma9551_event_handler,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
MMA9551_IRQ_NAME, indio_dev);
if (ret < 0) {
dev_err(dev, "request irq %d failed\n", data->irqs[i]);
return ret;
}
dev_dbg(dev, "gpio resource, no:%d irq:%d\n",
desc_to_gpio(gpio), data->irqs[i]);
}
return 0;
}
static const char *mma9551_match_acpi_device(struct device *dev)
{
const struct acpi_device_id *id;
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return NULL;
return dev_name(dev);
}
static int mma9551_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mma9551_data *data;
struct iio_dev *indio_dev;
const char *name = NULL;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
if (id)
name = id->name;
else if (ACPI_HANDLE(&client->dev))
name = mma9551_match_acpi_device(&client->dev);
ret = mma9551_init(data);
if (ret < 0)
return ret;
mutex_init(&data->mutex);
indio_dev->dev.parent = &client->dev;
indio_dev->channels = mma9551_channels;
indio_dev->num_channels = ARRAY_SIZE(mma9551_channels);
indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mma9551_info;
ret = mma9551_gpio_probe(indio_dev);
if (ret < 0)
goto out_poweroff;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "unable to register iio device\n");
goto out_poweroff;
}
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
goto out_iio_unregister;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
MMA9551_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
return 0;
out_iio_unregister:
iio_device_unregister(indio_dev);
out_poweroff:
mma9551_set_device_state(client, false);
return ret;
}
static int mma9551_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mma9551_data *data = iio_priv(indio_dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
iio_device_unregister(indio_dev);
mutex_lock(&data->mutex);
mma9551_set_device_state(data->client, false);
mutex_unlock(&data->mutex);
return 0;
}
#ifdef CONFIG_PM
static int mma9551_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->mutex);
ret = mma9551_set_device_state(data->client, false);
mutex_unlock(&data->mutex);
if (ret < 0) {
dev_err(&data->client->dev, "powering off device failed\n");
return -EAGAIN;
}
return 0;
}
static int mma9551_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
ret = mma9551_set_device_state(data->client, true);
if (ret < 0)
return ret;
mma9551_sleep(MMA9551_DEFAULT_SAMPLE_RATE);
return 0;
}
#endif
#ifdef CONFIG_PM_SLEEP
static int mma9551_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->mutex);
ret = mma9551_set_device_state(data->client, false);
mutex_unlock(&data->mutex);
return ret;
}
static int mma9551_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->mutex);
ret = mma9551_set_device_state(data->client, true);
mutex_unlock(&data->mutex);
return ret;
}
#endif
static const struct dev_pm_ops mma9551_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume)
SET_RUNTIME_PM_OPS(mma9551_runtime_suspend,
mma9551_runtime_resume, NULL)
};
static const struct acpi_device_id mma9551_acpi_match[] = {
{"MMA9551", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, mma9551_acpi_match);
static const struct i2c_device_id mma9551_id[] = {
{"mma9551", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, mma9551_id);
static struct i2c_driver mma9551_driver = {
.driver = {
.name = MMA9551_DRV_NAME,
.acpi_match_table = ACPI_PTR(mma9551_acpi_match),
.pm = &mma9551_pm_ops,
},
.probe = mma9551_probe,
.remove = mma9551_remove,
.id_table = mma9551_id,
};
module_i2c_driver(mma9551_driver);
MODULE_AUTHOR("Irina Tirdea <[email protected]>");
MODULE_AUTHOR("Vlad Dogaru <[email protected]>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MMA9551L motion-sensing platform driver");

View file

@ -0,0 +1,798 @@
/*
* Common code for Freescale MMA955x Intelligent Sensor Platform drivers
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/pm_runtime.h>
#include "mma9551_core.h"
/* Command masks for mailbox write command */
#define MMA9551_CMD_READ_VERSION_INFO 0x00
#define MMA9551_CMD_READ_CONFIG 0x10
#define MMA9551_CMD_WRITE_CONFIG 0x20
#define MMA9551_CMD_READ_STATUS 0x30
/* Mailbox read command */
#define MMA9551_RESPONSE_COCO BIT(7)
/* Error-Status codes returned in mailbox read command */
#define MMA9551_MCI_ERROR_NONE 0x00
#define MMA9551_MCI_ERROR_PARAM 0x04
#define MMA9551_MCI_INVALID_COUNT 0x19
#define MMA9551_MCI_ERROR_COMMAND 0x1C
#define MMA9551_MCI_ERROR_INVALID_LENGTH 0x21
#define MMA9551_MCI_ERROR_FIFO_BUSY 0x22
#define MMA9551_MCI_ERROR_FIFO_ALLOCATED 0x23
#define MMA9551_MCI_ERROR_FIFO_OVERSIZE 0x24
/* GPIO Application */
#define MMA9551_GPIO_POL_MSB 0x08
#define MMA9551_GPIO_POL_LSB 0x09
/* Sleep/Wake application */
#define MMA9551_SLEEP_CFG 0x06
#define MMA9551_SLEEP_CFG_SNCEN BIT(0)
#define MMA9551_SLEEP_CFG_FLEEN BIT(1)
#define MMA9551_SLEEP_CFG_SCHEN BIT(2)
/* AFE application */
#define MMA9551_AFE_X_ACCEL_REG 0x00
#define MMA9551_AFE_Y_ACCEL_REG 0x02
#define MMA9551_AFE_Z_ACCEL_REG 0x04
/* Reset/Suspend/Clear application */
#define MMA9551_RSC_RESET 0x00
#define MMA9551_RSC_OFFSET(mask) (3 - (ffs(mask) - 1) / 8)
#define MMA9551_RSC_VAL(mask) (mask >> (((ffs(mask) - 1) / 8) * 8))
/*
* A response is composed of:
* - control registers: MB0-3
* - data registers: MB4-31
*
* A request is composed of:
* - mbox to write to (always 0)
* - control registers: MB1-4
* - data registers: MB5-31
*/
#define MMA9551_MAILBOX_CTRL_REGS 4
#define MMA9551_MAX_MAILBOX_DATA_REGS 28
#define MMA9551_MAILBOX_REGS 32
#define MMA9551_I2C_READ_RETRIES 5
#define MMA9551_I2C_READ_DELAY 50 /* us */
struct mma9551_mbox_request {
u8 start_mbox; /* Always 0. */
u8 app_id;
/*
* See Section 5.3.1 of the MMA955xL Software Reference Manual.
*
* Bit 7: reserved, always 0
* Bits 6-4: command
* Bits 3-0: upper bits of register offset
*/
u8 cmd_off;
u8 lower_off;
u8 nbytes;
u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS - 1];
} __packed;
struct mma9551_mbox_response {
u8 app_id;
/*
* See Section 5.3.3 of the MMA955xL Software Reference Manual.
*
* Bit 7: COCO
* Bits 6-0: Error code.
*/
u8 coco_err;
u8 nbytes;
u8 req_bytes;
u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS];
} __packed;
struct mma9551_version_info {
__be32 device_id;
u8 rom_version[2];
u8 fw_version[2];
u8 hw_version[2];
u8 fw_build[2];
};
static int mma9551_transfer(struct i2c_client *client,
u8 app_id, u8 command, u16 offset,
u8 *inbytes, int num_inbytes,
u8 *outbytes, int num_outbytes)
{
struct mma9551_mbox_request req;
struct mma9551_mbox_response rsp;
struct i2c_msg in, out;
u8 req_len, err_code;
int ret, retries;
if (offset >= 1 << 12) {
dev_err(&client->dev, "register offset too large\n");
return -EINVAL;
}
req_len = 1 + MMA9551_MAILBOX_CTRL_REGS + num_inbytes;
req.start_mbox = 0;
req.app_id = app_id;
req.cmd_off = command | (offset >> 8);
req.lower_off = offset;
if (command == MMA9551_CMD_WRITE_CONFIG)
req.nbytes = num_inbytes;
else
req.nbytes = num_outbytes;
if (num_inbytes)
memcpy(req.buf, inbytes, num_inbytes);
out.addr = client->addr;
out.flags = 0;
out.len = req_len;
out.buf = (u8 *)&req;
ret = i2c_transfer(client->adapter, &out, 1);
if (ret < 0) {
dev_err(&client->dev, "i2c write failed\n");
return ret;
}
retries = MMA9551_I2C_READ_RETRIES;
do {
udelay(MMA9551_I2C_READ_DELAY);
in.addr = client->addr;
in.flags = I2C_M_RD;
in.len = sizeof(rsp);
in.buf = (u8 *)&rsp;
ret = i2c_transfer(client->adapter, &in, 1);
if (ret < 0) {
dev_err(&client->dev, "i2c read failed\n");
return ret;
}
if (rsp.coco_err & MMA9551_RESPONSE_COCO)
break;
} while (--retries > 0);
if (retries == 0) {
dev_err(&client->dev,
"timed out while waiting for command response\n");
return -ETIMEDOUT;
}
if (rsp.app_id != app_id) {
dev_err(&client->dev,
"app_id mismatch in response got %02x expected %02x\n",
rsp.app_id, app_id);
return -EINVAL;
}
err_code = rsp.coco_err & ~MMA9551_RESPONSE_COCO;
if (err_code != MMA9551_MCI_ERROR_NONE) {
dev_err(&client->dev, "read returned error %x\n", err_code);
return -EINVAL;
}
if (rsp.nbytes != rsp.req_bytes) {
dev_err(&client->dev,
"output length mismatch got %d expected %d\n",
rsp.nbytes, rsp.req_bytes);
return -EINVAL;
}
if (num_outbytes)
memcpy(outbytes, rsp.buf, num_outbytes);
return 0;
}
/**
* mma9551_read_config_byte() - read 1 configuration byte
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Pointer to store value read
*
* Read one configuration byte from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed
* by one or more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 *val)
{
return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
reg, NULL, 0, val, 1);
}
EXPORT_SYMBOL(mma9551_read_config_byte);
/**
* mma9551_write_config_byte() - write 1 configuration byte
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Value to write
*
* Write one configuration byte from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed by one or
* more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 val)
{
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
&val, 1, NULL, 0);
}
EXPORT_SYMBOL(mma9551_write_config_byte);
/**
* mma9551_read_status_byte() - read 1 status byte
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Pointer to store value read
*
* Read one status byte from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed by one or
* more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 *val)
{
return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
reg, NULL, 0, val, 1);
}
EXPORT_SYMBOL(mma9551_read_status_byte);
/**
* mma9551_read_config_word() - read 1 config word
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Pointer to store value read
*
* Read one configuration word from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed by one or
* more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 *val)
{
int ret;
__be16 v;
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
reg, NULL, 0, (u8 *)&v, 2);
*val = be16_to_cpu(v);
return ret;
}
EXPORT_SYMBOL(mma9551_read_config_word);
/**
* mma9551_write_config_word() - write 1 config word
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Value to write
*
* Write one configuration word from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed by one or
* more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 val)
{
__be16 v = cpu_to_be16(val);
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
(u8 *) &v, 2, NULL, 0);
}
EXPORT_SYMBOL(mma9551_write_config_word);
/**
* mma9551_read_status_word() - read 1 status word
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Pointer to store value read
*
* Read one status word from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed by one or
* more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 *val)
{
int ret;
__be16 v;
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
reg, NULL, 0, (u8 *)&v, 2);
*val = be16_to_cpu(v);
return ret;
}
EXPORT_SYMBOL(mma9551_read_status_word);
/**
* mma9551_read_config_words() - read multiple config words
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to read in bytes
* @val: Array of words to read
*
* Read multiple configuration registers (word-sized registers).
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf)
{
int ret, i;
int len_words = len / sizeof(u16);
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
reg, NULL, 0, (u8 *) be_buf, len);
if (ret < 0)
return ret;
for (i = 0; i < len_words; i++)
buf[i] = be16_to_cpu(be_buf[i]);
return 0;
}
EXPORT_SYMBOL(mma9551_read_config_words);
/**
* mma9551_read_status_words() - read multiple status words
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to read in bytes
* @val: Array of words to read
*
* Read multiple status registers (word-sized registers).
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf)
{
int ret, i;
int len_words = len / sizeof(u16);
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
reg, NULL, 0, (u8 *) be_buf, len);
if (ret < 0)
return ret;
for (i = 0; i < len_words; i++)
buf[i] = be16_to_cpu(be_buf[i]);
return 0;
}
EXPORT_SYMBOL(mma9551_read_status_words);
/**
* mma9551_write_config_words() - write multiple config words
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to write in bytes
* @val: Array of words to write
*
* Write multiple configuration registers (word-sized registers).
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf)
{
int i;
int len_words = len / sizeof(u16);
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
for (i = 0; i < len_words; i++)
be_buf[i] = cpu_to_be16(buf[i]);
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
reg, (u8 *) be_buf, len, NULL, 0);
}
EXPORT_SYMBOL(mma9551_write_config_words);
/**
* mma9551_update_config_bits() - update bits in register
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @mask: Mask for the bits to update
* @val: Value of the bits to update
*
* Update bits in the given register using a bit mask.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
u16 reg, u8 mask, u8 val)
{
int ret;
u8 tmp, orig;
ret = mma9551_read_config_byte(client, app_id, reg, &orig);
if (ret < 0)
return ret;
tmp = orig & ~mask;
tmp |= val & mask;
if (tmp == orig)
return 0;
return mma9551_write_config_byte(client, app_id, reg, tmp);
}
EXPORT_SYMBOL(mma9551_update_config_bits);
/**
* mma9551_gpio_config() - configure gpio
* @client: I2C client
* @pin: GPIO pin to configure
* @app_id: Application ID
* @bitnum: Bit number of status register being assigned to the GPIO pin.
* @polarity: The polarity parameter is described in section 6.2.2, page 66,
* of the Software Reference Manual. Basically, polarity=0 means
* the interrupt line has the same value as the selected bit,
* while polarity=1 means the line is inverted.
*
* Assign a bit from an application’s status register to a specific GPIO pin.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
u8 app_id, u8 bitnum, int polarity)
{
u8 reg, pol_mask, pol_val;
int ret;
if (pin > mma9551_gpio_max) {
dev_err(&client->dev, "bad GPIO pin\n");
return -EINVAL;
}
/*
* Pin 6 is configured by regs 0x00 and 0x01, pin 7 by 0x02 and
* 0x03, and so on.
*/
reg = pin * 2;
ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
reg, app_id);
if (ret < 0) {
dev_err(&client->dev, "error setting GPIO app_id\n");
return ret;
}
ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
reg + 1, bitnum);
if (ret < 0) {
dev_err(&client->dev, "error setting GPIO bit number\n");
return ret;
}
switch (pin) {
case mma9551_gpio6:
reg = MMA9551_GPIO_POL_LSB;
pol_mask = 1 << 6;
break;
case mma9551_gpio7:
reg = MMA9551_GPIO_POL_LSB;
pol_mask = 1 << 7;
break;
case mma9551_gpio8:
reg = MMA9551_GPIO_POL_MSB;
pol_mask = 1 << 0;
break;
case mma9551_gpio9:
reg = MMA9551_GPIO_POL_MSB;
pol_mask = 1 << 1;
break;
}
pol_val = polarity ? pol_mask : 0;
ret = mma9551_update_config_bits(client, MMA9551_APPID_GPIO, reg,
pol_mask, pol_val);
if (ret < 0)
dev_err(&client->dev, "error setting GPIO polarity\n");
return ret;
}
EXPORT_SYMBOL(mma9551_gpio_config);
/**
* mma9551_read_version() - read device version information
* @client: I2C client
*
* Read version information and print device id and firmware version.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_version(struct i2c_client *client)
{
struct mma9551_version_info info;
int ret;
ret = mma9551_transfer(client, MMA9551_APPID_VERSION, 0x00, 0x00,
NULL, 0, (u8 *)&info, sizeof(info));
if (ret < 0)
return ret;
dev_info(&client->dev, "device ID 0x%x, firmware version %02x.%02x\n",
be32_to_cpu(info.device_id), info.fw_version[0],
info.fw_version[1]);
return 0;
}
EXPORT_SYMBOL(mma9551_read_version);
/**
* mma9551_set_device_state() - sets HW power mode
* @client: I2C client
* @enable: Use true to power on device, false to cause the device
* to enter sleep.
*
* Set power on/off for device using the Sleep/Wake Application.
* When enable is true, power on chip and enable doze mode.
* When enable is false, enter sleep mode (device remains in the
* lowest-power mode).
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_set_device_state(struct i2c_client *client, bool enable)
{
return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
MMA9551_SLEEP_CFG,
MMA9551_SLEEP_CFG_SNCEN |
MMA9551_SLEEP_CFG_FLEEN |
MMA9551_SLEEP_CFG_SCHEN,
enable ? MMA9551_SLEEP_CFG_SCHEN |
MMA9551_SLEEP_CFG_FLEEN :
MMA9551_SLEEP_CFG_SNCEN);
}
EXPORT_SYMBOL(mma9551_set_device_state);
/**
* mma9551_set_power_state() - sets runtime PM state
* @client: I2C client
* @on: Use true to power on device, false to power off
*
* Resume or suspend the device using Runtime PM.
* The device will suspend after the autosuspend delay.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_set_power_state(struct i2c_client *client, bool on)
{
#ifdef CONFIG_PM
int ret;
if (on)
ret = pm_runtime_get_sync(&client->dev);
else {
pm_runtime_mark_last_busy(&client->dev);
ret = pm_runtime_put_autosuspend(&client->dev);
}
if (ret < 0) {
dev_err(&client->dev,
"failed to change power state to %d\n", on);
if (on)
pm_runtime_put_noidle(&client->dev);
return ret;
}
#endif
return 0;
}
EXPORT_SYMBOL(mma9551_set_power_state);
/**
* mma9551_sleep() - sleep
* @freq: Application frequency
*
* Firmware applications run at a certain frequency on the
* device. Sleep for one application cycle to make sure the
* application had time to run once and initialize set values.
*/
void mma9551_sleep(int freq)
{
int sleep_val = 1000 / freq;
if (sleep_val < 20)
usleep_range(sleep_val * 1000, 20000);
else
msleep_interruptible(sleep_val);
}
EXPORT_SYMBOL(mma9551_sleep);
/**
* mma9551_read_accel_chan() - read accelerometer channel
* @client: I2C client
* @chan: IIO channel
* @val: Pointer to the accelerometer value read
* @val2: Unused
*
* Read accelerometer value for the specified channel.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: IIO_VAL_INT on success, negative value on failure.
*/
int mma9551_read_accel_chan(struct i2c_client *client,
const struct iio_chan_spec *chan,
int *val, int *val2)
{
u16 reg_addr;
s16 raw_accel;
int ret;
switch (chan->channel2) {
case IIO_MOD_X:
reg_addr = MMA9551_AFE_X_ACCEL_REG;
break;
case IIO_MOD_Y:
reg_addr = MMA9551_AFE_Y_ACCEL_REG;
break;
case IIO_MOD_Z:
reg_addr = MMA9551_AFE_Z_ACCEL_REG;
break;
default:
return -EINVAL;
}
ret = mma9551_set_power_state(client, true);
if (ret < 0)
return ret;
ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
reg_addr, &raw_accel);
if (ret < 0)
goto out_poweroff;
*val = raw_accel;
ret = IIO_VAL_INT;
out_poweroff:
mma9551_set_power_state(client, false);
return ret;
}
EXPORT_SYMBOL(mma9551_read_accel_chan);
/**
* mma9551_read_accel_scale() - read accelerometer scale
* @val: Pointer to the accelerometer scale (int value)
* @val2: Pointer to the accelerometer scale (micro value)
*
* Read accelerometer scale.
*
* Returns: IIO_VAL_INT_PLUS_MICRO.
*/
int mma9551_read_accel_scale(int *val, int *val2)
{
*val = 0;
*val2 = 2440;
return IIO_VAL_INT_PLUS_MICRO;
}
EXPORT_SYMBOL(mma9551_read_accel_scale);
/**
* mma9551_app_reset() - reset application
* @client: I2C client
* @app_mask: Application to reset
*
* Reset the given application (using the Reset/Suspend/Clear
* Control Application)
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
{
return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
MMA9551_RSC_RESET +
MMA9551_RSC_OFFSET(app_mask),
MMA9551_RSC_VAL(app_mask));
}
EXPORT_SYMBOL(mma9551_app_reset);
MODULE_AUTHOR("Irina Tirdea <[email protected]>");
MODULE_AUTHOR("Vlad Dogaru <[email protected]>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MMA955xL sensors core");

View file

@ -0,0 +1,81 @@
/*
* Common code for Freescale MMA955x Intelligent Sensor Platform drivers
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*/
#ifndef _MMA9551_CORE_H_
#define _MMA9551_CORE_H_
/* Applications IDs */
#define MMA9551_APPID_VERSION 0x00
#define MMA9551_APPID_GPIO 0x03
#define MMA9551_APPID_AFE 0x06
#define MMA9551_APPID_TILT 0x0B
#define MMA9551_APPID_SLEEP_WAKE 0x12
#define MMA9551_APPID_PEDOMETER 0x15
#define MMA9551_APPID_RCS 0x17
#define MMA9551_APPID_NONE 0xff
/* Reset/Suspend/Clear application app masks */
#define MMA9551_RSC_PED BIT(21)
#define MMA9551_AUTO_SUSPEND_DELAY_MS 2000
enum mma9551_gpio_pin {
mma9551_gpio6 = 0,
mma9551_gpio7,
mma9551_gpio8,
mma9551_gpio9,
mma9551_gpio_max = mma9551_gpio9,
};
#define MMA9551_ACCEL_CHANNEL(axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 *val);
int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 val);
int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 *val);
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 *val);
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 val);
int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 *val);
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf);
int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf);
int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf);
int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
u16 reg, u8 mask, u8 val);
int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
u8 app_id, u8 bitnum, int polarity);
int mma9551_read_version(struct i2c_client *client);
int mma9551_set_device_state(struct i2c_client *client, bool enable);
int mma9551_set_power_state(struct i2c_client *client, bool on);
void mma9551_sleep(int freq);
int mma9551_read_accel_chan(struct i2c_client *client,
const struct iio_chan_spec *chan,
int *val, int *val2);
int mma9551_read_accel_scale(int *val, int *val2);
int mma9551_app_reset(struct i2c_client *client, u32 app_mask);
#endif /* _MMA9551_CORE_H_ */

1334
drivers/iio/accel/mma9553.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,169 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/iio/common/ssp_sensors.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../common/ssp_sensors/ssp_iio_sensor.h"
#define SSP_CHANNEL_COUNT 3
#define SSP_ACCEL_NAME "ssp-accelerometer"
static const char ssp_accel_device_name[] = SSP_ACCEL_NAME;
enum ssp_accel_3d_channel {
SSP_CHANNEL_SCAN_INDEX_X,
SSP_CHANNEL_SCAN_INDEX_Y,
SSP_CHANNEL_SCAN_INDEX_Z,
SSP_CHANNEL_SCAN_INDEX_TIME,
};
static int ssp_accel_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
u32 t;
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
t = ssp_get_sensor_delay(data, SSP_ACCELEROMETER_SENSOR);
ssp_convert_to_freq(t, val, val2);
return IIO_VAL_INT_PLUS_MICRO;
default:
break;
}
return -EINVAL;
}
static int ssp_accel_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
int ret;
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = ssp_convert_to_time(val, val2);
ret = ssp_change_delay(data, SSP_ACCELEROMETER_SENSOR, ret);
if (ret < 0)
dev_err(&indio_dev->dev, "accel sensor enable fail\n");
return ret;
default:
break;
}
return -EINVAL;
}
static struct iio_info ssp_accel_iio_info = {
.read_raw = &ssp_accel_read_raw,
.write_raw = &ssp_accel_write_raw,
};
static const unsigned long ssp_accel_scan_mask[] = { 0x7, 0, };
static const struct iio_chan_spec ssp_acc_channels[] = {
SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X),
SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y),
SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z),
SSP_CHAN_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME),
};
static int ssp_process_accel_data(struct iio_dev *indio_dev, void *buf,
int64_t timestamp)
{
return ssp_common_process_data(indio_dev, buf, SSP_ACCELEROMETER_SIZE,
timestamp);
}
static const struct iio_buffer_setup_ops ssp_accel_buffer_ops = {
.postenable = &ssp_common_buffer_postenable,
.postdisable = &ssp_common_buffer_postdisable,
};
static int ssp_accel_probe(struct platform_device *pdev)
{
int ret;
struct iio_dev *indio_dev;
struct ssp_sensor_data *spd;
struct iio_buffer *buffer;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
if (!indio_dev)
return -ENOMEM;
spd = iio_priv(indio_dev);
spd->process_data = ssp_process_accel_data;
spd->type = SSP_ACCELEROMETER_SENSOR;
indio_dev->name = ssp_accel_device_name;
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->info = &ssp_accel_iio_info;
indio_dev->modes = INDIO_BUFFER_SOFTWARE;
indio_dev->channels = ssp_acc_channels;
indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels);
indio_dev->available_scan_masks = ssp_accel_scan_mask;
buffer = devm_iio_kfifo_allocate(&pdev->dev);
if (!buffer)
return -ENOMEM;
iio_device_attach_buffer(indio_dev, buffer);
indio_dev->setup_ops = &ssp_accel_buffer_ops;
platform_set_drvdata(pdev, indio_dev);
ret = iio_device_register(indio_dev);
if (ret < 0)
return ret;
/* ssp registering should be done after all iio setup */
ssp_register_consumer(indio_dev, SSP_ACCELEROMETER_SENSOR);
return 0;
}
static int ssp_accel_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);
return 0;
}
static struct platform_driver ssp_accel_driver = {
.driver = {
.name = SSP_ACCEL_NAME,
},
.probe = ssp_accel_probe,
.remove = ssp_accel_remove,
};
module_platform_driver(ssp_accel_driver);
MODULE_AUTHOR("Karol Wrona <[email protected]>");
MODULE_DESCRIPTION("Samsung sensorhub accelerometers driver");
MODULE_LICENSE("GPL");

View file

@ -135,6 +135,17 @@ config AXP288_ADC
device. Depending on platform configuration, this general purpose ADC can
be used for sampling sensors such as thermal resistors.
config CC10001_ADC
tristate "Cosmic Circuits 10001 ADC driver"
depends on HAS_IOMEM || HAVE_CLK || REGULATOR
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Cosmic Circuits 10001 ADC.
This driver can also be built as a module. If so, the module will be
called cc10001_adc.
config EXYNOS_ADC
tristate "Exynos ADC driver support"
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
@ -228,6 +239,20 @@ config QCOM_SPMI_IADC
To compile this driver as a module, choose M here: the module will
be called qcom-spmi-iadc.
config QCOM_SPMI_VADC
tristate "Qualcomm SPMI PMIC voltage ADC"
depends on SPMI
select REGMAP_SPMI
help
This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
The driver supports multiple channels read. The VADC is a 15-bit
sigma-delta ADC. Some of the channels are internally used for
calibration.
To compile this driver as a module, choose M here: the module will
be called qcom-spmi-vadc.
config ROCKCHIP_SARADC
tristate "Rockchip SARADC driver"
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)

View file

@ -15,6 +15,7 @@ obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1027) += max1027.o
@ -24,6 +25,7 @@ obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o

View file

@ -0,0 +1,423 @@
/*
* Copyright (c) 2014-2015 Imagination Technologies Ltd.
*
* 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.
*
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
/* Registers */
#define CC10001_ADC_CONFIG 0x00
#define CC10001_ADC_START_CONV BIT(4)
#define CC10001_ADC_MODE_SINGLE_CONV BIT(5)
#define CC10001_ADC_DDATA_OUT 0x04
#define CC10001_ADC_EOC 0x08
#define CC10001_ADC_EOC_SET BIT(0)
#define CC10001_ADC_CHSEL_SAMPLED 0x0c
#define CC10001_ADC_POWER_UP 0x10
#define CC10001_ADC_POWER_UP_SET BIT(0)
#define CC10001_ADC_DEBUG 0x14
#define CC10001_ADC_DATA_COUNT 0x20
#define CC10001_ADC_DATA_MASK GENMASK(9, 0)
#define CC10001_ADC_NUM_CHANNELS 8
#define CC10001_ADC_CH_MASK GENMASK(2, 0)
#define CC10001_INVALID_SAMPLED 0xffff
#define CC10001_MAX_POLL_COUNT 20
/*
* As per device specification, wait six clock cycles after power-up to
* activate START. Since adding two more clock cycles delay does not
* impact the performance too much, we are adding two additional cycles delay
* intentionally here.
*/
#define CC10001_WAIT_CYCLES 8
struct cc10001_adc_device {
void __iomem *reg_base;
struct clk *adc_clk;
struct regulator *reg;
u16 *buf;
struct mutex lock;
unsigned long channel_map;
unsigned int start_delay_ns;
unsigned int eoc_delay_ns;
};
static inline void cc10001_adc_write_reg(struct cc10001_adc_device *adc_dev,
u32 reg, u32 val)
{
writel(val, adc_dev->reg_base + reg);
}
static inline u32 cc10001_adc_read_reg(struct cc10001_adc_device *adc_dev,
u32 reg)
{
return readl(adc_dev->reg_base + reg);
}
static void cc10001_adc_start(struct cc10001_adc_device *adc_dev,
unsigned int channel)
{
u32 val;
/* Channel selection and mode of operation */
val = (channel & CC10001_ADC_CH_MASK) | CC10001_ADC_MODE_SINGLE_CONV;
cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
val = cc10001_adc_read_reg(adc_dev, CC10001_ADC_CONFIG);
val = val | CC10001_ADC_START_CONV;
cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
}
static u16 cc10001_adc_poll_done(struct iio_dev *indio_dev,
unsigned int channel,
unsigned int delay)
{
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
unsigned int poll_count = 0;
while (!(cc10001_adc_read_reg(adc_dev, CC10001_ADC_EOC) &
CC10001_ADC_EOC_SET)) {
ndelay(delay);
if (poll_count++ == CC10001_MAX_POLL_COUNT)
return CC10001_INVALID_SAMPLED;
}
poll_count = 0;
while ((cc10001_adc_read_reg(adc_dev, CC10001_ADC_CHSEL_SAMPLED) &
CC10001_ADC_CH_MASK) != channel) {
ndelay(delay);
if (poll_count++ == CC10001_MAX_POLL_COUNT)
return CC10001_INVALID_SAMPLED;
}
/* Read the 10 bit output register */
return cc10001_adc_read_reg(adc_dev, CC10001_ADC_DDATA_OUT) &
CC10001_ADC_DATA_MASK;
}
static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
{
struct cc10001_adc_device *adc_dev;
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev;
unsigned int delay_ns;
unsigned int channel;
bool sample_invalid;
u16 *data;
int i;
indio_dev = pf->indio_dev;
adc_dev = iio_priv(indio_dev);
data = adc_dev->buf;
mutex_lock(&adc_dev->lock);
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
CC10001_ADC_POWER_UP_SET);
/* Wait for 8 (6+2) clock cycles before activating START */
ndelay(adc_dev->start_delay_ns);
/* Calculate delay step for eoc and sampled data */
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
i = 0;
sample_invalid = false;
for_each_set_bit(channel, indio_dev->active_scan_mask,
indio_dev->masklength) {
cc10001_adc_start(adc_dev, channel);
data[i] = cc10001_adc_poll_done(indio_dev, channel, delay_ns);
if (data[i] == CC10001_INVALID_SAMPLED) {
dev_warn(&indio_dev->dev,
"invalid sample on channel %d\n", channel);
sample_invalid = true;
goto done;
}
i++;
}
done:
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
mutex_unlock(&adc_dev->lock);
if (!sample_invalid)
iio_push_to_buffers_with_timestamp(indio_dev, data,
iio_get_time_ns());
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan)
{
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
unsigned int delay_ns;
u16 val;
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
CC10001_ADC_POWER_UP_SET);
/* Wait for 8 (6+2) clock cycles before activating START */
ndelay(adc_dev->start_delay_ns);
/* Calculate delay step for eoc and sampled data */
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
cc10001_adc_start(adc_dev, chan->channel);
val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns);
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
return val;
}
static int cc10001_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
mutex_lock(&adc_dev->lock);
*val = cc10001_adc_read_raw_voltage(indio_dev, chan);
mutex_unlock(&adc_dev->lock);
if (*val == CC10001_INVALID_SAMPLED)
return -EIO;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = regulator_get_voltage(adc_dev->reg);
if (ret)
return ret;
*val = ret / 1000;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static int cc10001_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
kfree(adc_dev->buf);
adc_dev->buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
if (!adc_dev->buf)
return -ENOMEM;
return 0;
}
static const struct iio_info cc10001_adc_info = {
.driver_module = THIS_MODULE,
.read_raw = &cc10001_adc_read_raw,
.update_scan_mode = &cc10001_update_scan_mode,
};
static int cc10001_adc_channel_init(struct iio_dev *indio_dev)
{
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
struct iio_chan_spec *chan_array, *timestamp;
unsigned int bit, idx = 0;
indio_dev->num_channels = bitmap_weight(&adc_dev->channel_map,
CC10001_ADC_NUM_CHANNELS);
chan_array = devm_kcalloc(&indio_dev->dev, indio_dev->num_channels + 1,
sizeof(struct iio_chan_spec),
GFP_KERNEL);
if (!chan_array)
return -ENOMEM;
for_each_set_bit(bit, &adc_dev->channel_map, CC10001_ADC_NUM_CHANNELS) {
struct iio_chan_spec *chan = &chan_array[idx];
chan->type = IIO_VOLTAGE;
chan->indexed = 1;
chan->channel = bit;
chan->scan_index = idx;
chan->scan_type.sign = 'u';
chan->scan_type.realbits = 10;
chan->scan_type.storagebits = 16;
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
idx++;
}
timestamp = &chan_array[idx];
timestamp->type = IIO_TIMESTAMP;
timestamp->channel = -1;
timestamp->scan_index = idx;
timestamp->scan_type.sign = 's';
timestamp->scan_type.realbits = 64;
timestamp->scan_type.storagebits = 64;
indio_dev->channels = chan_array;
return 0;
}
static int cc10001_adc_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct cc10001_adc_device *adc_dev;
unsigned long adc_clk_rate;
struct resource *res;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
if (indio_dev == NULL)
return -ENOMEM;
adc_dev = iio_priv(indio_dev);
adc_dev->channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0);
if (!of_property_read_u32(node, "adc-reserved-channels", &ret))
adc_dev->channel_map &= ~ret;
adc_dev->reg = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(adc_dev->reg))
return PTR_ERR(adc_dev->reg);
ret = regulator_enable(adc_dev->reg);
if (ret)
return ret;
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);
indio_dev->info = &cc10001_adc_info;
indio_dev->modes = INDIO_DIRECT_MODE;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(adc_dev->reg_base)) {
ret = PTR_ERR(adc_dev->reg_base);
goto err_disable_reg;
}
adc_dev->adc_clk = devm_clk_get(&pdev->dev, "adc");
if (IS_ERR(adc_dev->adc_clk)) {
dev_err(&pdev->dev, "failed to get the clock\n");
ret = PTR_ERR(adc_dev->adc_clk);
goto err_disable_reg;
}
ret = clk_prepare_enable(adc_dev->adc_clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable the clock\n");
goto err_disable_reg;
}
adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
if (!adc_clk_rate) {
ret = -EINVAL;
dev_err(&pdev->dev, "null clock rate!\n");
goto err_disable_clk;
}
adc_dev->eoc_delay_ns = NSEC_PER_SEC / adc_clk_rate;
adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES;
/* Setup the ADC channels available on the device */
ret = cc10001_adc_channel_init(indio_dev);
if (ret < 0)
goto err_disable_clk;
mutex_init(&adc_dev->lock);
ret = iio_triggered_buffer_setup(indio_dev, NULL,
&cc10001_adc_trigger_h, NULL);
if (ret < 0)
goto err_disable_clk;
ret = iio_device_register(indio_dev);
if (ret < 0)
goto err_cleanup_buffer;
platform_set_drvdata(pdev, indio_dev);
return 0;
err_cleanup_buffer:
iio_triggered_buffer_cleanup(indio_dev);
err_disable_clk:
clk_disable_unprepare(adc_dev->adc_clk);
err_disable_reg:
regulator_disable(adc_dev->reg);
return ret;
}
static int cc10001_adc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
clk_disable_unprepare(adc_dev->adc_clk);
regulator_disable(adc_dev->reg);
return 0;
}
static const struct of_device_id cc10001_adc_dt_ids[] = {
{ .compatible = "cosmic,10001-adc", },
{ }
};
MODULE_DEVICE_TABLE(of, cc10001_adc_dt_ids);
static struct platform_driver cc10001_adc_driver = {
.driver = {
.name = "cc10001-adc",
.of_match_table = cc10001_adc_dt_ids,
},
.probe = cc10001_adc_probe,
.remove = cc10001_adc_remove,
};
module_platform_driver(cc10001_adc_driver);
MODULE_AUTHOR("Phani Movva <[email protected]>");
MODULE_DESCRIPTION("Cosmic Circuits ADC driver");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load diff

View file

@ -249,7 +249,7 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
struct iio_buffer *buffer;
int ret;
buffer = iio_kfifo_allocate(indio_dev);
buffer = iio_kfifo_allocate();
if (!buffer)
return -ENOMEM;
@ -263,16 +263,8 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
indio_dev->setup_ops = setup_ops;
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
ret = iio_buffer_register(indio_dev,
indio_dev->channels,
indio_dev->num_channels);
if (ret)
goto error_free_irq;
return 0;
error_free_irq:
free_irq(irq, indio_dev);
error_kfifo_free:
iio_kfifo_free(indio_dev->buffer);
return ret;
@ -284,7 +276,6 @@ static void tiadc_iio_buffered_hardware_remove(struct iio_dev *indio_dev)
free_irq(adc_dev->mfd_tscadc->irq, indio_dev);
iio_kfifo_free(indio_dev->buffer);
iio_buffer_unregister(indio_dev);
}

View file

@ -31,7 +31,7 @@ struct ad8366_state {
};
static int ad8366_write(struct iio_dev *indio_dev,
unsigned char ch_a, char unsigned ch_b)
unsigned char ch_a, unsigned char ch_b)
{
struct ad8366_state *st = iio_priv(indio_dev);
int ret;
@ -166,7 +166,7 @@ static int ad8366_probe(struct spi_device *spi)
if (ret)
goto error_disable_reg;
ad8366_write(indio_dev, 0 , 0);
ad8366_write(indio_dev, 0, 0);
return 0;

View file

@ -3,4 +3,5 @@
#
source "drivers/iio/common/hid-sensors/Kconfig"
source "drivers/iio/common/ssp_sensors/Kconfig"
source "drivers/iio/common/st_sensors/Kconfig"

View file

@ -8,4 +8,5 @@
# When adding new entries keep the list in alphabetical order
obj-y += hid-sensors/
obj-y += ssp_sensors/
obj-y += st_sensors/

View file

@ -22,16 +22,18 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/hid-sensor-hub.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/sysfs.h>
#include "hid-sensor-trigger.h"
int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
{
int state_val;
int report_val;
s32 poll_value = 0;
if (state) {
if (sensor_hub_device_open(st->hsdev))
@ -47,6 +49,8 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
st->report_state.report_id,
st->report_state.index,
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
poll_value = hid_sensor_read_poll_value(st);
} else {
if (!atomic_dec_and_test(&st->data_ready))
return 0;
@ -78,10 +82,36 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
st->power_state.index,
&state_val);
if (state && poll_value)
msleep_interruptible(poll_value * 2);
return 0;
}
EXPORT_SYMBOL(hid_sensor_power_state);
int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
{
#ifdef CONFIG_PM
int ret;
if (state)
ret = pm_runtime_get_sync(&st->pdev->dev);
else {
pm_runtime_mark_last_busy(&st->pdev->dev);
ret = pm_runtime_put_autosuspend(&st->pdev->dev);
}
if (ret < 0) {
if (state)
pm_runtime_put_noidle(&st->pdev->dev);
return ret;
}
return 0;
#else
return _hid_sensor_power_state(st, state);
#endif
}
static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
@ -125,8 +155,21 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
attrb->trigger = trig;
indio_dev->trig = iio_trigger_get(trig);
return ret;
ret = pm_runtime_set_active(&indio_dev->dev);
if (ret)
goto error_unreg_trigger;
iio_device_set_drvdata(indio_dev, attrb);
pm_suspend_ignore_children(&attrb->pdev->dev, true);
pm_runtime_enable(&attrb->pdev->dev);
/* Default to 3 seconds, but can be changed from sysfs */
pm_runtime_set_autosuspend_delay(&attrb->pdev->dev,
3000);
pm_runtime_use_autosuspend(&attrb->pdev->dev);
return ret;
error_unreg_trigger:
iio_trigger_unregister(trig);
error_free_trig:
iio_trigger_free(trig);
error_ret:
@ -134,6 +177,34 @@ error_ret:
}
EXPORT_SYMBOL(hid_sensor_setup_trigger);
#ifdef CONFIG_PM
static int hid_sensor_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
return _hid_sensor_power_state(attrb, false);
}
static int hid_sensor_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
return _hid_sensor_power_state(attrb, true);
}
#endif
const struct dev_pm_ops hid_sensor_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume)
SET_RUNTIME_PM_OPS(hid_sensor_suspend,
hid_sensor_resume, NULL)
};
EXPORT_SYMBOL(hid_sensor_pm_ops);
MODULE_AUTHOR("Srinivas Pandruvada <[email protected]>");
MODULE_DESCRIPTION("HID Sensor trigger processing");
MODULE_LICENSE("GPL");

View file

@ -19,6 +19,11 @@
#ifndef _HID_SENSOR_TRIGGER_H
#define _HID_SENSOR_TRIGGER_H
#include <linux/pm.h>
#include <linux/pm_runtime.h>
extern const struct dev_pm_ops hid_sensor_pm_ops;
int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
struct hid_sensor_common *attrb);
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb);

View file

@ -0,0 +1,26 @@
#
# SSP sensor drivers and commons configuration
#
menu "SSP Sensor Common"
config IIO_SSP_SENSORS_COMMONS
tristate "Commons for all SSP Sensor IIO drivers"
depends on IIO_SSP_SENSORHUB
select IIO_BUFFER
select IIO_KFIFO_BUF
help
Say yes here to build commons for SSP sensors.
To compile this as a module, choose M here: the module
will be called ssp_iio.
config IIO_SSP_SENSORHUB
tristate "Samsung Sensorhub driver"
depends on SPI
select MFD_CORE
help
SSP driver for sensorhub.
If you say yes here you get ssp support for sensorhub.
To compile this driver as a module, choose M here: the
module will be called sensorhub.
endmenu

View file

@ -0,0 +1,8 @@
#
# Makefile for SSP sensor drivers and commons.
#
sensorhub-objs := ssp_dev.o ssp_spi.o
obj-$(CONFIG_IIO_SSP_SENSORHUB) += sensorhub.o
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_iio.o

View file

@ -0,0 +1,257 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __SSP_SENSORHUB_H__
#define __SSP_SENSORHUB_H__
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/iio/common/ssp_sensors.h>
#include <linux/iio/iio.h>
#include <linux/spi/spi.h>
#define SSP_DEVICE_ID 0x55
#ifdef SSP_DBG
#define ssp_dbg(format, ...) pr_info("[SSP] "format, ##__VA_ARGS__)
#else
#define ssp_dbg(format, ...)
#endif
#define SSP_SW_RESET_TIME 3000
/* Sensor polling in ms */
#define SSP_DEFAULT_POLLING_DELAY 200
#define SSP_DEFAULT_RETRIES 3
#define SSP_DATA_PACKET_SIZE 960
#define SSP_HEADER_BUFFER_SIZE 4
enum {
SSP_KERNEL_BINARY = 0,
SSP_KERNEL_CRASHED_BINARY,
};
enum {
SSP_INITIALIZATION_STATE = 0,
SSP_NO_SENSOR_STATE,
SSP_ADD_SENSOR_STATE,
SSP_RUNNING_SENSOR_STATE,
};
/* Firmware download STATE */
enum {
SSP_FW_DL_STATE_FAIL = -1,
SSP_FW_DL_STATE_NONE = 0,
SSP_FW_DL_STATE_NEED_TO_SCHEDULE,
SSP_FW_DL_STATE_SCHEDULED,
SSP_FW_DL_STATE_DOWNLOADING,
SSP_FW_DL_STATE_SYNC,
SSP_FW_DL_STATE_DONE,
};
#define SSP_INVALID_REVISION 99999
#define SSP_INVALID_REVISION2 0xffffff
/* AP -> SSP Instruction */
#define SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD 0xa1
#define SSP_MSG2SSP_INST_BYPASS_SENSOR_RM 0xa2
#define SSP_MSG2SSP_INST_REMOVE_ALL 0xa3
#define SSP_MSG2SSP_INST_CHANGE_DELAY 0xa4
#define SSP_MSG2SSP_INST_LIBRARY_ADD 0xb1
#define SSP_MSG2SSP_INST_LIBRARY_REMOVE 0xb2
#define SSP_MSG2SSP_INST_LIB_NOTI 0xb4
#define SSP_MSG2SSP_INST_LIB_DATA 0xc1
#define SSP_MSG2SSP_AP_MCU_SET_GYRO_CAL 0xcd
#define SSP_MSG2SSP_AP_MCU_SET_ACCEL_CAL 0xce
#define SSP_MSG2SSP_AP_STATUS_SHUTDOWN 0xd0
#define SSP_MSG2SSP_AP_STATUS_WAKEUP 0xd1
#define SSP_MSG2SSP_AP_STATUS_SLEEP 0xd2
#define SSP_MSG2SSP_AP_STATUS_RESUME 0xd3
#define SSP_MSG2SSP_AP_STATUS_SUSPEND 0xd4
#define SSP_MSG2SSP_AP_STATUS_RESET 0xd5
#define SSP_MSG2SSP_AP_STATUS_POW_CONNECTED 0xd6
#define SSP_MSG2SSP_AP_STATUS_POW_DISCONNECTED 0xd7
#define SSP_MSG2SSP_AP_TEMPHUMIDITY_CAL_DONE 0xda
#define SSP_MSG2SSP_AP_MCU_SET_DUMPMODE 0xdb
#define SSP_MSG2SSP_AP_MCU_DUMP_CHECK 0xdc
#define SSP_MSG2SSP_AP_MCU_BATCH_FLUSH 0xdd
#define SSP_MSG2SSP_AP_MCU_BATCH_COUNT 0xdf
#define SSP_MSG2SSP_AP_WHOAMI 0x0f
#define SSP_MSG2SSP_AP_FIRMWARE_REV 0xf0
#define SSP_MSG2SSP_AP_SENSOR_FORMATION 0xf1
#define SSP_MSG2SSP_AP_SENSOR_PROXTHRESHOLD 0xf2
#define SSP_MSG2SSP_AP_SENSOR_BARCODE_EMUL 0xf3
#define SSP_MSG2SSP_AP_SENSOR_SCANNING 0xf4
#define SSP_MSG2SSP_AP_SET_MAGNETIC_HWOFFSET 0xf5
#define SSP_MSG2SSP_AP_GET_MAGNETIC_HWOFFSET 0xf6
#define SSP_MSG2SSP_AP_SENSOR_GESTURE_CURRENT 0xf7
#define SSP_MSG2SSP_AP_GET_THERM 0xf8
#define SSP_MSG2SSP_AP_GET_BIG_DATA 0xf9
#define SSP_MSG2SSP_AP_SET_BIG_DATA 0xfa
#define SSP_MSG2SSP_AP_START_BIG_DATA 0xfb
#define SSP_MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX 0xfd
#define SSP_MSG2SSP_AP_SENSOR_TILT 0xea
#define SSP_MSG2SSP_AP_MCU_SET_TIME 0xfe
#define SSP_MSG2SSP_AP_MCU_GET_TIME 0xff
#define SSP_MSG2SSP_AP_FUSEROM 0x01
/* voice data */
#define SSP_TYPE_WAKE_UP_VOICE_SERVICE 0x01
#define SSP_TYPE_WAKE_UP_VOICE_SOUND_SOURCE_AM 0x01
#define SSP_TYPE_WAKE_UP_VOICE_SOUND_SOURCE_GRAMMER 0x02
/* Factory Test */
#define SSP_ACCELEROMETER_FACTORY 0x80
#define SSP_GYROSCOPE_FACTORY 0x81
#define SSP_GEOMAGNETIC_FACTORY 0x82
#define SSP_PRESSURE_FACTORY 0x85
#define SSP_GESTURE_FACTORY 0x86
#define SSP_TEMPHUMIDITY_CRC_FACTORY 0x88
#define SSP_GYROSCOPE_TEMP_FACTORY 0x8a
#define SSP_GYROSCOPE_DPS_FACTORY 0x8b
#define SSP_MCU_FACTORY 0x8c
#define SSP_MCU_SLEEP_FACTORY 0x8d
/* SSP -> AP ACK about write CMD */
#define SSP_MSG_ACK 0x80 /* ACK from SSP to AP */
#define SSP_MSG_NAK 0x70 /* NAK from SSP to AP */
struct ssp_sensorhub_info {
char *fw_name;
char *fw_crashed_name;
unsigned int fw_rev;
const u8 * const mag_table;
const unsigned int mag_length;
};
/* ssp_msg options bit */
#define SSP_RW 0
#define SSP_INDEX 3
#define SSP_AP2HUB_READ 0
#define SSP_AP2HUB_WRITE 1
#define SSP_HUB2AP_WRITE 2
#define SSP_AP2HUB_READY 3
#define SSP_AP2HUB_RETURN 4
/**
* struct ssp_data - ssp platformdata structure
* @spi: spi device
* @sensorhub_info: info about sensorhub board specific features
* @wdt_timer: watchdog timer
* @work_wdt: watchdog work
* @work_firmware: firmware upgrade work queue
* @work_refresh: refresh work queue for reset request from MCU
* @shut_down: shut down flag
* @mcu_dump_mode: mcu dump mode for debug
* @time_syncing: time syncing indication flag
* @timestamp: previous time in ns calculated for time syncing
* @check_status: status table for each sensor
* @com_fail_cnt: communication fail count
* @reset_cnt: reset count
* @timeout_cnt: timeout count
* @available_sensors: available sensors seen by sensorhub (bit array)
* @cur_firm_rev: cached current firmware revision
* @last_resume_state: last AP resume/suspend state used to handle the PM
* state of ssp
* @last_ap_state: (obsolete) sleep notification for MCU
* @sensor_enable: sensor enable mask
* @delay_buf: data acquisition intervals table
* @batch_latency_buf: yet unknown but existing in communication protocol
* @batch_opt_buf: yet unknown but existing in communication protocol
* @accel_position: yet unknown but existing in communication protocol
* @mag_position: yet unknown but existing in communication protocol
* @fw_dl_state: firmware download state
* @comm_lock: lock protecting the handshake
* @pending_lock: lock protecting pending list and completion
* @mcu_reset_gpio: mcu reset line
* @ap_mcu_gpio: ap to mcu gpio line
* @mcu_ap_gpio: mcu to ap gpio line
* @pending_list: pending list for messages queued to be sent/read
* @sensor_devs: registered IIO devices table
* @enable_refcount: enable reference count for wdt (watchdog timer)
* @header_buffer: cache aligned buffer for packet header
*/
struct ssp_data {
struct spi_device *spi;
struct ssp_sensorhub_info *sensorhub_info;
struct timer_list wdt_timer;
struct work_struct work_wdt;
struct delayed_work work_refresh;
bool shut_down;
bool mcu_dump_mode;
bool time_syncing;
int64_t timestamp;
int check_status[SSP_SENSOR_MAX];
unsigned int com_fail_cnt;
unsigned int reset_cnt;
unsigned int timeout_cnt;
unsigned int available_sensors;
unsigned int cur_firm_rev;
char last_resume_state;
char last_ap_state;
unsigned int sensor_enable;
u32 delay_buf[SSP_SENSOR_MAX];
s32 batch_latency_buf[SSP_SENSOR_MAX];
s8 batch_opt_buf[SSP_SENSOR_MAX];
int accel_position;
int mag_position;
int fw_dl_state;
struct mutex comm_lock;
struct mutex pending_lock;
int mcu_reset_gpio;
int ap_mcu_gpio;
int mcu_ap_gpio;
struct list_head pending_list;
struct iio_dev *sensor_devs[SSP_SENSOR_MAX];
atomic_t enable_refcount;
__le16 header_buffer[SSP_HEADER_BUFFER_SIZE / sizeof(__le16)]
____cacheline_aligned;
};
void ssp_clean_pending_list(struct ssp_data *data);
int ssp_command(struct ssp_data *data, char command, int arg);
int ssp_send_instruction(struct ssp_data *data, u8 inst, u8 sensor_type,
u8 *send_buf, u8 length);
int ssp_irq_msg(struct ssp_data *data);
int ssp_get_chipid(struct ssp_data *data);
int ssp_set_magnetic_matrix(struct ssp_data *data);
unsigned int ssp_get_sensor_scanning_info(struct ssp_data *data);
unsigned int ssp_get_firmware_rev(struct ssp_data *data);
int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay);
#endif /* __SSP_SENSORHUB_H__ */

View file

@ -0,0 +1,712 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include "ssp.h"
#define SSP_WDT_TIME 10000
#define SSP_LIMIT_RESET_CNT 20
#define SSP_LIMIT_TIMEOUT_CNT 3
/* It is possible that it is max clk rate for version 1.0 of bootcode */
#define SSP_BOOT_SPI_HZ 400000
/*
* These fields can look enigmatic but this structure is used mainly to flat
* some values and depends on command type.
*/
struct ssp_instruction {
__le32 a;
__le32 b;
u8 c;
} __attribute__((__packed__));
static const u8 ssp_magnitude_table[] = {110, 85, 171, 71, 203, 195, 0, 67,
208, 56, 175, 244, 206, 213, 0, 92, 250, 0, 55, 48, 189, 252, 171,
243, 13, 45, 250};
static const struct ssp_sensorhub_info ssp_rinato_info = {
.fw_name = "ssp_B2.fw",
.fw_crashed_name = "ssp_crashed.fw",
.fw_rev = 14052300,
.mag_table = ssp_magnitude_table,
.mag_length = ARRAY_SIZE(ssp_magnitude_table),
};
static const struct ssp_sensorhub_info ssp_thermostat_info = {
.fw_name = "thermostat_B2.fw",
.fw_crashed_name = "ssp_crashed.fw",
.fw_rev = 14080600,
.mag_table = ssp_magnitude_table,
.mag_length = ARRAY_SIZE(ssp_magnitude_table),
};
static const struct mfd_cell sensorhub_sensor_devs[] = {
{
.name = "ssp-accelerometer",
},
{
.name = "ssp-gyroscope",
},
};
static void ssp_toggle_mcu_reset_gpio(struct ssp_data *data)
{
gpio_set_value(data->mcu_reset_gpio, 0);
usleep_range(1000, 1200);
gpio_set_value(data->mcu_reset_gpio, 1);
msleep(50);
}
static void ssp_sync_available_sensors(struct ssp_data *data)
{
int i, ret;
for (i = 0; i < SSP_SENSOR_MAX; ++i) {
if (data->available_sensors & BIT(i)) {
ret = ssp_enable_sensor(data, i, data->delay_buf[i]);
if (ret < 0) {
dev_err(&data->spi->dev,
"Sync sensor nr: %d fail\n", i);
continue;
}
}
}
ret = ssp_command(data, SSP_MSG2SSP_AP_MCU_SET_DUMPMODE,
data->mcu_dump_mode);
if (ret < 0)
dev_err(&data->spi->dev,
"SSP_MSG2SSP_AP_MCU_SET_DUMPMODE failed\n");
}
static void ssp_enable_mcu(struct ssp_data *data, bool enable)
{
dev_info(&data->spi->dev, "current shutdown = %d, old = %d\n", enable,
data->shut_down);
if (enable && data->shut_down) {
data->shut_down = false;
enable_irq(data->spi->irq);
enable_irq_wake(data->spi->irq);
} else if (!enable && !data->shut_down) {
data->shut_down = true;
disable_irq(data->spi->irq);
disable_irq_wake(data->spi->irq);
} else {
dev_warn(&data->spi->dev, "current shutdown = %d, old = %d\n",
enable, data->shut_down);
}
}
/*
* This function is the first one which communicates with the mcu so it is
* possible that the first attempt will fail
*/
static int ssp_check_fwbl(struct ssp_data *data)
{
int retries = 0;
while (retries++ < 5) {
data->cur_firm_rev = ssp_get_firmware_rev(data);
if (data->cur_firm_rev == SSP_INVALID_REVISION ||
data->cur_firm_rev == SSP_INVALID_REVISION2) {
dev_warn(&data->spi->dev,
"Invalid revision, trying %d time\n", retries);
} else {
break;
}
}
if (data->cur_firm_rev == SSP_INVALID_REVISION ||
data->cur_firm_rev == SSP_INVALID_REVISION2) {
dev_err(&data->spi->dev, "SSP_INVALID_REVISION\n");
return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;
}
dev_info(&data->spi->dev,
"MCU Firm Rev : Old = %8u, New = %8u\n",
data->cur_firm_rev,
data->sensorhub_info->fw_rev);
if (data->cur_firm_rev != data->sensorhub_info->fw_rev)
return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;
return SSP_FW_DL_STATE_NONE;
}
static void ssp_reset_mcu(struct ssp_data *data)
{
ssp_enable_mcu(data, false);
ssp_clean_pending_list(data);
ssp_toggle_mcu_reset_gpio(data);
ssp_enable_mcu(data, true);
}
static void ssp_wdt_work_func(struct work_struct *work)
{
struct ssp_data *data = container_of(work, struct ssp_data, work_wdt);
dev_err(&data->spi->dev, "%s - Sensor state: 0x%x, RC: %u, CC: %u\n",
__func__, data->available_sensors, data->reset_cnt,
data->com_fail_cnt);
ssp_reset_mcu(data);
data->com_fail_cnt = 0;
data->timeout_cnt = 0;
}
static void ssp_wdt_timer_func(unsigned long ptr)
{
struct ssp_data *data = (struct ssp_data *)ptr;
switch (data->fw_dl_state) {
case SSP_FW_DL_STATE_FAIL:
case SSP_FW_DL_STATE_DOWNLOADING:
case SSP_FW_DL_STATE_SYNC:
goto _mod;
}
if (data->timeout_cnt > SSP_LIMIT_TIMEOUT_CNT ||
data->com_fail_cnt > SSP_LIMIT_RESET_CNT)
queue_work(system_power_efficient_wq, &data->work_wdt);
_mod:
mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
}
static void ssp_enable_wdt_timer(struct ssp_data *data)
{
mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
}
static void ssp_disable_wdt_timer(struct ssp_data *data)
{
del_timer_sync(&data->wdt_timer);
cancel_work_sync(&data->work_wdt);
}
/**
* ssp_get_sensor_delay() - gets sensor data acquisition period
* @data: sensorhub structure
* @type: SSP sensor type
*
* Returns acquisition period in ms
*/
u32 ssp_get_sensor_delay(struct ssp_data *data, enum ssp_sensor_type type)
{
return data->delay_buf[type];
}
EXPORT_SYMBOL(ssp_get_sensor_delay);
/**
* ssp_enable_sensor() - enables data acquisition for sensor
* @data: sensorhub structure
* @type: SSP sensor type
* @delay: delay in ms
*
* Returns 0 or negative value in case of error
*/
int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,
u32 delay)
{
int ret;
struct ssp_instruction to_send;
to_send.a = cpu_to_le32(delay);
to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
to_send.c = data->batch_opt_buf[type];
switch (data->check_status[type]) {
case SSP_INITIALIZATION_STATE:
/* do calibration step, now just enable */
case SSP_ADD_SENSOR_STATE:
ret = ssp_send_instruction(data,
SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD,
type,
(u8 *)&to_send, sizeof(to_send));
if (ret < 0) {
dev_err(&data->spi->dev, "Enabling sensor failed\n");
data->check_status[type] = SSP_NO_SENSOR_STATE;
goto derror;
}
data->sensor_enable |= BIT(type);
data->check_status[type] = SSP_RUNNING_SENSOR_STATE;
break;
case SSP_RUNNING_SENSOR_STATE:
ret = ssp_send_instruction(data,
SSP_MSG2SSP_INST_CHANGE_DELAY, type,
(u8 *)&to_send, sizeof(to_send));
if (ret < 0) {
dev_err(&data->spi->dev,
"Changing sensor delay failed\n");
goto derror;
}
break;
default:
data->check_status[type] = SSP_ADD_SENSOR_STATE;
break;
}
data->delay_buf[type] = delay;
if (atomic_inc_return(&data->enable_refcount) == 1)
ssp_enable_wdt_timer(data);
return 0;
derror:
return ret;
}
EXPORT_SYMBOL(ssp_enable_sensor);
/**
* ssp_change_delay() - changes data acquisition for sensor
* @data: sensorhub structure
* @type: SSP sensor type
* @delay: delay in ms
*
* Returns 0 or negative value in case of error
*/
int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type,
u32 delay)
{
int ret;
struct ssp_instruction to_send;
to_send.a = cpu_to_le32(delay);
to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
to_send.c = data->batch_opt_buf[type];
ret = ssp_send_instruction(data, SSP_MSG2SSP_INST_CHANGE_DELAY, type,
(u8 *)&to_send, sizeof(to_send));
if (ret < 0) {
dev_err(&data->spi->dev, "Changing sensor delay failed\n");
return ret;
}
data->delay_buf[type] = delay;
return 0;
}
EXPORT_SYMBOL(ssp_change_delay);
/**
* ssp_disable_sensor() - disables sensor
*
* @data: sensorhub structure
* @type: SSP sensor type
*
* Returns 0 or negative value in case of error
*/
int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type)
{
int ret;
__le32 command;
if (data->sensor_enable & BIT(type)) {
command = cpu_to_le32(data->delay_buf[type]);
ret = ssp_send_instruction(data,
SSP_MSG2SSP_INST_BYPASS_SENSOR_RM,
type, (u8 *)&command,
sizeof(command));
if (ret < 0) {
dev_err(&data->spi->dev, "Remove sensor fail\n");
return ret;
}
data->sensor_enable &= ~BIT(type);
}
data->check_status[type] = SSP_ADD_SENSOR_STATE;
if (atomic_dec_and_test(&data->enable_refcount))
ssp_disable_wdt_timer(data);
return 0;
}
EXPORT_SYMBOL(ssp_disable_sensor);
static irqreturn_t ssp_irq_thread_fn(int irq, void *dev_id)
{
struct ssp_data *data = dev_id;
/*
* This wrapper is done to preserve error path for ssp_irq_msg, also
* it is defined in different file.
*/
ssp_irq_msg(data);
return IRQ_HANDLED;
}
static int ssp_initialize_mcu(struct ssp_data *data)
{
int ret;
ssp_clean_pending_list(data);
ret = ssp_get_chipid(data);
if (ret != SSP_DEVICE_ID) {
dev_err(&data->spi->dev, "%s - MCU %s ret = %d\n", __func__,
ret < 0 ? "is not working" : "identification failed",
ret);
return ret < 0 ? ret : -ENODEV;
}
dev_info(&data->spi->dev, "MCU device ID = %d\n", ret);
/*
* needs clarification, for now do not want to export all transfer
* methods to sensors' drivers
*/
ret = ssp_set_magnetic_matrix(data);
if (ret < 0) {
dev_err(&data->spi->dev,
"%s - ssp_set_magnetic_matrix failed\n", __func__);
return ret;
}
data->available_sensors = ssp_get_sensor_scanning_info(data);
if (data->available_sensors == 0) {
dev_err(&data->spi->dev,
"%s - ssp_get_sensor_scanning_info failed\n", __func__);
return -EIO;
}
data->cur_firm_rev = ssp_get_firmware_rev(data);
dev_info(&data->spi->dev, "MCU Firm Rev : New = %8u\n",
data->cur_firm_rev);
return ssp_command(data, SSP_MSG2SSP_AP_MCU_DUMP_CHECK, 0);
}
/*
* sensorhub can request its reinitialization as some brutal and rare error
* handling. It can be requested from the MCU.
*/
static void ssp_refresh_task(struct work_struct *work)
{
struct ssp_data *data = container_of((struct delayed_work *)work,
struct ssp_data, work_refresh);
dev_info(&data->spi->dev, "refreshing\n");
data->reset_cnt++;
if (ssp_initialize_mcu(data) >= 0) {
ssp_sync_available_sensors(data);
if (data->last_ap_state != 0)
ssp_command(data, data->last_ap_state, 0);
if (data->last_resume_state != 0)
ssp_command(data, data->last_resume_state, 0);
data->timeout_cnt = 0;
data->com_fail_cnt = 0;
}
}
int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay)
{
cancel_delayed_work_sync(&data->work_refresh);
return queue_delayed_work(system_power_efficient_wq,
&data->work_refresh,
msecs_to_jiffies(delay));
}
#ifdef CONFIG_OF
static struct of_device_id ssp_of_match[] = {
{
.compatible = "samsung,sensorhub-rinato",
.data = &ssp_rinato_info,
}, {
.compatible = "samsung,sensorhub-thermostat",
.data = &ssp_thermostat_info,
},
{},
};
MODULE_DEVICE_TABLE(of, ssp_of_match);
static struct ssp_data *ssp_parse_dt(struct device *dev)
{
int ret;
struct ssp_data *data;
struct device_node *node = dev->of_node;
const struct of_device_id *match;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return NULL;
data->mcu_ap_gpio = of_get_named_gpio(node, "mcu-ap-gpios", 0);
if (data->mcu_ap_gpio < 0)
goto err_free_pd;
data->ap_mcu_gpio = of_get_named_gpio(node, "ap-mcu-gpios", 0);
if (data->ap_mcu_gpio < 0)
goto err_free_pd;
data->mcu_reset_gpio = of_get_named_gpio(node, "mcu-reset-gpios", 0);
if (data->mcu_reset_gpio < 0)
goto err_free_pd;
ret = devm_gpio_request_one(dev, data->ap_mcu_gpio, GPIOF_OUT_INIT_HIGH,
"ap-mcu-gpios");
if (ret)
goto err_free_pd;
ret = devm_gpio_request_one(dev, data->mcu_reset_gpio,
GPIOF_OUT_INIT_HIGH, "mcu-reset-gpios");
if (ret)
goto err_ap_mcu;
match = of_match_node(ssp_of_match, node);
if (!match)
goto err_mcu_reset_gpio;
data->sensorhub_info = (struct ssp_sensorhub_info *)match->data;
dev_set_drvdata(dev, data);
return data;
err_mcu_reset_gpio:
devm_gpio_free(dev, data->mcu_reset_gpio);
err_ap_mcu:
devm_gpio_free(dev, data->ap_mcu_gpio);
err_free_pd:
devm_kfree(dev, data);
return NULL;
}
#else
static struct ssp_data *ssp_parse_dt(struct device *pdev)
{
return NULL;
}
#endif
/**
* ssp_register_consumer() - registers iio consumer in ssp framework
*
* @indio_dev: consumer iio device
* @type: ssp sensor type
*/
void ssp_register_consumer(struct iio_dev *indio_dev, enum ssp_sensor_type type)
{
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
data->sensor_devs[type] = indio_dev;
}
EXPORT_SYMBOL(ssp_register_consumer);
static int ssp_probe(struct spi_device *spi)
{
int ret, i;
struct ssp_data *data;
data = ssp_parse_dt(&spi->dev);
if (!data) {
dev_err(&spi->dev, "Failed to find platform data\n");
return -ENODEV;
}
ret = mfd_add_devices(&spi->dev, -1, sensorhub_sensor_devs,
ARRAY_SIZE(sensorhub_sensor_devs), NULL, 0, NULL);
if (ret < 0) {
dev_err(&spi->dev, "mfd add devices fail\n");
return ret;
}
spi->mode = SPI_MODE_1;
ret = spi_setup(spi);
if (ret < 0) {
dev_err(&spi->dev, "Failed to setup spi\n");
return ret;
}
data->fw_dl_state = SSP_FW_DL_STATE_NONE;
data->spi = spi;
spi_set_drvdata(spi, data);
mutex_init(&data->comm_lock);
for (i = 0; i < SSP_SENSOR_MAX; ++i) {
data->delay_buf[i] = SSP_DEFAULT_POLLING_DELAY;
data->batch_latency_buf[i] = 0;
data->batch_opt_buf[i] = 0;
data->check_status[i] = SSP_INITIALIZATION_STATE;
}
data->delay_buf[SSP_BIO_HRM_LIB] = 100;
data->time_syncing = true;
mutex_init(&data->pending_lock);
INIT_LIST_HEAD(&data->pending_list);
atomic_set(&data->enable_refcount, 0);
INIT_WORK(&data->work_wdt, ssp_wdt_work_func);
INIT_DELAYED_WORK(&data->work_refresh, ssp_refresh_task);
setup_timer(&data->wdt_timer, ssp_wdt_timer_func, (unsigned long)data);
ret = request_threaded_irq(data->spi->irq, NULL,
ssp_irq_thread_fn,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"SSP_Int", data);
if (ret < 0) {
dev_err(&spi->dev, "Irq request fail\n");
goto err_setup_irq;
}
/* Let's start with enabled one so irq balance could be ok */
data->shut_down = false;
/* just to avoid unbalanced irq set wake up */
enable_irq_wake(data->spi->irq);
data->fw_dl_state = ssp_check_fwbl(data);
if (data->fw_dl_state == SSP_FW_DL_STATE_NONE) {
ret = ssp_initialize_mcu(data);
if (ret < 0) {
dev_err(&spi->dev, "Initialize_mcu failed\n");
goto err_read_reg;
}
} else {
dev_err(&spi->dev, "Firmware version not supported\n");
ret = -EPERM;
goto err_read_reg;
}
return 0;
err_read_reg:
free_irq(data->spi->irq, data);
err_setup_irq:
mutex_destroy(&data->pending_lock);
mutex_destroy(&data->comm_lock);
dev_err(&spi->dev, "Probe failed!\n");
return ret;
}
static int ssp_remove(struct spi_device *spi)
{
struct ssp_data *data = spi_get_drvdata(spi);
if (ssp_command(data, SSP_MSG2SSP_AP_STATUS_SHUTDOWN, 0) < 0)
dev_err(&data->spi->dev,
"SSP_MSG2SSP_AP_STATUS_SHUTDOWN failed\n");
ssp_enable_mcu(data, false);
ssp_disable_wdt_timer(data);
ssp_clean_pending_list(data);
free_irq(data->spi->irq, data);
del_timer_sync(&data->wdt_timer);
cancel_work_sync(&data->work_wdt);
mutex_destroy(&data->comm_lock);
mutex_destroy(&data->pending_lock);
mfd_remove_devices(&spi->dev);
return 0;
}
static int ssp_suspend(struct device *dev)
{
int ret;
struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
data->last_resume_state = SSP_MSG2SSP_AP_STATUS_SUSPEND;
if (atomic_read(&data->enable_refcount) > 0)
ssp_disable_wdt_timer(data);
ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_SUSPEND, 0);
if (ret < 0) {
dev_err(&data->spi->dev,
"%s SSP_MSG2SSP_AP_STATUS_SUSPEND failed\n", __func__);
ssp_enable_wdt_timer(data);
return ret;
}
data->time_syncing = false;
disable_irq(data->spi->irq);
return 0;
}
static int ssp_resume(struct device *dev)
{
int ret;
struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
enable_irq(data->spi->irq);
if (atomic_read(&data->enable_refcount) > 0)
ssp_enable_wdt_timer(data);
ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_RESUME, 0);
if (ret < 0) {
dev_err(&data->spi->dev,
"%s SSP_MSG2SSP_AP_STATUS_RESUME failed\n", __func__);
ssp_disable_wdt_timer(data);
return ret;
}
/* timesyncing is set by MCU */
data->last_resume_state = SSP_MSG2SSP_AP_STATUS_RESUME;
return 0;
}
static const struct dev_pm_ops ssp_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ssp_suspend, ssp_resume)
};
static struct spi_driver ssp_driver = {
.probe = ssp_probe,
.remove = ssp_remove,
.driver = {
.pm = &ssp_pm_ops,
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ssp_of_match),
.name = "sensorhub"
},
};
module_spi_driver(ssp_driver);
MODULE_DESCRIPTION("ssp sensorhub driver");
MODULE_AUTHOR("Samsung Electronics");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,107 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/iio/common/ssp_sensors.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "ssp_iio_sensor.h"
/**
* ssp_common_buffer_postenable() - generic postenable callback for ssp buffer
*
* @indio_dev: iio device
*
* Returns 0 or negative value in case of error
*/
int ssp_common_buffer_postenable(struct iio_dev *indio_dev)
{
struct ssp_sensor_data *spd = iio_priv(indio_dev);
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
/* the allocation is made in post because scan size is known in this
* moment
* */
spd->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL | GFP_DMA);
if (!spd->buffer)
return -ENOMEM;
return ssp_enable_sensor(data, spd->type,
ssp_get_sensor_delay(data, spd->type));
}
EXPORT_SYMBOL(ssp_common_buffer_postenable);
/**
* ssp_common_buffer_postdisable() - generic postdisable callback for ssp buffer
*
* @indio_dev: iio device
*
* Returns 0 or negative value in case of error
*/
int ssp_common_buffer_postdisable(struct iio_dev *indio_dev)
{
int ret;
struct ssp_sensor_data *spd = iio_priv(indio_dev);
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
ret = ssp_disable_sensor(data, spd->type);
if (ret < 0)
return ret;
kfree(spd->buffer);
return ret;
}
EXPORT_SYMBOL(ssp_common_buffer_postdisable);
/**
* ssp_common_process_data() - Common process data callback for ssp sensors
*
* @indio_dev: iio device
* @buf: source buffer
* @len: sensor data length
* @timestamp: system timestamp
*
* Returns 0 or negative value in case of error
*/
int ssp_common_process_data(struct iio_dev *indio_dev, void *buf,
unsigned int len, int64_t timestamp)
{
__le32 time;
int64_t calculated_time;
struct ssp_sensor_data *spd = iio_priv(indio_dev);
if (indio_dev->scan_bytes == 0)
return 0;
/*
* it always sends full set of samples, remember about available masks
*/
memcpy(spd->buffer, buf, len);
if (indio_dev->scan_timestamp) {
memcpy(&time, &((char *)buf)[len], SSP_TIME_SIZE);
calculated_time =
timestamp + (int64_t)le32_to_cpu(time) * 1000000;
}
return iio_push_to_buffers_with_timestamp(indio_dev, spd->buffer,
calculated_time);
}
EXPORT_SYMBOL(ssp_common_process_data);
MODULE_AUTHOR("Karol Wrona <[email protected]>");
MODULE_DESCRIPTION("Samsung sensorhub commons");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,71 @@
#ifndef __SSP_IIO_SENSOR_H__
#define __SSP_IIO_SENSOR_H__
#define SSP_CHANNEL_AG(_type, _mod, _index) \
{ \
.type = _type,\
.modified = 1,\
.channel2 = _mod,\
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
.scan_index = _index,\
.scan_type = {\
.sign = 's',\
.realbits = 16,\
.storagebits = 16,\
.shift = 0,\
.endianness = IIO_LE,\
},\
}
/* It is defined here as it is a mixed timestamp */
#define SSP_CHAN_TIMESTAMP(_si) { \
.type = IIO_TIMESTAMP, \
.channel = -1, \
.scan_index = _si, \
.scan_type = { \
.sign = 's', \
.realbits = 64, \
.storagebits = 64, \
}, \
}
#define SSP_MS_PER_S 1000
#define SSP_INVERTED_SCALING_FACTOR 1000000U
#define SSP_FACTOR_WITH_MS \
(SSP_INVERTED_SCALING_FACTOR * SSP_MS_PER_S)
int ssp_common_buffer_postenable(struct iio_dev *indio_dev);
int ssp_common_buffer_postdisable(struct iio_dev *indio_dev);
int ssp_common_process_data(struct iio_dev *indio_dev, void *buf,
unsigned int len, int64_t timestamp);
/* Converts time in ms to frequency */
static inline void ssp_convert_to_freq(u32 time, int *integer_part,
int *fractional)
{
if (time == 0) {
*fractional = 0;
*integer_part = 0;
return;
}
*integer_part = SSP_FACTOR_WITH_MS / time;
*fractional = *integer_part % SSP_INVERTED_SCALING_FACTOR;
*integer_part = *integer_part / SSP_INVERTED_SCALING_FACTOR;
}
/* Converts frequency to time in ms */
static inline int ssp_convert_to_time(int integer_part, int fractional)
{
u64 value;
value = (u64)integer_part * SSP_INVERTED_SCALING_FACTOR + fractional;
if (value == 0)
return 0;
return div64_u64((u64)SSP_FACTOR_WITH_MS, value);
}
#endif /* __SSP_IIO_SENSOR_H__ */

View file

@ -0,0 +1,608 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "ssp.h"
#define SSP_DEV (&data->spi->dev)
#define SSP_GET_MESSAGE_TYPE(data) (data & (3 << SSP_RW))
/*
* SSP -> AP Instruction
* They tell what packet type can be expected. In the future there will
* be less of them. BYPASS means common sensor packets with accel, gyro,
* hrm etc. data. LIBRARY and META are mock-up's for now.
*/
#define SSP_MSG2AP_INST_BYPASS_DATA 0x37
#define SSP_MSG2AP_INST_LIBRARY_DATA 0x01
#define SSP_MSG2AP_INST_DEBUG_DATA 0x03
#define SSP_MSG2AP_INST_BIG_DATA 0x04
#define SSP_MSG2AP_INST_META_DATA 0x05
#define SSP_MSG2AP_INST_TIME_SYNC 0x06
#define SSP_MSG2AP_INST_RESET 0x07
#define SSP_UNIMPLEMENTED -1
struct ssp_msg_header {
u8 cmd;
__le16 length;
__le16 options;
__le32 data;
} __attribute__((__packed__));
struct ssp_msg {
u16 length;
u16 options;
struct list_head list;
struct completion *done;
struct ssp_msg_header *h;
char *buffer;
};
static const int ssp_offset_map[SSP_SENSOR_MAX] = {
[SSP_ACCELEROMETER_SENSOR] = SSP_ACCELEROMETER_SIZE +
SSP_TIME_SIZE,
[SSP_GYROSCOPE_SENSOR] = SSP_GYROSCOPE_SIZE +
SSP_TIME_SIZE,
[SSP_GEOMAGNETIC_UNCALIB_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_GEOMAGNETIC_RAW] = SSP_UNIMPLEMENTED,
[SSP_GEOMAGNETIC_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_PRESSURE_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_GESTURE_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_PROXIMITY_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_TEMPERATURE_HUMIDITY_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_LIGHT_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_PROXIMITY_RAW] = SSP_UNIMPLEMENTED,
[SSP_ORIENTATION_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_STEP_DETECTOR] = SSP_UNIMPLEMENTED,
[SSP_SIG_MOTION_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_GYRO_UNCALIB_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_GAME_ROTATION_VECTOR] = SSP_UNIMPLEMENTED,
[SSP_ROTATION_VECTOR] = SSP_UNIMPLEMENTED,
[SSP_STEP_COUNTER] = SSP_UNIMPLEMENTED,
[SSP_BIO_HRM_RAW] = SSP_BIO_HRM_RAW_SIZE +
SSP_TIME_SIZE,
[SSP_BIO_HRM_RAW_FAC] = SSP_BIO_HRM_RAW_FAC_SIZE +
SSP_TIME_SIZE,
[SSP_BIO_HRM_LIB] = SSP_BIO_HRM_LIB_SIZE +
SSP_TIME_SIZE,
};
#define SSP_HEADER_SIZE (sizeof(struct ssp_msg_header))
#define SSP_HEADER_SIZE_ALIGNED (ALIGN(SSP_HEADER_SIZE, 4))
static struct ssp_msg *ssp_create_msg(u8 cmd, u16 len, u16 opt, u32 data)
{
struct ssp_msg_header h;
struct ssp_msg *msg;
msg = kzalloc(sizeof(*msg), GFP_KERNEL);
if (!msg)
return NULL;
h.cmd = cmd;
h.length = cpu_to_le16(len);
h.options = cpu_to_le16(opt);
h.data = cpu_to_le32(data);
msg->buffer = kzalloc(SSP_HEADER_SIZE_ALIGNED + len,
GFP_KERNEL | GFP_DMA);
if (!msg->buffer) {
kfree(msg);
return NULL;
}
msg->length = len;
msg->options = opt;
memcpy(msg->buffer, &h, SSP_HEADER_SIZE);
return msg;
}
/*
* It is a bit heavy to do it this way but often the function is used to compose
* the message from smaller chunks which are placed on the stack. Often the
* chunks are small so memcpy should be optimalized.
*/
static inline void ssp_fill_buffer(struct ssp_msg *m, unsigned int offset,
const void *src, unsigned int len)
{
memcpy(&m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], src, len);
}
static inline void ssp_get_buffer(struct ssp_msg *m, unsigned int offset,
void *dest, unsigned int len)
{
memcpy(dest, &m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], len);
}
#define SSP_GET_BUFFER_AT_INDEX(m, index) \
(m->buffer[SSP_HEADER_SIZE_ALIGNED + index])
#define SSP_SET_BUFFER_AT_INDEX(m, index, val) \
(m->buffer[SSP_HEADER_SIZE_ALIGNED + index] = val)
static void ssp_clean_msg(struct ssp_msg *m)
{
kfree(m->buffer);
kfree(m);
}
static int ssp_print_mcu_debug(char *data_frame, int *data_index,
int received_len)
{
int length = data_frame[(*data_index)++];
if (length > received_len - *data_index || length <= 0) {
ssp_dbg("[SSP]: MSG From MCU-invalid debug length(%d/%d)\n",
length, received_len);
return length ? length : -EPROTO;
}
ssp_dbg("[SSP]: MSG From MCU - %s\n", &data_frame[*data_index]);
*data_index += length;
return 0;
}
/*
* It was designed that way - additional lines to some kind of handshake,
* please do not ask why - only the firmware guy can know it.
*/
static int ssp_check_lines(struct ssp_data *data, bool state)
{
int delay_cnt = 0;
gpio_set_value_cansleep(data->ap_mcu_gpio, state);
while (gpio_get_value_cansleep(data->mcu_ap_gpio) != state) {
usleep_range(3000, 3500);
if (data->shut_down || delay_cnt++ > 500) {
dev_err(SSP_DEV, "%s:timeout, hw ack wait fail %d\n",
__func__, state);
if (!state)
gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
return -ETIMEDOUT;
}
}
return 0;
}
static int ssp_do_transfer(struct ssp_data *data, struct ssp_msg *msg,
struct completion *done, int timeout)
{
int status;
/*
* check if this is a short one way message or the whole transfer has
* second part after an interrupt
*/
const bool use_no_irq = msg->length == 0;
if (data->shut_down)
return -EPERM;
msg->done = done;
mutex_lock(&data->comm_lock);
status = ssp_check_lines(data, false);
if (status < 0)
goto _error_locked;
status = spi_write(data->spi, msg->buffer, SSP_HEADER_SIZE);
if (status < 0) {
gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
dev_err(SSP_DEV, "%s spi_write fail\n", __func__);
goto _error_locked;
}
if (!use_no_irq) {
mutex_lock(&data->pending_lock);
list_add_tail(&msg->list, &data->pending_list);
mutex_unlock(&data->pending_lock);
}
status = ssp_check_lines(data, true);
if (status < 0) {
if (!use_no_irq) {
mutex_lock(&data->pending_lock);
list_del(&msg->list);
mutex_unlock(&data->pending_lock);
}
goto _error_locked;
}
mutex_unlock(&data->comm_lock);
if (!use_no_irq && done)
if (wait_for_completion_timeout(done,
msecs_to_jiffies(timeout)) ==
0) {
mutex_lock(&data->pending_lock);
list_del(&msg->list);
mutex_unlock(&data->pending_lock);
data->timeout_cnt++;
return -ETIMEDOUT;
}
return 0;
_error_locked:
mutex_unlock(&data->comm_lock);
data->timeout_cnt++;
return status;
}
static inline int ssp_spi_sync_command(struct ssp_data *data,
struct ssp_msg *msg)
{
return ssp_do_transfer(data, msg, NULL, 0);
}
static int ssp_spi_sync(struct ssp_data *data, struct ssp_msg *msg,
int timeout)
{
DECLARE_COMPLETION_ONSTACK(done);
if (WARN_ON(!msg->length))
return -EPERM;
return ssp_do_transfer(data, msg, &done, timeout);
}
static int ssp_handle_big_data(struct ssp_data *data, char *dataframe, int *idx)
{
/* mock-up, it will be changed with adding another sensor types */
*idx += 8;
return 0;
}
static int ssp_parse_dataframe(struct ssp_data *data, char *dataframe, int len)
{
int idx, sd;
struct timespec ts;
struct ssp_sensor_data *spd;
struct iio_dev **indio_devs = data->sensor_devs;
getnstimeofday(&ts);
for (idx = 0; idx < len;) {
switch (dataframe[idx++]) {
case SSP_MSG2AP_INST_BYPASS_DATA:
sd = dataframe[idx++];
if (sd < 0 || sd >= SSP_SENSOR_MAX) {
dev_err(SSP_DEV,
"Mcu data frame1 error %d\n", sd);
return -EPROTO;
}
if (indio_devs[sd]) {
spd = iio_priv(indio_devs[sd]);
if (spd->process_data)
spd->process_data(indio_devs[sd],
&dataframe[idx],
data->timestamp);
} else {
dev_err(SSP_DEV, "no client for frame\n");
}
idx += ssp_offset_map[sd];
break;
case SSP_MSG2AP_INST_DEBUG_DATA:
sd = ssp_print_mcu_debug(dataframe, &idx, len);
if (sd) {
dev_err(SSP_DEV,
"Mcu data frame3 error %d\n", sd);
return sd;
}
break;
case SSP_MSG2AP_INST_LIBRARY_DATA:
idx += len;
break;
case SSP_MSG2AP_INST_BIG_DATA:
ssp_handle_big_data(data, dataframe, &idx);
break;
case SSP_MSG2AP_INST_TIME_SYNC:
data->time_syncing = true;
break;
case SSP_MSG2AP_INST_RESET:
ssp_queue_ssp_refresh_task(data, 0);
break;
}
}
if (data->time_syncing)
data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
return 0;
}
/* threaded irq */
int ssp_irq_msg(struct ssp_data *data)
{
bool found = false;
char *buffer;
u8 msg_type;
int ret;
u16 length, msg_options;
struct ssp_msg *msg, *n;
ret = spi_read(data->spi, data->header_buffer, SSP_HEADER_BUFFER_SIZE);
if (ret < 0) {
dev_err(SSP_DEV, "header read fail\n");
return ret;
}
length = le16_to_cpu(data->header_buffer[1]);
msg_options = le16_to_cpu(data->header_buffer[0]);
if (length == 0) {
dev_err(SSP_DEV, "length received from mcu is 0\n");
return -EINVAL;
}
msg_type = SSP_GET_MESSAGE_TYPE(msg_options);
switch (msg_type) {
case SSP_AP2HUB_READ:
case SSP_AP2HUB_WRITE:
/*
* this is a small list, a few elements - the packets can be
* received with no order
*/
mutex_lock(&data->pending_lock);
list_for_each_entry_safe(msg, n, &data->pending_list, list) {
if (msg->options == msg_options) {
list_del(&msg->list);
found = true;
break;
}
}
if (!found) {
/*
* here can be implemented dead messages handling
* but the slave should not send such ones - it is to
* check but let's handle this
*/
buffer = kmalloc(length, GFP_KERNEL | GFP_DMA);
if (!buffer) {
ret = -ENOMEM;
goto _unlock;
}
/* got dead packet so it is always an error */
ret = spi_read(data->spi, buffer, length);
if (ret >= 0)
ret = -EPROTO;
kfree(buffer);
dev_err(SSP_DEV, "No match error %x\n",
msg_options);
goto _unlock;
}
if (msg_type == SSP_AP2HUB_READ)
ret = spi_read(data->spi,
&msg->buffer[SSP_HEADER_SIZE_ALIGNED],
msg->length);
if (msg_type == SSP_AP2HUB_WRITE) {
ret = spi_write(data->spi,
&msg->buffer[SSP_HEADER_SIZE_ALIGNED],
msg->length);
if (msg_options & SSP_AP2HUB_RETURN) {
msg->options =
SSP_AP2HUB_READ | SSP_AP2HUB_RETURN;
msg->length = 1;
list_add_tail(&msg->list, &data->pending_list);
goto _unlock;
}
}
if (msg->done)
if (!completion_done(msg->done))
complete(msg->done);
_unlock:
mutex_unlock(&data->pending_lock);
break;
case SSP_HUB2AP_WRITE:
buffer = kzalloc(length, GFP_KERNEL | GFP_DMA);
if (!buffer)
return -ENOMEM;
ret = spi_read(data->spi, buffer, length);
if (ret < 0) {
dev_err(SSP_DEV, "spi read fail\n");
kfree(buffer);
break;
}
ret = ssp_parse_dataframe(data, buffer, length);
kfree(buffer);
break;
default:
dev_err(SSP_DEV, "unknown msg type\n");
return -EPROTO;
}
return ret;
}
void ssp_clean_pending_list(struct ssp_data *data)
{
struct ssp_msg *msg, *n;
mutex_lock(&data->pending_lock);
list_for_each_entry_safe(msg, n, &data->pending_list, list) {
list_del(&msg->list);
if (msg->done)
if (!completion_done(msg->done))
complete(msg->done);
}
mutex_unlock(&data->pending_lock);
}
int ssp_command(struct ssp_data *data, char command, int arg)
{
int ret;
struct ssp_msg *msg;
msg = ssp_create_msg(command, 0, SSP_AP2HUB_WRITE, arg);
if (!msg)
return -ENOMEM;
ssp_dbg("%s - command 0x%x %d\n", __func__, command, arg);
ret = ssp_spi_sync_command(data, msg);
ssp_clean_msg(msg);
return ret;
}
int ssp_send_instruction(struct ssp_data *data, u8 inst, u8 sensor_type,
u8 *send_buf, u8 length)
{
int ret;
struct ssp_msg *msg;
if (data->fw_dl_state == SSP_FW_DL_STATE_DOWNLOADING) {
dev_err(SSP_DEV, "%s - Skip Inst! DL state = %d\n",
__func__, data->fw_dl_state);
return -EBUSY;
} else if (!(data->available_sensors & BIT(sensor_type)) &&
(inst <= SSP_MSG2SSP_INST_CHANGE_DELAY)) {
dev_err(SSP_DEV, "%s - Bypass Inst Skip! - %u\n",
__func__, sensor_type);
return -EIO; /* just fail */
}
msg = ssp_create_msg(inst, length + 2, SSP_AP2HUB_WRITE, 0);
if (!msg)
return -ENOMEM;
ssp_fill_buffer(msg, 0, &sensor_type, 1);
ssp_fill_buffer(msg, 1, send_buf, length);
ssp_dbg("%s - Inst = 0x%x, Sensor Type = 0x%x, data = %u\n",
__func__, inst, sensor_type, send_buf[1]);
ret = ssp_spi_sync(data, msg, 1000);
ssp_clean_msg(msg);
return ret;
}
int ssp_get_chipid(struct ssp_data *data)
{
int ret;
char buffer;
struct ssp_msg *msg;
msg = ssp_create_msg(SSP_MSG2SSP_AP_WHOAMI, 1, SSP_AP2HUB_READ, 0);
if (!msg)
return -ENOMEM;
ret = ssp_spi_sync(data, msg, 1000);
buffer = SSP_GET_BUFFER_AT_INDEX(msg, 0);
ssp_clean_msg(msg);
return ret < 0 ? ret : buffer;
}
int ssp_set_magnetic_matrix(struct ssp_data *data)
{
int ret;
struct ssp_msg *msg;
msg = ssp_create_msg(SSP_MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX,
data->sensorhub_info->mag_length, SSP_AP2HUB_WRITE,
0);
if (!msg)
return -ENOMEM;
ssp_fill_buffer(msg, 0, data->sensorhub_info->mag_table,
data->sensorhub_info->mag_length);
ret = ssp_spi_sync(data, msg, 1000);
ssp_clean_msg(msg);
return ret;
}
unsigned int ssp_get_sensor_scanning_info(struct ssp_data *data)
{
int ret;
__le32 result;
u32 cpu_result = 0;
struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_SENSOR_SCANNING, 4,
SSP_AP2HUB_READ, 0);
if (!msg)
return 0;
ret = ssp_spi_sync(data, msg, 1000);
if (ret < 0) {
dev_err(SSP_DEV, "%s - spi read fail %d\n", __func__, ret);
goto _exit;
}
ssp_get_buffer(msg, 0, &result, 4);
cpu_result = le32_to_cpu(result);
dev_info(SSP_DEV, "%s state: 0x%08x\n", __func__, cpu_result);
_exit:
ssp_clean_msg(msg);
return cpu_result;
}
unsigned int ssp_get_firmware_rev(struct ssp_data *data)
{
int ret;
__le32 result;
struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_FIRMWARE_REV, 4,
SSP_AP2HUB_READ, 0);
if (!msg)
return SSP_INVALID_REVISION;
ret = ssp_spi_sync(data, msg, 1000);
if (ret < 0) {
dev_err(SSP_DEV, "%s - transfer fail %d\n", __func__, ret);
ret = SSP_INVALID_REVISION;
goto _exit;
}
ssp_get_buffer(msg, 0, &result, 4);
ret = le32_to_cpu(result);
_exit:
ssp_clean_msg(msg);
return ret;
}

View file

@ -54,7 +54,7 @@ static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
if (err)
goto acc_spi_read_error;
memcpy(data, tb->rx_buf, len*sizeof(u8));
memcpy(data, tb->rx_buf, len);
mutex_unlock(&tb->buf_lock);
return len;

View file

@ -445,7 +445,7 @@ static int ad9523_store_eeprom(struct iio_dev *indio_dev)
tmp = 4;
do {
msleep(16);
msleep(20);
ret = ad9523_read(indio_dev,
AD9523_EEPROM_DATA_XFER_STATUS);
if (ret < 0)

View file

@ -387,10 +387,8 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
int ret;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(dev, "could not allocate memory for platform data\n");
if (!pdata)
return NULL;
}
strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1);
@ -613,9 +611,8 @@ static int adf4350_remove(struct spi_device *spi)
if (st->clk)
clk_disable_unprepare(st->clk);
if (!IS_ERR(reg)) {
if (!IS_ERR(reg))
regulator_disable(reg);
}
return 0;
}

View file

@ -16,6 +16,8 @@ itg3200-y := itg3200_core.o
itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
obj-$(CONFIG_ITG3200) += itg3200.o
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_gyro_sensor.o
obj-$(CONFIG_IIO_ST_GYRO_3AXIS) += st_gyro.o
st_gyro-y := st_gyro_core.o
st_gyro-$(CONFIG_IIO_BUFFER) += st_gyro_buffer.o

View file

@ -111,19 +111,12 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
switch (mask) {
case 0:
poll_value = hid_sensor_read_poll_value(
&gyro_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&gyro_state->common_attributes, true);
msleep_interruptible(poll_value * 2);
report_id = gyro_state->gyro[chan->scan_index].report_id;
address = gyro_3d_addresses[chan->scan_index];
if (report_id >= 0)
@ -416,6 +409,7 @@ static struct platform_driver hid_gyro_3d_platform_driver = {
.id_table = hid_gyro_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_gyro_3d_probe,
.remove = hid_gyro_3d_remove,

View file

@ -0,0 +1,168 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/iio/common/ssp_sensors.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../common/ssp_sensors/ssp_iio_sensor.h"
#define SSP_CHANNEL_COUNT 3
#define SSP_GYROSCOPE_NAME "ssp-gyroscope"
static const char ssp_gyro_name[] = SSP_GYROSCOPE_NAME;
enum ssp_gyro_3d_channel {
SSP_CHANNEL_SCAN_INDEX_X,
SSP_CHANNEL_SCAN_INDEX_Y,
SSP_CHANNEL_SCAN_INDEX_Z,
SSP_CHANNEL_SCAN_INDEX_TIME,
};
static int ssp_gyro_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
u32 t;
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
t = ssp_get_sensor_delay(data, SSP_GYROSCOPE_SENSOR);
ssp_convert_to_freq(t, val, val2);
return IIO_VAL_INT_PLUS_MICRO;
default:
break;
}
return -EINVAL;
}
static int ssp_gyro_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
int ret;
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = ssp_convert_to_time(val, val2);
ret = ssp_change_delay(data, SSP_GYROSCOPE_SENSOR, ret);
if (ret < 0)
dev_err(&indio_dev->dev, "gyro sensor enable fail\n");
return ret;
default:
break;
}
return -EINVAL;
}
static struct iio_info ssp_gyro_iio_info = {
.read_raw = &ssp_gyro_read_raw,
.write_raw = &ssp_gyro_write_raw,
};
static const unsigned long ssp_gyro_scan_mask[] = { 0x07, 0, };
static const struct iio_chan_spec ssp_gyro_channels[] = {
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X),
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y),
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z),
SSP_CHAN_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME),
};
static int ssp_process_gyro_data(struct iio_dev *indio_dev, void *buf,
int64_t timestamp)
{
return ssp_common_process_data(indio_dev, buf, SSP_GYROSCOPE_SIZE,
timestamp);
}
static const struct iio_buffer_setup_ops ssp_gyro_buffer_ops = {
.postenable = &ssp_common_buffer_postenable,
.postdisable = &ssp_common_buffer_postdisable,
};
static int ssp_gyro_probe(struct platform_device *pdev)
{
int ret;
struct iio_dev *indio_dev;
struct ssp_sensor_data *spd;
struct iio_buffer *buffer;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
if (!indio_dev)
return -ENOMEM;
spd = iio_priv(indio_dev);
spd->process_data = ssp_process_gyro_data;
spd->type = SSP_GYROSCOPE_SENSOR;
indio_dev->name = ssp_gyro_name;
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &ssp_gyro_iio_info;
indio_dev->modes = INDIO_BUFFER_SOFTWARE;
indio_dev->channels = ssp_gyro_channels;
indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels);
indio_dev->available_scan_masks = ssp_gyro_scan_mask;
buffer = devm_iio_kfifo_allocate(&pdev->dev);
if (!buffer)
return -ENOMEM;
iio_device_attach_buffer(indio_dev, buffer);
indio_dev->setup_ops = &ssp_gyro_buffer_ops;
platform_set_drvdata(pdev, indio_dev);
ret = iio_device_register(indio_dev);
if (ret < 0)
return ret;
/* ssp registering should be done after all iio setup */
ssp_register_consumer(indio_dev, SSP_GYROSCOPE_SENSOR);
return 0;
}
static int ssp_gyro_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);
return 0;
}
static struct platform_driver ssp_gyro_driver = {
.driver = {
.name = SSP_GYROSCOPE_NAME,
},
.probe = ssp_gyro_probe,
.remove = ssp_gyro_remove,
};
module_platform_driver(ssp_gyro_driver);
MODULE_AUTHOR("Karol Wrona <[email protected]>");
MODULE_DESCRIPTION("Samsung sensorhub gyroscopes driver");
MODULE_LICENSE("GPL");

View file

@ -48,6 +48,8 @@ unsigned int iio_buffer_poll(struct file *filp,
ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
size_t n, loff_t *f_ps);
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev);
#define iio_buffer_poll_addr (&iio_buffer_poll)
#define iio_buffer_read_first_n_outer_addr (&iio_buffer_read_first_n_outer)
@ -60,6 +62,13 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
#define iio_buffer_poll_addr NULL
#define iio_buffer_read_first_n_outer_addr NULL
static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
return 0;
}
static inline void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) {}
static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {}
static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {}

View file

@ -25,6 +25,17 @@ config ADIS16480
Say yes here to build support for Analog Devices ADIS16375, ADIS16480,
ADIS16485, ADIS16488 inertial sensors.
config KMX61
tristate "Kionix KMX61 6-axis accelerometer and magnetometer"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say Y here if you want to build a driver for Kionix KMX61 6-axis
accelerometer and magnetometer.
To compile this driver as module, choose M here: the module will
be called kmx61.
source "drivers/iio/imu/inv_mpu6050/Kconfig"
endmenu

View file

@ -14,3 +14,5 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
obj-y += inv_mpu6050/
obj-$(CONFIG_KMX61) += kmx61.o

View file

@ -7,6 +7,7 @@ config INV_MPU6050_IIO
depends on I2C && SYSFS
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select I2C_MUX
help
This driver supports the Invensense MPU6050 devices.
This driver can also support MPU6500 in MPU6050 compatibility mode

View file

@ -23,6 +23,8 @@
#include <linux/kfifo.h>
#include <linux/spinlock.h>
#include <linux/iio/iio.h>
#include <linux/i2c-mux.h>
#include <linux/acpi.h>
#include "inv_mpu_iio.h"
/*
@ -52,6 +54,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = {
.int_enable = INV_MPU6050_REG_INT_ENABLE,
.pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
.pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
};
static const struct inv_mpu6050_chip_config chip_config_6050 = {
@ -77,6 +80,83 @@ int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 d)
return i2c_smbus_write_i2c_block_data(st->client, reg, 1, &d);
}
/*
* The i2c read/write needs to happen in unlocked mode. As the parent
* adapter is common. If we use locked versions, it will fail as
* the mux adapter will lock the parent i2c adapter, while calling
* select/deselect functions.
*/
static int inv_mpu6050_write_reg_unlocked(struct inv_mpu6050_state *st,
u8 reg, u8 d)
{
int ret;
u8 buf[2];
struct i2c_msg msg[1] = {
{
.addr = st->client->addr,
.flags = 0,
.len = sizeof(buf),
.buf = buf,
}
};
buf[0] = reg;
buf[1] = d;
ret = __i2c_transfer(st->client->adapter, msg, 1);
if (ret != 1)
return ret;
return 0;
}
static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv,
u32 chan_id)
{
struct iio_dev *indio_dev = mux_priv;
struct inv_mpu6050_state *st = iio_priv(indio_dev);
int ret = 0;
/* Use the same mutex which was used everywhere to protect power-op */
mutex_lock(&indio_dev->mlock);
if (!st->powerup_count) {
ret = inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1,
0);
if (ret)
goto write_error;
msleep(INV_MPU6050_REG_UP_TIME);
}
if (!ret) {
st->powerup_count++;
ret = inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg,
st->client->irq |
INV_MPU6050_BIT_BYPASS_EN);
}
write_error:
mutex_unlock(&indio_dev->mlock);
return ret;
}
static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap,
void *mux_priv, u32 chan_id)
{
struct iio_dev *indio_dev = mux_priv;
struct inv_mpu6050_state *st = iio_priv(indio_dev);
mutex_lock(&indio_dev->mlock);
/* It doesn't really mattter, if any of the calls fails */
inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg,
st->client->irq);
st->powerup_count--;
if (!st->powerup_count)
inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_SLEEP);
mutex_unlock(&indio_dev->mlock);
return 0;
}
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
{
u8 d, mgmt_1;
@ -133,13 +213,22 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on)
{
int result;
int result = 0;
if (power_on) {
/* Already under indio-dev->mlock mutex */
if (!st->powerup_count)
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
0);
if (!result)
st->powerup_count++;
} else {
st->powerup_count--;
if (!st->powerup_count)
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_SLEEP);
}
if (power_on)
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, 0);
else
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_SLEEP);
if (result)
return result;
@ -673,6 +762,7 @@ static int inv_mpu_probe(struct i2c_client *client,
st = iio_priv(indio_dev);
st->client = client;
st->powerup_count = 0;
pdata = dev_get_platdata(&client->dev);
if (pdata)
st->plat_data = *pdata;
@ -720,8 +810,21 @@ static int inv_mpu_probe(struct i2c_client *client,
goto out_remove_trigger;
}
st->mux_adapter = i2c_add_mux_adapter(client->adapter,
&client->dev,
indio_dev,
0, 0, 0,
inv_mpu6050_select_bypass,
inv_mpu6050_deselect_bypass);
if (!st->mux_adapter) {
result = -ENODEV;
goto out_unreg_device;
}
return 0;
out_unreg_device:
iio_device_unregister(indio_dev);
out_remove_trigger:
inv_mpu6050_remove_trigger(st);
out_unreg_ring:
@ -734,6 +837,7 @@ static int inv_mpu_remove(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
i2c_del_mux_adapter(st->mux_adapter);
iio_device_unregister(indio_dev);
inv_mpu6050_remove_trigger(st);
iio_triggered_buffer_cleanup(indio_dev);
@ -772,6 +876,13 @@ static const struct i2c_device_id inv_mpu_id[] = {
MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
static const struct acpi_device_id inv_acpi_match[] = {
{"INVN6500", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
static struct i2c_driver inv_mpu_driver = {
.probe = inv_mpu_probe,
.remove = inv_mpu_remove,
@ -780,6 +891,7 @@ static struct i2c_driver inv_mpu_driver = {
.owner = THIS_MODULE,
.name = "inv-mpu6050",
.pm = INV_MPU6050_PMOPS,
.acpi_match_table = ACPI_PTR(inv_acpi_match),
},
};

View file

@ -54,6 +54,7 @@ struct inv_mpu6050_reg_map {
u8 int_enable;
u8 pwr_mgmt_1;
u8 pwr_mgmt_2;
u8 int_pin_cfg;
};
/*device enum */
@ -119,6 +120,8 @@ struct inv_mpu6050_state {
enum inv_devices chip_type;
spinlock_t time_stamp_lock;
struct i2c_client *client;
struct i2c_adapter *mux_adapter;
unsigned int powerup_count;
struct inv_mpu6050_platform_data plat_data;
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
};
@ -179,6 +182,9 @@ struct inv_mpu6050_state {
/* 6 + 6 round up and plus 8 */
#define INV_MPU6050_OUTPUT_DATA_SIZE 24
#define INV_MPU6050_REG_INT_PIN_CFG 0x37
#define INV_MPU6050_BIT_BYPASS_EN 0x2
/* init parameters */
#define INV_MPU6050_INIT_FIFO_RATE 50
#define INV_MPU6050_TIME_STAMP_TOR 5

View file

@ -116,40 +116,35 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
int ret;
struct inv_mpu6050_state *st = iio_priv(indio_dev);
st->trig = iio_trigger_alloc("%s-dev%d",
indio_dev->name,
indio_dev->id);
if (st->trig == NULL) {
ret = -ENOMEM;
goto error_ret;
}
ret = request_irq(st->client->irq, &iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING,
"inv_mpu",
st->trig);
st->trig = devm_iio_trigger_alloc(&indio_dev->dev,
"%s-dev%d",
indio_dev->name,
indio_dev->id);
if (!st->trig)
return -ENOMEM;
ret = devm_request_irq(&indio_dev->dev, st->client->irq,
&iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING,
"inv_mpu",
st->trig);
if (ret)
goto error_free_trig;
return ret;
st->trig->dev.parent = &st->client->dev;
st->trig->ops = &inv_mpu_trigger_ops;
iio_trigger_set_drvdata(st->trig, indio_dev);
ret = iio_trigger_register(st->trig);
if (ret)
goto error_free_irq;
return ret;
indio_dev->trig = iio_trigger_get(st->trig);
return 0;
error_free_irq:
free_irq(st->client->irq, st->trig);
error_free_trig:
iio_trigger_free(st->trig);
error_ret:
return ret;
}
void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st)
{
iio_trigger_unregister(st->trig);
free_irq(st->client->irq, st->trig);
iio_trigger_free(st->trig);
}

1595
drivers/iio/imu/kmx61.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -178,6 +178,80 @@ static ssize_t iio_scan_el_show(struct device *dev,
return sprintf(buf, "%d\n", ret);
}
/* Note NULL used as error indicator as it doesn't make sense. */
static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,
unsigned int masklength,
const unsigned long *mask)
{
if (bitmap_empty(mask, masklength))
return NULL;
while (*av_masks) {
if (bitmap_subset(mask, av_masks, masklength))
return av_masks;
av_masks += BITS_TO_LONGS(masklength);
}
return NULL;
}
static bool iio_validate_scan_mask(struct iio_dev *indio_dev,
const unsigned long *mask)
{
if (!indio_dev->setup_ops->validate_scan_mask)
return true;
return indio_dev->setup_ops->validate_scan_mask(indio_dev, mask);
}
/**
* iio_scan_mask_set() - set particular bit in the scan mask
* @indio_dev: the iio device
* @buffer: the buffer whose scan mask we are interested in
* @bit: the bit to be set.
*
* Note that at this point we have no way of knowing what other
* buffers might request, hence this code only verifies that the
* individual buffers request is plausible.
*/
static int iio_scan_mask_set(struct iio_dev *indio_dev,
struct iio_buffer *buffer, int bit)
{
const unsigned long *mask;
unsigned long *trialmask;
trialmask = kmalloc(sizeof(*trialmask)*
BITS_TO_LONGS(indio_dev->masklength),
GFP_KERNEL);
if (trialmask == NULL)
return -ENOMEM;
if (!indio_dev->masklength) {
WARN_ON("Trying to set scanmask prior to registering buffer\n");
goto err_invalid_mask;
}
bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength);
set_bit(bit, trialmask);
if (!iio_validate_scan_mask(indio_dev, trialmask))
goto err_invalid_mask;
if (indio_dev->available_scan_masks) {
mask = iio_scan_mask_match(indio_dev->available_scan_masks,
indio_dev->masklength,
trialmask);
if (!mask)
goto err_invalid_mask;
}
bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength);
kfree(trialmask);
return 0;
err_invalid_mask:
kfree(trialmask);
return -EINVAL;
}
static int iio_scan_mask_clear(struct iio_buffer *buffer, int bit)
{
clear_bit(bit, buffer->scan_mask);
@ -309,115 +383,19 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
return ret;
}
static const char * const iio_scan_elements_group_name = "scan_elements";
int iio_buffer_register(struct iio_dev *indio_dev,
const struct iio_chan_spec *channels,
int num_channels)
{
struct iio_dev_attr *p;
struct attribute **attr;
struct iio_buffer *buffer = indio_dev->buffer;
int ret, i, attrn, attrcount, attrcount_orig = 0;
if (buffer->attrs)
indio_dev->groups[indio_dev->groupcounter++] = buffer->attrs;
if (buffer->scan_el_attrs != NULL) {
attr = buffer->scan_el_attrs->attrs;
while (*attr++ != NULL)
attrcount_orig++;
}
attrcount = attrcount_orig;
INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
if (channels) {
/* new magic */
for (i = 0; i < num_channels; i++) {
if (channels[i].scan_index < 0)
continue;
/* Establish necessary mask length */
if (channels[i].scan_index >
(int)indio_dev->masklength - 1)
indio_dev->masklength
= channels[i].scan_index + 1;
ret = iio_buffer_add_channel_sysfs(indio_dev,
&channels[i]);
if (ret < 0)
goto error_cleanup_dynamic;
attrcount += ret;
if (channels[i].type == IIO_TIMESTAMP)
indio_dev->scan_index_timestamp =
channels[i].scan_index;
}
if (indio_dev->masklength && buffer->scan_mask == NULL) {
buffer->scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
sizeof(*buffer->scan_mask),
GFP_KERNEL);
if (buffer->scan_mask == NULL) {
ret = -ENOMEM;
goto error_cleanup_dynamic;
}
}
}
buffer->scan_el_group.name = iio_scan_elements_group_name;
buffer->scan_el_group.attrs = kcalloc(attrcount + 1,
sizeof(buffer->scan_el_group.attrs[0]),
GFP_KERNEL);
if (buffer->scan_el_group.attrs == NULL) {
ret = -ENOMEM;
goto error_free_scan_mask;
}
if (buffer->scan_el_attrs)
memcpy(buffer->scan_el_group.attrs, buffer->scan_el_attrs,
sizeof(buffer->scan_el_group.attrs[0])*attrcount_orig);
attrn = attrcount_orig;
list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr;
indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group;
return 0;
error_free_scan_mask:
kfree(buffer->scan_mask);
error_cleanup_dynamic:
iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
return ret;
}
EXPORT_SYMBOL(iio_buffer_register);
void iio_buffer_unregister(struct iio_dev *indio_dev)
{
kfree(indio_dev->buffer->scan_mask);
kfree(indio_dev->buffer->scan_el_group.attrs);
iio_free_chan_devattr_list(&indio_dev->buffer->scan_el_dev_attr_list);
}
EXPORT_SYMBOL(iio_buffer_unregister);
ssize_t iio_buffer_read_length(struct device *dev,
struct device_attribute *attr,
char *buf)
static ssize_t iio_buffer_read_length(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_buffer *buffer = indio_dev->buffer;
if (buffer->access->get_length)
return sprintf(buf, "%d\n",
buffer->access->get_length(buffer));
return 0;
return sprintf(buf, "%d\n", buffer->length);
}
EXPORT_SYMBOL(iio_buffer_read_length);
ssize_t iio_buffer_write_length(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
static ssize_t iio_buffer_write_length(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_buffer *buffer = indio_dev->buffer;
@ -428,47 +406,28 @@ ssize_t iio_buffer_write_length(struct device *dev,
if (ret)
return ret;
if (buffer->access->get_length)
if (val == buffer->access->get_length(buffer))
return len;
if (val == buffer->length)
return len;
mutex_lock(&indio_dev->mlock);
if (iio_buffer_is_active(indio_dev->buffer)) {
ret = -EBUSY;
} else {
if (buffer->access->set_length)
buffer->access->set_length(buffer, val);
buffer->access->set_length(buffer, val);
ret = 0;
}
mutex_unlock(&indio_dev->mlock);
return ret ? ret : len;
}
EXPORT_SYMBOL(iio_buffer_write_length);
ssize_t iio_buffer_show_enable(struct device *dev,
struct device_attribute *attr,
char *buf)
static ssize_t iio_buffer_show_enable(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
return sprintf(buf, "%d\n", iio_buffer_is_active(indio_dev->buffer));
}
EXPORT_SYMBOL(iio_buffer_show_enable);
/* Note NULL used as error indicator as it doesn't make sense. */
static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,
unsigned int masklength,
const unsigned long *mask)
{
if (bitmap_empty(mask, masklength))
return NULL;
while (*av_masks) {
if (bitmap_subset(mask, av_masks, masklength))
return av_masks;
av_masks += BITS_TO_LONGS(masklength);
}
return NULL;
}
static int iio_compute_scan_bytes(struct iio_dev *indio_dev,
const unsigned long *mask, bool timestamp)
@ -680,6 +639,8 @@ static int __iio_update_buffers(struct iio_dev *indio_dev,
indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;
} else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
indio_dev->currentmode = INDIO_BUFFER_HARDWARE;
} else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) {
indio_dev->currentmode = INDIO_BUFFER_SOFTWARE;
} else { /* Should never be reached */
ret = -EINVAL;
goto error_run_postdisable;
@ -755,10 +716,10 @@ out_unlock:
}
EXPORT_SYMBOL_GPL(iio_update_buffers);
ssize_t iio_buffer_store_enable(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
static ssize_t iio_buffer_store_enable(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
{
int ret;
bool requested_state;
@ -790,7 +751,130 @@ done:
mutex_unlock(&indio_dev->mlock);
return (ret < 0) ? ret : len;
}
EXPORT_SYMBOL(iio_buffer_store_enable);
static const char * const iio_scan_elements_group_name = "scan_elements";
static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length,
iio_buffer_write_length);
static struct device_attribute dev_attr_length_ro = __ATTR(length,
S_IRUGO, iio_buffer_read_length, NULL);
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR,
iio_buffer_show_enable, iio_buffer_store_enable);
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
struct iio_dev_attr *p;
struct attribute **attr;
struct iio_buffer *buffer = indio_dev->buffer;
int ret, i, attrn, attrcount, attrcount_orig = 0;
const struct iio_chan_spec *channels;
if (!buffer)
return 0;
attrcount = 0;
if (buffer->attrs) {
while (buffer->attrs[attrcount] != NULL)
attrcount++;
}
buffer->buffer_group.name = "buffer";
buffer->buffer_group.attrs = kcalloc(attrcount + 3,
sizeof(*buffer->buffer_group.attrs), GFP_KERNEL);
if (!buffer->buffer_group.attrs)
return -ENOMEM;
if (buffer->access->set_length)
buffer->buffer_group.attrs[0] = &dev_attr_length.attr;
else
buffer->buffer_group.attrs[0] = &dev_attr_length_ro.attr;
buffer->buffer_group.attrs[1] = &dev_attr_enable.attr;
if (buffer->attrs)
memcpy(&buffer->buffer_group.attrs[2], buffer->attrs,
sizeof(*&buffer->buffer_group.attrs) * attrcount);
buffer->buffer_group.attrs[attrcount+2] = NULL;
indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group;
if (buffer->scan_el_attrs != NULL) {
attr = buffer->scan_el_attrs->attrs;
while (*attr++ != NULL)
attrcount_orig++;
}
attrcount = attrcount_orig;
INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
channels = indio_dev->channels;
if (channels) {
/* new magic */
for (i = 0; i < indio_dev->num_channels; i++) {
if (channels[i].scan_index < 0)
continue;
/* Establish necessary mask length */
if (channels[i].scan_index >
(int)indio_dev->masklength - 1)
indio_dev->masklength
= channels[i].scan_index + 1;
ret = iio_buffer_add_channel_sysfs(indio_dev,
&channels[i]);
if (ret < 0)
goto error_cleanup_dynamic;
attrcount += ret;
if (channels[i].type == IIO_TIMESTAMP)
indio_dev->scan_index_timestamp =
channels[i].scan_index;
}
if (indio_dev->masklength && buffer->scan_mask == NULL) {
buffer->scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
sizeof(*buffer->scan_mask),
GFP_KERNEL);
if (buffer->scan_mask == NULL) {
ret = -ENOMEM;
goto error_cleanup_dynamic;
}
}
}
buffer->scan_el_group.name = iio_scan_elements_group_name;
buffer->scan_el_group.attrs = kcalloc(attrcount + 1,
sizeof(buffer->scan_el_group.attrs[0]),
GFP_KERNEL);
if (buffer->scan_el_group.attrs == NULL) {
ret = -ENOMEM;
goto error_free_scan_mask;
}
if (buffer->scan_el_attrs)
memcpy(buffer->scan_el_group.attrs, buffer->scan_el_attrs,
sizeof(buffer->scan_el_group.attrs[0])*attrcount_orig);
attrn = attrcount_orig;
list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr;
indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group;
return 0;
error_free_scan_mask:
kfree(buffer->scan_mask);
error_cleanup_dynamic:
iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
kfree(indio_dev->buffer->buffer_group.attrs);
return ret;
}
void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
{
if (!indio_dev->buffer)
return;
kfree(indio_dev->buffer->scan_mask);
kfree(indio_dev->buffer->buffer_group.attrs);
kfree(indio_dev->buffer->scan_el_group.attrs);
iio_free_chan_devattr_list(&indio_dev->buffer->scan_el_dev_attr_list);
}
/**
* iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected
@ -808,66 +892,6 @@ bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
}
EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot);
static bool iio_validate_scan_mask(struct iio_dev *indio_dev,
const unsigned long *mask)
{
if (!indio_dev->setup_ops->validate_scan_mask)
return true;
return indio_dev->setup_ops->validate_scan_mask(indio_dev, mask);
}
/**
* iio_scan_mask_set() - set particular bit in the scan mask
* @indio_dev: the iio device
* @buffer: the buffer whose scan mask we are interested in
* @bit: the bit to be set.
*
* Note that at this point we have no way of knowing what other
* buffers might request, hence this code only verifies that the
* individual buffers request is plausible.
*/
int iio_scan_mask_set(struct iio_dev *indio_dev,
struct iio_buffer *buffer, int bit)
{
const unsigned long *mask;
unsigned long *trialmask;
trialmask = kmalloc(sizeof(*trialmask)*
BITS_TO_LONGS(indio_dev->masklength),
GFP_KERNEL);
if (trialmask == NULL)
return -ENOMEM;
if (!indio_dev->masklength) {
WARN_ON("Trying to set scanmask prior to registering buffer\n");
goto err_invalid_mask;
}
bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength);
set_bit(bit, trialmask);
if (!iio_validate_scan_mask(indio_dev, trialmask))
goto err_invalid_mask;
if (indio_dev->available_scan_masks) {
mask = iio_scan_mask_match(indio_dev->available_scan_masks,
indio_dev->masklength,
trialmask);
if (!mask)
goto err_invalid_mask;
}
bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength);
kfree(trialmask);
return 0;
err_invalid_mask:
kfree(trialmask);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(iio_scan_mask_set);
int iio_scan_mask_query(struct iio_dev *indio_dev,
struct iio_buffer *buffer, int bit)
{

View file

@ -70,6 +70,11 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_CCT] = "cct",
[IIO_PRESSURE] = "pressure",
[IIO_HUMIDITYRELATIVE] = "humidityrelative",
[IIO_ACTIVITY] = "activity",
[IIO_STEPS] = "steps",
[IIO_ENERGY] = "energy",
[IIO_DISTANCE] = "distance",
[IIO_VELOCITY] = "velocity",
};
static const char * const iio_modifier_names[] = {
@ -91,6 +96,11 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_NORTH_TRUE] = "from_north_true",
[IIO_MOD_NORTH_MAGN_TILT_COMP] = "from_north_magnetic_tilt_comp",
[IIO_MOD_NORTH_TRUE_TILT_COMP] = "from_north_true_tilt_comp",
[IIO_MOD_RUNNING] = "running",
[IIO_MOD_JOGGING] = "jogging",
[IIO_MOD_WALKING] = "walking",
[IIO_MOD_STILL] = "still",
[IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
};
/* relies on pairs of these shared then separate */
@ -113,6 +123,11 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain",
[IIO_CHAN_INFO_HYSTERESIS] = "hysteresis",
[IIO_CHAN_INFO_INT_TIME] = "integration_time",
[IIO_CHAN_INFO_ENABLE] = "en",
[IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight",
[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
[IIO_CHAN_INFO_DEBOUNCE_COUNT] = "debounce_count",
[IIO_CHAN_INFO_DEBOUNCE_TIME] = "debounce_time",
};
/**
@ -1035,7 +1050,6 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)
if (!ptr)
return NULL;
/* use raw alloc_dr for kmalloc caller tracing */
iio_dev = iio_device_alloc(sizeof_priv);
if (iio_dev) {
*ptr = iio_dev;
@ -1127,6 +1141,29 @@ static const struct file_operations iio_buffer_fileops = {
.compat_ioctl = iio_ioctl,
};
static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
{
int i, j;
const struct iio_chan_spec *channels = indio_dev->channels;
if (!(indio_dev->modes & INDIO_ALL_BUFFER_MODES))
return 0;
for (i = 0; i < indio_dev->num_channels - 1; i++) {
if (channels[i].scan_index < 0)
continue;
for (j = i + 1; j < indio_dev->num_channels; j++)
if (channels[i].scan_index == channels[j].scan_index) {
dev_err(&indio_dev->dev,
"Duplicate scan index %d\n",
channels[i].scan_index);
return -EINVAL;
}
}
return 0;
}
static const struct iio_buffer_setup_ops noop_ring_setup_ops;
/**
@ -1141,6 +1178,10 @@ int iio_device_register(struct iio_dev *indio_dev)
if (!indio_dev->dev.of_node && indio_dev->dev.parent)
indio_dev->dev.of_node = indio_dev->dev.parent->of_node;
ret = iio_check_unique_scan_index(indio_dev);
if (ret < 0)
return ret;
/* configure elements for the chrdev */
indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
@ -1150,11 +1191,19 @@ int iio_device_register(struct iio_dev *indio_dev)
"Failed to register debugfs interfaces\n");
return ret;
}
ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
if (ret) {
dev_err(indio_dev->dev.parent,
"Failed to create buffer sysfs interfaces\n");
goto error_unreg_debugfs;
}
ret = iio_device_register_sysfs(indio_dev);
if (ret) {
dev_err(indio_dev->dev.parent,
"Failed to register sysfs interfaces\n");
goto error_unreg_debugfs;
goto error_buffer_free_sysfs;
}
ret = iio_device_register_eventset(indio_dev);
if (ret) {
@ -1187,6 +1236,8 @@ error_unreg_eventset:
iio_device_unregister_eventset(indio_dev);
error_free_sysfs:
iio_device_unregister_sysfs(indio_dev);
error_buffer_free_sysfs:
iio_buffer_free_sysfs_and_mask(indio_dev);
error_unreg_debugfs:
iio_device_unregister_debugfs(indio_dev);
return ret;
@ -1215,6 +1266,8 @@ void iio_device_unregister(struct iio_dev *indio_dev)
iio_buffer_wakeup_poll(indio_dev);
mutex_unlock(&indio_dev->info_exist_lock);
iio_buffer_free_sysfs_and_mask(indio_dev);
}
EXPORT_SYMBOL(iio_device_unregister);

View file

@ -197,6 +197,7 @@ static const char * const iio_ev_type_text[] = {
[IIO_EV_TYPE_ROC] = "roc",
[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
[IIO_EV_TYPE_CHANGE] = "change",
};
static const char * const iio_ev_dir_text[] = {
@ -327,9 +328,15 @@ static int iio_device_add_event(struct iio_dev *indio_dev,
for_each_set_bit(i, mask, sizeof(*mask)*8) {
if (i >= ARRAY_SIZE(iio_ev_info_text))
return -EINVAL;
postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
iio_ev_type_text[type], iio_ev_dir_text[dir],
iio_ev_info_text[i]);
if (dir != IIO_EV_DIR_NONE)
postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
iio_ev_type_text[type],
iio_ev_dir_text[dir],
iio_ev_info_text[i]);
else
postfix = kasprintf(GFP_KERNEL, "%s_%s",
iio_ev_type_text[type],
iio_ev_info_text[i]);
if (postfix == NULL)
return -ENOMEM;
@ -404,7 +411,7 @@ static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev)
{
int j, ret, attrcount = 0;
/* Dynically created from the channels array */
/* Dynamically created from the channels array */
for (j = 0; j < indio_dev->num_channels; j++) {
ret = iio_device_add_event_sysfs(indio_dev,
&indio_dev->channels[j]);

View file

@ -32,7 +32,7 @@ static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
*
* This function combines some common tasks which will normally be performed
* when setting up a triggered buffer. It will allocate the buffer and the
* pollfunc, as well as register the buffer with the IIO core.
* pollfunc.
*
* Before calling this function the indio_dev structure should already be
* completely initialized, but not yet registered. In practice this means that
@ -49,7 +49,7 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
struct iio_buffer *buffer;
int ret;
buffer = iio_kfifo_allocate(indio_dev);
buffer = iio_kfifo_allocate();
if (!buffer) {
ret = -ENOMEM;
goto error_ret;
@ -78,16 +78,8 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
/* Flag that polled ring buffering is possible */
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
ret = iio_buffer_register(indio_dev,
indio_dev->channels,
indio_dev->num_channels);
if (ret)
goto error_dealloc_pollfunc;
return 0;
error_dealloc_pollfunc:
iio_dealloc_pollfunc(indio_dev->pollfunc);
error_kfifo_free:
iio_kfifo_free(indio_dev->buffer);
error_ret:
@ -101,7 +93,6 @@ EXPORT_SYMBOL(iio_triggered_buffer_setup);
*/
void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev)
{
iio_buffer_unregister(indio_dev);
iio_dealloc_pollfunc(indio_dev->pollfunc);
iio_kfifo_free(indio_dev->buffer);
}

View file

@ -116,8 +116,11 @@ static int __of_iio_simple_xlate(struct iio_dev *indio_dev,
if (!iiospec->args_count)
return 0;
if (iiospec->args[0] >= indio_dev->num_channels)
if (iiospec->args[0] >= indio_dev->num_channels) {
dev_err(&indio_dev->dev, "invalid channel index %u\n",
iiospec->args[0]);
return -EINVAL;
}
return iiospec->args[0];
}
@ -634,3 +637,28 @@ err_unlock:
return ret;
}
EXPORT_SYMBOL_GPL(iio_get_channel_type);
static int iio_channel_write(struct iio_channel *chan, int val, int val2,
enum iio_chan_info_enum info)
{
return chan->indio_dev->info->write_raw(chan->indio_dev,
chan->channel, val, val2, info);
}
int iio_write_channel_raw(struct iio_channel *chan, int val)
{
int ret;
mutex_lock(&chan->indio_dev->info_exist_lock);
if (chan->indio_dev->info == NULL) {
ret = -ENODEV;
goto err_unlock;
}
ret = iio_channel_write(chan, val, 0, IIO_CHAN_INFO_RAW);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
return ret;
}
EXPORT_SYMBOL_GPL(iio_write_channel_raw);

View file

@ -47,30 +47,6 @@ static int iio_request_update_kfifo(struct iio_buffer *r)
return ret;
}
static int iio_get_length_kfifo(struct iio_buffer *r)
{
return r->length;
}
static IIO_BUFFER_ENABLE_ATTR;
static IIO_BUFFER_LENGTH_ATTR;
static struct attribute *iio_kfifo_attributes[] = {
&dev_attr_length.attr,
&dev_attr_enable.attr,
NULL,
};
static struct attribute_group iio_kfifo_attribute_group = {
.attrs = iio_kfifo_attributes,
.name = "buffer",
};
static int iio_get_bytes_per_datum_kfifo(struct iio_buffer *r)
{
return r->bytes_per_datum;
}
static int iio_mark_update_needed_kfifo(struct iio_buffer *r)
{
struct iio_kfifo *kf = iio_to_kfifo(r);
@ -159,26 +135,25 @@ static const struct iio_buffer_access_funcs kfifo_access_funcs = {
.read_first_n = &iio_read_first_n_kfifo,
.data_available = iio_kfifo_buf_data_available,
.request_update = &iio_request_update_kfifo,
.get_bytes_per_datum = &iio_get_bytes_per_datum_kfifo,
.set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
.get_length = &iio_get_length_kfifo,
.set_length = &iio_set_length_kfifo,
.release = &iio_kfifo_buffer_release,
};
struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev)
struct iio_buffer *iio_kfifo_allocate(void)
{
struct iio_kfifo *kf;
kf = kzalloc(sizeof *kf, GFP_KERNEL);
kf = kzalloc(sizeof(*kf), GFP_KERNEL);
if (!kf)
return NULL;
kf->update_needed = true;
iio_buffer_init(&kf->buffer);
kf->buffer.attrs = &iio_kfifo_attribute_group;
kf->buffer.access = &kfifo_access_funcs;
kf->buffer.length = 2;
mutex_init(&kf->user_lock);
return &kf->buffer;
}
EXPORT_SYMBOL(iio_kfifo_allocate);
@ -189,4 +164,58 @@ void iio_kfifo_free(struct iio_buffer *r)
}
EXPORT_SYMBOL(iio_kfifo_free);
static void devm_iio_kfifo_release(struct device *dev, void *res)
{
iio_kfifo_free(*(struct iio_buffer **)res);
}
static int devm_iio_kfifo_match(struct device *dev, void *res, void *data)
{
struct iio_buffer **r = res;
if (WARN_ON(!r || !*r))
return 0;
return *r == data;
}
/**
* devm_iio_fifo_allocate - Resource-managed iio_kfifo_allocate()
* @dev: Device to allocate kfifo buffer for
*
* RETURNS:
* Pointer to allocated iio_buffer on success, NULL on failure.
*/
struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev)
{
struct iio_buffer **ptr, *r;
ptr = devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return NULL;
r = iio_kfifo_allocate();
if (r) {
*ptr = r;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return r;
}
EXPORT_SYMBOL(devm_iio_kfifo_allocate);
/**
* devm_iio_fifo_free - Resource-managed iio_kfifo_free()
* @dev: Device the buffer belongs to
* @r: The buffer associated with the device
*/
void devm_iio_kfifo_free(struct device *dev, struct iio_buffer *r)
{
WARN_ON(devres_release(dev, devm_iio_kfifo_release,
devm_iio_kfifo_match, r));
}
EXPORT_SYMBOL(devm_iio_kfifo_free);
MODULE_LICENSE("GPL");

View file

@ -48,6 +48,17 @@ config CM32181
To compile this driver as a module, choose M here:
the module will be called cm32181.
config CM3232
depends on I2C
tristate "CM3232 ambient light sensor"
help
Say Y here if you use cm3232.
This option enables ambient light sensor using
Capella Microsystems cm3232 device driver.
To compile this driver as a module, choose M here:
the module will be called cm3232.
config CM36651
depends on I2C
tristate "CM36651 driver"
@ -95,6 +106,9 @@ config HID_SENSOR_ALS
Say yes here to build support for the HID SENSOR
Ambient light sensor.
To compile this driver as a module, choose M here: the
module will be called hid-sensor-als.
config HID_SENSOR_PROX
depends on HID_SENSOR_HUB
select IIO_BUFFER
@ -109,6 +123,16 @@ config HID_SENSOR_PROX
To compile this driver as a module, choose M here: the
module will be called hid-sensor-prox.
config JSA1212
tristate "JSA1212 ALS and proximity sensor driver"
depends on I2C
help
Say Y here if you want to build a IIO driver for JSA1212
proximity & ALS sensor device.
To compile this driver as a module, choose M here:
the module will be called jsa1212.
config SENSORS_LM3533
tristate "LM3533 ambient light sensor"
depends on MFD_LM3533

View file

@ -7,11 +7,13 @@ obj-$(CONFIG_ADJD_S311) += adjd_s311.o
obj-$(CONFIG_AL3320A) += al3320a.o
obj-$(CONFIG_APDS9300) += apds9300.o
obj-$(CONFIG_CM32181) += cm32181.o
obj-$(CONFIG_CM3232) += cm3232.o
obj-$(CONFIG_CM36651) += cm36651.o
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
obj-$(CONFIG_ISL29125) += isl29125.o
obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_LTR501) += ltr501.o
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o

View file

@ -169,7 +169,7 @@ static int cm32181_write_als_it(struct cm32181_chip *cm32181, int val)
* @cm32181: pointer of struct cm32181.
*
* Convert sensor raw data to lux. It depends on integration
* time and claibscale variable.
* time and calibscale variable.
*
* Return: Positive value is lux, otherwise is error code.
*/

403
drivers/iio/light/cm3232.c Normal file
View file

@ -0,0 +1,403 @@
/*
* CM3232 Ambient Light Sensor
*
* Copyright (C) 2014-2015 Capella Microsystems Inc.
* Author: Kevin Tsai <ktsai@capellamicro.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.
*
* IIO driver for CM3232 (7-bit I2C slave address 0x10).
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/init.h>
/* Registers Address */
#define CM3232_REG_ADDR_CMD 0x00
#define CM3232_REG_ADDR_ALS 0x50
#define CM3232_REG_ADDR_ID 0x53
#define CM3232_CMD_ALS_DISABLE BIT(0)
#define CM3232_CMD_ALS_IT_SHIFT 2
#define CM3232_CMD_ALS_IT_MASK (BIT(2) | BIT(3) | BIT(4))
#define CM3232_CMD_ALS_IT_DEFAULT (0x01 << CM3232_CMD_ALS_IT_SHIFT)
#define CM3232_CMD_ALS_RESET BIT(6)
#define CM3232_CMD_DEFAULT CM3232_CMD_ALS_IT_DEFAULT
#define CM3232_HW_ID 0x32
#define CM3232_CALIBSCALE_DEFAULT 100000
#define CM3232_CALIBSCALE_RESOLUTION 100000
#define CM3232_MLUX_PER_LUX 1000
#define CM3232_MLUX_PER_BIT_DEFAULT 64
#define CM3232_MLUX_PER_BIT_BASE_IT 100000
static const struct {
int val;
int val2;
u8 it;
} cm3232_als_it_scales[] = {
{0, 100000, 0}, /* 0.100000 */
{0, 200000, 1}, /* 0.200000 */
{0, 400000, 2}, /* 0.400000 */
{0, 800000, 3}, /* 0.800000 */
{1, 600000, 4}, /* 1.600000 */
{3, 200000, 5}, /* 3.200000 */
};
struct cm3232_als_info {
u8 regs_cmd_default;
u8 hw_id;
int calibscale;
int mlux_per_bit;
int mlux_per_bit_base_it;
};
static struct cm3232_als_info cm3232_als_info_default = {
.regs_cmd_default = CM3232_CMD_DEFAULT,
.hw_id = CM3232_HW_ID,
.calibscale = CM3232_CALIBSCALE_DEFAULT,
.mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT,
.mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT,
};
struct cm3232_chip {
struct i2c_client *client;
struct cm3232_als_info *als_info;
u8 regs_cmd;
u16 regs_als;
};
/**
* cm3232_reg_init() - Initialize CM3232
* @chip: pointer of struct cm3232_chip.
*
* Check and initialize CM3232 ambient light sensor.
*
* Return: 0 for success; otherwise for error code.
*/
static int cm3232_reg_init(struct cm3232_chip *chip)
{
struct i2c_client *client = chip->client;
s32 ret;
chip->als_info = &cm3232_als_info_default;
/* Identify device */
ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID);
if (ret < 0) {
dev_err(&chip->client->dev, "Error reading addr_id\n");
return ret;
}
if ((ret & 0xFF) != chip->als_info->hw_id)
return -ENODEV;
/* Disable and reset device */
chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET;
ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
chip->regs_cmd);
if (ret < 0) {
dev_err(&chip->client->dev, "Error writing reg_cmd\n");
return ret;
}
/* Register default value */
chip->regs_cmd = chip->als_info->regs_cmd_default;
/* Configure register */
ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
chip->regs_cmd);
if (ret < 0)
dev_err(&chip->client->dev, "Error writing reg_cmd\n");
return 0;
}
/**
* cm3232_read_als_it() - Get sensor integration time
* @chip: pointer of struct cm3232_chip
* @val: pointer of int to load the integration (sec).
* @val2: pointer of int to load the integration time (microsecond).
*
* Report the current integration time.
*
* Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
*/
static int cm3232_read_als_it(struct cm3232_chip *chip, int *val, int *val2)
{
u16 als_it;
int i;
als_it = chip->regs_cmd;
als_it &= CM3232_CMD_ALS_IT_MASK;
als_it >>= CM3232_CMD_ALS_IT_SHIFT;
for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
if (als_it == cm3232_als_it_scales[i].it) {
*val = cm3232_als_it_scales[i].val;
*val2 = cm3232_als_it_scales[i].val2;
return IIO_VAL_INT_PLUS_MICRO;
}
}
return -EINVAL;
}
/**
* cm3232_write_als_it() - Write sensor integration time
* @chip: pointer of struct cm3232_chip.
* @val: integration time in second.
* @val2: integration time in microsecond.
*
* Convert integration time to sensor value.
*
* Return: i2c_smbus_write_byte_data command return value.
*/
static int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2)
{
struct i2c_client *client = chip->client;
u16 als_it, cmd;
int i;
s32 ret;
for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
if (val == cm3232_als_it_scales[i].val &&
val2 == cm3232_als_it_scales[i].val2) {
als_it = cm3232_als_it_scales[i].it;
als_it <<= CM3232_CMD_ALS_IT_SHIFT;
cmd = chip->regs_cmd & ~CM3232_CMD_ALS_IT_MASK;
cmd |= als_it;
ret = i2c_smbus_write_byte_data(client,
CM3232_REG_ADDR_CMD,
cmd);
if (ret < 0)
return ret;
chip->regs_cmd = cmd;
return 0;
}
}
return -EINVAL;
}
/**
* cm3232_get_lux() - report current lux value
* @chip: pointer of struct cm3232_chip.
*
* Convert sensor data to lux. It depends on integration
* time and calibscale variable.
*
* Return: Zero or positive value is lux, otherwise error code.
*/
static int cm3232_get_lux(struct cm3232_chip *chip)
{
struct i2c_client *client = chip->client;
struct cm3232_als_info *als_info = chip->als_info;
int ret;
int val, val2;
int als_it;
u64 lux;
/* Calculate mlux per bit based on als_it */
ret = cm3232_read_als_it(chip, &val, &val2);
if (ret < 0)
return -EINVAL;
als_it = val * 1000000 + val2;
lux = (__force u64)als_info->mlux_per_bit;
lux *= als_info->mlux_per_bit_base_it;
lux = div_u64(lux, als_it);
ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ALS);
if (ret < 0) {
dev_err(&client->dev, "Error reading reg_addr_als\n");
return ret;
}
chip->regs_als = (u16)ret;
lux *= chip->regs_als;
lux *= als_info->calibscale;
lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION);
lux = div_u64(lux, CM3232_MLUX_PER_LUX);
if (lux > 0xFFFF)
lux = 0xFFFF;
return (int)lux;
}
static int cm3232_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct cm3232_chip *chip = iio_priv(indio_dev);
struct cm3232_als_info *als_info = chip->als_info;
int ret;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
ret = cm3232_get_lux(chip);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBSCALE:
*val = als_info->calibscale;
return IIO_VAL_INT;
case IIO_CHAN_INFO_INT_TIME:
return cm3232_read_als_it(chip, val, val2);
}
return -EINVAL;
}
static int cm3232_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct cm3232_chip *chip = iio_priv(indio_dev);
struct cm3232_als_info *als_info = chip->als_info;
switch (mask) {
case IIO_CHAN_INFO_CALIBSCALE:
als_info->calibscale = val;
return 0;
case IIO_CHAN_INFO_INT_TIME:
return cm3232_write_als_it(chip, val, val2);
}
return -EINVAL;
}
/**
* cm3232_get_it_available() - Get available ALS IT value
* @dev: pointer of struct device.
* @attr: pointer of struct device_attribute.
* @buf: pointer of return string buffer.
*
* Display the available integration time in second.
*
* Return: string length.
*/
static ssize_t cm3232_get_it_available(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i, len;
for (i = 0, len = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
cm3232_als_it_scales[i].val,
cm3232_als_it_scales[i].val2);
return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");
}
static const struct iio_chan_spec cm3232_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate =
BIT(IIO_CHAN_INFO_PROCESSED) |
BIT(IIO_CHAN_INFO_CALIBSCALE) |
BIT(IIO_CHAN_INFO_INT_TIME),
}
};
static IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
S_IRUGO, cm3232_get_it_available, NULL, 0);
static struct attribute *cm3232_attributes[] = {
&iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
NULL,
};
static const struct attribute_group cm3232_attribute_group = {
.attrs = cm3232_attributes
};
static const struct iio_info cm3232_info = {
.driver_module = THIS_MODULE,
.read_raw = &cm3232_read_raw,
.write_raw = &cm3232_write_raw,
.attrs = &cm3232_attribute_group,
};
static int cm3232_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct cm3232_chip *chip;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
if (!indio_dev)
return -ENOMEM;
chip = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
chip->client = client;
indio_dev->dev.parent = &client->dev;
indio_dev->channels = cm3232_channels;
indio_dev->num_channels = ARRAY_SIZE(cm3232_channels);
indio_dev->info = &cm3232_info;
indio_dev->name = id->name;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = cm3232_reg_init(chip);
if (ret) {
dev_err(&client->dev,
"%s: register init failed\n",
__func__);
return ret;
}
return iio_device_register(indio_dev);
}
static int cm3232_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
CM3232_CMD_ALS_DISABLE);
iio_device_unregister(indio_dev);
return 0;
}
static const struct i2c_device_id cm3232_id[] = {
{"cm3232", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, cm3232_id);
static const struct of_device_id cm3232_of_match[] = {
{.compatible = "capella,cm3232"},
{}
};
static struct i2c_driver cm3232_driver = {
.driver = {
.name = "cm3232",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(cm3232_of_match),
},
.id_table = cm3232_id,
.probe = cm3232_probe,
.remove = cm3232_remove,
};
module_i2c_driver(cm3232_driver);
MODULE_AUTHOR("Kevin Tsai <[email protected]>");
MODULE_DESCRIPTION("CM3232 ambient light sensor driver");
MODULE_LICENSE("GPL");

View file

@ -80,7 +80,6 @@ static int als_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
@ -97,15 +96,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
break;
}
if (report_id >= 0) {
poll_value = hid_sensor_read_poll_value(
&als_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&als_state->common_attributes,
true);
msleep_interruptible(poll_value * 2);
*val = sensor_hub_input_attr_get_raw_value(
als_state->common_attributes.hsdev,
HID_USAGE_SENSOR_ALS, address,
@ -381,6 +373,7 @@ static struct platform_driver hid_als_platform_driver = {
.id_table = hid_als_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_als_probe,
.remove = hid_als_remove,

View file

@ -75,7 +75,6 @@ static int prox_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
@ -92,16 +91,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
break;
}
if (report_id >= 0) {
poll_value = hid_sensor_read_poll_value(
&prox_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&prox_state->common_attributes,
true);
msleep_interruptible(poll_value * 2);
*val = sensor_hub_input_attr_get_raw_value(
prox_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PROX, address,
@ -373,6 +364,7 @@ static struct platform_driver hid_prox_platform_driver = {
.id_table = hid_prox_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_prox_probe,
.remove = hid_prox_remove,

471
drivers/iio/light/jsa1212.c Normal file
View file

@ -0,0 +1,471 @@
/*
* JSA1212 Ambient Light & Proximity Sensor Driver
*
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* JSA1212 I2C slave address: 0x44(ADDR tied to GND), 0x45(ADDR tied to VDD)
*
* TODO: Interrupt support, thresholds, range support.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/acpi.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
/* JSA1212 reg address */
#define JSA1212_CONF_REG 0x01
#define JSA1212_INT_REG 0x02
#define JSA1212_PXS_LT_REG 0x03
#define JSA1212_PXS_HT_REG 0x04
#define JSA1212_ALS_TH1_REG 0x05
#define JSA1212_ALS_TH2_REG 0x06
#define JSA1212_ALS_TH3_REG 0x07
#define JSA1212_PXS_DATA_REG 0x08
#define JSA1212_ALS_DT1_REG 0x09
#define JSA1212_ALS_DT2_REG 0x0A
#define JSA1212_ALS_RNG_REG 0x0B
#define JSA1212_MAX_REG 0x0C
/* JSA1212 reg masks */
#define JSA1212_CONF_MASK 0xFF
#define JSA1212_INT_MASK 0xFF
#define JSA1212_PXS_LT_MASK 0xFF
#define JSA1212_PXS_HT_MASK 0xFF
#define JSA1212_ALS_TH1_MASK 0xFF
#define JSA1212_ALS_TH2_LT_MASK 0x0F
#define JSA1212_ALS_TH2_HT_MASK 0xF0
#define JSA1212_ALS_TH3_MASK 0xFF
#define JSA1212_PXS_DATA_MASK 0xFF
#define JSA1212_ALS_DATA_MASK 0x0FFF
#define JSA1212_ALS_DT1_MASK 0xFF
#define JSA1212_ALS_DT2_MASK 0x0F
#define JSA1212_ALS_RNG_MASK 0x07
/* JSA1212 CONF REG bits */
#define JSA1212_CONF_PXS_MASK 0x80
#define JSA1212_CONF_PXS_ENABLE 0x80
#define JSA1212_CONF_PXS_DISABLE 0x00
#define JSA1212_CONF_ALS_MASK 0x04
#define JSA1212_CONF_ALS_ENABLE 0x04
#define JSA1212_CONF_ALS_DISABLE 0x00
#define JSA1212_CONF_IRDR_MASK 0x08
/* Proxmity sensing IRDR current sink settings */
#define JSA1212_CONF_IRDR_200MA 0x08
#define JSA1212_CONF_IRDR_100MA 0x00
#define JSA1212_CONF_PXS_SLP_MASK 0x70
#define JSA1212_CONF_PXS_SLP_0MS 0x70
#define JSA1212_CONF_PXS_SLP_12MS 0x60
#define JSA1212_CONF_PXS_SLP_50MS 0x50
#define JSA1212_CONF_PXS_SLP_75MS 0x40
#define JSA1212_CONF_PXS_SLP_100MS 0x30
#define JSA1212_CONF_PXS_SLP_200MS 0x20
#define JSA1212_CONF_PXS_SLP_400MS 0x10
#define JSA1212_CONF_PXS_SLP_800MS 0x00
/* JSA1212 INT REG bits */
#define JSA1212_INT_CTRL_MASK 0x01
#define JSA1212_INT_CTRL_EITHER 0x00
#define JSA1212_INT_CTRL_BOTH 0x01
#define JSA1212_INT_ALS_PRST_MASK 0x06
#define JSA1212_INT_ALS_PRST_1CONV 0x00
#define JSA1212_INT_ALS_PRST_4CONV 0x02
#define JSA1212_INT_ALS_PRST_8CONV 0x04
#define JSA1212_INT_ALS_PRST_16CONV 0x06
#define JSA1212_INT_ALS_FLAG_MASK 0x08
#define JSA1212_INT_ALS_FLAG_CLR 0x00
#define JSA1212_INT_PXS_PRST_MASK 0x60
#define JSA1212_INT_PXS_PRST_1CONV 0x00
#define JSA1212_INT_PXS_PRST_4CONV 0x20
#define JSA1212_INT_PXS_PRST_8CONV 0x40
#define JSA1212_INT_PXS_PRST_16CONV 0x60
#define JSA1212_INT_PXS_FLAG_MASK 0x80
#define JSA1212_INT_PXS_FLAG_CLR 0x00
/* JSA1212 ALS RNG REG bits */
#define JSA1212_ALS_RNG_0_2048 0x00
#define JSA1212_ALS_RNG_0_1024 0x01
#define JSA1212_ALS_RNG_0_512 0x02
#define JSA1212_ALS_RNG_0_256 0x03
#define JSA1212_ALS_RNG_0_128 0x04
/* JSA1212 INT threshold range */
#define JSA1212_ALS_TH_MIN 0x0000
#define JSA1212_ALS_TH_MAX 0x0FFF
#define JSA1212_PXS_TH_MIN 0x00
#define JSA1212_PXS_TH_MAX 0xFF
#define JSA1212_ALS_DELAY_MS 200
#define JSA1212_PXS_DELAY_MS 100
#define JSA1212_DRIVER_NAME "jsa1212"
#define JSA1212_REGMAP_NAME "jsa1212_regmap"
enum jsa1212_op_mode {
JSA1212_OPMODE_ALS_EN,
JSA1212_OPMODE_PXS_EN,
};
struct jsa1212_data {
struct i2c_client *client;
struct mutex lock;
u8 als_rng_idx;
bool als_en; /* ALS enable status */
bool pxs_en; /* proximity enable status */
struct regmap *regmap;
};
/* ALS range idx to val mapping */
static const int jsa1212_als_range_val[] = {2048, 1024, 512, 256, 128,
128, 128, 128};
/* Enables or disables ALS function based on status */
static int jsa1212_als_enable(struct jsa1212_data *data, u8 status)
{
int ret;
ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
JSA1212_CONF_ALS_MASK,
status);
if (ret < 0)
return ret;
data->als_en = !!status;
return 0;
}
/* Enables or disables PXS function based on status */
static int jsa1212_pxs_enable(struct jsa1212_data *data, u8 status)
{
int ret;
ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
JSA1212_CONF_PXS_MASK,
status);
if (ret < 0)
return ret;
data->pxs_en = !!status;
return 0;
}
static int jsa1212_read_als_data(struct jsa1212_data *data,
unsigned int *val)
{
int ret;
__le16 als_data;
ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE);
if (ret < 0)
return ret;
/* Delay for data output */
msleep(JSA1212_ALS_DELAY_MS);
/* Read 12 bit data */
ret = regmap_bulk_read(data->regmap, JSA1212_ALS_DT1_REG, &als_data, 2);
if (ret < 0) {
dev_err(&data->client->dev, "als data read err\n");
goto als_data_read_err;
}
*val = le16_to_cpu(als_data);
als_data_read_err:
return jsa1212_als_enable(data, JSA1212_CONF_ALS_DISABLE);
}
static int jsa1212_read_pxs_data(struct jsa1212_data *data,
unsigned int *val)
{
int ret;
unsigned int pxs_data;
ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE);
if (ret < 0)
return ret;
/* Delay for data output */
msleep(JSA1212_PXS_DELAY_MS);
/* Read out all data */
ret = regmap_read(data->regmap, JSA1212_PXS_DATA_REG, &pxs_data);
if (ret < 0) {
dev_err(&data->client->dev, "pxs data read err\n");
goto pxs_data_read_err;
}
*val = pxs_data & JSA1212_PXS_DATA_MASK;
pxs_data_read_err:
return jsa1212_pxs_enable(data, JSA1212_CONF_PXS_DISABLE);
}
static int jsa1212_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
int ret;
struct jsa1212_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->lock);
switch (chan->type) {
case IIO_LIGHT:
ret = jsa1212_read_als_data(data, val);
break;
case IIO_PROXIMITY:
ret = jsa1212_read_pxs_data(data, val);
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&data->lock);
return ret < 0 ? ret : IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_LIGHT:
*val = jsa1212_als_range_val[data->als_rng_idx];
*val2 = BIT(12); /* Max 12 bit value */
return IIO_VAL_FRACTIONAL;
default:
break;
}
break;
default:
break;
}
return -EINVAL;
}
static const struct iio_chan_spec jsa1212_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
},
{
.type = IIO_PROXIMITY,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}
};
static const struct iio_info jsa1212_info = {
.driver_module = THIS_MODULE,
.read_raw = &jsa1212_read_raw,
};
static int jsa1212_chip_init(struct jsa1212_data *data)
{
int ret;
ret = regmap_write(data->regmap, JSA1212_CONF_REG,
(JSA1212_CONF_PXS_SLP_50MS |
JSA1212_CONF_IRDR_200MA));
if (ret < 0)
return ret;
ret = regmap_write(data->regmap, JSA1212_INT_REG,
JSA1212_INT_ALS_PRST_4CONV);
if (ret < 0)
return ret;
data->als_rng_idx = JSA1212_ALS_RNG_0_2048;
return 0;
}
static bool jsa1212_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case JSA1212_PXS_DATA_REG:
case JSA1212_ALS_DT1_REG:
case JSA1212_ALS_DT2_REG:
case JSA1212_INT_REG:
return true;
default:
return false;
}
}
static struct regmap_config jsa1212_regmap_config = {
.name = JSA1212_REGMAP_NAME,
.reg_bits = 8,
.val_bits = 8,
.max_register = JSA1212_MAX_REG,
.cache_type = REGCACHE_RBTREE,
.volatile_reg = jsa1212_is_volatile_reg,
};
static int jsa1212_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct jsa1212_data *data;
struct iio_dev *indio_dev;
struct regmap *regmap;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &jsa1212_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Regmap initialization failed.\n");
return PTR_ERR(regmap);
}
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->regmap = regmap;
mutex_init(&data->lock);
ret = jsa1212_chip_init(data);
if (ret < 0)
return ret;
indio_dev->dev.parent = &client->dev;
indio_dev->channels = jsa1212_channels;
indio_dev->num_channels = ARRAY_SIZE(jsa1212_channels);
indio_dev->name = JSA1212_DRIVER_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &jsa1212_info;
ret = iio_device_register(indio_dev);
if (ret < 0)
dev_err(&client->dev, "%s: register device failed\n", __func__);
return ret;
}
/* power off the device */
static int jsa1212_power_off(struct jsa1212_data *data)
{
int ret;
mutex_lock(&data->lock);
ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
JSA1212_CONF_ALS_MASK |
JSA1212_CONF_PXS_MASK,
JSA1212_CONF_ALS_DISABLE |
JSA1212_CONF_PXS_DISABLE);
if (ret < 0)
dev_err(&data->client->dev, "power off cmd failed\n");
mutex_unlock(&data->lock);
return ret;
}
static int jsa1212_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct jsa1212_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
return jsa1212_power_off(data);
}
#ifdef CONFIG_PM_SLEEP
static int jsa1212_suspend(struct device *dev)
{
struct jsa1212_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
return jsa1212_power_off(data);
}
static int jsa1212_resume(struct device *dev)
{
int ret = 0;
struct jsa1212_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
mutex_lock(&data->lock);
if (data->als_en) {
ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE);
if (ret < 0) {
dev_err(dev, "als resume failed\n");
goto unlock_and_ret;
}
}
if (data->pxs_en) {
ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE);
if (ret < 0)
dev_err(dev, "pxs resume failed\n");
}
unlock_and_ret:
mutex_unlock(&data->lock);
return ret;
}
static SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend, jsa1212_resume);
#define JSA1212_PM_OPS (&jsa1212_pm_ops)
#else
#define JSA1212_PM_OPS NULL
#endif
static const struct acpi_device_id jsa1212_acpi_match[] = {
{"JSA1212", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, jsa1212_acpi_match);
static const struct i2c_device_id jsa1212_id[] = {
{ JSA1212_DRIVER_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, jsa1212_id);
static struct i2c_driver jsa1212_driver = {
.driver = {
.name = JSA1212_DRIVER_NAME,
.pm = JSA1212_PM_OPS,
.owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(jsa1212_acpi_match),
},
.probe = jsa1212_probe,
.remove = jsa1212_remove,
.id_table = jsa1212_id,
};
module_i2c_driver(jsa1212_driver);
MODULE_AUTHOR("Sathya Kuppuswamy <[email protected]>");
MODULE_DESCRIPTION("JSA1212 proximity/ambient light sensor driver");
MODULE_LICENSE("GPL v2");

View file

@ -657,7 +657,7 @@ static ALS_HYSTERESIS_ATTR_RO(3);
#define ILLUMINANCE_ATTR_RO(_name) \
DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO, show_##_name, NULL)
#define ILLUMINANCE_ATTR_RW(_name) \
DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR , \
DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR, \
show_##_name, store_##_name)
/*
* ALS Zone threshold-event enable

View file

@ -149,8 +149,8 @@ static int tcs3414_read_raw(struct iio_dev *indio_dev,
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT;
*val = tcs3414_scales[i][0];
i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT;
*val = tcs3414_scales[i][0];
*val2 = tcs3414_scales[i][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_INT_TIME:

View file

@ -6,26 +6,21 @@
menu "Magnetometer sensors"
config AK8975
tristate "Asahi Kasei AK8975 3-Axis Magnetometer"
tristate "Asahi Kasei AK 3-Axis Magnetometer"
depends on I2C
depends on GPIOLIB
help
Say yes here to build support for Asahi Kasei AK8975 3-Axis
Magnetometer. This driver can also support AK8963, if i2c
device name is identified as ak8963.
Say yes here to build support for Asahi Kasei AK8975, AK8963,
AK09911 or AK09912 3-Axis Magnetometer.
To compile this driver as a module, choose M here: the module
will be called ak8975.
config AK09911
tristate "Asahi Kasei AK09911 3-axis Compass"
depends on I2C
select AK8975
help
Say yes here to build support for Asahi Kasei AK09911 3-Axis
Magnetometer.
To compile this driver as a module, choose M here: the module
will be called ak09911.
Deprecated: AK09911 is now supported by AK8975 driver.
config MAG3110
tristate "Freescale MAG3110 3-Axis Magnetometer"

View file

@ -3,7 +3,6 @@
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AK09911) += ak09911.o
obj-$(CONFIG_AK8975) += ak8975.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o

View file

@ -1,326 +0,0 @@
/*
* AK09911 3-axis compass driver
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/acpi.h>
#include <linux/iio/iio.h>
#define AK09911_REG_WIA1 0x00
#define AK09911_REG_WIA2 0x01
#define AK09911_WIA1_VALUE 0x48
#define AK09911_WIA2_VALUE 0x05
#define AK09911_REG_ST1 0x10
#define AK09911_REG_HXL 0x11
#define AK09911_REG_HXH 0x12
#define AK09911_REG_HYL 0x13
#define AK09911_REG_HYH 0x14
#define AK09911_REG_HZL 0x15
#define AK09911_REG_HZH 0x16
#define AK09911_REG_ASAX 0x60
#define AK09911_REG_ASAY 0x61
#define AK09911_REG_ASAZ 0x62
#define AK09911_REG_CNTL1 0x30
#define AK09911_REG_CNTL2 0x31
#define AK09911_REG_CNTL3 0x32
#define AK09911_MODE_SNG_MEASURE 0x01
#define AK09911_MODE_SELF_TEST 0x10
#define AK09911_MODE_FUSE_ACCESS 0x1F
#define AK09911_MODE_POWERDOWN 0x00
#define AK09911_RESET_DATA 0x01
#define AK09911_REG_CNTL1 0x30
#define AK09911_REG_CNTL2 0x31
#define AK09911_REG_CNTL3 0x32
#define AK09911_RAW_TO_GAUSS(asa) ((((asa) + 128) * 6000) / 256)
#define AK09911_MAX_CONVERSION_TIMEOUT_MS 500
#define AK09911_CONVERSION_DONE_POLL_TIME_MS 10
struct ak09911_data {
struct i2c_client *client;
struct mutex lock;
u8 asa[3];
long raw_to_gauss[3];
};
static const int ak09911_index_to_reg[] = {
AK09911_REG_HXL, AK09911_REG_HYL, AK09911_REG_HZL,
};
static int ak09911_set_mode(struct i2c_client *client, u8 mode)
{
int ret;
switch (mode) {
case AK09911_MODE_SNG_MEASURE:
case AK09911_MODE_SELF_TEST:
case AK09911_MODE_FUSE_ACCESS:
case AK09911_MODE_POWERDOWN:
ret = i2c_smbus_write_byte_data(client,
AK09911_REG_CNTL2, mode);
if (ret < 0) {
dev_err(&client->dev, "set_mode error\n");
return ret;
}
/* After mode change wait atleast 100us */
usleep_range(100, 500);
break;
default:
dev_err(&client->dev,
"%s: Unknown mode(%d).", __func__, mode);
return -EINVAL;
}
return ret;
}
/* Get Sensitivity Adjustment value */
static int ak09911_get_asa(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ak09911_data *data = iio_priv(indio_dev);
int ret;
ret = ak09911_set_mode(client, AK09911_MODE_FUSE_ACCESS);
if (ret < 0)
return ret;
/* Get asa data and store in the device data. */
ret = i2c_smbus_read_i2c_block_data(client, AK09911_REG_ASAX,
3, data->asa);
if (ret < 0) {
dev_err(&client->dev, "Not able to read asa data\n");
return ret;
}
ret = ak09911_set_mode(client, AK09911_MODE_POWERDOWN);
if (ret < 0)
return ret;
data->raw_to_gauss[0] = AK09911_RAW_TO_GAUSS(data->asa[0]);
data->raw_to_gauss[1] = AK09911_RAW_TO_GAUSS(data->asa[1]);
data->raw_to_gauss[2] = AK09911_RAW_TO_GAUSS(data->asa[2]);
return 0;
}
static int ak09911_verify_chip_id(struct i2c_client *client)
{
u8 wia_val[2];
int ret;
ret = i2c_smbus_read_i2c_block_data(client, AK09911_REG_WIA1,
2, wia_val);
if (ret < 0) {
dev_err(&client->dev, "Error reading WIA\n");
return ret;
}
dev_dbg(&client->dev, "WIA %02x %02x\n", wia_val[0], wia_val[1]);
if (wia_val[0] != AK09911_WIA1_VALUE ||
wia_val[1] != AK09911_WIA2_VALUE) {
dev_err(&client->dev, "Device ak09911 not found\n");
return -ENODEV;
}
return 0;
}
static int wait_conversion_complete_polled(struct ak09911_data *data)
{
struct i2c_client *client = data->client;
u8 read_status;
u32 timeout_ms = AK09911_MAX_CONVERSION_TIMEOUT_MS;
int ret;
/* Wait for the conversion to complete. */
while (timeout_ms) {
msleep_interruptible(AK09911_CONVERSION_DONE_POLL_TIME_MS);
ret = i2c_smbus_read_byte_data(client, AK09911_REG_ST1);
if (ret < 0) {
dev_err(&client->dev, "Error in reading ST1\n");
return ret;
}
read_status = ret & 0x01;
if (read_status)
break;
timeout_ms -= AK09911_CONVERSION_DONE_POLL_TIME_MS;
}
if (!timeout_ms) {
dev_err(&client->dev, "Conversion timeout happened\n");
return -EIO;
}
return read_status;
}
static int ak09911_read_axis(struct iio_dev *indio_dev, int index, int *val)
{
struct ak09911_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
int ret;
mutex_lock(&data->lock);
ret = ak09911_set_mode(client, AK09911_MODE_SNG_MEASURE);
if (ret < 0)
goto fn_exit;
ret = wait_conversion_complete_polled(data);
if (ret < 0)
goto fn_exit;
/* Read data */
ret = i2c_smbus_read_word_data(client, ak09911_index_to_reg[index]);
if (ret < 0) {
dev_err(&client->dev, "Read axis data fails\n");
goto fn_exit;
}
mutex_unlock(&data->lock);
/* Clamp to valid range. */
*val = sign_extend32(clamp_t(s16, ret, -8192, 8191), 13);
return IIO_VAL_INT;
fn_exit:
mutex_unlock(&data->lock);
return ret;
}
static int ak09911_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2,
long mask)
{
struct ak09911_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return ak09911_read_axis(indio_dev, chan->address, val);
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = data->raw_to_gauss[chan->address];
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
}
#define AK09911_CHANNEL(axis, index) \
{ \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.address = index, \
}
static const struct iio_chan_spec ak09911_channels[] = {
AK09911_CHANNEL(X, 0), AK09911_CHANNEL(Y, 1), AK09911_CHANNEL(Z, 2),
};
static const struct iio_info ak09911_info = {
.read_raw = &ak09911_read_raw,
.driver_module = THIS_MODULE,
};
static const struct acpi_device_id ak_acpi_match[] = {
{"AK009911", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, ak_acpi_match);
static int ak09911_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct ak09911_data *data;
const char *name;
int ret;
ret = ak09911_verify_chip_id(client);
if (ret) {
dev_err(&client->dev, "AK00911 not detected\n");
return -ENODEV;
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (indio_dev == NULL)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
mutex_init(&data->lock);
ret = ak09911_get_asa(client);
if (ret)
return ret;
if (id)
name = id->name;
else if (ACPI_HANDLE(&client->dev))
name = dev_name(&client->dev);
else
return -ENODEV;
dev_dbg(&client->dev, "Asahi compass chip %s\n", name);
indio_dev->dev.parent = &client->dev;
indio_dev->channels = ak09911_channels;
indio_dev->num_channels = ARRAY_SIZE(ak09911_channels);
indio_dev->info = &ak09911_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = name;
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id ak09911_id[] = {
{"ak09911", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ak09911_id);
static struct i2c_driver ak09911_driver = {
.driver = {
.name = "ak09911",
.acpi_match_table = ACPI_PTR(ak_acpi_match),
},
.probe = ak09911_probe,
.id_table = ak09911_id,
};
module_i2c_driver(ak09911_driver);
MODULE_AUTHOR("Srinivas Pandruvada <[email protected]>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("AK09911 Compass driver");

View file

@ -64,10 +64,10 @@
#define AK8975_REG_CNTL 0x0A
#define AK8975_REG_CNTL_MODE_SHIFT 0
#define AK8975_REG_CNTL_MODE_MASK (0xF << AK8975_REG_CNTL_MODE_SHIFT)
#define AK8975_REG_CNTL_MODE_POWER_DOWN 0
#define AK8975_REG_CNTL_MODE_ONCE 1
#define AK8975_REG_CNTL_MODE_SELF_TEST 8
#define AK8975_REG_CNTL_MODE_FUSE_ROM 0xF
#define AK8975_REG_CNTL_MODE_POWER_DOWN 0x00
#define AK8975_REG_CNTL_MODE_ONCE 0x01
#define AK8975_REG_CNTL_MODE_SELF_TEST 0x08
#define AK8975_REG_CNTL_MODE_FUSE_ROM 0x0F
#define AK8975_REG_RSVC 0x0B
#define AK8975_REG_ASTC 0x0C
@ -80,19 +80,279 @@
#define AK8975_MAX_REGS AK8975_REG_ASAZ
/*
* AK09912 Register definitions
*/
#define AK09912_REG_WIA1 0x00
#define AK09912_REG_WIA2 0x01
#define AK09912_DEVICE_ID 0x04
#define AK09911_DEVICE_ID 0x05
#define AK09911_REG_INFO1 0x02
#define AK09911_REG_INFO2 0x03
#define AK09912_REG_ST1 0x10
#define AK09912_REG_ST1_DRDY_SHIFT 0
#define AK09912_REG_ST1_DRDY_MASK (1 << AK09912_REG_ST1_DRDY_SHIFT)
#define AK09912_REG_HXL 0x11
#define AK09912_REG_HXH 0x12
#define AK09912_REG_HYL 0x13
#define AK09912_REG_HYH 0x14
#define AK09912_REG_HZL 0x15
#define AK09912_REG_HZH 0x16
#define AK09912_REG_TMPS 0x17
#define AK09912_REG_ST2 0x18
#define AK09912_REG_ST2_HOFL_SHIFT 3
#define AK09912_REG_ST2_HOFL_MASK (1 << AK09912_REG_ST2_HOFL_SHIFT)
#define AK09912_REG_CNTL1 0x30
#define AK09912_REG_CNTL2 0x31
#define AK09912_REG_CNTL_MODE_POWER_DOWN 0x00
#define AK09912_REG_CNTL_MODE_ONCE 0x01
#define AK09912_REG_CNTL_MODE_SELF_TEST 0x10
#define AK09912_REG_CNTL_MODE_FUSE_ROM 0x1F
#define AK09912_REG_CNTL2_MODE_SHIFT 0
#define AK09912_REG_CNTL2_MODE_MASK (0x1F << AK09912_REG_CNTL2_MODE_SHIFT)
#define AK09912_REG_CNTL3 0x32
#define AK09912_REG_TS1 0x33
#define AK09912_REG_TS2 0x34
#define AK09912_REG_TS3 0x35
#define AK09912_REG_I2CDIS 0x36
#define AK09912_REG_TS4 0x37
#define AK09912_REG_ASAX 0x60
#define AK09912_REG_ASAY 0x61
#define AK09912_REG_ASAZ 0x62
#define AK09912_MAX_REGS AK09912_REG_ASAZ
/*
* Miscellaneous values.
*/
#define AK8975_MAX_CONVERSION_TIMEOUT 500
#define AK8975_CONVERSION_DONE_POLL_TIME 10
#define AK8975_DATA_READY_TIMEOUT ((100*HZ)/1000)
#define RAW_TO_GAUSS_8975(asa) ((((asa) + 128) * 3000) / 256)
#define RAW_TO_GAUSS_8963(asa) ((((asa) + 128) * 6000) / 256)
/*
* Precalculate scale factor (in Gauss units) for each axis and
* store in the device data.
*
* This scale factor is axis-dependent, and is derived from 3 calibration
* factors ASA(x), ASA(y), and ASA(z).
*
* These ASA values are read from the sensor device at start of day, and
* cached in the device context struct.
*
* Adjusting the flux value with the sensitivity adjustment value should be
* done via the following formula:
*
* Hadj = H * ( ( ( (ASA-128)*0.5 ) / 128 ) + 1 )
* where H is the raw value, ASA is the sensitivity adjustment, and Hadj
* is the resultant adjusted value.
*
* We reduce the formula to:
*
* Hadj = H * (ASA + 128) / 256
*
* H is in the range of -4096 to 4095. The magnetometer has a range of
* +-1229uT. To go from the raw value to uT is:
*
* HuT = H * 1229/4096, or roughly, 3/10.
*
* Since 1uT = 0.01 gauss, our final scale factor becomes:
*
* Hadj = H * ((ASA + 128) / 256) * 3/10 * 1/100
* Hadj = H * ((ASA + 128) * 0.003) / 256
*
* Since ASA doesn't change, we cache the resultant scale factor into the
* device context in ak8975_setup().
*
* Given we use IIO_VAL_INT_PLUS_MICRO bit when displaying the scale, we
* multiply the stored scale value by 1e6.
*/
static long ak8975_raw_to_gauss(u16 data)
{
return (((long)data + 128) * 3000) / 256;
}
/*
* For AK8963 and AK09911, same calculation, but the device is less sensitive:
*
* H is in the range of +-8190. The magnetometer has a range of
* +-4912uT. To go from the raw value to uT is:
*
* HuT = H * 4912/8190, or roughly, 6/10, instead of 3/10.
*/
static long ak8963_09911_raw_to_gauss(u16 data)
{
return (((long)data + 128) * 6000) / 256;
}
/*
* For AK09912, same calculation, except the device is more sensitive:
*
* H is in the range of -32752 to 32752. The magnetometer has a range of
* +-4912uT. To go from the raw value to uT is:
*
* HuT = H * 4912/32752, or roughly, 3/20, instead of 3/10.
*/
static long ak09912_raw_to_gauss(u16 data)
{
return (((long)data + 128) * 1500) / 256;
}
/* Compatible Asahi Kasei Compass parts */
enum asahi_compass_chipset {
AK8975,
AK8963,
AK09911,
AK09912,
AK_MAX_TYPE
};
enum ak_ctrl_reg_addr {
ST1,
ST2,
CNTL,
ASA_BASE,
MAX_REGS,
REGS_END,
};
enum ak_ctrl_reg_mask {
ST1_DRDY,
ST2_HOFL,
ST2_DERR,
CNTL_MODE,
MASK_END,
};
enum ak_ctrl_mode {
POWER_DOWN,
MODE_ONCE,
SELF_TEST,
FUSE_ROM,
MODE_END,
};
struct ak_def {
enum asahi_compass_chipset type;
long (*raw_to_gauss)(u16 data);
u16 range;
u8 ctrl_regs[REGS_END];
u8 ctrl_masks[MASK_END];
u8 ctrl_modes[MODE_END];
u8 data_regs[3];
};
static struct ak_def ak_def_array[AK_MAX_TYPE] = {
{
.type = AK8975,
.raw_to_gauss = ak8975_raw_to_gauss,
.range = 4096,
.ctrl_regs = {
AK8975_REG_ST1,
AK8975_REG_ST2,
AK8975_REG_CNTL,
AK8975_REG_ASAX,
AK8975_MAX_REGS},
.ctrl_masks = {
AK8975_REG_ST1_DRDY_MASK,
AK8975_REG_ST2_HOFL_MASK,
AK8975_REG_ST2_DERR_MASK,
AK8975_REG_CNTL_MODE_MASK},
.ctrl_modes = {
AK8975_REG_CNTL_MODE_POWER_DOWN,
AK8975_REG_CNTL_MODE_ONCE,
AK8975_REG_CNTL_MODE_SELF_TEST,
AK8975_REG_CNTL_MODE_FUSE_ROM},
.data_regs = {
AK8975_REG_HXL,
AK8975_REG_HYL,
AK8975_REG_HZL},
},
{
.type = AK8963,
.raw_to_gauss = ak8963_09911_raw_to_gauss,
.range = 8190,
.ctrl_regs = {
AK8975_REG_ST1,
AK8975_REG_ST2,
AK8975_REG_CNTL,
AK8975_REG_ASAX,
AK8975_MAX_REGS},
.ctrl_masks = {
AK8975_REG_ST1_DRDY_MASK,
AK8975_REG_ST2_HOFL_MASK,
0,
AK8975_REG_CNTL_MODE_MASK},
.ctrl_modes = {
AK8975_REG_CNTL_MODE_POWER_DOWN,
AK8975_REG_CNTL_MODE_ONCE,
AK8975_REG_CNTL_MODE_SELF_TEST,
AK8975_REG_CNTL_MODE_FUSE_ROM},
.data_regs = {
AK8975_REG_HXL,
AK8975_REG_HYL,
AK8975_REG_HZL},
},
{
.type = AK09911,
.raw_to_gauss = ak8963_09911_raw_to_gauss,
.range = 8192,
.ctrl_regs = {
AK09912_REG_ST1,
AK09912_REG_ST2,
AK09912_REG_CNTL2,
AK09912_REG_ASAX,
AK09912_MAX_REGS},
.ctrl_masks = {
AK09912_REG_ST1_DRDY_MASK,
AK09912_REG_ST2_HOFL_MASK,
0,
AK09912_REG_CNTL2_MODE_MASK},
.ctrl_modes = {
AK09912_REG_CNTL_MODE_POWER_DOWN,
AK09912_REG_CNTL_MODE_ONCE,
AK09912_REG_CNTL_MODE_SELF_TEST,
AK09912_REG_CNTL_MODE_FUSE_ROM},
.data_regs = {
AK09912_REG_HXL,
AK09912_REG_HYL,
AK09912_REG_HZL},
},
{
.type = AK09912,
.raw_to_gauss = ak09912_raw_to_gauss,
.range = 32752,
.ctrl_regs = {
AK09912_REG_ST1,
AK09912_REG_ST2,
AK09912_REG_CNTL2,
AK09912_REG_ASAX,
AK09912_MAX_REGS},
.ctrl_masks = {
AK09912_REG_ST1_DRDY_MASK,
AK09912_REG_ST2_HOFL_MASK,
0,
AK09912_REG_CNTL2_MODE_MASK},
.ctrl_modes = {
AK09912_REG_CNTL_MODE_POWER_DOWN,
AK09912_REG_CNTL_MODE_ONCE,
AK09912_REG_CNTL_MODE_SELF_TEST,
AK09912_REG_CNTL_MODE_FUSE_ROM},
.data_regs = {
AK09912_REG_HXL,
AK09912_REG_HYL,
AK09912_REG_HZL},
}
};
/*
@ -100,40 +360,82 @@ enum asahi_compass_chipset {
*/
struct ak8975_data {
struct i2c_client *client;
struct ak_def *def;
struct attribute_group attrs;
struct mutex lock;
u8 asa[3];
long raw_to_gauss[3];
u8 reg_cache[AK8975_MAX_REGS];
int eoc_gpio;
int eoc_irq;
wait_queue_head_t data_ready_queue;
unsigned long flags;
enum asahi_compass_chipset chipset;
};
static const int ak8975_index_to_reg[] = {
AK8975_REG_HXL, AK8975_REG_HYL, AK8975_REG_HZL,
u8 cntl_cache;
};
/*
* Helper function to write to the I2C device's registers.
* Return 0 if the i2c device is the one we expect.
* return a negative error number otherwise
*/
static int ak8975_write_data(struct i2c_client *client,
u8 reg, u8 val, u8 mask, u8 shift)
static int ak8975_who_i_am(struct i2c_client *client,
enum asahi_compass_chipset type)
{
u8 wia_val[2];
int ret;
/*
* Signature for each device:
* Device | WIA1 | WIA2
* AK09912 | DEVICE_ID | AK09912_DEVICE_ID
* AK09911 | DEVICE_ID | AK09911_DEVICE_ID
* AK8975 | DEVICE_ID | NA
* AK8963 | DEVICE_ID | NA
*/
ret = i2c_smbus_read_i2c_block_data(client, AK09912_REG_WIA1,
2, wia_val);
if (ret < 0) {
dev_err(&client->dev, "Error reading WIA\n");
return ret;
}
if (wia_val[0] != AK8975_DEVICE_ID)
return -ENODEV;
switch (type) {
case AK8975:
case AK8963:
return 0;
case AK09911:
if (wia_val[1] == AK09911_DEVICE_ID)
return 0;
break;
case AK09912:
if (wia_val[1] == AK09912_DEVICE_ID)
return 0;
break;
default:
dev_err(&client->dev, "Type %d unknown\n", type);
}
return -ENODEV;
}
/*
* Helper function to write to CNTL register.
*/
static int ak8975_set_mode(struct ak8975_data *data, enum ak_ctrl_mode mode)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ak8975_data *data = iio_priv(indio_dev);
u8 regval;
int ret;
regval = (data->reg_cache[reg] & ~mask) | (val << shift);
ret = i2c_smbus_write_byte_data(client, reg, regval);
regval = (data->cntl_cache & ~data->def->ctrl_masks[CNTL_MODE]) |
data->def->ctrl_modes[mode];
ret = i2c_smbus_write_byte_data(data->client,
data->def->ctrl_regs[CNTL], regval);
if (ret < 0) {
dev_err(&client->dev, "Write to device fails status %x\n", ret);
return ret;
}
data->reg_cache[reg] = regval;
data->cntl_cache = regval;
/* After mode change wait atleast 100us */
usleep_range(100, 500);
return 0;
}
@ -166,8 +468,8 @@ static int ak8975_setup_irq(struct ak8975_data *data)
irq = gpio_to_irq(data->eoc_gpio);
rc = devm_request_irq(&client->dev, irq, ak8975_irq_handler,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
dev_name(&client->dev), data);
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
dev_name(&client->dev), data);
if (rc < 0) {
dev_err(&client->dev,
"irq %d request failed, (gpio %d): %d\n",
@ -191,34 +493,18 @@ static int ak8975_setup(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ak8975_data *data = iio_priv(indio_dev);
u8 device_id;
int ret;
/* Confirm that the device we're talking to is really an AK8975. */
ret = i2c_smbus_read_byte_data(client, AK8975_REG_WIA);
if (ret < 0) {
dev_err(&client->dev, "Error reading WIA\n");
return ret;
}
device_id = ret;
if (device_id != AK8975_DEVICE_ID) {
dev_err(&client->dev, "Device ak8975 not found\n");
return -ENODEV;
}
/* Write the fused rom access mode. */
ret = ak8975_write_data(client,
AK8975_REG_CNTL,
AK8975_REG_CNTL_MODE_FUSE_ROM,
AK8975_REG_CNTL_MODE_MASK,
AK8975_REG_CNTL_MODE_SHIFT);
ret = ak8975_set_mode(data, FUSE_ROM);
if (ret < 0) {
dev_err(&client->dev, "Error in setting fuse access mode\n");
return ret;
}
/* Get asa data and store in the device data. */
ret = i2c_smbus_read_i2c_block_data(client, AK8975_REG_ASAX,
ret = i2c_smbus_read_i2c_block_data(client,
data->def->ctrl_regs[ASA_BASE],
3, data->asa);
if (ret < 0) {
dev_err(&client->dev, "Not able to read asa data\n");
@ -226,13 +512,13 @@ static int ak8975_setup(struct i2c_client *client)
}
/* After reading fuse ROM data set power-down mode */
ret = ak8975_write_data(client,
AK8975_REG_CNTL,
AK8975_REG_CNTL_MODE_POWER_DOWN,
AK8975_REG_CNTL_MODE_MASK,
AK8975_REG_CNTL_MODE_SHIFT);
ret = ak8975_set_mode(data, POWER_DOWN);
if (ret < 0) {
dev_err(&client->dev, "Error in setting power-down mode\n");
return ret;
}
if (data->eoc_gpio > 0 || client->irq) {
if (data->eoc_gpio > 0 || client->irq > 0) {
ret = ak8975_setup_irq(data);
if (ret < 0) {
dev_err(&client->dev,
@ -241,61 +527,9 @@ static int ak8975_setup(struct i2c_client *client)
}
}
if (ret < 0) {
dev_err(&client->dev, "Error in setting power-down mode\n");
return ret;
}
/*
* Precalculate scale factor (in Gauss units) for each axis and
* store in the device data.
*
* This scale factor is axis-dependent, and is derived from 3 calibration
* factors ASA(x), ASA(y), and ASA(z).
*
* These ASA values are read from the sensor device at start of day, and
* cached in the device context struct.
*
* Adjusting the flux value with the sensitivity adjustment value should be
* done via the following formula:
*
* Hadj = H * ( ( ( (ASA-128)*0.5 ) / 128 ) + 1 )
*
* where H is the raw value, ASA is the sensitivity adjustment, and Hadj
* is the resultant adjusted value.
*
* We reduce the formula to:
*
* Hadj = H * (ASA + 128) / 256
*
* H is in the range of -4096 to 4095. The magnetometer has a range of
* +-1229uT. To go from the raw value to uT is:
*
* HuT = H * 1229/4096, or roughly, 3/10.
*
* Since 1uT = 0.01 gauss, our final scale factor becomes:
*
* Hadj = H * ((ASA + 128) / 256) * 3/10 * 1/100
* Hadj = H * ((ASA + 128) * 0.003) / 256
*
* Since ASA doesn't change, we cache the resultant scale factor into the
* device context in ak8975_setup().
*/
if (data->chipset == AK8963) {
/*
* H range is +-8190 and magnetometer range is +-4912.
* So HuT using the above explanation for 8975,
* 4912/8190 = ~ 6/10.
* So the Hadj should use 6/10 instead of 3/10.
*/
data->raw_to_gauss[0] = RAW_TO_GAUSS_8963(data->asa[0]);
data->raw_to_gauss[1] = RAW_TO_GAUSS_8963(data->asa[1]);
data->raw_to_gauss[2] = RAW_TO_GAUSS_8963(data->asa[2]);
} else {
data->raw_to_gauss[0] = RAW_TO_GAUSS_8975(data->asa[0]);
data->raw_to_gauss[1] = RAW_TO_GAUSS_8975(data->asa[1]);
data->raw_to_gauss[2] = RAW_TO_GAUSS_8975(data->asa[2]);
}
data->raw_to_gauss[0] = data->def->raw_to_gauss(data->asa[0]);
data->raw_to_gauss[1] = data->def->raw_to_gauss(data->asa[1]);
data->raw_to_gauss[2] = data->def->raw_to_gauss(data->asa[2]);
return 0;
}
@ -318,7 +552,7 @@ static int wait_conversion_complete_gpio(struct ak8975_data *data)
return -EINVAL;
}
ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1);
ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST1]);
if (ret < 0)
dev_err(&client->dev, "Error in reading ST1\n");
@ -335,7 +569,8 @@ static int wait_conversion_complete_polled(struct ak8975_data *data)
/* Wait for the conversion to complete. */
while (timeout_ms) {
msleep(AK8975_CONVERSION_DONE_POLL_TIME);
ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1);
ret = i2c_smbus_read_byte_data(client,
data->def->ctrl_regs[ST1]);
if (ret < 0) {
dev_err(&client->dev, "Error in reading ST1\n");
return ret;
@ -378,11 +613,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
mutex_lock(&data->lock);
/* Set up the device for taking a sample. */
ret = ak8975_write_data(client,
AK8975_REG_CNTL,
AK8975_REG_CNTL_MODE_ONCE,
AK8975_REG_CNTL_MODE_MASK,
AK8975_REG_CNTL_MODE_SHIFT);
ret = ak8975_set_mode(data, MODE_ONCE);
if (ret < 0) {
dev_err(&client->dev, "Error in setting operating mode\n");
goto exit;
@ -399,14 +630,15 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
goto exit;
/* This will be executed only for non-interrupt based waiting case */
if (ret & AK8975_REG_ST1_DRDY_MASK) {
ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST2);
if (ret & data->def->ctrl_masks[ST1_DRDY]) {
ret = i2c_smbus_read_byte_data(client,
data->def->ctrl_regs[ST2]);
if (ret < 0) {
dev_err(&client->dev, "Error in reading ST2\n");
goto exit;
}
if (ret & (AK8975_REG_ST2_DERR_MASK |
AK8975_REG_ST2_HOFL_MASK)) {
if (ret & (data->def->ctrl_masks[ST2_DERR] |
data->def->ctrl_masks[ST2_HOFL])) {
dev_err(&client->dev, "ST2 status error 0x%x\n", ret);
ret = -EINVAL;
goto exit;
@ -415,7 +647,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
/* Read the flux value from the appropriate register
(the register is specified in the iio device attributes). */
ret = i2c_smbus_read_word_data(client, ak8975_index_to_reg[index]);
ret = i2c_smbus_read_word_data(client, data->def->data_regs[index]);
if (ret < 0) {
dev_err(&client->dev, "Read axis data fails\n");
goto exit;
@ -424,7 +656,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
mutex_unlock(&data->lock);
/* Clamp to valid range. */
*val = clamp_t(s16, ret, -4096, 4095);
*val = clamp_t(s16, ret, -data->def->range, data->def->range);
return IIO_VAL_INT;
exit:
@ -473,6 +705,8 @@ static const struct acpi_device_id ak_acpi_match[] = {
{"AK8975", AK8975},
{"AK8963", AK8963},
{"INVN6500", AK8963},
{"AK09911", AK09911},
{"AK09912", AK09912},
{ },
};
MODULE_DEVICE_TABLE(acpi, ak_acpi_match);
@ -498,6 +732,7 @@ static int ak8975_probe(struct i2c_client *client,
int eoc_gpio;
int err;
const char *name = NULL;
enum asahi_compass_chipset chipset;
/* Grab and set up the supplied GPIO. */
if (client->dev.platform_data)
@ -537,42 +772,50 @@ static int ak8975_probe(struct i2c_client *client,
/* id will be NULL when enumerated via ACPI */
if (id) {
data->chipset =
(enum asahi_compass_chipset)(id->driver_data);
chipset = (enum asahi_compass_chipset)(id->driver_data);
name = id->name;
} else if (ACPI_HANDLE(&client->dev))
name = ak8975_match_acpi_device(&client->dev, &data->chipset);
name = ak8975_match_acpi_device(&client->dev, &chipset);
else
return -ENOSYS;
if (chipset >= AK_MAX_TYPE) {
dev_err(&client->dev, "AKM device type unsupported: %d\n",
chipset);
return -ENODEV;
}
data->def = &ak_def_array[chipset];
err = ak8975_who_i_am(client, data->def->type);
if (err < 0) {
dev_err(&client->dev, "Unexpected device\n");
return err;
}
dev_dbg(&client->dev, "Asahi compass chip %s\n", name);
/* Perform some basic start-of-day setup of the device. */
err = ak8975_setup(client);
if (err < 0) {
dev_err(&client->dev, "AK8975 initialization fails\n");
dev_err(&client->dev, "%s initialization fails\n", name);
return err;
}
data->client = client;
mutex_init(&data->lock);
data->eoc_gpio = eoc_gpio;
indio_dev->dev.parent = &client->dev;
indio_dev->channels = ak8975_channels;
indio_dev->num_channels = ARRAY_SIZE(ak8975_channels);
indio_dev->info = &ak8975_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = name;
err = devm_iio_device_register(&client->dev, indio_dev);
if (err < 0)
return err;
return 0;
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id ak8975_id[] = {
{"ak8975", AK8975},
{"ak8963", AK8963},
{"AK8963", AK8963},
{"ak09911", AK09911},
{"ak09912", AK09912},
{}
};
@ -581,14 +824,20 @@ MODULE_DEVICE_TABLE(i2c, ak8975_id);
static const struct of_device_id ak8975_of_match[] = {
{ .compatible = "asahi-kasei,ak8975", },
{ .compatible = "ak8975", },
{ }
{ .compatible = "asahi-kasei,ak8963", },
{ .compatible = "ak8963", },
{ .compatible = "asahi-kasei,ak09911", },
{ .compatible = "ak09911", },
{ .compatible = "asahi-kasei,ak09912", },
{ .compatible = "ak09912", },
{}
};
MODULE_DEVICE_TABLE(of, ak8975_of_match);
static struct i2c_driver ak8975_driver = {
.driver = {
.name = "ak8975",
.of_match_table = ak8975_of_match,
.of_match_table = of_match_ptr(ak8975_of_match),
.acpi_match_table = ACPI_PTR(ak_acpi_match),
},
.probe = ak8975_probe,

View file

@ -157,20 +157,12 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
switch (mask) {
case 0:
poll_value = hid_sensor_read_poll_value(
&magn_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&magn_state->common_attributes, true);
msleep_interruptible(poll_value * 2);
report_id =
magn_state->magn[chan->address].report_id;
address = magn_3d_addresses[chan->address];
@ -530,6 +522,7 @@ static struct platform_driver hid_magn_3d_platform_driver = {
.id_table = hid_magn_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_magn_3d_probe,
.remove = hid_magn_3d_remove,

View file

@ -111,20 +111,12 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
switch (mask) {
case IIO_CHAN_INFO_RAW:
poll_value = hid_sensor_read_poll_value(
&incl_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&incl_state->common_attributes, true);
msleep_interruptible(poll_value * 2);
report_id =
incl_state->incl[chan->scan_index].report_id;
address = incl_3d_addresses[chan->scan_index];
@ -437,6 +429,7 @@ static struct platform_driver hid_incl_3d_platform_driver = {
.id_table = hid_incl_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_incl_3d_probe,
.remove = hid_incl_3d_remove,

View file

@ -80,16 +80,12 @@ struct bmp280_data {
s32 t_fine;
};
/* Compensation parameters. */
struct bmp280_comp_temp {
u16 dig_t1;
s16 dig_t2, dig_t3;
};
struct bmp280_comp_press {
u16 dig_p1;
s16 dig_p2, dig_p3, dig_p4, dig_p5, dig_p6, dig_p7, dig_p8, dig_p9;
};
/*
* These enums are used for indexing into the array of compensation
* parameters.
*/
enum { T1, T2, T3 };
enum { P1, P2, P3, P4, P5, P6, P7, P8, P9 };
static const struct iio_chan_spec bmp280_channels[] = {
{
@ -141,10 +137,18 @@ static const struct regmap_config bmp280_regmap_config = {
.volatile_reg = bmp280_is_volatile_reg,
};
static int bmp280_read_compensation_temp(struct bmp280_data *data,
struct bmp280_comp_temp *comp)
/*
* Returns temperature in DegC, resolution is 0.01 DegC. Output value of
* "5123" equals 51.23 DegC. t_fine carries fine temperature as global
* value.
*
* Taken from datasheet, Section 3.11.3, "Compensation formula".
*/
static s32 bmp280_compensate_temp(struct bmp280_data *data,
s32 adc_temp)
{
int ret;
s32 var1, var2;
__le16 buf[BMP280_COMP_TEMP_REG_COUNT / 2];
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START,
@ -155,63 +159,21 @@ static int bmp280_read_compensation_temp(struct bmp280_data *data,
return ret;
}
comp->dig_t1 = (u16) le16_to_cpu(buf[0]);
comp->dig_t2 = (s16) le16_to_cpu(buf[1]);
comp->dig_t3 = (s16) le16_to_cpu(buf[2]);
/*
* The double casts are necessary because le16_to_cpu returns an
* unsigned 16-bit value. Casting that value directly to a
* signed 32-bit will not do proper sign extension.
*
* Conversely, T1 and P1 are unsigned values, so they can be
* cast straight to the larger type.
*/
var1 = (((adc_temp >> 3) - ((s32)le16_to_cpu(buf[T1]) << 1)) *
((s32)(s16)le16_to_cpu(buf[T2]))) >> 11;
var2 = (((((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1]))) *
((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1])))) >> 12) *
((s32)(s16)le16_to_cpu(buf[T3]))) >> 14;
return 0;
}
static int bmp280_read_compensation_press(struct bmp280_data *data,
struct bmp280_comp_press *comp)
{
int ret;
__le16 buf[BMP280_COMP_PRESS_REG_COUNT / 2];
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START,
buf, BMP280_COMP_PRESS_REG_COUNT);
if (ret < 0) {
dev_err(&data->client->dev,
"failed to read pressure calibration parameters\n");
return ret;
}
comp->dig_p1 = (u16) le16_to_cpu(buf[0]);
comp->dig_p2 = (s16) le16_to_cpu(buf[1]);
comp->dig_p3 = (s16) le16_to_cpu(buf[2]);
comp->dig_p4 = (s16) le16_to_cpu(buf[3]);
comp->dig_p5 = (s16) le16_to_cpu(buf[4]);
comp->dig_p6 = (s16) le16_to_cpu(buf[5]);
comp->dig_p7 = (s16) le16_to_cpu(buf[6]);
comp->dig_p8 = (s16) le16_to_cpu(buf[7]);
comp->dig_p9 = (s16) le16_to_cpu(buf[8]);
return 0;
}
/*
* Returns temperature in DegC, resolution is 0.01 DegC. Output value of
* "5123" equals 51.23 DegC. t_fine carries fine temperature as global
* value.
*
* Taken from datasheet, Section 3.11.3, "Compensation formula".
*/
static s32 bmp280_compensate_temp(struct bmp280_data *data,
struct bmp280_comp_temp *comp,
s32 adc_temp)
{
s32 var1, var2, t;
var1 = (((adc_temp >> 3) - ((s32) comp->dig_t1 << 1)) *
((s32) comp->dig_t2)) >> 11;
var2 = (((((adc_temp >> 4) - ((s32) comp->dig_t1)) *
((adc_temp >> 4) - ((s32) comp->dig_t1))) >> 12) *
((s32) comp->dig_t3)) >> 14;
data->t_fine = var1 + var2;
t = (data->t_fine * 5 + 128) >> 8;
return t;
return (data->t_fine * 5 + 128) >> 8;
}
/*
@ -222,29 +184,38 @@ static s32 bmp280_compensate_temp(struct bmp280_data *data,
* Taken from datasheet, Section 3.11.3, "Compensation formula".
*/
static u32 bmp280_compensate_press(struct bmp280_data *data,
struct bmp280_comp_press *comp,
s32 adc_press)
{
int ret;
s64 var1, var2, p;
__le16 buf[BMP280_COMP_PRESS_REG_COUNT / 2];
var1 = ((s64) data->t_fine) - 128000;
var2 = var1 * var1 * (s64) comp->dig_p6;
var2 = var2 + ((var1 * (s64) comp->dig_p5) << 17);
var2 = var2 + (((s64) comp->dig_p4) << 35);
var1 = ((var1 * var1 * (s64) comp->dig_p3) >> 8) +
((var1 * (s64) comp->dig_p2) << 12);
var1 = (((((s64) 1) << 47) + var1)) * ((s64) comp->dig_p1) >> 33;
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START,
buf, BMP280_COMP_PRESS_REG_COUNT);
if (ret < 0) {
dev_err(&data->client->dev,
"failed to read pressure calibration parameters\n");
return ret;
}
var1 = ((s64)data->t_fine) - 128000;
var2 = var1 * var1 * (s64)(s16)le16_to_cpu(buf[P6]);
var2 += (var1 * (s64)(s16)le16_to_cpu(buf[P5])) << 17;
var2 += ((s64)(s16)le16_to_cpu(buf[P4])) << 35;
var1 = ((var1 * var1 * (s64)(s16)le16_to_cpu(buf[P3])) >> 8) +
((var1 * (s64)(s16)le16_to_cpu(buf[P2])) << 12);
var1 = ((((s64)1) << 47) + var1) * ((s64)le16_to_cpu(buf[P1])) >> 33;
if (var1 == 0)
return 0;
p = ((((s64) 1048576 - adc_press) << 31) - var2) * 3125;
p = ((((s64)1048576 - adc_press) << 31) - var2) * 3125;
p = div64_s64(p, var1);
var1 = (((s64) comp->dig_p9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((s64) comp->dig_p8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((s64) comp->dig_p7) << 4);
var1 = (((s64)(s16)le16_to_cpu(buf[P9])) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((s64)(s16)le16_to_cpu(buf[P8])) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((s64)(s16)le16_to_cpu(buf[P7])) << 4);
return (u32) p;
return (u32)p;
}
static int bmp280_read_temp(struct bmp280_data *data,
@ -253,11 +224,6 @@ static int bmp280_read_temp(struct bmp280_data *data,
int ret;
__be32 tmp = 0;
s32 adc_temp, comp_temp;
struct bmp280_comp_temp comp;
ret = bmp280_read_compensation_temp(data, &comp);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB,
(u8 *) &tmp, 3);
@ -267,7 +233,7 @@ static int bmp280_read_temp(struct bmp280_data *data,
}
adc_temp = be32_to_cpu(tmp) >> 12;
comp_temp = bmp280_compensate_temp(data, &comp, adc_temp);
comp_temp = bmp280_compensate_temp(data, adc_temp);
/*
* val might be NULL if we're called by the read_press routine,
@ -288,11 +254,6 @@ static int bmp280_read_press(struct bmp280_data *data,
__be32 tmp = 0;
s32 adc_press;
u32 comp_press;
struct bmp280_comp_press comp;
ret = bmp280_read_compensation_press(data, &comp);
if (ret < 0)
return ret;
/* Read and compensate temperature so we get a reading of t_fine. */
ret = bmp280_read_temp(data, NULL);
@ -307,7 +268,7 @@ static int bmp280_read_press(struct bmp280_data *data,
}
adc_press = be32_to_cpu(tmp) >> 12;
comp_press = bmp280_compensate_press(data, &comp, adc_press);
comp_press = bmp280_compensate_press(data, adc_press);
*val = comp_press;
*val2 = 256000;
@ -366,7 +327,7 @@ static int bmp280_chip_init(struct bmp280_data *data)
BMP280_MODE_NORMAL);
if (ret < 0) {
dev_err(&data->client->dev,
"failed to write config register\n");
"failed to write ctrl_meas register\n");
return ret;
}
@ -394,7 +355,6 @@ static int bmp280_probe(struct i2c_client *client,
if (!indio_dev)
return -ENOMEM;
i2c_set_clientdata(client, indio_dev);
data = iio_priv(indio_dev);
mutex_init(&data->lock);
data->client = client;

View file

@ -79,7 +79,6 @@ static int press_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
@ -96,15 +95,8 @@ static int press_read_raw(struct iio_dev *indio_dev,
break;
}
if (report_id >= 0) {
poll_value = hid_sensor_read_poll_value(
&press_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&press_state->common_attributes,
true);
msleep_interruptible(poll_value * 2);
*val = sensor_hub_input_attr_get_raw_value(
press_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PRESSURE, address,
@ -382,6 +374,7 @@ static struct platform_driver hid_press_platform_driver = {
.id_table = hid_press_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_press_probe,
.remove = hid_press_remove,

View file

@ -17,3 +17,20 @@ config AS3935
module will be called as3935
endmenu
menu "Proximity sensors"
config SX9500
tristate "SX9500 Semtech proximity sensor"
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select REGMAP_I2C
depends on I2C
help
Say Y here to build a driver for Semtech's SX9500 capacitive
proximity/button sensor.
To compile this driver as a module, choose M here: the
module will be called sx9500.
endmenu

View file

@ -4,3 +4,4 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AS3935) += as3935.o
obj-$(CONFIG_SX9500) += sx9500.o

View file

@ -273,9 +273,9 @@ static void calibrate_as3935(struct as3935_state *st)
}
#ifdef CONFIG_PM_SLEEP
static int as3935_suspend(struct spi_device *spi, pm_message_t msg)
static int as3935_suspend(struct device *dev)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct as3935_state *st = iio_priv(indio_dev);
int val, ret;
@ -293,9 +293,9 @@ err_suspend:
return ret;
}
static int as3935_resume(struct spi_device *spi)
static int as3935_resume(struct device *dev)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct as3935_state *st = iio_priv(indio_dev);
int val, ret;
@ -311,9 +311,12 @@ err_resume:
return ret;
}
static SIMPLE_DEV_PM_OPS(as3935_pm_ops, as3935_suspend, as3935_resume);
#define AS3935_PM_OPS (&as3935_pm_ops)
#else
#define as3935_suspend NULL
#define as3935_resume NULL
#define AS3935_PM_OPS NULL
#endif
static int as3935_probe(struct spi_device *spi)
@ -441,12 +444,11 @@ static struct spi_driver as3935_driver = {
.driver = {
.name = "as3935",
.owner = THIS_MODULE,
.pm = AS3935_PM_OPS,
},
.probe = as3935_probe,
.remove = as3935_remove,
.id_table = as3935_id,
.suspend = as3935_suspend,
.resume = as3935_resume,
};
module_spi_driver(as3935_driver);

View file

@ -0,0 +1,752 @@
/*
* Copyright (c) 2014 Intel Corporation
*
* Driver for Semtech's SX9500 capacitive proximity/button solution.
* Datasheet available at
* <http://www.semtech.com/images/datasheet/sx9500.pdf>.
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/acpi.h>
#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#define SX9500_DRIVER_NAME "sx9500"
#define SX9500_IRQ_NAME "sx9500_event"
#define SX9500_GPIO_NAME "sx9500_gpio"
/* Register definitions. */
#define SX9500_REG_IRQ_SRC 0x00
#define SX9500_REG_STAT 0x01
#define SX9500_REG_IRQ_MSK 0x03
#define SX9500_REG_PROX_CTRL0 0x06
#define SX9500_REG_PROX_CTRL1 0x07
#define SX9500_REG_PROX_CTRL2 0x08
#define SX9500_REG_PROX_CTRL3 0x09
#define SX9500_REG_PROX_CTRL4 0x0a
#define SX9500_REG_PROX_CTRL5 0x0b
#define SX9500_REG_PROX_CTRL6 0x0c
#define SX9500_REG_PROX_CTRL7 0x0d
#define SX9500_REG_PROX_CTRL8 0x0e
#define SX9500_REG_SENSOR_SEL 0x20
#define SX9500_REG_USE_MSB 0x21
#define SX9500_REG_USE_LSB 0x22
#define SX9500_REG_AVG_MSB 0x23
#define SX9500_REG_AVG_LSB 0x24
#define SX9500_REG_DIFF_MSB 0x25
#define SX9500_REG_DIFF_LSB 0x26
#define SX9500_REG_OFFSET_MSB 0x27
#define SX9500_REG_OFFSET_LSB 0x28
#define SX9500_REG_RESET 0x7f
/* Write this to REG_RESET to do a soft reset. */
#define SX9500_SOFT_RESET 0xde
#define SX9500_SCAN_PERIOD_MASK GENMASK(6, 4)
#define SX9500_SCAN_PERIOD_SHIFT 4
/*
* These serve for identifying IRQ source in the IRQ_SRC register, and
* also for masking the IRQs in the IRQ_MSK register.
*/
#define SX9500_CLOSE_IRQ BIT(6)
#define SX9500_FAR_IRQ BIT(5)
#define SX9500_CONVDONE_IRQ BIT(3)
#define SX9500_PROXSTAT_SHIFT 4
#define SX9500_NUM_CHANNELS 4
struct sx9500_data {
struct mutex mutex;
struct i2c_client *client;
struct iio_trigger *trig;
struct regmap *regmap;
/*
* Last reading of the proximity status for each channel. We
* only send an event to user space when this changes.
*/
bool prox_stat[SX9500_NUM_CHANNELS];
bool event_enabled[SX9500_NUM_CHANNELS];
bool trigger_enabled;
u16 *buffer;
};
static const struct iio_event_spec sx9500_events[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
},
};
#define SX9500_CHANNEL(idx) \
{ \
.type = IIO_PROXIMITY, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.indexed = 1, \
.channel = idx, \
.event_spec = sx9500_events, \
.num_event_specs = ARRAY_SIZE(sx9500_events), \
.scan_index = idx, \
.scan_type = { \
.sign = 'u', \
.realbits = 16, \
.storagebits = 16, \
.shift = 0, \
}, \
}
static const struct iio_chan_spec sx9500_channels[] = {
SX9500_CHANNEL(0),
SX9500_CHANNEL(1),
SX9500_CHANNEL(2),
SX9500_CHANNEL(3),
IIO_CHAN_SOFT_TIMESTAMP(4),
};
static const struct {
int val;
int val2;
} sx9500_samp_freq_table[] = {
{33, 333333},
{16, 666666},
{11, 111111},
{8, 333333},
{6, 666666},
{5, 0},
{3, 333333},
{2, 500000},
};
static const struct regmap_range sx9500_writable_reg_ranges[] = {
regmap_reg_range(SX9500_REG_IRQ_MSK, SX9500_REG_IRQ_MSK),
regmap_reg_range(SX9500_REG_PROX_CTRL0, SX9500_REG_PROX_CTRL8),
regmap_reg_range(SX9500_REG_SENSOR_SEL, SX9500_REG_SENSOR_SEL),
regmap_reg_range(SX9500_REG_OFFSET_MSB, SX9500_REG_OFFSET_LSB),
regmap_reg_range(SX9500_REG_RESET, SX9500_REG_RESET),
};
static const struct regmap_access_table sx9500_writeable_regs = {
.yes_ranges = sx9500_writable_reg_ranges,
.n_yes_ranges = ARRAY_SIZE(sx9500_writable_reg_ranges),
};
/*
* All allocated registers are readable, so we just list unallocated
* ones.
*/
static const struct regmap_range sx9500_non_readable_reg_ranges[] = {
regmap_reg_range(SX9500_REG_STAT + 1, SX9500_REG_STAT + 1),
regmap_reg_range(SX9500_REG_IRQ_MSK + 1, SX9500_REG_PROX_CTRL0 - 1),
regmap_reg_range(SX9500_REG_PROX_CTRL8 + 1, SX9500_REG_SENSOR_SEL - 1),
regmap_reg_range(SX9500_REG_OFFSET_LSB + 1, SX9500_REG_RESET - 1),
};
static const struct regmap_access_table sx9500_readable_regs = {
.no_ranges = sx9500_non_readable_reg_ranges,
.n_no_ranges = ARRAY_SIZE(sx9500_non_readable_reg_ranges),
};
static const struct regmap_range sx9500_volatile_reg_ranges[] = {
regmap_reg_range(SX9500_REG_IRQ_SRC, SX9500_REG_STAT),
regmap_reg_range(SX9500_REG_USE_MSB, SX9500_REG_OFFSET_LSB),
regmap_reg_range(SX9500_REG_RESET, SX9500_REG_RESET),
};
static const struct regmap_access_table sx9500_volatile_regs = {
.yes_ranges = sx9500_volatile_reg_ranges,
.n_yes_ranges = ARRAY_SIZE(sx9500_volatile_reg_ranges),
};
static const struct regmap_config sx9500_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = SX9500_REG_RESET,
.cache_type = REGCACHE_RBTREE,
.wr_table = &sx9500_writeable_regs,
.rd_table = &sx9500_readable_regs,
.volatile_table = &sx9500_volatile_regs,
};
static int sx9500_read_proximity(struct sx9500_data *data,
const struct iio_chan_spec *chan,
int *val)
{
int ret;
__be16 regval;
ret = regmap_write(data->regmap, SX9500_REG_SENSOR_SEL, chan->channel);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, SX9500_REG_USE_MSB, &regval, 2);
if (ret < 0)
return ret;
*val = 32767 - (s16)be16_to_cpu(regval);
return IIO_VAL_INT;
}
static int sx9500_read_samp_freq(struct sx9500_data *data,
int *val, int *val2)
{
int ret;
unsigned int regval;
mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0, &regval);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
regval = (regval & SX9500_SCAN_PERIOD_MASK) >> SX9500_SCAN_PERIOD_SHIFT;
*val = sx9500_samp_freq_table[regval].val;
*val2 = sx9500_samp_freq_table[regval].val2;
return IIO_VAL_INT_PLUS_MICRO;
}
static int sx9500_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long mask)
{
struct sx9500_data *data = iio_priv(indio_dev);
int ret;
switch (chan->type) {
case IIO_PROXIMITY:
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
mutex_lock(&data->mutex);
ret = sx9500_read_proximity(data, chan, val);
mutex_unlock(&data->mutex);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
return sx9500_read_samp_freq(data, val, val2);
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int sx9500_set_samp_freq(struct sx9500_data *data,
int val, int val2)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(sx9500_samp_freq_table); i++)
if (val == sx9500_samp_freq_table[i].val &&
val2 == sx9500_samp_freq_table[i].val2)
break;
if (i == ARRAY_SIZE(sx9500_samp_freq_table))
return -EINVAL;
mutex_lock(&data->mutex);
ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
SX9500_SCAN_PERIOD_MASK,
i << SX9500_SCAN_PERIOD_SHIFT);
mutex_unlock(&data->mutex);
return ret;
}
static int sx9500_write_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int val, int val2, long mask)
{
struct sx9500_data *data = iio_priv(indio_dev);
switch (chan->type) {
case IIO_PROXIMITY:
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
return sx9500_set_samp_freq(data, val, val2);
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static irqreturn_t sx9500_irq_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct sx9500_data *data = iio_priv(indio_dev);
if (data->trigger_enabled)
iio_trigger_poll(data->trig);
/*
* Even if no event is enabled, we need to wake the thread to
* clear the interrupt state by reading SX9500_REG_IRQ_SRC. It
* is not possible to do that here because regmap_read takes a
* mutex.
*/
return IRQ_WAKE_THREAD;
}
static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct sx9500_data *data = iio_priv(indio_dev);
int ret;
unsigned int val, chan;
mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
if (ret < 0) {
dev_err(&data->client->dev, "i2c transfer error in irq\n");
goto out;
}
if (!(val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ)))
goto out;
ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
if (ret < 0) {
dev_err(&data->client->dev, "i2c transfer error in irq\n");
goto out;
}
val >>= SX9500_PROXSTAT_SHIFT;
for (chan = 0; chan < SX9500_NUM_CHANNELS; chan++) {
int dir;
u64 ev;
bool new_prox = val & BIT(chan);
if (!data->event_enabled[chan])
continue;
if (new_prox == data->prox_stat[chan])
/* No change on this channel. */
continue;
dir = new_prox ? IIO_EV_DIR_FALLING :
IIO_EV_DIR_RISING;
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
chan,
IIO_EV_TYPE_THRESH,
dir);
iio_push_event(indio_dev, ev, iio_get_time_ns());
data->prox_stat[chan] = new_prox;
}
out:
mutex_unlock(&data->mutex);
return IRQ_HANDLED;
}
static int sx9500_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
struct sx9500_data *data = iio_priv(indio_dev);
if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
dir != IIO_EV_DIR_EITHER)
return -EINVAL;
return data->event_enabled[chan->channel];
}
static int sx9500_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
int state)
{
struct sx9500_data *data = iio_priv(indio_dev);
int ret, i;
bool any_active = false;
unsigned int irqmask;
if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
dir != IIO_EV_DIR_EITHER)
return -EINVAL;
mutex_lock(&data->mutex);
data->event_enabled[chan->channel] = state;
for (i = 0; i < SX9500_NUM_CHANNELS; i++)
if (data->event_enabled[i]) {
any_active = true;
break;
}
irqmask = SX9500_CLOSE_IRQ | SX9500_FAR_IRQ;
if (any_active)
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
irqmask, irqmask);
else
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
irqmask, 0);
mutex_unlock(&data->mutex);
return ret;
}
static int sx9500_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct sx9500_data *data = iio_priv(indio_dev);
mutex_lock(&data->mutex);
kfree(data->buffer);
data->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
mutex_unlock(&data->mutex);
if (data->buffer == NULL)
return -ENOMEM;
return 0;
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
"2.500000 3.333333 5 6.666666 8.333333 11.111111 16.666666 33.333333");
static struct attribute *sx9500_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
NULL,
};
static const struct attribute_group sx9500_attribute_group = {
.attrs = sx9500_attributes,
};
static const struct iio_info sx9500_info = {
.driver_module = THIS_MODULE,
.attrs = &sx9500_attribute_group,
.read_raw = &sx9500_read_raw,
.write_raw = &sx9500_write_raw,
.read_event_config = &sx9500_read_event_config,
.write_event_config = &sx9500_write_event_config,
.update_scan_mode = &sx9500_update_scan_mode,
};
static int sx9500_set_trigger_state(struct iio_trigger *trig,
bool state)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct sx9500_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->mutex);
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
SX9500_CONVDONE_IRQ,
state ? SX9500_CONVDONE_IRQ : 0);
if (ret == 0)
data->trigger_enabled = state;
mutex_unlock(&data->mutex);
return ret;
}
static const struct iio_trigger_ops sx9500_trigger_ops = {
.set_trigger_state = sx9500_set_trigger_state,
.owner = THIS_MODULE,
};
static irqreturn_t sx9500_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct sx9500_data *data = iio_priv(indio_dev);
int val, bit, ret, i = 0;
mutex_lock(&data->mutex);
for_each_set_bit(bit, indio_dev->buffer->scan_mask,
indio_dev->masklength) {
ret = sx9500_read_proximity(data, &indio_dev->channels[bit],
&val);
if (ret < 0)
goto out;
data->buffer[i++] = val;
}
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
iio_get_time_ns());
out:
mutex_unlock(&data->mutex);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
struct sx9500_reg_default {
u8 reg;
u8 def;
};
static const struct sx9500_reg_default sx9500_default_regs[] = {
{
.reg = SX9500_REG_PROX_CTRL1,
/* Shield enabled, small range. */
.def = 0x43,
},
{
.reg = SX9500_REG_PROX_CTRL2,
/* x8 gain, 167kHz frequency, finest resolution. */
.def = 0x77,
},
{
.reg = SX9500_REG_PROX_CTRL3,
/* Doze enabled, 2x scan period doze, no raw filter. */
.def = 0x40,
},
{
.reg = SX9500_REG_PROX_CTRL4,
/* Average threshold. */
.def = 0x30,
},
{
.reg = SX9500_REG_PROX_CTRL5,
/*
* Debouncer off, lowest average negative filter,
* highest average postive filter.
*/
.def = 0x0f,
},
{
.reg = SX9500_REG_PROX_CTRL6,
/* Proximity detection threshold: 280 */
.def = 0x0e,
},
{
.reg = SX9500_REG_PROX_CTRL7,
/*
* No automatic compensation, compensate each pin
* independently, proximity hysteresis: 32, close
* debouncer off, far debouncer off.
*/
.def = 0x00,
},
{
.reg = SX9500_REG_PROX_CTRL8,
/* No stuck timeout, no periodic compensation. */
.def = 0x00,
},
{
.reg = SX9500_REG_PROX_CTRL0,
/* Scan period: 30ms, all sensors enabled. */
.def = 0x0f,
},
};
static int sx9500_init_device(struct iio_dev *indio_dev)
{
struct sx9500_data *data = iio_priv(indio_dev);
int ret, i;
unsigned int val;
ret = regmap_write(data->regmap, SX9500_REG_IRQ_MSK, 0);
if (ret < 0)
return ret;
ret = regmap_write(data->regmap, SX9500_REG_RESET,
SX9500_SOFT_RESET);
if (ret < 0)
return ret;
ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
if (ret < 0)
return ret;
for (i = 0; i < ARRAY_SIZE(sx9500_default_regs); i++) {
ret = regmap_write(data->regmap,
sx9500_default_regs[i].reg,
sx9500_default_regs[i].def);
if (ret < 0)
return ret;
}
return 0;
}
static int sx9500_gpio_probe(struct i2c_client *client,
struct sx9500_data *data)
{
struct device *dev;
struct gpio_desc *gpio;
int ret;
if (!client)
return -EINVAL;
dev = &client->dev;
/* data ready gpio interrupt pin */
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0);
if (IS_ERR(gpio)) {
dev_err(dev, "acpi gpio get index failed\n");
return PTR_ERR(gpio);
}
ret = gpiod_direction_input(gpio);
if (ret)
return ret;
ret = gpiod_to_irq(gpio);
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
return ret;
}
static int sx9500_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct sx9500_data *data;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (indio_dev == NULL)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->mutex);
data->trigger_enabled = false;
data->regmap = devm_regmap_init_i2c(client, &sx9500_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
sx9500_init_device(indio_dev);
indio_dev->dev.parent = &client->dev;
indio_dev->name = SX9500_DRIVER_NAME;
indio_dev->channels = sx9500_channels;
indio_dev->num_channels = ARRAY_SIZE(sx9500_channels);
indio_dev->info = &sx9500_info;
indio_dev->modes = INDIO_DIRECT_MODE;
i2c_set_clientdata(client, indio_dev);
if (client->irq <= 0)
client->irq = sx9500_gpio_probe(client, data);
if (client->irq > 0) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
sx9500_irq_handler, sx9500_irq_thread_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
SX9500_IRQ_NAME, indio_dev);
if (ret < 0)
return ret;
data->trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d", indio_dev->name, indio_dev->id);
if (!data->trig)
return -ENOMEM;
data->trig->dev.parent = &client->dev;
data->trig->ops = &sx9500_trigger_ops;
iio_trigger_set_drvdata(data->trig, indio_dev);
ret = iio_trigger_register(data->trig);
if (ret)
return ret;
}
ret = iio_triggered_buffer_setup(indio_dev, NULL,
sx9500_trigger_handler, NULL);
if (ret < 0)
goto out_trigger_unregister;
ret = iio_device_register(indio_dev);
if (ret < 0)
goto out_buffer_cleanup;
return 0;
out_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
out_trigger_unregister:
if (client->irq > 0)
iio_trigger_unregister(data->trig);
return ret;
}
static int sx9500_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct sx9500_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (client->irq > 0)
iio_trigger_unregister(data->trig);
kfree(data->buffer);
return 0;
}
static const struct acpi_device_id sx9500_acpi_match[] = {
{"SSX9500", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
static const struct i2c_device_id sx9500_id[] = {
{"sx9500", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, sx9500_id);
static struct i2c_driver sx9500_driver = {
.driver = {
.name = SX9500_DRIVER_NAME,
.acpi_match_table = ACPI_PTR(sx9500_acpi_match),
},
.probe = sx9500_probe,
.remove = sx9500_remove,
.id_table = sx9500_id,
};
module_i2c_driver(sx9500_driver);
MODULE_AUTHOR("Vlad Dogaru <[email protected]>");
MODULE_DESCRIPTION("Driver for Semtech SX9500 proximity sensor");
MODULE_LICENSE("GPL v2");

View file

@ -135,6 +135,7 @@ static int iio_sysfs_trigger_probe(int id)
struct iio_sysfs_trig *t;
int ret;
bool foundit = false;
mutex_lock(&iio_sysfs_trig_list_mut);
list_for_each_entry(t, &iio_sysfs_trig_list, l)
if (id == t->id) {
@ -185,6 +186,7 @@ static int iio_sysfs_trigger_remove(int id)
{
bool foundit = false;
struct iio_sysfs_trig *t;
mutex_lock(&iio_sysfs_trig_list_mut);
list_for_each_entry(t, &iio_sysfs_trig_list, l)
if (id == t->id) {