331 lines
7.7 KiB
C
331 lines
7.7 KiB
C
/* 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);
|