cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mc3230.c (4808B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * mCube MC3230 3-Axis Accelerometer
      4 *
      5 * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
      6 *
      7 * IIO driver for mCube MC3230; 7-bit I2C address: 0x4c.
      8 */
      9
     10#include <linux/i2c.h>
     11#include <linux/module.h>
     12#include <linux/iio/iio.h>
     13#include <linux/iio/sysfs.h>
     14
     15#define MC3230_REG_XOUT			0x00
     16#define MC3230_REG_YOUT			0x01
     17#define MC3230_REG_ZOUT			0x02
     18
     19#define MC3230_REG_MODE			0x07
     20#define MC3230_MODE_OPCON_MASK		0x03
     21#define MC3230_MODE_OPCON_WAKE		0x01
     22#define MC3230_MODE_OPCON_STANDBY	0x03
     23
     24#define MC3230_REG_CHIP_ID		0x18
     25#define MC3230_CHIP_ID			0x01
     26
     27#define MC3230_REG_PRODUCT_CODE		0x3b
     28#define MC3230_PRODUCT_CODE		0x19
     29
     30/*
     31 * The accelerometer has one measurement range:
     32 *
     33 * -1.5g - +1.5g (8-bit, signed)
     34 *
     35 * scale = (1.5 + 1.5) * 9.81 / (2^8 - 1)	= 0.115411765
     36 */
     37
     38static const int mc3230_nscale = 115411765;
     39
     40#define MC3230_CHANNEL(reg, axis) {	\
     41	.type = IIO_ACCEL,	\
     42	.address = reg,	\
     43	.modified = 1,	\
     44	.channel2 = IIO_MOD_##axis,	\
     45	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
     46	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
     47}
     48
     49static const struct iio_chan_spec mc3230_channels[] = {
     50	MC3230_CHANNEL(MC3230_REG_XOUT, X),
     51	MC3230_CHANNEL(MC3230_REG_YOUT, Y),
     52	MC3230_CHANNEL(MC3230_REG_ZOUT, Z),
     53};
     54
     55struct mc3230_data {
     56	struct i2c_client *client;
     57};
     58
     59static int mc3230_set_opcon(struct mc3230_data *data, int opcon)
     60{
     61	int ret;
     62	struct i2c_client *client = data->client;
     63
     64	ret = i2c_smbus_read_byte_data(client, MC3230_REG_MODE);
     65	if (ret < 0) {
     66		dev_err(&client->dev, "failed to read mode reg: %d\n", ret);
     67		return ret;
     68	}
     69
     70	ret &= ~MC3230_MODE_OPCON_MASK;
     71	ret |= opcon;
     72
     73	ret = i2c_smbus_write_byte_data(client, MC3230_REG_MODE, ret);
     74	if (ret < 0) {
     75		dev_err(&client->dev, "failed to write mode reg: %d\n", ret);
     76		return ret;
     77	}
     78
     79	return 0;
     80}
     81
     82static int mc3230_read_raw(struct iio_dev *indio_dev,
     83				struct iio_chan_spec const *chan,
     84				int *val, int *val2, long mask)
     85{
     86	struct mc3230_data *data = iio_priv(indio_dev);
     87	int ret;
     88
     89	switch (mask) {
     90	case IIO_CHAN_INFO_RAW:
     91		ret = i2c_smbus_read_byte_data(data->client, chan->address);
     92		if (ret < 0)
     93			return ret;
     94		*val = sign_extend32(ret, 7);
     95		return IIO_VAL_INT;
     96	case IIO_CHAN_INFO_SCALE:
     97		*val = 0;
     98		*val2 = mc3230_nscale;
     99		return IIO_VAL_INT_PLUS_NANO;
    100	default:
    101		return -EINVAL;
    102	}
    103}
    104
    105static const struct iio_info mc3230_info = {
    106	.read_raw	= mc3230_read_raw,
    107};
    108
    109static int mc3230_probe(struct i2c_client *client,
    110			const struct i2c_device_id *id)
    111{
    112	int ret;
    113	struct iio_dev *indio_dev;
    114	struct mc3230_data *data;
    115
    116	/* First check chip-id and product-id */
    117	ret = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID);
    118	if (ret != MC3230_CHIP_ID)
    119		return (ret < 0) ? ret : -ENODEV;
    120
    121	ret = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE);
    122	if (ret != MC3230_PRODUCT_CODE)
    123		return (ret < 0) ? ret : -ENODEV;
    124
    125	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
    126	if (!indio_dev) {
    127		dev_err(&client->dev, "iio allocation failed!\n");
    128		return -ENOMEM;
    129	}
    130
    131	data = iio_priv(indio_dev);
    132	data->client = client;
    133	i2c_set_clientdata(client, indio_dev);
    134
    135	indio_dev->info = &mc3230_info;
    136	indio_dev->name = "mc3230";
    137	indio_dev->modes = INDIO_DIRECT_MODE;
    138	indio_dev->channels = mc3230_channels;
    139	indio_dev->num_channels = ARRAY_SIZE(mc3230_channels);
    140
    141	ret = mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
    142	if (ret < 0)
    143		return ret;
    144
    145	ret = iio_device_register(indio_dev);
    146	if (ret < 0) {
    147		dev_err(&client->dev, "device_register failed\n");
    148		mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
    149	}
    150
    151	return ret;
    152}
    153
    154static int mc3230_remove(struct i2c_client *client)
    155{
    156	struct iio_dev *indio_dev = i2c_get_clientdata(client);
    157
    158	iio_device_unregister(indio_dev);
    159
    160	return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY);
    161}
    162
    163static int mc3230_suspend(struct device *dev)
    164{
    165	struct mc3230_data *data;
    166
    167	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
    168
    169	return mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
    170}
    171
    172static int mc3230_resume(struct device *dev)
    173{
    174	struct mc3230_data *data;
    175
    176	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
    177
    178	return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
    179}
    180
    181static DEFINE_SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume);
    182
    183static const struct i2c_device_id mc3230_i2c_id[] = {
    184	{"mc3230", 0},
    185	{}
    186};
    187MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id);
    188
    189static struct i2c_driver mc3230_driver = {
    190	.driver = {
    191		.name = "mc3230",
    192		.pm = pm_sleep_ptr(&mc3230_pm_ops),
    193	},
    194	.probe		= mc3230_probe,
    195	.remove		= mc3230_remove,
    196	.id_table	= mc3230_i2c_id,
    197};
    198
    199module_i2c_driver(mc3230_driver);
    200
    201MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
    202MODULE_DESCRIPTION("mCube MC3230 3-Axis Accelerometer driver");
    203MODULE_LICENSE("GPL v2");