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");  |