Browse Source

staging: iio: add support for hmc5883/hmc5883l to hmc5843 magnetometer driver

v3 addresses review comments:
* rotate tables (Jonathan Cameron)
* remove trailing space, add newline in sysfs output (Jonathan Cameron)
* split out patch for reorganization of hmc5843_set_rate() (Jonathan Cameron)
* use static table to describe chip variants (Jonathan Cameron)

v2 addresses review comments:
* fixes and cleanups have been split out (Jonathan Cameron)
* constants are generally prefixed HMC5843_, except when related
  specifically to hmc5883 (Jonathan Cameron)
* simplify code and avoid temp buffer in
  hmc5843_show_sampling_frequencies_available() (Lars-Peter Clausen)
* use sysfs_streq() instead of strncmp()/strlen() in
  hmc5843_check_sampling_frequency() (Lars-Peter Clausen)

Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net>
Acked-by: Jonathan Cameron <jic23@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Peter Meerwald 13 years ago
parent
commit
357fcff5bd
2 changed files with 149 additions and 45 deletions
  1. 4 4
      drivers/staging/iio/magnetometer/Kconfig
  2. 145 41
      drivers/staging/iio/magnetometer/hmc5843.c

+ 4 - 4
drivers/staging/iio/magnetometer/Kconfig

@@ -15,13 +15,13 @@ config SENSORS_AK8975
 	  will be called ak8975.
 	  will be called ak8975.
 
 
 config SENSORS_HMC5843
 config SENSORS_HMC5843
-	tristate "Honeywell HMC5843 3-Axis Magnetometer"
+	tristate "Honeywell HMC5843/5883/5883L 3-Axis Magnetometer"
 	depends on I2C
 	depends on I2C
 	help
 	help
-	  Say Y here to add support for the Honeywell HMC 5843 3-Axis
-	  Magnetometer (digital compass).
+	  Say Y here to add support for the Honeywell HMC5843, HMC5883 and
+	  HMC5883L 3-Axis Magnetometer (digital compass).
 
 
 	  To compile this driver as a module, choose M here: the module
 	  To compile this driver as a module, choose M here: the module
-	  will be called hmc5843
+	  will be called hmc5843.
 
 
 endmenu
 endmenu

+ 145 - 41
drivers/staging/iio/magnetometer/hmc5843.c

@@ -2,6 +2,8 @@
     Author: Shubhrajyoti Datta <shubhrajyoti@ti.com>
     Author: Shubhrajyoti Datta <shubhrajyoti@ti.com>
     Acknowledgement: Jonathan Cameron <jic23@cam.ac.uk> for valuable inputs.
     Acknowledgement: Jonathan Cameron <jic23@cam.ac.uk> for valuable inputs.
 
 
+    Support for HMC5883 and HMC5883L by Peter Meerwald <pmeerw@pmeerw.net>.
+
     This program is free software; you can redistribute it and/or modify
     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
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     the Free Software Foundation; either version 2 of the License, or
@@ -44,6 +46,11 @@
 #define HMC5843_ID_REG_B			0x0B
 #define HMC5843_ID_REG_B			0x0B
 #define HMC5843_ID_REG_C			0x0C
 #define HMC5843_ID_REG_C			0x0C
 
 
+enum hmc5843_ids {
+	HMC5843_ID,
+	HMC5883_ID,
+	HMC5883L_ID,
+};
 
 
 /*
 /*
  * Beware: identification of the HMC5883 is still "H43";
  * Beware: identification of the HMC5883 is still "H43";
@@ -103,8 +110,16 @@ static const int hmc5843_regval_to_nanoscale[] = {
 	6173, 7692, 10309, 12821, 18868, 21739, 25641, 35714
 	6173, 7692, 10309, 12821, 18868, 21739, 25641, 35714
 };
 };
 
 
+static const int hmc5883_regval_to_nanoscale[] = {
+	7812, 9766, 13021, 16287, 24096, 27701, 32573, 45662
+};
+
+static const int hmc5883l_regval_to_nanoscale[] = {
+	7299, 9174, 12195, 15152, 22727, 25641, 30303, 43478
+};
+
 /*
 /*
- * From the datasheet:
+ * From the HMC5843 datasheet:
  * Value	| Sensor input field range (Ga)	| Gain (counts/milli-Gauss)
  * Value	| Sensor input field range (Ga)	| Gain (counts/milli-Gauss)
  * 0		| (+-)0.7			| 1620
  * 0		| (+-)0.7			| 1620
  * 1		| (+-)1.0			| 1300
  * 1		| (+-)1.0			| 1300
@@ -114,44 +129,75 @@ static const int hmc5843_regval_to_nanoscale[] = {
  * 5		| (+-)3.8			| 460
  * 5		| (+-)3.8			| 460
  * 6		| (+-)4.5			| 390
  * 6		| (+-)4.5			| 390
  * 7		| (+-)6.5			| 280
  * 7		| (+-)6.5			| 280
+ *
+ * From the HMC5883 datasheet:
+ * Value	| Recommended sensor field range (Ga)	| Gain (counts/Gauss)
+ * 0		| (+-)0.9				| 1280
+ * 1		| (+-)1.2				| 1024
+ * 2		| (+-)1.9				| 768
+ * 3		| (+-)2.5				| 614
+ * 4		| (+-)4.0				| 415
+ * 5		| (+-)4.6				| 361
+ * 6		| (+-)5.5				| 307
+ * 7		| (+-)7.9				| 219
+ *
+ * From the HMC5883L datasheet:
+ * Value	| Recommended sensor field range (Ga)	| Gain (LSB/Gauss)
+ * 0		| (+-)0.88				| 1370
+ * 1		| (+-)1.3				| 1090
+ * 2		| (+-)1.9				| 820
+ * 3		| (+-)2.5				| 660
+ * 4		| (+-)4.0				| 440
+ * 5		| (+-)4.7				| 390
+ * 6		| (+-)5.6				| 330
+ * 7		| (+-)8.1				| 230
  */
  */
 static const int hmc5843_regval_to_input_field_mga[] = {
 static const int hmc5843_regval_to_input_field_mga[] = {
-	700,
-	1000,
-	1500,
-	2000,
-	3200,
-	3800,
-	4500,
-	6500
+	700, 1000, 1500, 2000, 3200, 3800, 4500, 6500
+};
+
+static const int hmc5883_regval_to_input_field_mga[] = {
+	900, 1200, 1900, 2500, 4000, 4600, 5500, 7900
+};
+
+static const int hmc5883l_regval_to_input_field_mga[] = {
+	880, 1300, 1900, 2500, 4000, 4700, 5600, 8100
 };
 };
 
 
 /*
 /*
  * From the datasheet:
  * From the datasheet:
- * Value	| Data output rate (Hz)
- * 0		| 0.5
- * 1		| 1
- * 2		| 2
- * 3		| 5
- * 4		| 10 (default)
- * 5		| 20
- * 6		| 50
- * 7		| Not used
+ * Value	| HMC5843		| HMC5883/HMC5883L
+ *		| Data output rate (Hz)	| Data output rate (Hz)
+ * 0		| 0.5			| 0.75
+ * 1		| 1			| 1.5
+ * 2		| 2			| 3
+ * 3		| 5			| 7.5
+ * 4		| 10 (default)		| 15
+ * 5		| 20			| 30
+ * 6		| 50			| 75
+ * 7		| Not used		| Not used
  */
  */
 static const char * const hmc5843_regval_to_sample_freq[] = {
 static const char * const hmc5843_regval_to_sample_freq[] = {
-	"0.5",
-	"1",
-	"2",
-	"5",
-	"10",
-	"20",
-	"50",
+	"0.5", "1", "2", "5", "10", "20", "50",
+};
+
+static const char * const hmc5883_regval_to_sample_freq[] = {
+	"0.75", "1.5", "3", "7.5", "15", "30", "75",
 };
 };
 
 
 /* Addresses to scan: 0x1E */
 /* Addresses to scan: 0x1E */
 static const unsigned short normal_i2c[] = { HMC5843_I2C_ADDRESS,
 static const unsigned short normal_i2c[] = { HMC5843_I2C_ADDRESS,
 					     I2C_CLIENT_END };
 					     I2C_CLIENT_END };
 
 
+/* Describe chip variants */
+struct hmc5843_chip_info {
+	const struct iio_chan_spec *channels;
+	int num_channels;
+	const char * const *regval_to_sample_freq;
+	const int *regval_to_input_field_mga;
+	const int *regval_to_nanoscale;
+};
+
 /* Each client has this additional data */
 /* Each client has this additional data */
 struct hmc5843_data {
 struct hmc5843_data {
 	struct mutex lock;
 	struct mutex lock;
@@ -159,6 +205,7 @@ struct hmc5843_data {
 	u8 meas_conf;
 	u8 meas_conf;
 	u8 operating_mode;
 	u8 operating_mode;
 	u8 range;
 	u8 range;
+	const struct hmc5843_chip_info *variant;
 };
 };
 
 
 /* The lower two bits contain the current conversion mode */
 /* The lower two bits contain the current conversion mode */
@@ -334,7 +381,27 @@ static IIO_DEVICE_ATTR(meas_conf,
 			hmc5843_set_measurement_configuration,
 			hmc5843_set_measurement_configuration,
 			0);
 			0);
 
 
-static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("0.5 1 2 5 10 20 50");
+static ssize_t hmc5843_show_sampling_frequencies_available(struct device *dev,
+						struct device_attribute *attr,
+						char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct hmc5843_data *data = iio_priv(indio_dev);
+	ssize_t total_n = 0;
+	int i;
+
+	for (i = 0; i < HMC5843_RATE_NOT_USED; i++) {
+		ssize_t n = sprintf(buf, "%s ", data->variant->regval_to_sample_freq[i]);
+		buf += n;
+		total_n += n;
+	}
+	/* replace trailing space by newline */
+	buf[-1] = '\n';
+
+	return total_n;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hmc5843_show_sampling_frequencies_available);
 
 
 static s32 hmc5843_set_rate(struct i2c_client *client,
 static s32 hmc5843_set_rate(struct i2c_client *client,
 				u8 rate)
 				u8 rate)
@@ -356,12 +423,11 @@ static s32 hmc5843_set_rate(struct i2c_client *client,
 static int hmc5843_check_sampling_frequency(struct hmc5843_data *data,
 static int hmc5843_check_sampling_frequency(struct hmc5843_data *data,
 						const char *buf)
 						const char *buf)
 {
 {
-	const char * const *samp_freq = hmc5843_regval_to_sample_freq;
+	const char * const *samp_freq = data->variant->regval_to_sample_freq;
 	int i;
 	int i;
 
 
 	for (i = 0; i < HMC5843_RATE_NOT_USED; i++) {
 	for (i = 0; i < HMC5843_RATE_NOT_USED; i++) {
-		if (strncmp(buf, samp_freq[i],
-			strlen(samp_freq[i])) == 0)
+		if (sysfs_streq(buf, samp_freq[i]))
 			return i;
 			return i;
 	}
 	}
 
 
@@ -404,13 +470,14 @@ static ssize_t hmc5843_show_sampling_frequency(struct device *dev,
 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 	struct i2c_client *client = to_i2c_client(indio_dev->dev.parent);
 	struct i2c_client *client = to_i2c_client(indio_dev->dev.parent);
 	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
 	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	struct hmc5843_data *data = iio_priv(indio_dev);
 	s32 rate;
 	s32 rate;
 
 
 	rate = i2c_smbus_read_byte_data(client, this_attr->address);
 	rate = i2c_smbus_read_byte_data(client, this_attr->address);
 	if (rate < 0)
 	if (rate < 0)
 		return rate;
 		return rate;
 	rate = (rate & HMC5843_RATE_BITMASK) >> HMC5843_RATE_OFFSET;
 	rate = (rate & HMC5843_RATE_BITMASK) >> HMC5843_RATE_OFFSET;
-	return sprintf(buf, "%s\n", hmc5843_regval_to_sample_freq[rate]);
+	return sprintf(buf, "%s\n", data->variant->regval_to_sample_freq[rate]);
 }
 }
 
 
 static IIO_DEVICE_ATTR(sampling_frequency,
 static IIO_DEVICE_ATTR(sampling_frequency,
@@ -428,7 +495,7 @@ static ssize_t hmc5843_show_range_gain(struct device *dev,
 	struct hmc5843_data *data = iio_priv(indio_dev);
 	struct hmc5843_data *data = iio_priv(indio_dev);
 
 
 	range = data->range;
 	range = data->range;
-	return sprintf(buf, "%d\n", hmc5843_regval_to_input_field_mga[range]);
+	return sprintf(buf, "%d\n", data->variant->regval_to_input_field_mga[range]);
 }
 }
 
 
 static ssize_t hmc5843_set_range_gain(struct device *dev,
 static ssize_t hmc5843_set_range_gain(struct device *dev,
@@ -486,7 +553,7 @@ static int hmc5843_read_raw(struct iio_dev *indio_dev,
 						val);
 						val);
 	case IIO_CHAN_INFO_SCALE:
 	case IIO_CHAN_INFO_SCALE:
 		*val = 0;
 		*val = 0;
-		*val2 = hmc5843_regval_to_nanoscale[data->range];
+		*val2 = data->variant->regval_to_nanoscale[data->range];
 		return IIO_VAL_INT_PLUS_NANO;
 		return IIO_VAL_INT_PLUS_NANO;
 	};
 	};
 	return -EINVAL;
 	return -EINVAL;
@@ -508,12 +575,18 @@ static const struct iio_chan_spec hmc5843_channels[] = {
 	HMC5843_CHANNEL(Z, HMC5843_DATA_OUT_Z_MSB_REG),
 	HMC5843_CHANNEL(Z, HMC5843_DATA_OUT_Z_MSB_REG),
 };
 };
 
 
+static const struct iio_chan_spec hmc5883_channels[] = {
+	HMC5843_CHANNEL(X, HMC5843_DATA_OUT_X_MSB_REG),
+	HMC5843_CHANNEL(Y, HMC5883_DATA_OUT_Y_MSB_REG),
+	HMC5843_CHANNEL(Z, HMC5883_DATA_OUT_Z_MSB_REG),
+};
+
 static struct attribute *hmc5843_attributes[] = {
 static struct attribute *hmc5843_attributes[] = {
 	&iio_dev_attr_meas_conf.dev_attr.attr,
 	&iio_dev_attr_meas_conf.dev_attr.attr,
 	&iio_dev_attr_operating_mode.dev_attr.attr,
 	&iio_dev_attr_operating_mode.dev_attr.attr,
 	&iio_dev_attr_sampling_frequency.dev_attr.attr,
 	&iio_dev_attr_sampling_frequency.dev_attr.attr,
 	&iio_dev_attr_in_magn_range.dev_attr.attr,
 	&iio_dev_attr_in_magn_range.dev_attr.attr,
-	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
 	NULL
 	NULL
 };
 };
 
 
@@ -521,6 +594,33 @@ static const struct attribute_group hmc5843_group = {
 	.attrs = hmc5843_attributes,
 	.attrs = hmc5843_attributes,
 };
 };
 
 
+static const struct hmc5843_chip_info hmc5843_chip_info_tbl[] = {
+	[HMC5843_ID] = {
+		.channels = hmc5843_channels,
+		.num_channels = ARRAY_SIZE(hmc5843_channels),
+		.regval_to_sample_freq = hmc5843_regval_to_sample_freq,
+		.regval_to_input_field_mga =
+			hmc5843_regval_to_input_field_mga,
+		.regval_to_nanoscale = hmc5843_regval_to_nanoscale,
+	},
+	[HMC5883_ID] = {
+		.channels = hmc5883_channels,
+		.num_channels = ARRAY_SIZE(hmc5883_channels),
+		.regval_to_sample_freq = hmc5883_regval_to_sample_freq,
+		.regval_to_input_field_mga =
+			hmc5883_regval_to_input_field_mga,
+		.regval_to_nanoscale = hmc5883_regval_to_nanoscale,
+	},
+	[HMC5883L_ID] = {
+		.channels = hmc5883_channels,
+		.num_channels = ARRAY_SIZE(hmc5883_channels),
+		.regval_to_sample_freq = hmc5883_regval_to_sample_freq,
+		.regval_to_input_field_mga =
+			hmc5883l_regval_to_input_field_mga,
+		.regval_to_nanoscale = hmc5883l_regval_to_nanoscale,
+	},
+};
+
 static int hmc5843_detect(struct i2c_client *client,
 static int hmc5843_detect(struct i2c_client *client,
 			  struct i2c_board_info *info)
 			  struct i2c_board_info *info)
 {
 {
@@ -540,19 +640,23 @@ static int hmc5843_detect(struct i2c_client *client,
 	return 0;
 	return 0;
 }
 }
 
 
-/* Called when we have found a new HMC5843 */
-static void hmc5843_init_client(struct i2c_client *client)
+/* Called when we have found a new HMC58X3 */
+static void hmc5843_init_client(struct i2c_client *client,
+				const struct i2c_device_id *id)
 {
 {
 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
 	struct hmc5843_data *data = iio_priv(indio_dev);
 	struct hmc5843_data *data = iio_priv(indio_dev);
 
 
+	data->variant = &hmc5843_chip_info_tbl[id->driver_data];
+	indio_dev->channels = data->variant->channels;
+	indio_dev->num_channels = data->variant->num_channels;
 	hmc5843_set_meas_conf(client, data->meas_conf);
 	hmc5843_set_meas_conf(client, data->meas_conf);
 	hmc5843_set_rate(client, data->rate);
 	hmc5843_set_rate(client, data->rate);
 	hmc5843_configure(client, data->operating_mode);
 	hmc5843_configure(client, data->operating_mode);
 	i2c_smbus_write_byte_data(client, HMC5843_CONFIG_REG_B, data->range);
 	i2c_smbus_write_byte_data(client, HMC5843_CONFIG_REG_B, data->range);
 	mutex_init(&data->lock);
 	mutex_init(&data->lock);
 
 
-	pr_info("HMC5843 initialized\n");
+	pr_info("%s initialized\n", id->name);
 }
 }
 
 
 static const struct iio_info hmc5843_info = {
 static const struct iio_info hmc5843_info = {
@@ -581,12 +685,10 @@ static int hmc5843_probe(struct i2c_client *client,
 	data->operating_mode = HMC5843_MODE_CONVERSION_CONTINUOUS;
 	data->operating_mode = HMC5843_MODE_CONVERSION_CONTINUOUS;
 
 
 	i2c_set_clientdata(client, indio_dev);
 	i2c_set_clientdata(client, indio_dev);
-	hmc5843_init_client(client);
+	hmc5843_init_client(client, id);
 
 
 	indio_dev->info = &hmc5843_info;
 	indio_dev->info = &hmc5843_info;
 	indio_dev->name = id->name;
 	indio_dev->name = id->name;
-	indio_dev->channels = hmc5843_channels;
-	indio_dev->num_channels = ARRAY_SIZE(hmc5843_channels);
 	indio_dev->dev.parent = &client->dev;
 	indio_dev->dev.parent = &client->dev;
 	indio_dev->modes = INDIO_DIRECT_MODE;
 	indio_dev->modes = INDIO_DIRECT_MODE;
 
 
@@ -639,7 +741,9 @@ static SIMPLE_DEV_PM_OPS(hmc5843_pm_ops, hmc5843_suspend, hmc5843_resume);
 #endif
 #endif
 
 
 static const struct i2c_device_id hmc5843_id[] = {
 static const struct i2c_device_id hmc5843_id[] = {
-	{ "hmc5843", 0 },
+	{ "hmc5843", HMC5843_ID },
+	{ "hmc5883", HMC5883_ID },
+	{ "hmc5883l", HMC5883L_ID },
 	{ }
 	{ }
 };
 };
 MODULE_DEVICE_TABLE(i2c, hmc5843_id);
 MODULE_DEVICE_TABLE(i2c, hmc5843_id);
@@ -658,5 +762,5 @@ static struct i2c_driver hmc5843_driver = {
 module_i2c_driver(hmc5843_driver);
 module_i2c_driver(hmc5843_driver);
 
 
 MODULE_AUTHOR("Shubhrajyoti Datta <shubhrajyoti@ti.com");
 MODULE_AUTHOR("Shubhrajyoti Datta <shubhrajyoti@ti.com");
-MODULE_DESCRIPTION("HMC5843 driver");
+MODULE_DESCRIPTION("HMC5843/5883/5883L driver");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");