blob: 6ef6eb96c0e767d57e9beb676717948311b31884 [file] [log] [blame]
/* Copyright (c) 2011 Bosch Sensortec GmbH
Copyright (c) 2011 Unixphere
Based on:
BMP085 driver, bmp085.c
Copyright (c) 2010 Christoph Mair <christoph.mair@gmail.com>
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 <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#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 <eric.andersson@unixphere.com>");
MODULE_DESCRIPTION("BMP18X I2C bus driver");
MODULE_LICENSE("GPL");
module_init(bmp18x_i2c_init);
module_exit(bmp18x_i2c_exit);