diff options
Diffstat (limited to 'drivers/hwmon/mcp3021.c')
-rw-r--r-- | drivers/hwmon/mcp3021.c | 67 |
1 files changed, 48 insertions, 19 deletions
diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c index d0afc0cd3ff..eedb32292d6 100644 --- a/drivers/hwmon/mcp3021.c +++ b/drivers/hwmon/mcp3021.c @@ -1,8 +1,9 @@ /* - * mcp3021.c - driver for the Microchip MCP3021 chip + * mcp3021.c - driver for Microchip MCP3021 and MCP3221 * * Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc. * Author: Mingkai Hu <Mingkai.hu@freescale.com> + * Reworked by Sven Schuchmann <schuchmann@schleissheimer.de> * * This driver export the value of analog input voltage to sysfs, the * voltage unit is mV. Through the sysfs interface, lm-sensors tool @@ -34,16 +35,31 @@ #define MCP3021_OUTPUT_RES 10 /* 10-bit resolution */ #define MCP3021_OUTPUT_SCALE 4 +#define MCP3221_SAR_SHIFT 0 +#define MCP3221_SAR_MASK 0xfff +#define MCP3221_OUTPUT_RES 12 /* 12-bit resolution */ +#define MCP3221_OUTPUT_SCALE 1 + +enum chips { + mcp3021, + mcp3221 +}; + /* * Client data (each client gets its own) */ struct mcp3021_data { struct device *hwmon_dev; u32 vdd; /* device power supply */ + u16 sar_shift; + u16 sar_mask; + u8 output_res; + u8 output_scale; }; static int mcp3021_read16(struct i2c_client *client) { + struct mcp3021_data *data = i2c_get_clientdata(client); int ret; u16 reg; __be16 buf; @@ -61,20 +77,20 @@ static int mcp3021_read16(struct i2c_client *client) * The ten-bit output code is composed of the lower 4-bit of the * first byte and the upper 6-bit of the second byte. */ - reg = (reg >> MCP3021_SAR_SHIFT) & MCP3021_SAR_MASK; + reg = (reg >> data->sar_shift) & data->sar_mask; return reg; } -static inline u16 volts_from_reg(u16 vdd, u16 val) +static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val) { if (val == 0) return 0; - val = val * MCP3021_OUTPUT_SCALE - MCP3021_OUTPUT_SCALE / 2; + val = val * data->output_scale - data->output_scale / 2; - return val * DIV_ROUND_CLOSEST(vdd, - (1 << MCP3021_OUTPUT_RES) * MCP3021_OUTPUT_SCALE); + return val * DIV_ROUND_CLOSEST(data->vdd, + (1 << data->output_res) * data->output_scale); } static ssize_t show_in_input(struct device *dev, struct device_attribute *attr, @@ -88,7 +104,8 @@ static ssize_t show_in_input(struct device *dev, struct device_attribute *attr, if (reg < 0) return reg; - in_input = volts_from_reg(data->vdd, reg); + in_input = volts_from_reg(data, reg); + return sprintf(buf, "%d\n", in_input); } @@ -103,25 +120,39 @@ static int mcp3021_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; - data = kzalloc(sizeof(struct mcp3021_data), GFP_KERNEL); + data = devm_kzalloc(&client->dev, sizeof(struct mcp3021_data), + GFP_KERNEL); if (!data) return -ENOMEM; i2c_set_clientdata(client, data); + switch (id->driver_data) { + case mcp3021: + data->sar_shift = MCP3021_SAR_SHIFT; + data->sar_mask = MCP3021_SAR_MASK; + data->output_res = MCP3021_OUTPUT_RES; + data->output_scale = MCP3021_OUTPUT_SCALE; + break; + + case mcp3221: + data->sar_shift = MCP3221_SAR_SHIFT; + data->sar_mask = MCP3221_SAR_MASK; + data->output_res = MCP3221_OUTPUT_RES; + data->output_scale = MCP3221_OUTPUT_SCALE; + break; + } + if (client->dev.platform_data) { data->vdd = *(u32 *)client->dev.platform_data; - if (data->vdd > MCP3021_VDD_MAX || - data->vdd < MCP3021_VDD_MIN) { - err = -EINVAL; - goto exit_free; - } + if (data->vdd > MCP3021_VDD_MAX || data->vdd < MCP3021_VDD_MIN) + return -EINVAL; } else data->vdd = MCP3021_VDD_REF; err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr); if (err) - goto exit_free; + return err; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -133,8 +164,6 @@ static int mcp3021_probe(struct i2c_client *client, exit_remove: sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); -exit_free: - kfree(data); return err; } @@ -144,13 +173,13 @@ static int mcp3021_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); - kfree(data); return 0; } static const struct i2c_device_id mcp3021_id[] = { - { "mcp3021", 0 }, + { "mcp3021", mcp3021 }, + { "mcp3221", mcp3221 }, { } }; MODULE_DEVICE_TABLE(i2c, mcp3021_id); @@ -167,5 +196,5 @@ static struct i2c_driver mcp3021_driver = { module_i2c_driver(mcp3021_driver); MODULE_AUTHOR("Mingkai Hu <Mingkai.hu@freescale.com>"); -MODULE_DESCRIPTION("Microchip MCP3021 driver"); +MODULE_DESCRIPTION("Microchip MCP3021/MCP3221 driver"); MODULE_LICENSE("GPL"); |