/* Copyright (c) 2011 Bosch Sensortec GmbH Copyright (c) 2011 Unixphere Based on: BMP085 driver, bmp085.c Copyright (c) 2010 Christoph Mair This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include "bmp18x.h" struct sensor_regulator { struct regulator *vreg; const char *name; u32 min_uV; u32 max_uV; }; struct sensor_regulator bmp_vreg[] = { {NULL, "vdd", 2850000, 2850000}, {NULL, "vddio", 1800000, 1800000}, }; static int bmp18x_config_regulator(struct i2c_client *client, bool on) { int rc = 0, i; int num_vreg = ARRAY_SIZE(bmp_vreg); if (on) { for (i = 0; i < num_vreg; i++) { bmp_vreg[i].vreg = regulator_get(&client->dev, bmp_vreg[i].name); if (IS_ERR(bmp_vreg[i].vreg)) { rc = PTR_ERR(bmp_vreg[i].vreg); dev_err(&client->dev, "%s:regulator get failed rc=%d\n", __func__, rc); bmp_vreg[i].vreg = NULL; goto error_vdd; } if (regulator_count_voltages(bmp_vreg[i].vreg) > 0) { rc = regulator_set_voltage(bmp_vreg[i].vreg, bmp_vreg[i].min_uV, bmp_vreg[i].max_uV); if (rc) { dev_err(&client->dev, "%s:set_voltage failed rc=%d\n", __func__, rc); regulator_put(bmp_vreg[i].vreg); bmp_vreg[i].vreg = NULL; goto error_vdd; } } rc = regulator_enable(bmp_vreg[i].vreg); if (rc) { dev_err(&client->dev, "%s: regulator_enable failed rc =%d\n", __func__, rc); if (regulator_count_voltages(bmp_vreg[i].vreg) > 0) { regulator_set_voltage(bmp_vreg[i].vreg, 0, bmp_vreg[i].max_uV); } regulator_put(bmp_vreg[i].vreg); bmp_vreg[i].vreg = NULL; goto error_vdd; } } return rc; } else { i = num_vreg; } error_vdd: while (--i >= 0) { if (!IS_ERR_OR_NULL(bmp_vreg[i].vreg)) { if (regulator_count_voltages( bmp_vreg[i].vreg) > 0) { regulator_set_voltage(bmp_vreg[i].vreg, 0, bmp_vreg[i].max_uV); } regulator_disable(bmp_vreg[i].vreg); regulator_put(bmp_vreg[i].vreg); bmp_vreg[i].vreg = NULL; } } return rc; } static int bmp18x_init_hw(struct bmp18x_data_bus *data_bus) { int ret = 0; if (data_bus->client) { ret = bmp18x_config_regulator(data_bus->client, 1); /* The minimum start up time of bmp18x is 10ms */ usleep_range(15000, 20000); } return ret; } static void bmp18x_deinit_hw(struct bmp18x_data_bus *data_bus) { if (data_bus->client) bmp18x_config_regulator(data_bus->client, 0); } static int bmp18x_set_power(struct bmp18x_data *data, int on) { int rc = 0; int num_vreg = ARRAY_SIZE(bmp_vreg); int i; if (!on && data->power_enabled) { for (i = 0; i < num_vreg; i++) { rc = regulator_disable(bmp_vreg[i].vreg); if (rc) { dev_err(data->dev, "Regulator vdd disable failed rc=%d\n", rc); return rc; } } data->power_enabled = false; } else if (on && !data->power_enabled) { for (i = 0; i < num_vreg; i++) { rc = regulator_enable(bmp_vreg[i].vreg); if (rc) { dev_err(data->dev, "Regulator vdd enable failed rc=%d\n", rc); return rc; } } /* The minimum start up time of bmp18x is 10ms */ usleep_range(15000, 20000); data->power_enabled = true; } else { dev_warn(data->dev, "Power on=%d. enabled=%d\n", on, data->power_enabled); } return rc; } #ifdef CONFIG_OF static int bmp18x_parse_dt(struct device *dev, struct bmp18x_platform_data *pdata) { int ret = 0; u32 val; ret = of_property_read_u32(dev->of_node, "bosch,chip-id", &val); if (ret) { dev_err(dev, "no chip_id from dt\n"); return ret; } pdata->chip_id = (u8)val; ret = of_property_read_u32(dev->of_node, "bosch,oversample", &val); if (ret) { dev_err(dev, "no default_oversampling from dt\n"); return ret; } pdata->default_oversampling = (u8)val; ret = of_property_read_u32(dev->of_node, "bosch,period", &pdata->temp_measurement_period); if (ret) { dev_err(dev, "no temp_measurement_period from dt\n"); return ret; } pdata->default_sw_oversampling = of_property_read_bool(dev->of_node, "bosch,sw-oversample"); return 0; } #else static int bmp18x_parse_dt(struct device *dev, struct bmp18x_platform_data *pdata) { return -EINVAL; } #endif static int bmp18x_i2c_read_block(void *client, u8 reg, int len, char *buf) { return i2c_smbus_read_i2c_block_data(client, reg, len, buf); } static int bmp18x_i2c_read_byte(void *client, u8 reg) { return i2c_smbus_read_byte_data(client, reg); } static int bmp18x_i2c_write_byte(void *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); } static const struct bmp18x_bus_ops bmp18x_i2c_bus_ops = { .read_block = bmp18x_i2c_read_block, .read_byte = bmp18x_i2c_read_byte, .write_byte = bmp18x_i2c_write_byte }; static int bmp18x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bmp18x_data_bus data_bus = { .bops = &bmp18x_i2c_bus_ops, .client = client }; struct bmp18x_platform_data *pdata; int ret; if (client->dev.of_node) { pdata = devm_kzalloc(&client->dev, sizeof(struct bmp18x_platform_data), GFP_KERNEL); if (!pdata) { dev_err(&client->dev, "Failed to allocate memory\n"); return -ENOMEM; } ret = bmp18x_parse_dt(&client->dev, pdata); if (ret) { dev_err(&client->dev, "Failed to parse device tree\n"); return ret; } pdata->init_hw = bmp18x_init_hw; pdata->deinit_hw = bmp18x_deinit_hw; pdata->set_power = bmp18x_set_power; client->dev.platform_data = pdata; } return bmp18x_probe(&client->dev, &data_bus); } static void bmp18x_i2c_shutdown(struct i2c_client *client) { bmp18x_disable(&client->dev); } static int bmp18x_i2c_remove(struct i2c_client *client) { return bmp18x_remove(&client->dev); } #ifdef CONFIG_PM static int bmp18x_i2c_suspend(struct device *dev) { int ret = 0; struct bmp18x_data *data = dev_get_drvdata(dev); if (data->enable) ret = bmp18x_disable(dev); return ret; } static int bmp18x_i2c_resume(struct device *dev) { int ret = 0; struct bmp18x_data *data = dev_get_drvdata(dev); if (data->enable) ret = bmp18x_enable(dev); return ret; } static const struct dev_pm_ops bmp18x_i2c_pm_ops = { .suspend = bmp18x_i2c_suspend, .resume = bmp18x_i2c_resume }; #endif static const struct i2c_device_id bmp18x_id[] = { { BMP18X_NAME, 0 }, { } }; MODULE_DEVICE_TABLE(i2c, bmp18x_id); static const struct of_device_id bmp18x_of_match[] = { { .compatible = "bosch,bmp180", }, { }, }; static struct i2c_driver bmp18x_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = BMP18X_NAME, #ifdef CONFIG_PM .pm = &bmp18x_i2c_pm_ops, #endif .of_match_table = bmp18x_of_match, }, .id_table = bmp18x_id, .probe = bmp18x_i2c_probe, .shutdown = bmp18x_i2c_shutdown, .remove = bmp18x_i2c_remove }; static int __init bmp18x_i2c_init(void) { return i2c_add_driver(&bmp18x_i2c_driver); } static void __exit bmp18x_i2c_exit(void) { i2c_del_driver(&bmp18x_i2c_driver); } MODULE_AUTHOR("Eric Andersson "); MODULE_DESCRIPTION("BMP18X I2C bus driver"); MODULE_LICENSE("GPL"); module_init(bmp18x_i2c_init); module_exit(bmp18x_i2c_exit);