162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/hwmon/lis3lv02d_i2c.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Implements I2C interface for lis3lv02d (STMicroelectronics) accelerometer. 662306a36Sopenharmony_ci * Driver is based on corresponding SPI driver written by Daniel Mack 762306a36Sopenharmony_ci * (lis3lv02d_spi.c (C) 2009 Daniel Mack <daniel@caiaq.de> ). 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/i2c.h> 1862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1962306a36Sopenharmony_ci#include <linux/delay.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/of_platform.h> 2262306a36Sopenharmony_ci#include <linux/of_device.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "lis3lv02d.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DRV_NAME "lis3lv02d_i2c" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const char reg_vdd[] = "Vdd"; 2962306a36Sopenharmony_cistatic const char reg_vdd_io[] = "Vdd_IO"; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int lis3_reg_ctrl(struct lis3lv02d *lis3, bool state) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci int ret; 3462306a36Sopenharmony_ci if (state == LIS3_REG_OFF) { 3562306a36Sopenharmony_ci ret = regulator_bulk_disable(ARRAY_SIZE(lis3->regulators), 3662306a36Sopenharmony_ci lis3->regulators); 3762306a36Sopenharmony_ci } else { 3862306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(lis3->regulators), 3962306a36Sopenharmony_ci lis3->regulators); 4062306a36Sopenharmony_ci /* Chip needs time to wakeup. Not mentioned in datasheet */ 4162306a36Sopenharmony_ci usleep_range(10000, 20000); 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci return ret; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct i2c_client *c = lis3->bus_priv; 4962306a36Sopenharmony_ci return i2c_smbus_write_byte_data(c, reg, value); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct i2c_client *c = lis3->bus_priv; 5562306a36Sopenharmony_ci *v = i2c_smbus_read_byte_data(c, reg); 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic inline s32 lis3_i2c_blockread(struct lis3lv02d *lis3, int reg, int len, 6062306a36Sopenharmony_ci u8 *v) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct i2c_client *c = lis3->bus_priv; 6362306a36Sopenharmony_ci reg |= (1 << 7); /* 7th bit enables address auto incrementation */ 6462306a36Sopenharmony_ci return i2c_smbus_read_i2c_block_data(c, reg, len, v); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int lis3_i2c_init(struct lis3lv02d *lis3) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci u8 reg; 7062306a36Sopenharmony_ci int ret; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci lis3_reg_ctrl(lis3, LIS3_REG_ON); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci lis3->read(lis3, WHO_AM_I, ®); 7562306a36Sopenharmony_ci if (reg != lis3->whoami) 7662306a36Sopenharmony_ci printk(KERN_ERR "lis3: power on failure\n"); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* power up the device */ 7962306a36Sopenharmony_ci ret = lis3->read(lis3, CTRL_REG1, ®); 8062306a36Sopenharmony_ci if (ret < 0) 8162306a36Sopenharmony_ci return ret; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (lis3->whoami == WAI_3DLH) 8462306a36Sopenharmony_ci reg |= CTRL1_PM0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; 8562306a36Sopenharmony_ci else 8662306a36Sopenharmony_ci reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return lis3->write(lis3, CTRL_REG1, reg); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* Default axis mapping but it can be overwritten by platform data */ 9262306a36Sopenharmony_cistatic union axis_conversion lis3lv02d_axis_map = 9362306a36Sopenharmony_ci { .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } }; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#ifdef CONFIG_OF 9662306a36Sopenharmony_cistatic const struct of_device_id lis3lv02d_i2c_dt_ids[] = { 9762306a36Sopenharmony_ci { .compatible = "st,lis3lv02d" }, 9862306a36Sopenharmony_ci {} 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, lis3lv02d_i2c_dt_ids); 10162306a36Sopenharmony_ci#endif 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int lis3lv02d_i2c_probe(struct i2c_client *client) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci int ret = 0; 10662306a36Sopenharmony_ci struct lis3lv02d_platform_data *pdata = client->dev.platform_data; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#ifdef CONFIG_OF 10962306a36Sopenharmony_ci if (of_match_device(lis3lv02d_i2c_dt_ids, &client->dev)) { 11062306a36Sopenharmony_ci lis3_dev.of_node = client->dev.of_node; 11162306a36Sopenharmony_ci ret = lis3lv02d_init_dt(&lis3_dev); 11262306a36Sopenharmony_ci if (ret) 11362306a36Sopenharmony_ci return ret; 11462306a36Sopenharmony_ci pdata = lis3_dev.pdata; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci#endif 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (pdata) { 11962306a36Sopenharmony_ci if ((pdata->driver_features & LIS3_USE_BLOCK_READ) && 12062306a36Sopenharmony_ci (i2c_check_functionality(client->adapter, 12162306a36Sopenharmony_ci I2C_FUNC_SMBUS_I2C_BLOCK))) 12262306a36Sopenharmony_ci lis3_dev.blkread = lis3_i2c_blockread; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (pdata->axis_x) 12562306a36Sopenharmony_ci lis3lv02d_axis_map.x = pdata->axis_x; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (pdata->axis_y) 12862306a36Sopenharmony_ci lis3lv02d_axis_map.y = pdata->axis_y; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (pdata->axis_z) 13162306a36Sopenharmony_ci lis3lv02d_axis_map.z = pdata->axis_z; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (pdata->setup_resources) 13462306a36Sopenharmony_ci ret = pdata->setup_resources(); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (ret) 13762306a36Sopenharmony_ci goto fail; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci lis3_dev.regulators[0].supply = reg_vdd; 14162306a36Sopenharmony_ci lis3_dev.regulators[1].supply = reg_vdd_io; 14262306a36Sopenharmony_ci ret = regulator_bulk_get(&client->dev, 14362306a36Sopenharmony_ci ARRAY_SIZE(lis3_dev.regulators), 14462306a36Sopenharmony_ci lis3_dev.regulators); 14562306a36Sopenharmony_ci if (ret < 0) 14662306a36Sopenharmony_ci goto fail; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci lis3_dev.pdata = pdata; 14962306a36Sopenharmony_ci lis3_dev.bus_priv = client; 15062306a36Sopenharmony_ci lis3_dev.init = lis3_i2c_init; 15162306a36Sopenharmony_ci lis3_dev.read = lis3_i2c_read; 15262306a36Sopenharmony_ci lis3_dev.write = lis3_i2c_write; 15362306a36Sopenharmony_ci lis3_dev.reg_ctrl = lis3_reg_ctrl; 15462306a36Sopenharmony_ci lis3_dev.irq = client->irq; 15562306a36Sopenharmony_ci lis3_dev.ac = lis3lv02d_axis_map; 15662306a36Sopenharmony_ci lis3_dev.pm_dev = &client->dev; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci i2c_set_clientdata(client, &lis3_dev); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Provide power over the init call */ 16162306a36Sopenharmony_ci lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = lis3lv02d_init_device(&lis3_dev); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (ret) 16862306a36Sopenharmony_ci goto fail2; 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cifail2: 17262306a36Sopenharmony_ci regulator_bulk_free(ARRAY_SIZE(lis3_dev.regulators), 17362306a36Sopenharmony_ci lis3_dev.regulators); 17462306a36Sopenharmony_cifail: 17562306a36Sopenharmony_ci if (pdata && pdata->release_resources) 17662306a36Sopenharmony_ci pdata->release_resources(); 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void lis3lv02d_i2c_remove(struct i2c_client *client) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct lis3lv02d *lis3 = i2c_get_clientdata(client); 18362306a36Sopenharmony_ci struct lis3lv02d_platform_data *pdata = client->dev.platform_data; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (pdata && pdata->release_resources) 18662306a36Sopenharmony_ci pdata->release_resources(); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci lis3lv02d_joystick_disable(lis3); 18962306a36Sopenharmony_ci lis3lv02d_remove_fs(&lis3_dev); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci regulator_bulk_free(ARRAY_SIZE(lis3->regulators), 19262306a36Sopenharmony_ci lis3_dev.regulators); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 19662306a36Sopenharmony_cistatic int lis3lv02d_i2c_suspend(struct device *dev) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 19962306a36Sopenharmony_ci struct lis3lv02d *lis3 = i2c_get_clientdata(client); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!lis3->pdata || !lis3->pdata->wakeup_flags) 20262306a36Sopenharmony_ci lis3lv02d_poweroff(lis3); 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int lis3lv02d_i2c_resume(struct device *dev) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 20962306a36Sopenharmony_ci struct lis3lv02d *lis3 = i2c_get_clientdata(client); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* 21262306a36Sopenharmony_ci * pm_runtime documentation says that devices should always 21362306a36Sopenharmony_ci * be powered on at resume. Pm_runtime turns them off after system 21462306a36Sopenharmony_ci * wide resume is complete. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci if (!lis3->pdata || !lis3->pdata->wakeup_flags || 21762306a36Sopenharmony_ci pm_runtime_suspended(dev)) 21862306a36Sopenharmony_ci lis3lv02d_poweron(lis3); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci#ifdef CONFIG_PM 22562306a36Sopenharmony_cistatic int lis3_i2c_runtime_suspend(struct device *dev) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 22862306a36Sopenharmony_ci struct lis3lv02d *lis3 = i2c_get_clientdata(client); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci lis3lv02d_poweroff(lis3); 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int lis3_i2c_runtime_resume(struct device *dev) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 23762306a36Sopenharmony_ci struct lis3lv02d *lis3 = i2c_get_clientdata(client); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci lis3lv02d_poweron(lis3); 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci#endif /* CONFIG_PM */ 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic const struct i2c_device_id lis3lv02d_id[] = { 24562306a36Sopenharmony_ci {"lis3lv02d", LIS3LV02D}, 24662306a36Sopenharmony_ci {"lis331dlh", LIS331DLH}, 24762306a36Sopenharmony_ci {} 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lis3lv02d_id); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic const struct dev_pm_ops lis3_pm_ops = { 25362306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(lis3lv02d_i2c_suspend, 25462306a36Sopenharmony_ci lis3lv02d_i2c_resume) 25562306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(lis3_i2c_runtime_suspend, 25662306a36Sopenharmony_ci lis3_i2c_runtime_resume, 25762306a36Sopenharmony_ci NULL) 25862306a36Sopenharmony_ci}; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic struct i2c_driver lis3lv02d_i2c_driver = { 26162306a36Sopenharmony_ci .driver = { 26262306a36Sopenharmony_ci .name = DRV_NAME, 26362306a36Sopenharmony_ci .pm = &lis3_pm_ops, 26462306a36Sopenharmony_ci .of_match_table = of_match_ptr(lis3lv02d_i2c_dt_ids), 26562306a36Sopenharmony_ci }, 26662306a36Sopenharmony_ci .probe = lis3lv02d_i2c_probe, 26762306a36Sopenharmony_ci .remove = lis3lv02d_i2c_remove, 26862306a36Sopenharmony_ci .id_table = lis3lv02d_id, 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cimodule_i2c_driver(lis3lv02d_i2c_driver); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ciMODULE_AUTHOR("Nokia Corporation"); 27462306a36Sopenharmony_ciMODULE_DESCRIPTION("lis3lv02d I2C interface"); 27562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 276