diff options
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/Kconfig | 39 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 6 | ||||
-rw-r--r-- | drivers/hwmon/da9052-adc.c | 644 | ||||
-rw-r--r-- | drivers/hwmon/isl29003.c | 439 | ||||
-rw-r--r-- | drivers/hwmon/max17135-hwmon.c | 173 | ||||
-rw-r--r-- | drivers/hwmon/mxc_mma7450.c | 788 | ||||
-rw-r--r-- | drivers/hwmon/mxc_mma8450.c | 390 | ||||
-rw-r--r-- | drivers/hwmon/mxc_mma8451.c | 371 |
8 files changed, 2850 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e19cf8eb6ccf..b0fdae27b74c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -300,6 +300,16 @@ config SENSORS_ATXP1 This driver can also be built as a module. If so, the module will be called atxp1. +config SENSORS_DA9052 + tristate "Dialog DA9052 HWMon" + depends on PMIC_DA9052 + help + Say y here to support the ADC found on + Dialog Semiconductor DA9052 PMIC. + + This driver can also be built as a module. If so, the module + will be called da9052_hwmon. + config SENSORS_DS1621 tristate "Dallas Semiconductor DS1621 and DS1625" depends on I2C @@ -652,6 +662,15 @@ config SENSORS_MAX1619 This driver can also be built as a module. If so, the module will be called max1619. +config SENSORS_MAX17135 + tristate "Maxim MAX17135" + depends on I2C + help + If you say yes here you get support for MAX17135 PMIC sensor. + + This driver can also be built as a module. If so, the module + will be called max17135_sensor. + config SENSORS_MAX6650 tristate "Maxim MAX6650 sensor chip" depends on I2C && EXPERIMENTAL @@ -1166,4 +1185,24 @@ config SENSORS_LIS3LV02D endif # ACPI +config MXC_MMA7450 + tristate "MMA7450 device driver" + depends on MACH_MX31_3DS || MACH_MX23EVK + default n + +config SENSORS_ISL29003 + tristate "ISL29003 Light Sensor" + depends on MACH_MX37_3DS || MACH_MX51_3DS + default y + +config MXC_MMA8450 + tristate "MMA8450 device driver" + depends on I2C + default y + +config MXC_MMA8451 + tristate "MMA8451 device driver" + depends on I2C + default y + endif # HWMON diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 2138ceb1a713..0dfdc7826dfa 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_SENSORS_AMS) += ams/ obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o +obj-$(CONFIG_SENSORS_DA9052) += da9052-adc.o obj-$(CONFIG_SENSORS_DME1737) += dme1737.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o @@ -78,6 +79,7 @@ obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o +obj-$(CONFIG_SENSORS_MAX17135) += max17135-hwmon.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o @@ -103,6 +105,10 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o +obj-$(CONFIG_SENSORS_ISL29003) += isl29003.o +obj-$(CONFIG_MXC_MMA7450) += mxc_mma7450.o +obj-$(CONFIG_MXC_MMA8450) += mxc_mma8450.o +obj-$(CONFIG_MXC_MMA8451) += mxc_mma8451.o ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/hwmon/da9052-adc.c b/drivers/hwmon/da9052-adc.c new file mode 100644 index 000000000000..647e580e2131 --- /dev/null +++ b/drivers/hwmon/da9052-adc.c @@ -0,0 +1,644 @@ +/* + * da9052-adc.c -- ADC Driver for Dialog DA9052 + * + * Copyright(c) 2009 Dialog Semiconductor Ltd. + * + * Author: Dialog Semiconductor Ltd <dchen@diasemi.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. + * + */ +#include <linux/platform_device.h> +#include <linux/hwmon-sysfs.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/hwmon.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/mfd/da9052/da9052.h> +#include <linux/mfd/da9052/reg.h> +#include <linux/mfd/da9052/adc.h> + +#define DRIVER_NAME "da9052-adc" + +static const char *input_names[] = { + [DA9052_ADC_VDDOUT] = "VDDOUT", + [DA9052_ADC_ICH] = "CHARGING CURRENT", + [DA9052_ADC_TBAT] = "BATTERY TEMP", + [DA9052_ADC_VBAT] = "BATTERY VOLTAGE", + [DA9052_ADC_ADCIN4] = "ADC INPUT 4", + [DA9052_ADC_ADCIN5] = "ADC INPUT 5", + [DA9052_ADC_ADCIN6] = "ADC INPUT 6", + [DA9052_ADC_TSI] = "TSI", + [DA9052_ADC_TJUNC] = "BATTERY JUNCTION TEMP", + [DA9052_ADC_VBBAT] = "BACK-UP BATTERY TEMP", +}; + + +int da9052_manual_read(struct da9052 *da9052, + unsigned char channel) +{ + unsigned char man_timeout_cnt = DA9052_ADC_MAX_MANCONV_RETRY_COUNT; + struct da9052_ssc_msg msg; + unsigned short calc_data; + unsigned int ret; + u16 data = 0; + + msg.addr = DA9052_ADCMAN_REG; + msg.data = channel; + msg.data = (msg.data | DA9052_ADCMAN_MANCONV); + + mutex_lock(&da9052->manconv_lock); + da9052_lock(da9052); + + ret = da9052->write(da9052, &msg); + if (ret) + goto err_ssc_comm; + da9052_unlock(da9052); + + /* Wait for the event */ + do { + msg.addr = DA9052_ADCCONT_REG; + msg.data = 0; + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto err_ssc_comm; + da9052_unlock(da9052); + + if (DA9052_ADCCONT_ADCMODE & msg.data) + msleep(1); + else + msleep(10); + + msg.addr = DA9052_ADCMAN_REG; + msg.data = 0; + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto err_ssc_comm; + da9052_unlock(da9052); + + /* Counter to avoid endless while loop */ + man_timeout_cnt--; + if (man_timeout_cnt == 1) { + if (!(msg.data & DA9052_ADCMAN_MANCONV)) + break; + else + goto err_ssc_comm; + } + /* Wait until the MAN_CONV bit is cleared to zero */ + } while (msg.data & DA9052_ADCMAN_MANCONV); + + msg.addr = DA9052_ADCRESH_REG; + msg.data = 0; + + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto err_ssc_comm; + da9052_unlock(da9052); + + calc_data = (unsigned short)msg.data; + data = (calc_data << 2); + + msg.addr = DA9052_ADCRESL_REG; + msg.data = 0; + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto err_ssc_comm; + da9052_unlock(da9052); + + /* Clear first 14 bits before ORing */ + calc_data = (unsigned short)msg.data & 0x0003; + data |= calc_data; + + mutex_unlock(&da9052->manconv_lock); + + return data; +err_ssc_comm: + mutex_unlock(&da9052->manconv_lock); + da9052_unlock(da9052); + return -EIO; +} +EXPORT_SYMBOL(da9052_manual_read); + +int da9052_read_tjunc(struct da9052 *da9052, char *buf) +{ + struct da9052_ssc_msg msg; + unsigned char temp; + int ret; + + msg.addr = DA9052_TJUNCRES_REG; + msg.data = 0; + + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto err_ssc_comm; + + temp = msg.data; + + msg.addr = DA9052_TOFFSET_REG; + msg.data = 0; + ret = da9052->read(da9052, &msg); + if (ret) + goto err_ssc_comm; + da9052_unlock(da9052); + /* Calculate Junction temperature */ + temp = (temp - msg.data); + *buf = temp; + return 0; +err_ssc_comm: + da9052_unlock(da9052); + return -EIO; +} +EXPORT_SYMBOL(da9052_read_tjunc); + +int da9052_read_tbat_ich(struct da9052 *da9052, char *data, int channel_no) +{ + struct da9052_ssc_msg msg; + int ret; + + /* Read TBAT conversion result */ + switch (channel_no) { + case DA9052_ADC_TBAT: + msg.addr = DA9052_TBATRES_REG; + break; + case DA9052_ADC_ICH: + msg.addr = DA9052_ICHGAV_REG; + break; + default: + return -EINVAL; + } + msg.data = 0; + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto err_ssc_comm; + da9052_unlock(da9052); + *data = msg.data; + printk(KERN_INFO"msg.data 1= %d\n", msg.data); + msg.data = 28; + da9052_lock(da9052); + ret = da9052->write(da9052, &msg); + if (ret) + goto err_ssc_comm; + da9052_unlock(da9052); + printk(KERN_INFO"msg.data2 = %d\n", msg.data); + msg.data = 0; + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto err_ssc_comm; + da9052_unlock(da9052); + printk(KERN_INFO"msg.data3 = %d\n", msg.data); + return 0; + +err_ssc_comm: + da9052_unlock(da9052); + return ret; +} +EXPORT_SYMBOL(da9052_read_tbat_ich); + +static int da9052_start_adc(struct da9052 *da9052, unsigned channel) +{ + struct da9052_ssc_msg msg; + int ret; + + msg.addr = DA9052_ADCCONT_REG; + msg.data = 0; + + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret != 0) + goto err_ssc_comm; + + if (channel == DA9052_ADC_VDDOUT) + msg.data = (msg.data | DA9052_ADCCONT_AUTOVDDEN); + else if (channel == DA9052_ADC_ADCIN4) + msg.data = (msg.data | DA9052_ADCCONT_AUTOAD4EN); + else if (channel == DA9052_ADC_ADCIN5) + msg.data = (msg.data | DA9052_ADCCONT_AUTOAD5EN); + else if (channel == DA9052_ADC_ADCIN6) + msg.data = (msg.data | DA9052_ADCCONT_AUTOAD6EN); + else + return -EINVAL; + + ret = da9052->write(da9052, &msg); + if (ret != 0) + goto err_ssc_comm; + da9052_unlock(da9052); + return 0; + +err_ssc_comm: + da9052_unlock(da9052); + return -EIO; +} + +static int da9052_stop_adc(struct da9052 *da9052, unsigned channel) +{ + int ret; + struct da9052_ssc_msg msg; + + msg.addr = DA9052_ADCCONT_REG; + msg.data = 0; + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret != 0) + goto err_ssc_comm; + + if (channel == DA9052_ADC_VDDOUT) + msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOVDDEN)); + else if (channel == DA9052_ADC_ADCIN4) + msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOAD4EN)); + else if (channel == DA9052_ADC_ADCIN5) + msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOAD5EN)); + else if (channel == DA9052_ADC_ADCIN6) + msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOAD6EN)); + else + return -EINVAL; + + ret = da9052->write(da9052, &msg); + if (ret != 0) + goto err_ssc_comm; + da9052_unlock(da9052); + + return 0; +err_ssc_comm: + da9052_unlock(da9052); + return -EIO; +} + +static ssize_t da9052_adc_read_start_stop(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct da9052_adc_priv *priv = platform_get_drvdata(pdev); + struct da9052_ssc_msg msg; + int channel = to_sensor_dev_attr(devattr)->index; + int ret; + + ret = da9052_start_adc(priv->da9052, channel); + if (ret < 0) + return ret; + + /* Read the ADC converted value */ + switch (channel) { + case DA9052_ADC_VDDOUT: + msg.addr = DA9052_VDDRES_REG; + break; +#if (DA9052_ADC_CONF_ADC4 == 1) + case DA9052_ADC_ADCIN4: + msg.addr = DA9052_ADCIN4RES_REG; + break; +#endif +#if (DA9052_ADC_CONF_ADC5 == 1) + case DA9052_ADC_ADCIN5: + msg.addr = DA9052_ADCIN5RES_REG; + break; +#endif +#if (DA9052_ADC_CONF_ADC6 == 1) + case DA9052_ADC_ADCIN6: + msg.addr = DA9052_ADCIN6RES_REG; + break; +#endif + default: + return -EINVAL; + } + msg.data = 0; + da9052_lock(priv->da9052); + ret = priv->da9052->read(priv->da9052, &msg); + if (ret != 0) + goto err_ssc_comm; + da9052_unlock(priv->da9052); + + ret = da9052_stop_adc(priv->da9052, channel); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", msg.data); + +err_ssc_comm: + da9052_unlock(priv->da9052); + return ret; +} + +static ssize_t da9052_adc_read_ich(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct da9052_adc_priv *priv = platform_get_drvdata(pdev); + int ret; + + ret = da9052_read_tbat_ich(priv->da9052, buf, DA9052_ADC_ICH); + if (ret < 0) + return ret; + return sprintf(buf, "%u\n", *buf); +} + +static ssize_t da9052_adc_read_tbat(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct da9052_adc_priv *priv = platform_get_drvdata(pdev); + int ret; + + ret = da9052_read_tbat_ich(priv->da9052, buf, DA9052_ADC_TBAT); + if (ret < 0) + return ret; + return sprintf(buf, "%u\n", *buf); +} + +static ssize_t da9052_adc_read_vbat(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct da9052_adc_priv *priv = platform_get_drvdata(pdev); + s32 ret; + + ret = da9052_manual_read(priv->da9052, DA9052_ADC_VBAT); + if (ret < 0) + return ret; + return sprintf(buf, "%u\n", ret); +} + +static ssize_t da9052_adc_read_tjunc(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct da9052_adc_priv *priv = platform_get_drvdata(pdev); + int ret; + ret = da9052_read_tjunc(priv->da9052, buf); + if (ret < 0) + return ret; + return sprintf(buf, "%u\n", *buf); +} + +static ssize_t da9052_adc_read_vbbat(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct da9052_adc_priv *priv = platform_get_drvdata(pdev); + s32 ret; + + ret = da9052_manual_read(priv->da9052, DA9052_ADC_VBBAT); + if (ret < 0) + return ret; + return sprintf(buf, "%u\n", ret); +} + +static int da9052_adc_hw_init(struct da9052 *da9052) +{ + struct da9052_ssc_msg msg; + int ret; + + /* ADC channel 4 and 5 are by default enabled */ +#if (DA9052_ADC_CONF_ADC4 == 1) + msg.addr = DA9052_GPIO0001_REG; + msg.data = 0; + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto err_ssc_comm; + + msg.data = (msg.data & ~(DA9052_GPIO0001_GPIO0PIN)); + ret = da9052->write(da9052, &msg); + if (ret != 0) + goto err_ssc_comm; + da9052_unlock(da9052); +#endif + +#if (DA9052_ADC_CONF_ADC5 == 1) + msg.addr = DA9052_GPIO0001_REG; + msg.data = 0; + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto err_ssc_comm; + + msg.data = (msg.data & ~(DA9052_GPIO0001_GPIO0PIN)); + ret = da9052->write(da9052, &msg); + if (ret != 0) + goto err_ssc_comm; + da9052_unlock(da9052); +#endif + +#if (DA9052_ADC_CONF_ADC6 == 1) + msg.addr = DA9052_GPIO0203_REG; + msg.data = 0; + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto err_ssc_comm; + + msg.data = (msg.data & ~(DA9052_GPIO0203_GPIO2PIN)); + ret = da9052->write(da9052, &msg); + if (ret != 0) + goto err_ssc_comm; + da9052_unlock(da9052); +#endif +#if 0 + /* By default configure the Measurement sequence interval to 10ms */ + msg.addr = DA9052_ADCCONT_REG; + msg.data = 0; + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret != 0) + goto err_ssc_comm; + + /* Set the ADC MODE bit for 10msec sampling timer */ + msg.data = (msg.data & ~(DA9052_ADCCONT_ADCMODE)); + ret = da9052->write(da9052, &msg); + if (ret != 0) + goto err_ssc_comm; + da9052_unlock(da9052); +#endif + return 0; +err_ssc_comm: + da9052_unlock(da9052); + return -EIO; +} + +static ssize_t da9052_adc_show_name(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return sprintf(buf, "da9052-adc\n"); +} + +static ssize_t show_label(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int channel = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%s\n", input_names[channel]); +} +#define DA9052_ADC_CHANNELS(id, name) \ + static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \ + NULL, name) + +DA9052_ADC_CHANNELS(0, DA9052_ADC_VDDOUT); +DA9052_ADC_CHANNELS(1, DA9052_ADC_ICH); +DA9052_ADC_CHANNELS(2, DA9052_ADC_TBAT); +DA9052_ADC_CHANNELS(3, DA9052_ADC_VBAT); +#if (DA9052_ADC_CONF_ADC4 == 1) +DA9052_ADC_CHANNELS(4, DA9052_ADC_ADCIN4); +#endif +#if (DA9052_ADC_CONF_ADC5 == 1) +DA9052_ADC_CHANNELS(5, DA9052_ADC_ADCIN5); +#endif +#if (DA9052_ADC_CONF_ADC6 == 1) +DA9052_ADC_CHANNELS(6, DA9052_ADC_ADCIN6); +#endif +DA9052_ADC_CHANNELS(7, DA9052_ADC_TSI); +DA9052_ADC_CHANNELS(8, DA9052_ADC_TJUNC); +DA9052_ADC_CHANNELS(9, DA9052_ADC_VBBAT); + + +static DEVICE_ATTR(name, S_IRUGO, da9052_adc_show_name, NULL); +static SENSOR_DEVICE_ATTR(read_vddout, S_IRUGO, + da9052_adc_read_start_stop, NULL, + DA9052_ADC_VDDOUT); +static SENSOR_DEVICE_ATTR(read_ich, S_IRUGO, da9052_adc_read_ich, NULL, + DA9052_ADC_ICH); +static SENSOR_DEVICE_ATTR(read_tbat, S_IRUGO, da9052_adc_read_tbat, NULL, + DA9052_ADC_TBAT); +static SENSOR_DEVICE_ATTR(read_vbat, S_IRUGO, da9052_adc_read_vbat, NULL, + DA9052_ADC_VBAT); +#if (DA9052_ADC_CONF_ADC4 == 1) +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, da9052_adc_read_start_stop, NULL, + DA9052_ADC_ADCIN4); +#endif +#if (DA9052_ADC_CONF_ADC5 == 1) +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, da9052_adc_read_start_stop, NULL, + DA9052_ADC_ADCIN5); +#endif +#if (DA9052_ADC_CONF_ADC6 == 1) +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, da9052_adc_read_start_stop, NULL, + DA9052_ADC_ADCIN6); +#endif +static SENSOR_DEVICE_ATTR(read_tjunc, S_IRUGO, da9052_adc_read_tjunc, NULL, + DA9052_ADC_TJUNC); +static SENSOR_DEVICE_ATTR(read_vbbat, S_IRUGO, da9052_adc_read_vbbat, NULL, + DA9052_ADC_VBBAT); + +static struct attribute *da9052_attr[] = { + &dev_attr_name.attr, + &sensor_dev_attr_read_vddout.dev_attr.attr, + &sensor_dev_attr_in0_label.dev_attr.attr, + &sensor_dev_attr_read_ich.dev_attr.attr, + &sensor_dev_attr_in1_label.dev_attr.attr, + &sensor_dev_attr_read_tbat.dev_attr.attr, + &sensor_dev_attr_in2_label.dev_attr.attr, + &sensor_dev_attr_read_vbat.dev_attr.attr, + &sensor_dev_attr_in3_label.dev_attr.attr, +#if (DA9052_ADC_CONF_ADC4 == 1) + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_label.dev_attr.attr, +#endif +#if (DA9052_ADC_CONF_ADC5 == 1) + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_label.dev_attr.attr, +#endif +#if (DA9052_ADC_CONF_ADC6 == 1) + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_label.dev_attr.attr, +#endif + &sensor_dev_attr_in7_label.dev_attr.attr, + &sensor_dev_attr_read_tjunc.dev_attr.attr, + &sensor_dev_attr_in8_label.dev_attr.attr, + &sensor_dev_attr_read_vbbat.dev_attr.attr, + &sensor_dev_attr_in9_label.dev_attr.attr, + NULL +}; + +static const struct attribute_group da9052_group = { + .attrs = da9052_attr, +}; + +static int __init da9052_adc_probe(struct platform_device *pdev) +{ + struct da9052_adc_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->da9052 = dev_get_drvdata(pdev->dev.parent); + + platform_set_drvdata(pdev, priv); + + /* Register sysfs hooks */ + ret = sysfs_create_group(&pdev->dev.kobj, &da9052_group); + if (ret) + goto out_err_create1; + + priv->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(priv->hwmon_dev)) { + ret = PTR_ERR(priv->hwmon_dev); + goto out_err_create2; + } + /* Initializes the hardware for ADC module */ + da9052_adc_hw_init(priv->da9052); + + /* Initialize mutex required for ADC Manual read */ + mutex_init(&priv->da9052->manconv_lock); + + return 0; + +out_err_create2: + sysfs_remove_group(&pdev->dev.kobj, &da9052_group); +out_err_create1: + platform_set_drvdata(pdev, NULL); + kfree(priv); + + return ret; +} + +static int __devexit da9052_adc_remove(struct platform_device *pdev) +{ + struct da9052_adc_priv *priv = platform_get_drvdata(pdev); + + mutex_destroy(&priv->da9052->manconv_lock); + + hwmon_device_unregister(priv->hwmon_dev); + + sysfs_remove_group(&pdev->dev.kobj, &da9052_group); + + platform_set_drvdata(pdev, NULL); + kfree(priv); + + return 0; +} + +static struct platform_driver da9052_adc_driver = { + .remove = __devexit_p(da9052_adc_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +static int __init da9052_adc_init(void) +{ + return platform_driver_probe(&da9052_adc_driver, da9052_adc_probe); +} +module_init(da9052_adc_init); + +static void __exit da9052_adc_exit(void) +{ + platform_driver_unregister(&da9052_adc_driver); +} +module_exit(da9052_adc_exit); + +MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>") +MODULE_DESCRIPTION("DA9052 ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/hwmon/isl29003.c b/drivers/hwmon/isl29003.c new file mode 100644 index 000000000000..18c9947b3cd8 --- /dev/null +++ b/drivers/hwmon/isl29003.c @@ -0,0 +1,439 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file drivers/hwmon/isl29003.c + * + * @brief ISL29003 light sensor Driver + * + * @ingroup + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/regulator/consumer.h> +#include <linux/fsl_devices.h> +#include <linux/slab.h> + +enum isl29003_width { + ISL29003_WIDTH_16 = 0, + ISL29003_WIDTH_12, + ISL29003_WIDTH_8, + ISL29003_WIDTH_4, +}; + +enum isl29003_gain { + ISL29003_GAIN_1000 = 0, + ISL29003_GAIN_4000, + ISL29003_GAIN_16000, + ISL29003_GAIN_64000, +}; + +enum isl29003_mode { + ISL29003_MODE_DIODE1 = 0, + ISL29003_MODE_DIODE2, + ISL29003_MODE_DIODE1_2, +}; + +struct isl29003_param { + enum isl29003_width width; + enum isl29003_gain gain; + enum isl29003_mode mode; +}; + +/* bit definition for ISL29003_CMD reg */ +#define ENABLE 7 +#define ADCPD 6 +#define TIMEING_MODE 5 +#define MODE 2 +#define WIDTH 0 + +/* bit definition for ISL29003_CTRL reg */ +#define INT_FLAG 5 +#define GAIN 2 +#define INT_PERSIST 0 + +enum isl29003_reg { + ISL29003_CMD = 0, + ISL29003_CTRL, + ISL29003_THRS_HI, + ISL29003_THRS_LO, + ISL29003_LSB_S, + ISL29003_MSB_S, + ISL29003_LSB_T, + ISL29003_MSB_T, + ISL29003_SYNC_IIC = 0x80, + ISL29003_CLAR_INT = 0x40 +}; + +/* default configure for ISL29003 */ +#define ISL29003_WIDTH_DEFAULT ISL29003_WIDTH_16 +#define ISL29003_GAIN_DEFAULT ISL29003_GAIN_16000 +#define ISL29003_MODE_DEFAULT ISL29003_MODE_DIODE1 + +/* range table for different GAIN settings */ +int range[4] = { 973, 3892, 15568, 62272 }; + +/* width table for different WIDTH settings */ +int width[4] = { 16, 1, 256, 16 }; + +struct isl29003_data { + struct i2c_client *client; + struct device *hwmon_dev; + struct regulator *vdd_reg; + struct isl29003_param param; + int lux_coeff; + unsigned char enable; +}; + +static struct i2c_client *isl29003_client; + +/*! + * This function do the isl29003 register read. + */ +int isl29003_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/*! + * This function do the isl29003 register write. + */ +int isl29003_write(struct i2c_client *client, u8 reg, char value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/*! + * This function do the isl29003 config and enable. + */ +static int isl29003_on(void) +{ + unsigned char cmd; + int err = 0; + struct mxc_lightsensor_platform_data *ls_data; + struct isl29003_data *data = i2c_get_clientdata(isl29003_client); + + if (data->enable) + goto exit; + + ls_data = (struct mxc_lightsensor_platform_data *) + (isl29003_client->dev).platform_data; + + /* coeff=range*100k/rext/2^n */ + data->lux_coeff = range[data->param.gain] * 100 / + ls_data->rext / width[data->param.width]; + + if (data->vdd_reg) + regulator_enable(data->vdd_reg); + msleep(100); + + cmd = data->param.gain << GAIN; + if (isl29003_write(isl29003_client, ISL29003_CTRL, cmd)) { + err = -ENODEV; + goto exit; + } + + cmd = (data->param.width << WIDTH) | (data->param.mode << MODE) | + (1 << ENABLE); + if (isl29003_write(isl29003_client, ISL29003_CMD, cmd)) { + err = -ENODEV; + goto exit; + } + + data->enable = 1; + + pr_info("isl29003 on\n"); + return 0; +exit: + return err; +} + +/*! + * This function shut down the isl29003. + */ +static int isl29003_off(void) +{ + struct isl29003_data *data = i2c_get_clientdata(isl29003_client); + int cmd; + + if (!data->enable) + return 0; + + cmd = isl29003_read(isl29003_client, ISL29003_CMD); + if (cmd < 0) + return -ENODEV; + + cmd = ((cmd | (1 << ADCPD)) & (~(1 << ENABLE))); + if (isl29003_write(isl29003_client, ISL29003_CMD, (char)cmd)) + return -ENODEV; + + if (data->vdd_reg) + regulator_disable(data->vdd_reg); + + data->enable = 0; + + pr_info("isl29003 off\n"); + return 0; +} + +/*! + * This function read the isl29003 lux registers and convert them to the lux + * value. + * + * @output buffer this param holds the lux value, when =-1, read fail + * + * @return 0 + */ +static int isl29003_read_lux(void) +{ + int d; + int lux; + struct isl29003_data *data = i2c_get_clientdata(isl29003_client); + + d = isl29003_read(isl29003_client, ISL29003_MSB_S); + if (d < 0) + goto err; + + lux = d; + d = isl29003_read(isl29003_client, ISL29003_LSB_S); + if (d < 0) + goto err; + + lux = (lux << 8) + d; + + if (data->param.width < ISL29003_WIDTH_8) + lux = (data->lux_coeff * lux) >> 12; + else + lux = data->lux_coeff * lux; + + return lux; +err: + return -1; +} + +static ssize_t ls_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + char *endp; + int enable = simple_strtoul(buf, &endp, 0); + size_t size = endp - buf; + + if (*endp && isspace(*endp)) + size++; + if (size != count) + return -EINVAL; + + if (enable == 1) { + if (isl29003_on()) + pr_info("device open fail\n"); + } + if (enable == 0) { + if (isl29003_off()) + pr_info("device powerdown fail\n"); + } + + return count; +} + +static SENSOR_DEVICE_ATTR(enable, S_IWUGO, NULL, ls_enable, 0); + +static ssize_t show_lux(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", isl29003_read_lux()); +} + +static SENSOR_DEVICE_ATTR(lux, S_IRUGO, show_lux, NULL, 0); + +static int isl29003_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + int err = 0; + struct isl29003_data *data; + struct regulator *vdd_reg; + struct mxc_lightsensor_platform_data *ls_data; + + ls_data = (struct mxc_lightsensor_platform_data *) + (client->dev).platform_data; + + if (ls_data && ls_data->vdd_reg) + vdd_reg = regulator_get(&client->dev, ls_data->vdd_reg); + else + vdd_reg = NULL; + + /* check the existence of the device */ + if (vdd_reg) + regulator_enable(vdd_reg); + msleep(100); + + if (isl29003_write(client, ISL29003_CMD, 0)) + err = -ENODEV; + + if (!err) + if (isl29003_read(client, ISL29003_CMD)) + err = -ENODEV; + + if (vdd_reg) + regulator_disable(vdd_reg); + if (err < 0) + goto exit1; + + isl29003_client = client; + data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL); + if (data == NULL) { + err = -ENOMEM; + goto exit1; + } + + i2c_set_clientdata(client, data); + data->client = client; + + data->param.width = ISL29003_WIDTH_DEFAULT; + data->param.gain = ISL29003_GAIN_DEFAULT; + data->param.mode = ISL29003_MODE_DEFAULT; + + data->enable = 0; + + err = device_create_file(&client->dev, + &sensor_dev_attr_enable.dev_attr); + if (err) + goto exit2; + + err = device_create_file(&client->dev, &sensor_dev_attr_lux.dev_attr); + if (err) + goto exit_remove1; + + /* Register sysfs hooks */ + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove2; + } + + data->vdd_reg = vdd_reg; + return 0; + +exit_remove2: + device_remove_file(&client->dev, &sensor_dev_attr_lux.dev_attr); +exit_remove1: + device_remove_file(&client->dev, &sensor_dev_attr_enable.dev_attr); +exit2: + kfree(data); +exit1: + if (vdd_reg) { + regulator_put(vdd_reg); + vdd_reg = NULL; + } + isl29003_client = NULL; + return err; +} + +static int isl29003_i2c_remove(struct i2c_client *client) +{ + struct isl29003_data *data = i2c_get_clientdata(client); + + if (data->vdd_reg) { + regulator_put(data->vdd_reg); + data->vdd_reg = NULL; + } + hwmon_device_unregister(data->hwmon_dev); + device_remove_file(&client->dev, &sensor_dev_attr_enable.dev_attr); + device_remove_file(&client->dev, &sensor_dev_attr_lux.dev_attr); + kfree(data); + return 0; +} + +static int isl29003_suspend(struct i2c_client *client, pm_message_t message) +{ + int cmd; + + struct isl29003_data *data = i2c_get_clientdata(client); + + if (!data->enable) + goto exit; + + cmd = isl29003_read(client, ISL29003_CMD); + if (cmd < 0) + goto err; + + cmd = (cmd | (1 << ADCPD)); + if (isl29003_write(client, ISL29003_CMD, (char)cmd)) + goto err; +exit: + return 0; +err: + return -ENODEV; +} + +static int isl29003_resume(struct i2c_client *client) +{ + int cmd; + + struct isl29003_data *data = i2c_get_clientdata(client); + + if (!data->enable) + goto exit; + + cmd = isl29003_read(client, ISL29003_CMD); + if (cmd < 0) + goto err; + + cmd = (cmd & (~(1 << ADCPD))); + if (isl29003_write(client, ISL29003_CMD, (char)cmd)) + goto err; +exit: + return 0; +err: + return -ENODEV; +} + +static const struct i2c_device_id isl29003_id[] = { + {"isl29003", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, isl29003_id); + +static struct i2c_driver isl29003_driver = { + .driver = { + .name = "isl29003", + }, + .probe = isl29003_i2c_probe, + .remove = isl29003_i2c_remove, + .suspend = isl29003_suspend, + .resume = isl29003_resume, + .id_table = isl29003_id, +}; + +static int __init isl29003_init(void) +{ + return i2c_add_driver(&isl29003_driver);; +} + +static void __exit isl29003_cleanup(void) +{ + i2c_del_driver(&isl29003_driver); +} + +module_init(isl29003_init); +module_exit(isl29003_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("ISL29003 light sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/max17135-hwmon.c b/drivers/hwmon/max17135-hwmon.c new file mode 100644 index 000000000000..8aa9aadbd44a --- /dev/null +++ b/drivers/hwmon/max17135-hwmon.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* + * max17135.c + * + * Based on the MAX1619 driver. + * Copyright (C) 2003-2004 Alexey Fisher <fishor@mail.ru> + * Jean Delvare <khali@linux-fr.org> + * + * The MAX17135 is a sensor chip made by Maxim. + * It reports up to two temperatures (its own plus up to + * one external one). + */ + + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/sysfs.h> +#include <linux/platform_device.h> +#include <linux/mfd/max17135.h> + +/* + * Conversions + */ +static int temp_from_reg(int val) +{ + return val >> 8; +} + +/* + * Functions declaration + */ +static int max17135_sensor_probe(struct platform_device *pdev); +static int max17135_sensor_remove(struct platform_device *pdev); + +/* + * Driver data (common to all clients) + */ +static struct platform_driver max17135_sensor_driver = { + .probe = max17135_sensor_probe, + .remove = max17135_sensor_remove, + .driver = { + .name = "max17135_sensor", + }, +}; + + +/* + * Client data (each client gets its own) + */ +struct max17135_data { + struct device *hwmon_dev; +}; + +/* + * Sysfs stuff + */ +static ssize_t show_temp_input1(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int reg_val; + max17135_reg_read(REG_MAX17135_INT_TEMP, ®_val); + return snprintf(buf, PAGE_SIZE, "%d\n", temp_from_reg(reg_val)); +} + +static ssize_t show_temp_input2(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int reg_val; + max17135_reg_read(REG_MAX17135_EXT_TEMP, ®_val); + return snprintf(buf, PAGE_SIZE, "%d\n", temp_from_reg(reg_val)); +} + +show_temp(temp_input1); +show_temp(temp_input2); + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); +static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL); + +static struct attribute *max17135_attributes[] = { + &dev_attr_temp1_input.attr, + &dev_attr_temp2_input.attr, + NULL +}; + +static const struct attribute_group max17135_group = { + .attrs = max17135_attributes, +}; + +/* + * Real code + */ +static int max17135_sensor_probe(struct platform_device *pdev) +{ + struct max17135_data *data; + int err; + + data = kzalloc(sizeof(struct max17135_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + err = sysfs_create_group(&pdev->dev.kobj, &max17135_group); + if (err) + goto exit_free; + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + platform_set_drvdata(pdev, data); + + return 0; + +exit_remove_files: + sysfs_remove_group(&pdev->dev.kobj, &max17135_group); +exit_free: + kfree(data); +exit: + return err; +} + +static int max17135_sensor_remove(struct platform_device *pdev) +{ + struct max17135_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &max17135_group); + + kfree(data); + return 0; +} + +static int __init sensors_max17135_init(void) +{ + return platform_driver_register(&max17135_sensor_driver); +} +module_init(sensors_max17135_init); + +static void __exit sensors_max17135_exit(void) +{ + platform_driver_unregister(&max17135_sensor_driver); +} +module_exit(sensors_max17135_exit); + +MODULE_DESCRIPTION("MAX17135 sensor driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/hwmon/mxc_mma7450.c b/drivers/hwmon/mxc_mma7450.c new file mode 100644 index 000000000000..fa1dd41267eb --- /dev/null +++ b/drivers/hwmon/mxc_mma7450.c @@ -0,0 +1,788 @@ +/* + * linux/drivers/hwmon/mma7450.c + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*include file*/ +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/input-polldev.h> +#include <linux/hwmon.h> +#include <linux/regulator/consumer.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/fsl_devices.h> + +/*macro define*/ +#define MMA7450_I2C_ADDR 0x1D +#define DEVICE_NAME "mma7450" +#define POLL_INTERVAL 100 +#define DEBUG + +#define INPUT_FUZZ 4 +#define INPUT_FLAT 4 + +enum { + REG_XOUTL = 0x00, + REG_XOUTH, + REG_YOUTL, + REG_YOUTH, + REG_ZOUTL, + REG_ZOUTH, + REG_XOUT8, + REG_YOUT8, + REG_ZOUT8, + REG_STATUS, + REG_DETSRC, + REG_TOUT, + REG_RESERVED_0, + REG_I2CAD, + REG_USRINF, + REG_WHOAMI, + REG_XOFFL, + REG_XOFFH, + REG_YOFFL, + REG_YOFFH, + REG_ZOFFL, + REG_ZOFFH, + REG_MCTL, + REG_INTRST, + REG_CTL1, + REG_CTL2, + REG_LDTH, + REG_PDTH, + REG_PD, + REG_LT, + REG_TW, + REG_REVERVED_1, +}; + +enum { + MOD_STANDBY = 0, + MOD_MEASURE, + MOD_LEVEL_D, + MOD_PULSE_D, +}; + +enum { + INT_1L_2P = 0, + INT_1P_2L, + INT_1SP_2P, +}; + +struct mma7450_status { + u8 mod; + u8 ctl1; + u8 ctl2; +}; + +/*forward declear*/ +static ssize_t mma7450_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t mma7450_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count); +static int mma7450_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int mma7450_remove(struct i2c_client *client); +static int mma7450_suspend(struct i2c_client *client, pm_message_t state); +static int mma7450_resume(struct i2c_client *client); +static void mma_bh_handler(struct work_struct *work); + +/*definition*/ +static struct regulator *reg_dvdd_io; +static struct regulator *reg_avdd; +static struct i2c_client *mma7450_client; +static struct device *hwmon_dev; +static struct input_polled_dev *mma7450_idev; +static struct mxc_mma7450_platform_data *plat_data; +static u8 mma7450_mode; +static struct device_attribute mma7450_dev_attr = { + .attr = { + .name = "mma7450_ctl", + .mode = S_IRUSR | S_IWUSR, + }, + .show = mma7450_show, + .store = mma7450_store, +}; + +static const struct i2c_device_id mma7450_id[] = { + {"mma7450", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, mma7450_id); + +static struct i2c_driver i2c_mma7450_driver = { + .driver = { + .name = "mma7450", + }, + .probe = mma7450_probe, + .remove = mma7450_remove, + .suspend = mma7450_suspend, + .resume = mma7450_resume, + .id_table = mma7450_id, +}; + +static struct mma7450_status mma_status = { + .mod = 0, + .ctl1 = 0, + .ctl2 = 0, +}; + +DECLARE_WORK(mma_work, mma_bh_handler); + +#ifdef DEBUG +enum { + MMA_REG_R = 0, + MMA_REG_W, + MMA_SET_MOD, + MMA_SET_L_THR, + MMA_SET_P_THR, + MMA_SET_INTP, + MMA_SET_INTB, + MMA_SET_G, + MMA_I2C_EABLE, + MMA_OFF_X, + MMA_OFF_Y, + MMA_OFF_Z, + MMA_SELF_TEST, + MMA_SET_LDPL, + MMA_SET_PDPL, + MMA_SET_PDV, + MMA_SET_LTV, + MMA_SET_TW, + MMA_CMD_MAX +}; + +static char *command[MMA_CMD_MAX] = { + [MMA_REG_R] = "readreg", + [MMA_REG_W] = "writereg", + [MMA_SET_MOD] = "setmod", + [MMA_SET_L_THR] = "setlt", + [MMA_SET_P_THR] = "setpt", + [MMA_SET_INTP] = "setintp", + [MMA_SET_INTB] = "setintb", + [MMA_SET_G] = "setg", + [MMA_I2C_EABLE] = "setie", + [MMA_OFF_X] = "setxo", + [MMA_OFF_Y] = "setyo", + [MMA_OFF_Z] = "setzo", + [MMA_SELF_TEST] = "selft", + [MMA_SET_LDPL] = "setldp", + [MMA_SET_PDPL] = "setpdp", + [MMA_SET_PDV] = "setpdv", + [MMA_SET_LTV] = "setltv", + [MMA_SET_TW] = "settw", +}; + +static void set_mod(u8 mode) +{ + int ret; + + ret = i2c_smbus_read_byte_data(mma7450_client, REG_MCTL); + /* shall I test the ret value? */ + ret = (ret & ~0x3) | (mode & 0x3); + mma_status.mod = ret; + i2c_smbus_write_byte_data(mma7450_client, REG_MCTL, ret); +} + +static void set_level_thr(u8 lth) +{ + i2c_smbus_write_byte_data(mma7450_client, REG_LDTH, lth); +} + +static void set_pulse_thr(u8 pth) +{ + i2c_smbus_write_byte_data(mma7450_client, REG_PDTH, pth); +} + +static void set_int_pin(u8 pin) +{ + int ret; + + ret = i2c_smbus_read_byte_data(mma7450_client, REG_CTL1); + ret = (ret & ~0x1) | (pin & 0x1); + mma_status.ctl1 = ret; + i2c_smbus_write_byte_data(mma7450_client, REG_CTL1, ret); +} + +static void set_int_bit(u8 bit) +{ + int ret; + + ret = i2c_smbus_read_byte_data(mma7450_client, REG_CTL1); + ret = (ret & ~0x6) | ((bit << 1) & 0x6); + mma_status.ctl1 = ret; + i2c_smbus_write_byte_data(mma7450_client, REG_CTL1, ret); +} + +static void set_g_level(u8 gl) +{ + int ret; + + ret = i2c_smbus_read_byte_data(mma7450_client, REG_MCTL); + ret = (ret & ~0xC) | ((gl << 2) & 0xC); + i2c_smbus_write_byte_data(mma7450_client, REG_MCTL, ret); +} + +static void set_i2c_enable(u8 i2c_e) +{ + int ret; + + ret = i2c_smbus_read_byte_data(mma7450_client, REG_I2CAD); + ret = (ret & ~0x80) | ((i2c_e << 7) & 0x80); + i2c_smbus_write_byte_data(mma7450_client, REG_I2CAD, ret); +} + +static void set_x_offset(u16 xo) +{ + u8 data; + + data = (xo & 0xFF); + i2c_smbus_write_byte_data(mma7450_client, REG_XOFFL, data); + data = (xo & 0xFF00) >> 8; + i2c_smbus_write_byte_data(mma7450_client, REG_XOFFH, data); +} + +static void set_y_offset(u16 yo) +{ + u8 data; + + data = (yo & 0xFF); + i2c_smbus_write_byte_data(mma7450_client, REG_YOFFL, data); + data = (yo & 0xFF00) >> 8; + i2c_smbus_write_byte_data(mma7450_client, REG_YOFFH, data); +} + +static void set_z_offset(u16 zo) +{ + u8 data; + + data = (zo & 0xFF); + i2c_smbus_write_byte_data(mma7450_client, REG_ZOFFL, data); + data = (zo & 0xFF00) >> 8; + i2c_smbus_write_byte_data(mma7450_client, REG_ZOFFH, data); +} + +static void selftest(u8 st) +{ + int ret; + + ret = i2c_smbus_read_byte_data(mma7450_client, REG_MCTL); + ret = (ret & ~0x10) | ((st << 4) & 0x10); + i2c_smbus_write_byte_data(mma7450_client, REG_MCTL, ret); +} + +static void set_level_det_p(u8 ldp) +{ + int ret; + + ret = i2c_smbus_read_byte_data(mma7450_client, REG_CTL2); + ret = (ret & ~0x1) | ((ldp << 0) & 0x1); + mma_status.ctl2 = ret; + i2c_smbus_write_byte_data(mma7450_client, REG_CTL2, ret); +} + +static void set_pulse_det_p(u8 pdp) +{ + int ret; + + ret = i2c_smbus_read_byte_data(mma7450_client, REG_CTL2); + ret = (ret & ~0x2) | ((pdp << 1) & 0x2); + mma_status.ctl2 = ret; + i2c_smbus_write_byte_data(mma7450_client, REG_CTL2, ret); +} + +static void set_pulse_duration(u8 pd) +{ + i2c_smbus_write_byte_data(mma7450_client, REG_PD, pd); +} + +static void set_latency_time(u8 lt) +{ + i2c_smbus_write_byte_data(mma7450_client, REG_LT, lt); +} + +static void set_time_window(u8 tw) +{ + i2c_smbus_write_byte_data(mma7450_client, REG_TW, tw); +} + +static void parse_arg(const char *arg, int *reg, int *value) +{ + const char *p; + + for (p = arg;; p++) { + if (*p == ' ' || *p == '\0') + break; + } + + p++; + + *reg = simple_strtoul(arg, NULL, 16); + *value = simple_strtoul(p, NULL, 16); +} + +static void cmd_read_reg(const char *arg) +{ + int reg, value, ret; + + parse_arg(arg, ®, &value); + ret = i2c_smbus_read_byte_data(mma7450_client, reg); + dev_info(&mma7450_client->dev, "read reg0x%x = %x\n", reg, ret); +} + +static void cmd_write_reg(const char *arg) +{ + int reg, value, ret; + + parse_arg(arg, ®, &value); + ret = i2c_smbus_write_byte_data(mma7450_client, reg, value); + dev_info(&mma7450_client->dev, "write reg result %s\n", + ret ? "failed" : "success"); +} + +static int exec_command(const char *buf, size_t count) +{ + const char *p, *s; + const char *arg; + int i, value = 0; + + for (p = buf;; p++) { + if (*p == ' ' || *p == '\0' || p - buf >= count) + break; + } + arg = p + 1; + + for (i = MMA_REG_R; i < MMA_CMD_MAX; i++) { + s = command[i]; + if (s && !strncmp(buf, s, p - buf)) { + dev_info(&mma7450_client->dev, "command %s\n", s); + goto mma_exec_command; + } + } + + dev_err(&mma7450_client->dev, "command is not found\n"); + return -1; + + mma_exec_command: + if (i != MMA_REG_R && i != MMA_REG_W) + value = simple_strtoul(arg, NULL, 16); + + switch (i) { + case MMA_REG_R: + cmd_read_reg(arg); + break; + case MMA_REG_W: + cmd_write_reg(arg); + break; + case MMA_SET_MOD: + set_mod(value); + break; + case MMA_SET_L_THR: + set_level_thr(value); + break; + case MMA_SET_P_THR: + set_pulse_thr(value); + break; + case MMA_SET_INTP: + set_int_pin(value); + break; + case MMA_SET_INTB: + set_int_bit(value); + break; + case MMA_SET_G: + set_g_level(value); + break; + case MMA_I2C_EABLE: + set_i2c_enable(value); + break; + case MMA_OFF_X: + set_x_offset(value); + break; + case MMA_OFF_Y: + set_y_offset(value); + break; + case MMA_OFF_Z: + set_z_offset(value); + break; + case MMA_SELF_TEST: + selftest(value); + break; + case MMA_SET_LDPL: + set_level_det_p(value); + break; + case MMA_SET_PDPL: + set_pulse_det_p(value); + break; + case MMA_SET_PDV: + set_pulse_duration(value); + break; + case MMA_SET_LTV: + set_latency_time(value); + break; + case MMA_SET_TW: + set_time_window(value); + break; + default: + dev_err(&mma7450_client->dev, "command is not found\n"); + break; + } + + return 0; +} + +static ssize_t mma7450_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, reg; + + for (reg = REG_XOUTL; reg < REG_REVERVED_1; reg++) { + ret = i2c_smbus_read_byte_data(mma7450_client, reg); + dev_info(&mma7450_client->dev, "reg0x%02x:\t%03d\t0x%02x\n", + reg, (s8) ret, ret); + } + + return 0; +} + +static ssize_t mma7450_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + exec_command(buf, count); + + return count; +} + +#else + +static ssize_t mma7450_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return 0; +} + +static ssize_t mma7450_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + return count; +} + +#endif + +static void report_abs(void) +{ + u8 status, mod = mma_status.mod; + s16 x, y, z; + + status = i2c_smbus_read_byte_data(mma7450_client, REG_STATUS); + if (!(status & 0x01)) { /* data ready in measurement mode? */ + return; + } + if ((mod & 0x0c) == 0) { /* 8g range */ + x = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_XOUTL); + x |= 0xFF00 & + (i2c_smbus_read_byte_data(mma7450_client, REG_XOUTH) << 8); + y = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_YOUTL); + y |= 0xFF00 & + (i2c_smbus_read_byte_data(mma7450_client, REG_YOUTH) << 8); + z = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_ZOUTL); + z |= 0xFF00 & + (i2c_smbus_read_byte_data(mma7450_client, REG_ZOUTH) << 8); + } else { /* 2g/4g range */ + x = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_XOUT8); + y = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_YOUT8); + z = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_ZOUT8); + } + + status = i2c_smbus_read_byte_data(mma7450_client, REG_STATUS); + if (status & 0x02) { /* data is overwrite */ + return; + } + + /* convert signed 10bits to signed 16bits */ + x = (short)(x << 6) >> 6; + y = (short)(y << 6) >> 6; + z = (short)(z << 6) >> 6; + + input_report_abs(mma7450_idev->input, ABS_X, x); + input_report_abs(mma7450_idev->input, ABS_Y, y); + input_report_abs(mma7450_idev->input, ABS_Z, z); + input_sync(mma7450_idev->input); +} + +static void mma_bh_handler(struct work_struct *work) +{ +} + +static void mma7450_dev_poll(struct input_polled_dev *dev) +{ + report_abs(); +} + +static irqreturn_t mma7450_interrupt(int irq, void *dev_id) +{ + struct input_dev *input_dev = dev_id; + u8 int_bit, int_pin; + + int_bit = mma_status.ctl1 & 0x6; + int_pin = mma_status.ctl1 & 0x1; + + switch (mma_status.mod & 0x03) { + case 1: + /*only int1 report data ready int */ + if (plat_data->int1 != irq) + goto error_bad_int; + schedule_work(&mma_work); + break; + case 2: + /* for level and pulse detection mode, + * choice tasklet to handle interrupt quickly. + * Currently, leave it doing nothing*/ + if (plat_data->int1 == irq) { + if ((int_bit == 0) && (int_pin != 0)) + goto error_bad_int; + if ((int_bit == 0x2) && (int_pin != 0x1)) + goto error_bad_int; + if (int_bit == 0x4) + goto error_bad_int; + } + if (plat_data->int2 == irq) { + if ((int_bit == 0) && (int_pin != 0x1)) + goto error_bad_int; + if ((int_bit == 0x2) && (int_pin != 0)) + goto error_bad_int; + if (int_bit == 0x4) + goto error_bad_int; + } + + dev_info(&input_dev->dev, "motion detected in level mod\n"); + + break; + case 3: + if (plat_data->int1 == irq) { + if ((int_bit == 0) && (int_pin != 0x1)) + goto error_bad_int; + if ((int_bit == 0x2) && (int_pin != 0)) + goto error_bad_int; + if ((int_bit == 0x4) && (int_pin != 0x1)) + goto error_bad_int; + } + if (plat_data->int2 == irq) { + if ((int_bit == 0) && (int_pin != 0)) + goto error_bad_int; + if ((int_bit == 0x2) && (int_pin != 0x1)) + goto error_bad_int; + if ((int_bit == 0x4) && (int_pin != 0)) + goto error_bad_int; + } + + if (mma_status.ctl2 & 0x02) + dev_info(&input_dev->dev, + "freefall detected in pulse mod\n"); + else + dev_info(&input_dev->dev, + "motion detected in pulse mod\n"); + + break; + case 0: + default: + break; + } + error_bad_int: + return IRQ_RETVAL(1); +} + +static int mma7450_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct input_dev *idev; + + plat_data = + (struct mxc_mma7450_platform_data *)client->dev.platform_data; + if (plat_data == NULL) { + dev_err(&client->dev, "lack of platform data!\n"); + return -ENODEV; + } + + /*enable power supply */ + /*when to power on/off the power is to be considered later */ + /*shall I check the return value */ + reg_dvdd_io = regulator_get(&client->dev, plat_data->reg_dvdd_io); + if (reg_dvdd_io != ERR_PTR(-ENOENT)) + regulator_enable(reg_dvdd_io); + else + return -EINVAL; + + reg_avdd = regulator_get(&client->dev, plat_data->reg_avdd); + if (reg_avdd != ERR_PTR(-ENOENT)) + regulator_enable(reg_avdd); + else { + regulator_put(reg_dvdd_io); + return -EINVAL; + } + + /*bind the right device to the driver */ + ret = i2c_smbus_read_byte_data(client, REG_I2CAD); + if (MMA7450_I2C_ADDR != (0x7F & ret)) { /*compare the address value */ + dev_err(&client->dev, + "read chip ID 0x%x is not equal to 0x%x!\n", ret, + MMA7450_I2C_ADDR); + goto error_disable_power; + } + mma7450_client = client; + + /*interrupt register */ + /*when to register interrupt is to be considered later */ + + /*create device file in sysfs as user interface */ + ret = device_create_file(&client->dev, &mma7450_dev_attr); + if (ret) { + dev_err(&client->dev, "create device file failed!\n"); + goto error_disable_power; + } + + /*register to hwmon device */ + hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(hwmon_dev)) { + dev_err(&client->dev, "hwmon register failed!\n"); + ret = PTR_ERR(hwmon_dev); + goto error_rm_dev_file; + } + + /*input poll device register */ + mma7450_idev = input_allocate_polled_device(); + if (!mma7450_idev) { + dev_err(&client->dev, "alloc poll device failed!\n"); + ret = -ENOMEM; + goto error_rm_hwmon_dev; + } + mma7450_idev->poll = mma7450_dev_poll; + mma7450_idev->poll_interval = POLL_INTERVAL; + idev = mma7450_idev->input; + idev->name = DEVICE_NAME; + idev->id.bustype = BUS_I2C; + idev->dev.parent = &client->dev; + idev->evbit[0] = BIT_MASK(EV_ABS); + + input_set_abs_params(idev, ABS_X, -512, 512, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(idev, ABS_Y, -512, 512, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(idev, ABS_Z, -512, 512, INPUT_FUZZ, INPUT_FLAT); + ret = input_register_polled_device(mma7450_idev); + if (ret) { + dev_err(&client->dev, "register poll device failed!\n"); + goto error_free_poll_dev; + } + + /* configure gpio as input for interrupt monitor */ + plat_data->gpio_pin_get(); + + set_irq_type(plat_data->int1, IRQF_TRIGGER_RISING); + /* register interrupt handle */ + ret = request_irq(plat_data->int1, mma7450_interrupt, + IRQF_TRIGGER_RISING, DEVICE_NAME, idev); + + if (ret) { + dev_err(&client->dev, "request_irq(%d) returned error %d\n", + plat_data->int1, ret); + goto error_rm_poll_dev; + } + + set_irq_type(plat_data->int2, IRQF_TRIGGER_RISING); + ret = request_irq(plat_data->int2, mma7450_interrupt, + IRQF_TRIGGER_RISING, DEVICE_NAME, idev); + if (ret) { + dev_err(&client->dev, "request_irq(%d) returned error %d\n", + plat_data->int2, ret); + goto error_free_irq1; + } + + dev_info(&client->dev, "mma7450 device is probed successfully.\n"); + + set_mod(1); + return 0; /*what value shall be return */ + + /*error handle */ + error_free_irq1: + free_irq(plat_data->int1, 0); + error_rm_poll_dev: + input_unregister_polled_device(mma7450_idev); + error_free_poll_dev: + input_free_polled_device(mma7450_idev); + error_rm_hwmon_dev: + hwmon_device_unregister(hwmon_dev); + error_rm_dev_file: + device_remove_file(&client->dev, &mma7450_dev_attr); + error_disable_power: + regulator_disable(reg_dvdd_io); /*shall I check the return value */ + regulator_disable(reg_avdd); + regulator_put(reg_dvdd_io); + regulator_put(reg_avdd); + + return ret; +} + +static int mma7450_remove(struct i2c_client *client) +{ + free_irq(plat_data->int2, mma7450_idev->input); + free_irq(plat_data->int1, mma7450_idev->input); + plat_data->gpio_pin_put(); + input_unregister_polled_device(mma7450_idev); + input_free_polled_device(mma7450_idev); + hwmon_device_unregister(hwmon_dev); + device_remove_file(&client->dev, &mma7450_dev_attr); + regulator_disable(reg_dvdd_io); /*shall I check the return value */ + regulator_disable(reg_avdd); + regulator_put(reg_dvdd_io); + regulator_put(reg_avdd); + return 0; +} + +static int mma7450_suspend(struct i2c_client *client, pm_message_t state) +{ + mma7450_mode = i2c_smbus_read_byte_data(mma7450_client, REG_MCTL); + i2c_smbus_write_byte_data(mma7450_client, REG_MCTL, + mma7450_mode & ~0x3); + return 0; +} + +static int mma7450_resume(struct i2c_client *client) +{ + i2c_smbus_write_byte_data(mma7450_client, REG_MCTL, mma7450_mode); + return 0; +} + +static int __init init_mma7450(void) +{ + /*register driver */ + printk(KERN_INFO "add mma i2c driver\n"); + return i2c_add_driver(&i2c_mma7450_driver); +} + +static void __exit exit_mma7450(void) +{ + printk(KERN_INFO "del mma i2c driver.\n"); + return i2c_del_driver(&i2c_mma7450_driver); +} + +module_init(init_mma7450); +module_exit(exit_mma7450); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MMA7450 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/mxc_mma8450.c b/drivers/hwmon/mxc_mma8450.c new file mode 100644 index 000000000000..3d4c1ef1f4d9 --- /dev/null +++ b/drivers/hwmon/mxc_mma8450.c @@ -0,0 +1,390 @@ +/* + * mma8450.c - Linux kernel modules for 3-Axis Orientation/Motion + * Detection Sensor + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/input-polldev.h> + +/* + * Defines + */ +#define assert(expr)\ + if (!(expr)) {\ + printk(KERN_ERR "Assertion failed! %s,%d,%s,%s\n",\ + __FILE__, __LINE__, __func__, #expr);\ + } + +#define MMA8450_DRV_NAME "mma8450" +#define MMA8450_I2C_ADDR 0x1C +#define MMA8450_ID 0xC6 +#define MMA8450_STATUS 0x00 +#define MODE_CHANGE_DELAY_MS 100 +#define POLL_INTERVAL 100 +#define INPUT_FUZZ 32 +#define INPUT_FLAT 32 + +/* register enum for mma8450 registers */ +enum { + MMA8450_STATUS1 = 0x00, + MMA8450_OUT_X8, + MMA8450_OUT_Y8, + MMA8450_OUT_Z8, + + MMA8450_STATUS2, + MMA8450_OUT_X_LSB, + MMA8450_OUT_X_MSB, + MMA8450_OUT_Y_LSB, + MMA8450_OUT_Y_MSB, + MMA8450_OUT_Z_LSB, + MMA8450_OUT_Z_MSB, + + MMA8450_STATUS3, + MMA8450_OUT_X_DELTA, + MMA8450_OUT_Y_DELTA, + MMA8450_OUT_Z_DELTA, + + MMA8450_WHO_AM_I, + + MMA8450_F_STATUS, + MMA8450_F_8DATA, + MMA8450_F_12DATA, + MMA8450_F_SETUP, + + MMA8450_SYSMOD, + MMA8450_INT_SOURCE, + MMA8450_XYZ_DATA_CFG, + MMA8450_HP_FILTER_CUTOFF, + + MMA8450_PL_STATUS, + MMA8450_PL_PRE_STATUS, + MMA8450_PL_CFG, + MMA8450_PL_COUNT, + MMA8450_PL_BF_ZCOMP, + MMA8450_PL_P_L_THS_REG1, + MMA8450_PL_P_L_THS_REG2, + MMA8450_PL_P_L_THS_REG3, + MMA8450_PL_L_P_THS_REG1, + MMA8450_PL_L_P_THS_REG2, + MMA8450_PL_L_P_THS_REG3, + + MMA8450_FF_MT_CFG_1, + MMA8450_FF_MT_SRC_1, + MMA8450_FF_MT_THS_1, + MMA8450_FF_MT_COUNT_1, + MMA8450_FF_MT_CFG_2, + MMA8450_FF_MT_SRC_2, + MMA8450_FF_MT_THS_2, + MMA8450_FF_MT_COUNT_2, + + MMA8450_TRANSIENT_CFG, + MMA8450_TRANSIENT_SRC, + MMA8450_TRANSIENT_THS, + MMA8450_TRANSIENT_COUNT, + + MMA8450_PULSE_CFG, + MMA8450_PULSE_SRC, + MMA8450_PULSE_THSX, + MMA8450_PULSE_THSY, + MMA8450_PULSE_THSZ, + MMA8450_PULSE_TMLT, + MMA8450_PULSE_LTCY, + MMA8450_PULSE_WIND, + + MMA8450_ASLP_COUNT, + MMA8450_CTRL_REG1, + MMA8450_CTRL_REG2, + MMA8450_CTRL_REG3, + MMA8450_CTRL_REG4, + MMA8450_CTRL_REG5, + + MMA8450_OFF_X, + MMA8450_OFF_Y, + MMA8450_OFF_Z, + + MMA8450_REG_END, +}; + +enum { + MODE_STANDBY, + MODE_2G, + MODE_4G, + MODE_8G, +}; + +/* mma8450 status */ +struct mma8450_status { + u8 mode; + u8 ctl_reg2; + u8 ctl_reg1; +}; + +static struct mma8450_status mma_status = { + .mode = 0, + .ctl_reg2 = 0, + .ctl_reg1 = 0 +}; + +static struct device *hwmon_dev; +static struct i2c_client *mma8450_i2c_client; +static struct input_polled_dev *mma8450_idev; + +/* + * Initialization function + */ +static int mma8450_init_client(struct i2c_client *client) +{ + int result; + + mma_status.mode = MODE_2G; + + result = i2c_smbus_write_byte_data(client, MMA8450_XYZ_DATA_CFG, 0x07); + assert(result == 0); + + result = + i2c_smbus_write_byte_data(client, MMA8450_CTRL_REG1, + mma_status.mode); + assert(result == 0); + + mdelay(MODE_CHANGE_DELAY_MS); + + return result; +} + +/* + * read sensor data from mma8450 + */ +static int mma8450_read_data(short *x, short *y, short *z) +{ + u8 tmp_data[7]; + + if (i2c_smbus_read_i2c_block_data + (mma8450_i2c_client, MMA8450_OUT_X_LSB, 7, tmp_data) < 7) { + dev_err(&mma8450_i2c_client->dev, "i2c block read failed\n"); + return -3; + } + + *x = ((tmp_data[1] << 8) & 0xff00) | ((tmp_data[0] << 4) & 0x00f0); + *y = ((tmp_data[3] << 8) & 0xff00) | ((tmp_data[2] << 4) & 0x00f0); + *z = ((tmp_data[5] << 8) & 0xff00) | ((tmp_data[4] << 4) & 0x00f0); + + *x = (short)(*x) >> 4; + *y = (short)(*y) >> 4; + *z = (short)(*z) >> 4; + + if (mma_status.mode == MODE_4G) { + (*x) = (*x) << 1; + (*y) = (*y) << 1; + (*z) = (*z) << 1; + } else if (mma_status.mode == MODE_8G) { + (*x) = (*x) << 2; + (*y) = (*y) << 2; + (*z) = (*z) << 2; + } + + return 0; +} + +static void report_abs(void) +{ + short x, y, z; + int result; + + do { + result = + i2c_smbus_read_byte_data(mma8450_i2c_client, + MMA8450_STATUS3); + } while (!(result & 0x08)); /* wait for new data */ + + if (mma8450_read_data(&x, &y, &z) != 0) + return; + + input_report_abs(mma8450_idev->input, ABS_X, x); + input_report_abs(mma8450_idev->input, ABS_Y, y); + input_report_abs(mma8450_idev->input, ABS_Z, z); + input_sync(mma8450_idev->input); +} + +static void mma8450_dev_poll(struct input_polled_dev *dev) +{ + report_abs(); +} + +/* + * I2C init/probing/exit functions + */ +static int __devinit mma8450_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int result; + struct i2c_adapter *adapter; + struct input_dev *idev; + + mma8450_i2c_client = client; + adapter = to_i2c_adapter(client->dev.parent); + + result = i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA); + assert(result); + + printk(KERN_INFO "check mma8450 chip ID\n"); + result = i2c_smbus_read_byte_data(client, MMA8450_WHO_AM_I); + + if (MMA8450_ID != (result)) { + dev_err(&client->dev, + "read chip ID 0x%x is not equal to 0x%x!\n", result, + MMA8450_ID); + printk(KERN_INFO "read chip ID failed\n"); + result = -EINVAL; + goto err_detach_client; + } + + /* Initialize the MMA8450 chip */ + result = mma8450_init_client(client); + assert(result == 0); + + hwmon_dev = hwmon_device_register(&client->dev); + assert(!(IS_ERR(hwmon_dev))); + + dev_info(&client->dev, "build time %s %s\n", __DATE__, __TIME__); + + /*input poll device register */ + mma8450_idev = input_allocate_polled_device(); + if (!mma8450_idev) { + dev_err(&client->dev, "alloc poll device failed!\n"); + result = -ENOMEM; + return result; + } + mma8450_idev->poll = mma8450_dev_poll; + mma8450_idev->poll_interval = POLL_INTERVAL; + idev = mma8450_idev->input; + idev->name = MMA8450_DRV_NAME; + idev->id.bustype = BUS_I2C; + idev->dev.parent = &client->dev; + idev->evbit[0] = BIT_MASK(EV_ABS); + + input_set_abs_params(idev, ABS_X, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(idev, ABS_Y, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(idev, ABS_Z, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + result = input_register_polled_device(mma8450_idev); + if (result) { + dev_err(&client->dev, "register poll device failed!\n"); + return result; + } + + return result; + +err_detach_client: + return result; +} + +static int __devexit mma8450_remove(struct i2c_client *client) +{ + int result; + mma_status.ctl_reg1 = + i2c_smbus_read_byte_data(client, MMA8450_CTRL_REG1); + result = + i2c_smbus_write_byte_data(client, MMA8450_CTRL_REG1, + mma_status.ctl_reg1 & 0xFC); + assert(result == 0); + + hwmon_device_unregister(hwmon_dev); + + return result; +} + +static int mma8450_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int result; + mma_status.ctl_reg1 = + i2c_smbus_read_byte_data(client, MMA8450_CTRL_REG1); + result = + i2c_smbus_write_byte_data(client, MMA8450_CTRL_REG1, + mma_status.ctl_reg1 & 0xFC); + assert(result == 0); + return result; +} + +static int mma8450_resume(struct i2c_client *client) +{ + int result; + result = + i2c_smbus_write_byte_data(client, MMA8450_CTRL_REG1, + mma_status.mode); + assert(result == 0); + return result; +} + +static const struct i2c_device_id mma8450_id[] = { + {MMA8450_DRV_NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, mma8450_id); + +static struct i2c_driver mma8450_driver = { + .driver = { + .name = MMA8450_DRV_NAME, + .owner = THIS_MODULE, + }, + .suspend = mma8450_suspend, + .resume = mma8450_resume, + .probe = mma8450_probe, + .remove = __devexit_p(mma8450_remove), + .id_table = mma8450_id, +}; + +static int __init mma8450_init(void) +{ + /* register driver */ + int res; + + res = i2c_add_driver(&mma8450_driver); + if (res < 0) { + printk(KERN_INFO "add mma8450 i2c driver failed\n"); + return -ENODEV; + } + printk(KERN_INFO "add mma8450 i2c driver\n"); + + return res; +} + +static void __exit mma8450_exit(void) +{ + printk(KERN_INFO "remove mma8450 i2c driver.\n"); + i2c_del_driver(&mma8450_driver); +} + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MMA8450 3-Axis Orientation/Motion Detection Sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(mma8450_init); +module_exit(mma8450_exit); diff --git a/drivers/hwmon/mxc_mma8451.c b/drivers/hwmon/mxc_mma8451.c new file mode 100644 index 000000000000..901be5cedb37 --- /dev/null +++ b/drivers/hwmon/mxc_mma8451.c @@ -0,0 +1,371 @@ +/* + * mma8451.c - Linux kernel modules for 3-Axis Orientation/Motion + * Detection Sensor + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/input-polldev.h> + +/* + * Defines + */ +#define assert(expr)\ + if (!(expr)) {\ + printk(KERN_ERR "Assertion failed! %s,%d,%s,%s\n",\ + __FILE__, __LINE__, __func__, #expr);\ + } + +#define MMA8451_DRV_NAME "mma8451" +#define MMA8451_I2C_ADDR 0x1C +#define MMA8451_ID 0x1A + +#define POLL_INTERVAL 100 +#define INPUT_FUZZ 32 +#define INPUT_FLAT 32 +#define MODE_CHANGE_DELAY_MS 100 + +/* register enum for mma8451 registers */ +enum { + MMA8451_STATUS = 0x00, + MMA8451_OUT_X_MSB, + MMA8451_OUT_X_LSB, + MMA8451_OUT_Y_MSB, + MMA8451_OUT_Y_LSB, + MMA8451_OUT_Z_MSB, + MMA8451_OUT_Z_LSB, + + MMA8451_F_SETUP = 0x09, + MMA8451_TRIG_CFG, + MMA8451_SYSMOD, + MMA8451_INT_SOURCE, + MMA8451_WHO_AM_I, + MMA8451_XYZ_DATA_CFG, + MMA8451_HP_FILTER_CUTOFF, + + MMA8451_PL_STATUS, + MMA8451_PL_CFG, + MMA8451_PL_COUNT, + MMA8451_PL_BF_ZCOMP, + MMA8451_P_L_THS_REG, + + MMA8451_FF_MT_CFG, + MMA8451_FF_MT_SRC, + MMA8451_FF_MT_THS, + MMA8451_FF_MT_COUNT, + + MMA8451_TRANSIENT_CFG = 0x1D, + MMA8451_TRANSIENT_SRC, + MMA8451_TRANSIENT_THS, + MMA8451_TRANSIENT_COUNT, + + MMA8451_PULSE_CFG, + MMA8451_PULSE_SRC, + MMA8451_PULSE_THSX, + MMA8451_PULSE_THSY, + MMA8451_PULSE_THSZ, + MMA8451_PULSE_TMLT, + MMA8451_PULSE_LTCY, + MMA8451_PULSE_WIND, + + MMA8451_ASLP_COUNT, + MMA8451_CTRL_REG1, + MMA8451_CTRL_REG2, + MMA8451_CTRL_REG3, + MMA8451_CTRL_REG4, + MMA8451_CTRL_REG5, + + MMA8451_OFF_X, + MMA8451_OFF_Y, + MMA8451_OFF_Z, + + MMA8451_REG_END, +}; + +enum { + MODE_2G = 0, + MODE_4G, + MODE_8G, +}; + +/* mma8451 status */ +struct mma8451_status { + u8 mode; + u8 ctl_reg1; +}; + +static struct mma8451_status mma_status = { + .mode = 0, + .ctl_reg1 = 0 +}; + +static struct input_polled_dev *mma8451_idev; +static struct device *hwmon_dev; +static int test_test_mode = 1; +static struct i2c_client *mma8451_i2c_client; + +/*************************************************************** + * + * Initialization function + * + **************************************************************/ +static int mma8451_init_client(struct i2c_client *client) +{ + int result; + + mma_status.ctl_reg1 = 0x00; + result = + i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, + mma_status.ctl_reg1); + assert(result == 0); + + mma_status.mode = MODE_2G; + result = + i2c_smbus_write_byte_data(client, MMA8451_XYZ_DATA_CFG, + mma_status.mode); + assert(result == 0); + + mma_status.ctl_reg1 |= 0x01; + result = + i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, + mma_status.ctl_reg1); + assert(result == 0); + + mdelay(MODE_CHANGE_DELAY_MS); + + return result; +} + +/*************************************************************** +* +* read sensor data from mma8451 +* +***************************************************************/ +static int mma8451_read_data(short *x, short *y, short *z) +{ + u8 tmp_data[7]; + + if (i2c_smbus_read_i2c_block_data + (mma8451_i2c_client, MMA8451_OUT_X_MSB, 7, tmp_data) < 7) { + dev_err(&mma8451_i2c_client->dev, "i2c block read failed\n"); + return -3; + } + + *x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1]; + *y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3]; + *z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5]; + + *x = (short)(*x) >> 2; + *y = (short)(*y) >> 2; + *z = (short)(*z) >> 2; + + if (mma_status.mode == MODE_4G) { + (*x) = (*x) << 1; + (*y) = (*y) << 1; + (*z) = (*z) << 1; + } else if (mma_status.mode == MODE_8G) { + (*x) = (*x) << 2; + (*y) = (*y) << 2; + (*z) = (*z) << 2; + } + + return 0; +} + +static void report_abs(void) +{ + short x, y, z; + int result; + + do { + result = + i2c_smbus_read_byte_data(mma8451_i2c_client, + MMA8451_STATUS); + } while (!(result & 0x08)); /* wait for new data */ + + if (mma8451_read_data(&x, &y, &z) != 0) + return; + + input_report_abs(mma8451_idev->input, ABS_X, x); + input_report_abs(mma8451_idev->input, ABS_Y, y); + input_report_abs(mma8451_idev->input, ABS_Z, z); + input_sync(mma8451_idev->input); +} + +static void mma8451_dev_poll(struct input_polled_dev *dev) +{ + report_abs(); +} + +static int __devinit mma8451_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int result; + mma8451_i2c_client = client; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + + struct input_dev *idev; + + result = i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA); + assert(result); + + printk(KERN_INFO "check mma8451 chip ID\n"); + result = i2c_smbus_read_byte_data(client, MMA8451_WHO_AM_I); + + if (MMA8451_ID != result) { + dev_err(&client->dev, + "read chip ID 0x%x is not equal to 0x%x!\n", result, + MMA8451_ID); + printk(KERN_INFO "read chip ID failed\n"); + result = -EINVAL; + goto err_detach_client; + } + + /* Initialize the MMA8451 chip */ + result = mma8451_init_client(client); + assert(result == 0); + + hwmon_dev = hwmon_device_register(&client->dev); + assert(!(IS_ERR(hwmon_dev))); + + dev_info(&client->dev, "build time %s %s\n", __DATE__, __TIME__); + + /*input poll device register */ + mma8451_idev = input_allocate_polled_device(); + if (!mma8451_idev) { + dev_err(&client->dev, "alloc poll device failed!\n"); + result = -ENOMEM; + return result; + } + mma8451_idev->poll = mma8451_dev_poll; + mma8451_idev->poll_interval = POLL_INTERVAL; + idev = mma8451_idev->input; + idev->name = MMA8451_DRV_NAME; + idev->id.bustype = BUS_I2C; + idev->dev.parent = &client->dev; + idev->evbit[0] = BIT_MASK(EV_ABS); + + input_set_abs_params(idev, ABS_X, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(idev, ABS_Y, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(idev, ABS_Z, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + result = input_register_polled_device(mma8451_idev); + if (result) { + dev_err(&client->dev, "register poll device failed!\n"); + return result; + } + return result; + +err_detach_client: + return result; +} + +static int __devexit mma8451_remove(struct i2c_client *client) +{ + int result; + mma_status.ctl_reg1 = + i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); + result = + i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, + mma_status.ctl_reg1 & 0xFE); + assert(result == 0); + + hwmon_device_unregister(hwmon_dev); + + return result; +} + +static int mma8451_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int result; + mma_status.ctl_reg1 = + i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); + result = + i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, + mma_status.ctl_reg1 & 0xFE); + assert(result == 0); + return result; +} + +static int mma8451_resume(struct i2c_client *client) +{ + int result; + result = + i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, + mma_status.ctl_reg1); + assert(result == 0); + return result; +} + +static const struct i2c_device_id mma8451_id[] = { + {MMA8451_DRV_NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, mma8451_id); + +static struct i2c_driver mma8451_driver = { + .driver = { + .name = MMA8451_DRV_NAME, + .owner = THIS_MODULE, + }, + .suspend = mma8451_suspend, + .resume = mma8451_resume, + .probe = mma8451_probe, + .remove = __devexit_p(mma8451_remove), + .id_table = mma8451_id, +}; + +static int __init mma8451_init(void) +{ + /* register driver */ + int res; + + res = i2c_add_driver(&mma8451_driver); + if (res < 0) { + printk(KERN_INFO "add mma8451 i2c driver failed\n"); + return -ENODEV; + } + printk(KERN_INFO "add mma8451 i2c driver\n"); + + return res; +} + +static void __exit mma8451_exit(void) +{ + printk(KERN_INFO "remove mma8451 i2c driver.\n"); + i2c_del_driver(&mma8451_driver); +} + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MMA8451 3-Axis Orientation/Motion Detection Sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(mma8451_init); +module_exit(mma8451_exit); |