/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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");