summaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-11-07 09:14:03 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-11-07 09:14:03 +0100
commit7970b9e6a524fc978a4c3013ca14efcfe37b84e3 (patch)
tree001842b051f718d246cedbd79823efadfdaaaa2c /drivers/platform
parentc2b6028fd10617252c679b21b41204c531347a00 (diff)
parentc2b0d2cfb0317275cf04257fc3f5dfbdd9ffa274 (diff)
Merge tag 'iio-for-4.10b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-testing
Jonathan writes: Second round of new device support, cleanups and fixes for IIO in the 4.10 cycle This includes two branch merges for elements that may also go via MFD. New device support * cros_ec - new driver to support these Chrome OS contiguous sensors which are behind the Chrome OS embedded controller. Requires a few minor MFD and chrome platform changes. One follow up fix deals with some dependency issues in Kconfig. * mpu-3050 - new driver and device tree bindings for this venerable device. * st_accel - support for the lng2dm an Driver features * ad7192 - Add DVdd regulator handling * ad9832 - Add DVDD regulator handling * at91 - Suspend and resume support * si7020 - Device tree bindings * ti-am335x - DMA support - uses dma to accelerate short bursts of read back rather than full blown DMA buffer support. Greatly improved performance. Includes an MFD addition to give access to the address needed for DMA. * tsl2583 - Device tree bindings Cleanups and minor fixes * ad7192 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) - Rename reg variable to reflect which regulator it is * ad5933 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) * ad7746 - Fix a missing return value (fallout from previous patch set) * ad7780 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) * ad9832 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) - Rename reg regulator to reflect which one it is * ad9834 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) * hts221 - Remove a duplicated include * maxim thermocouple - Handle a wrong storage side in read function. Prevent any problems that might be introduced by additions to this driver in future. * tsl2583 - big set from Brian Masney to drive this towards a staging graduation. - Convert to iio_chan_spec and read_raw / write_raw (in a couple of steps) - Improved error handling in various functions - Drop redundant power_state custom sysfs attribute. - Use IIO_*_ATTR* macros for remaining attributes. - Return an error code to userspace on invalid parameters being writen to sysfs files. - Add locking to various attribute accesses to remove possible races. - Add defines for various magic numbers. - Use smbus_read_byte_data instead of a write_byte followed by read_byte. - Query only relevant registers in probe. - Tidy up ordering of code comments. - Remove a pointless power off sequence in taos_chip_on. - Don't bother shutting down the chip when updating the lux table. The table is held entirely in the driver and doesn't effect the chip at all. - Drop a redundant i2c call in taos_als_calibrate where the same register is read twice in a row.
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/chrome/cros_ec_dev.c159
1 files changed, 159 insertions, 0 deletions
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
index 8abd80dbcbed..47268ecedc4d 100644
--- a/drivers/platform/chrome/cros_ec_dev.c
+++ b/drivers/platform/chrome/cros_ec_dev.c
@@ -18,6 +18,7 @@
*/
#include <linux/fs.h>
+#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -87,6 +88,41 @@ exit:
return ret;
}
+static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
+{
+ struct cros_ec_command *msg;
+ int ret;
+
+ if (ec->features[0] == -1U && ec->features[1] == -1U) {
+ /* features bitmap not read yet */
+
+ msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
+ msg->insize = sizeof(ec->features);
+ msg->outsize = 0;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0 || msg->result != EC_RES_SUCCESS) {
+ dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
+ ret, msg->result);
+ memset(ec->features, 0, sizeof(ec->features));
+ }
+
+ memcpy(ec->features, msg->data, sizeof(ec->features));
+
+ dev_dbg(ec->dev, "EC features %08x %08x\n",
+ ec->features[0], ec->features[1]);
+
+ kfree(msg);
+ }
+
+ return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
+}
+
/* Device file ops */
static int ec_device_open(struct inode *inode, struct file *filp)
{
@@ -230,6 +266,123 @@ static void __remove(struct device *dev)
kfree(ec);
}
+static void cros_ec_sensors_register(struct cros_ec_dev *ec)
+{
+ /*
+ * Issue a command to get the number of sensor reported.
+ * Build an array of sensors driver and register them all.
+ */
+ int ret, i, id, sensor_num;
+ struct mfd_cell *sensor_cells;
+ struct cros_ec_sensor_platform *sensor_platforms;
+ int sensor_type[MOTIONSENSE_TYPE_MAX];
+ struct ec_params_motion_sense *params;
+ struct ec_response_motion_sense *resp;
+ struct cros_ec_command *msg;
+
+ msg = kzalloc(sizeof(struct cros_ec_command) +
+ max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
+ if (msg == NULL)
+ return;
+
+ msg->version = 2;
+ msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
+ msg->outsize = sizeof(*params);
+ msg->insize = sizeof(*resp);
+
+ params = (struct ec_params_motion_sense *)msg->data;
+ params->cmd = MOTIONSENSE_CMD_DUMP;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0 || msg->result != EC_RES_SUCCESS) {
+ dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n",
+ ret, msg->result);
+ goto error;
+ }
+
+ resp = (struct ec_response_motion_sense *)msg->data;
+ sensor_num = resp->dump.sensor_count;
+ /* Allocate 2 extra sensors in case lid angle or FIFO are needed */
+ sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
+ GFP_KERNEL);
+ if (sensor_cells == NULL)
+ goto error;
+
+ sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) *
+ (sensor_num + 1), GFP_KERNEL);
+ if (sensor_platforms == NULL)
+ goto error_platforms;
+
+ memset(sensor_type, 0, sizeof(sensor_type));
+ id = 0;
+ for (i = 0; i < sensor_num; i++) {
+ params->cmd = MOTIONSENSE_CMD_INFO;
+ params->info.sensor_num = i;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0 || msg->result != EC_RES_SUCCESS) {
+ dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n",
+ i, ret, msg->result);
+ continue;
+ }
+ switch (resp->info.type) {
+ case MOTIONSENSE_TYPE_ACCEL:
+ sensor_cells[id].name = "cros-ec-accel";
+ break;
+ case MOTIONSENSE_TYPE_GYRO:
+ sensor_cells[id].name = "cros-ec-gyro";
+ break;
+ case MOTIONSENSE_TYPE_MAG:
+ sensor_cells[id].name = "cros-ec-mag";
+ break;
+ case MOTIONSENSE_TYPE_PROX:
+ sensor_cells[id].name = "cros-ec-prox";
+ break;
+ case MOTIONSENSE_TYPE_LIGHT:
+ sensor_cells[id].name = "cros-ec-light";
+ break;
+ case MOTIONSENSE_TYPE_ACTIVITY:
+ sensor_cells[id].name = "cros-ec-activity";
+ break;
+ default:
+ dev_warn(ec->dev, "unknown type %d\n", resp->info.type);
+ continue;
+ }
+ sensor_platforms[id].sensor_num = i;
+ sensor_cells[id].id = sensor_type[resp->info.type];
+ sensor_cells[id].platform_data = &sensor_platforms[id];
+ sensor_cells[id].pdata_size =
+ sizeof(struct cros_ec_sensor_platform);
+
+ sensor_type[resp->info.type]++;
+ id++;
+ }
+ if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
+ sensor_platforms[id].sensor_num = sensor_num;
+
+ sensor_cells[id].name = "cros-ec-angle";
+ sensor_cells[id].id = 0;
+ sensor_cells[id].platform_data = &sensor_platforms[id];
+ sensor_cells[id].pdata_size =
+ sizeof(struct cros_ec_sensor_platform);
+ id++;
+ }
+ if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
+ sensor_cells[id].name = "cros-ec-ring";
+ id++;
+ }
+
+ ret = mfd_add_devices(ec->dev, 0, sensor_cells, id,
+ NULL, 0, NULL);
+ if (ret)
+ dev_err(ec->dev, "failed to add EC sensors\n");
+
+ kfree(sensor_platforms);
+error_platforms:
+ kfree(sensor_cells);
+error:
+ kfree(msg);
+}
+
static int ec_device_probe(struct platform_device *pdev)
{
int retval = -ENOMEM;
@@ -245,6 +398,8 @@ static int ec_device_probe(struct platform_device *pdev)
ec->ec_dev = dev_get_drvdata(dev->parent);
ec->dev = dev;
ec->cmd_offset = ec_platform->cmd_offset;
+ ec->features[0] = -1U; /* Not cached yet */
+ ec->features[1] = -1U; /* Not cached yet */
device_initialize(&ec->class_dev);
cdev_init(&ec->cdev, &fops);
@@ -282,6 +437,10 @@ static int ec_device_probe(struct platform_device *pdev)
goto dev_reg_failed;
}
+ /* check whether this EC is a sensor hub. */
+ if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
+ cros_ec_sensors_register(ec);
+
return 0;
dev_reg_failed: