blob: fb916788a9df1461fc1165062a6bcd6f4cf1c46a [file] [log] [blame]
/*
* Copyright (C) 2012-2018 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/
#define pr_fmt(fmt) "inv_mpu: " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
#include <linux/jiffies.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include "inv_mpu_iio.h"
#include "inv_mpu_dts.h"
#define INV_SPI_READ 0x80
static int inv_spi_single_write(struct inv_mpu_state *st, u8 reg, u8 data)
{
struct spi_message msg;
int res;
u8 d[2];
struct spi_transfer xfers = {
.tx_buf = d,
.bits_per_word = 8,
.len = 2,
};
pr_debug("reg_write: reg=0x%x data=0x%x\n", reg, data);
d[0] = reg;
d[1] = data;
spi_message_init(&msg);
spi_message_add_tail(&xfers, &msg);
res = spi_sync(to_spi_device(st->dev), &msg);
return res;
}
static int inv_spi_read(struct inv_mpu_state *st, u8 reg, int len, u8 *data)
{
struct spi_message msg;
int res;
u8 d[1];
struct spi_transfer xfers[] = {
{
.tx_buf = d,
.bits_per_word = 8,
.len = 1,
},
{
.rx_buf = data,
.bits_per_word = 8,
.len = len,
}
};
if (!data)
return -EINVAL;
d[0] = (reg | INV_SPI_READ);
spi_message_init(&msg);
spi_message_add_tail(&xfers[0], &msg);
spi_message_add_tail(&xfers[1], &msg);
res = spi_sync(to_spi_device(st->dev), &msg);
if (len ==1)
pr_debug("reg_read: reg=0x%x length=%d data=0x%x\n",
reg, len, data[0]);
else
pr_debug("reg_read: reg=0x%x length=%d d0=0x%x d1=0x%x\n",
reg, len, data[0], data[1]);
return res;
}
static int inv_spi_mem_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
u32 len, u8 const *data)
{
struct spi_message msg;
u8 buf[258];
int res;
struct spi_transfer xfers = {
.tx_buf = buf,
.bits_per_word = 8,
.len = len + 1,
};
if (!data || !st)
return -EINVAL;
if (len > (sizeof(buf) - 1))
return -ENOMEM;
inv_plat_single_write(st, REG_MEM_BANK_SEL, mem_addr >> 8);
inv_plat_single_write(st, REG_MEM_START_ADDR, mem_addr & 0xFF);
buf[0] = REG_MEM_R_W;
memcpy(buf + 1, data, len);
spi_message_init(&msg);
spi_message_add_tail(&xfers, &msg);
res = spi_sync(to_spi_device(st->dev), &msg);
return res;
}
static int inv_spi_mem_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
u32 len, u8 *data)
{
int res;
if (!data || !st)
return -EINVAL;
if (len > 256)
return -EINVAL;
res = inv_plat_single_write(st, REG_MEM_BANK_SEL, mem_addr >> 8);
res = inv_plat_single_write(st, REG_MEM_START_ADDR, mem_addr & 0xFF);
res = inv_plat_read(st, REG_MEM_R_W, len, data);
return res;
}
/*
* inv_mpu_probe() - probe function.
*/
static int inv_mpu_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct inv_mpu_state *st;
struct iio_dev *indio_dev;
int result;
#ifdef KERNEL_VERSION_4_X
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (indio_dev == NULL) {
pr_err("memory allocation failed\n");
result = -ENOMEM;
goto out_no_free;
}
#else
indio_dev = iio_device_alloc(sizeof(*st));
if (indio_dev == NULL) {
pr_err("memory allocation failed\n");
result = -ENOMEM;
goto out_no_free;
}
#endif
st = iio_priv(indio_dev);
st->write = inv_spi_single_write;
st->read = inv_spi_read;
st->mem_write = inv_spi_mem_write;
st->mem_read = inv_spi_mem_read;
st->dev = &spi->dev;
st->irq = spi->irq;
#if !defined(CONFIG_INV_MPU_IIO_ICM20602) \
&& !defined(CONFIG_INV_MPU_IIO_IAM20680)
st->i2c_dis = BIT_I2C_IF_DIS;
#endif
st->bus_type = BUS_SPI;
spi_set_drvdata(spi, indio_dev);
indio_dev->dev.parent = &spi->dev;
indio_dev->name = id->name;
#ifdef CONFIG_OF
result = invensense_mpu_parse_dt(st->dev, &st->plat_data);
if (result)
# ifdef KERNEL_VERSION_4_X
return -ENODEV;
# else
goto out_free;
# endif
/* Power on device */
if (st->plat_data.power_on) {
result = st->plat_data.power_on(&st->plat_data);
if (result < 0) {
dev_err(st->dev, "power_on failed: %d\n", result);
# ifdef KERNEL_VERSION_4_X
return -ENODEV;
# else
goto out_free;
# endif
}
pr_info("%s: power on here.\n", __func__);
}
pr_info("%s: power on.\n", __func__);
msleep(100);
#else
if (dev_get_platdata(st->dev) == NULL)
# ifdef KERNEL_VERSION_4_X
return -ENODEV;
# else
goto out_free;
# endif
st->plat_data = *(struct mpu_platform_data *)dev_get_platdata(st->dev);
#endif
/* power is turned on inside check chip type */
result = inv_check_chip_type(indio_dev, id->name);
if (result)
#ifdef KERNEL_VERSION_4_X
return -ENODEV;
#else
goto out_free;
#endif
result = inv_mpu_configure_ring(indio_dev);
if (result) {
pr_err("configure ring buffer fail\n");
goto out_free;
}
#ifdef KERNEL_VERSION_4_X
result = devm_iio_device_register(st->dev, indio_dev);
if (result) {
pr_err("IIO device register fail\n");
goto out_unreg_ring;
}
#else
result = iio_buffer_register(indio_dev, indio_dev->channels,
indio_dev->num_channels);
if (result) {
pr_err("ring buffer register fail\n");
goto out_unreg_ring;
}
result = iio_device_register(indio_dev);
if (result) {
pr_err("IIO device register fail\n");
goto out_remove_ring;
}
#endif
result = inv_create_dmp_sysfs(indio_dev);
if (result) {
pr_err("create dmp sysfs failed\n");
goto out_unreg_iio;
}
init_waitqueue_head(&st->wait_queue);
st->resume_state = true;
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_init(&st->wake_lock, WAKE_LOCK_SUSPEND, "inv_mpu");
#else
wakeup_source_init(&st->wake_lock, "inv_mpu");
#endif
dev_info(st->dev, "%s ma-kernel-%s is ready to go!\n",
indio_dev->name, INVENSENSE_DRIVER_VERSION);
#ifdef SENSOR_DATA_FROM_REGISTERS
pr_info("Data read from registers\n");
#else
pr_info("Data read from FIFO\n");
#endif
#ifdef TIMER_BASED_BATCHING
pr_info("Timer based batching\n");
#endif
return 0;
#ifdef KERNEL_VERSION_4_X
out_unreg_iio:
devm_iio_device_unregister(st->dev, indio_dev);
out_unreg_ring:
inv_mpu_unconfigure_ring(indio_dev);
out_free:
devm_iio_device_free(st->dev, indio_dev);
out_no_free:
#else
out_unreg_iio:
iio_device_unregister(indio_dev);
out_remove_ring:
iio_buffer_unregister(indio_dev);
out_unreg_ring:
inv_mpu_unconfigure_ring(indio_dev);
out_free:
iio_device_free(indio_dev);
out_no_free:
#endif
dev_err(st->dev, "%s failed %d\n", __func__, result);
return -EIO;
}
static void inv_mpu_shutdown(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct inv_mpu_state *st = iio_priv(indio_dev);
int result;
mutex_lock(&indio_dev->mlock);
inv_switch_power_in_lp(st, true);
dev_dbg(st->dev, "Shutting down %s...\n", st->hw->name);
/* reset to make sure previous state are not there */
result = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_H_RESET);
if (result)
dev_err(st->dev, "Failed to reset %s\n",
st->hw->name);
msleep(POWER_UP_TIME);
/* turn off power to ensure gyro engine is off */
result = inv_set_power(st, false);
if (result)
dev_err(st->dev, "Failed to turn off %s\n",
st->hw->name);
inv_switch_power_in_lp(st, false);
mutex_unlock(&indio_dev->mlock);
}
/*
* inv_mpu_remove() - remove function.
*/
static int inv_mpu_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct inv_mpu_state *st = iio_priv(indio_dev);
#ifdef KERNEL_VERSION_4_X
devm_iio_device_unregister(st->dev, indio_dev);
#else
iio_device_unregister(indio_dev);
iio_buffer_unregister(indio_dev);
#endif
inv_mpu_unconfigure_ring(indio_dev);
#ifdef KERNEL_VERSION_4_X
devm_iio_device_free(st->dev, indio_dev);
#else
iio_device_free(indio_dev);
#endif
dev_info(st->dev, "inv-mpu-iio module removed.\n");
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int inv_mpu_spi_suspend(struct device *dev)
{
struct iio_dev *indio_dev = spi_get_drvdata(to_spi_device(dev));
return inv_mpu_suspend(indio_dev);
}
static void inv_mpu_spi_complete(struct device *dev)
{
struct iio_dev *indio_dev = spi_get_drvdata(to_spi_device(dev));
inv_mpu_complete(indio_dev);
}
#endif
static const struct dev_pm_ops inv_mpu_spi_pmops = {
#ifdef CONFIG_PM_SLEEP
.suspend = inv_mpu_spi_suspend,
.complete = inv_mpu_spi_complete,
#endif
};
/* device id table is used to identify what device can be
* supported by this driver
*/
static const struct spi_device_id inv_mpu_id[] = {
#ifdef CONFIG_INV_MPU_IIO_ICM20648
{"icm20645", ICM20645},
{"icm10340", ICM10340},
{"icm20648", ICM20648},
#else
{"icm20608d", ICM20608D},
{"icm20690", ICM20690},
{"icm20602", ICM20602},
{"iam20680", IAM20680},
#endif
{}
};
MODULE_DEVICE_TABLE(spi, inv_mpu_id);
static struct spi_driver inv_mpu_driver = {
.probe = inv_mpu_probe,
.remove = inv_mpu_remove,
.shutdown = inv_mpu_shutdown,
.id_table = inv_mpu_id,
.driver = {
.owner = THIS_MODULE,
.name = "inv-mpu-iio-spi",
.pm = &inv_mpu_spi_pmops,
},
};
module_spi_driver(inv_mpu_driver);
MODULE_AUTHOR("Invensense Corporation");
MODULE_DESCRIPTION("Invensense SPI device driver");
MODULE_LICENSE("GPL");