lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <5387F633.3060809@roeck-us.net>
Date:	Thu, 29 May 2014 20:08:35 -0700
From:	Guenter Roeck <linux@...ck-us.net>
To:	Wei-Chun Pan <weichun.pan@...antech.com.tw>,
	Samuel Ortiz <sameo@...ux.intel.com>,
	Lee Jones <lee.jones@...aro.org>,
	Jean Delvare <jdelvare@...e.de>,
	Wolfram Sang <wsa@...-dreams.de>
CC:	"Louis.Lu" <Louis.Lu@...antech.com.tw>,
	"Neo.Lo" <neo.lo@...antech.com.tw>,
	"Hank.Peng" <Hank.Peng@...antech.com.tw>,
	"Kevin.Ong" <Kevin.Ong@...antech.com.tw>,
	linux-kernel@...r.kernel.org
Subject: Re: [PATCH 2/3] hwmon: (iManager2) Add support for IT8516/18/28

On 05/28/2014 10:57 PM, Wei-Chun Pan wrote:
>      Advantech's new module comes equipped with "iManager" - an embedded controller (EC), providing embedded features for system integrators to increase reliability and simplify integration.
>      This patch add the MFD driver for enabling Advantech iManager V2.0 chipset. Available functions support HW Monitor base on ITE-IT85XX chip. These functions are tested on Advantech SOM-5892 board. All the embedded functions are configured by a utility. Advantech has done all the hard work for user with the release of a suite of Software APIs.
>      These provide not only the underlying drivers required but also a rich set of user-friendly, intelligent and integrated interfaces, which speeds development, enhances security and offers add-on value for Advantech platforms.
>
Doesn't really follow SubmittingPatches guidelines; lines should be shorter.

> Signed-off-by: Wei-Chun Pan Developer <weichun.pan@...antech.com.tw>

I assume 'Developer' is not part of your name ?

> ---
>   drivers/hwmon/Kconfig         |   7 +
>   drivers/hwmon/Makefile        |   1 +
>   drivers/hwmon/imanager2_hwm.c | 635 ++++++++++++++++++++++++++++++++++++++++++
>   drivers/hwmon/imanager2_hwm.h | 118 ++++++++
>   4 files changed, 761 insertions(+)
>   mode change 100644 => 100755 drivers/hwmon/Kconfig
>   mode change 100644 => 100755 drivers/hwmon/Makefile
>   create mode 100755 drivers/hwmon/imanager2_hwm.c
>   create mode 100755 drivers/hwmon/imanager2_hwm.h

Executable source files ? Note that checkpatch complains about this.
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> old mode 100644
> new mode 100755
> index bc196f4..d4aeab6
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -39,6 +39,13 @@ config HWMON_DEBUG_CHIP
>
>   comment "Native drivers"
>
> +config SENSORS_IMANAGER2
> +	tristate "Support for Advantech iManager2 EC H.W. Monitor"
> +	select MFD_CORE
> +	select MFD_IMANAGER2

It is customary to express this as dependency. Should be
	depends on MFD_IMANAGER2


> +	help
> +	  Support for the Advantech iManager2 EC H.W. Monitor
> +
>   config SENSORS_AB8500
>   	tristate "AB8500 thermal monitoring"
>   	depends on AB8500_GPADC && AB8500_BM
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> old mode 100644
> new mode 100755
> index c48f987..a2c8f07
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -146,6 +146,7 @@ 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_IMANAGER2)	+= imanager2_hwm.o
>
>   obj-$(CONFIG_PMBUS)		+= pmbus/
>
> diff --git a/drivers/hwmon/imanager2_hwm.c b/drivers/hwmon/imanager2_hwm.c
> new file mode 100755
> index 0000000..48fe3dd
> --- /dev/null
> +++ b/drivers/hwmon/imanager2_hwm.c
> @@ -0,0 +1,635 @@
> +/* imanager2_hwm.c - HW Monitoring interface for Advantech EC IT8516/18/28
> + * Copyright (C) 2014  Richard Vidal-Dorsch <richard.dorsch@...antech.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 3 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, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
Not needed; see below.

> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/mfd/advantech/imanager2.h>
> +#include "imanager2_hwm.h"
> +
> +#define DRV_NAME	CHIP_NAME "_hwm"

Note that "it85xx" as chip name is a bit too generic. You don't support all chips
starting with IT85, do you ? I would suggest to pick one of the supported chips as
driver name.

> +#define DRV_VERSION	"0.4.6"
> +
> +/* Voltage */
> +static int ec_get_voltage_adc(struct it85xx *ec, int index,

Same applies to this structure, really.

> +			      u32 *volt_millivolt)
> +{
> +	int ret;
> +	u8 portnum, tmp[2];
> +
> +	*volt_millivolt = 0;
> +
> +	spin_lock(&ec->lock);
> +
> +	if ((ec->flag & EC_F_MAILBOX) != 0) {

The "!= 0" in those comparisons is really unnecessary.
This is implied.

> +		ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
> +					     ec_volt_table[index].did, &tmp[0],
> +					     2);
> +	} else {
> +		u8 pin = ec->table.pinnum[ec->table.devid2itemnum[
> +						ec_volt_table[index].did]];
> +
> +		ret = ec_io_read(EC_CMD_ADC_INDEX, pin, &portnum, 1);
> +		if (ret != 0)
> +			goto unlock;
> +		if (portnum == 0xFF) {
> +			ret = -EFAULT;
> +			goto unlock;
> +		}
> +
> +		ret = ec_io_read_byte_without_offset(EC_CMD_ADC_READ_LSB,
> +						     &tmp[1]);
> +		if (ret != 0)
> +			goto unlock;
> +
> +		ret = ec_io_read_byte_without_offset(EC_CMD_ADC_READ_MSB,
> +						     &tmp[0]);
> +	}
> +unlock:
> +	spin_unlock(&ec->lock);
> +
> +	if (ret != 0)
> +		return ret;
> +
> +	*volt_millivolt = ((tmp[0] << 8 | tmp[1]) & EC_ADC_RESOLUTION_MAX) *
> +			  ec_volt_table[index].factor *
> +			  EC_ADC_VOLTAGE_VALUE_MAX / EC_ADC_RESOLUTION_MAX;
> +
> +	return 0;
> +}
> +
> +static void ec_volt_init_once(struct it85xx *ec, int index)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(ec_volt_table); i++) {
> +		if (ec->table.devid2itemnum[ec_volt_table[i].did] !=
> +							EC_TABLE_ITEM_UNUSED) {
> +			ec_volt_table[i].factor = 1;
> +			ec_volt_table[i].visible = 1;
> +		} else if (ec->table.devid2itemnum[ec_volt_table[i].did + 1] !=
> +							EC_TABLE_ITEM_UNUSED) {
> +			ec_volt_table[i].did += 1;
> +			ec_volt_table[i].factor = 2;
> +			ec_volt_table[i].visible = 1;
> +		} else if (ec->table.devid2itemnum[ec_volt_table[i].did + 2] !=
> +							EC_TABLE_ITEM_UNUSED) {
> +			ec_volt_table[i].did += 2;
> +			ec_volt_table[i].factor = 10;
> +			ec_volt_table[i].visible = 1;
> +		} else {
> +			ec_volt_table[i].visible = 0;
> +		}
> +	}
> +}
> +
> +static ssize_t show_in(struct device *dev, struct device_attribute *dev_attr,
> +		       char *buf)
> +{
> +	struct it85xx *ec = dev_get_drvdata(dev);
> +	u32 val;
> +	int i = to_sensor_dev_attr(dev_attr)->index;
> +	int ret = ec_get_voltage_adc(ec, i, &val);
> +
> +	if (ret != 0)
> +		return (ssize_t)ret;

Unnecessary typecast.

> +
> +	return sprintf(buf, "%u\n", val);
> +}
> +
> +static ssize_t show_in_label(struct device *dev,
> +			     struct device_attribute *dev_attr, char *buf)
> +{
> +	int i = to_sensor_dev_attr(dev_attr)->index;
> +	return sprintf(buf, "%s\n", ec_volt_table[i].name);
> +}
> +
> +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
> +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
> +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
> +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
> +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4);
> +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5);
> +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6);
> +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7);
> +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8);
> +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_in, NULL, 9);
> +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_in, NULL, 10);
> +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_in, NULL, 11);
> +
> +static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_in_label, NULL, 0);
> +static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_in_label, NULL, 1);
> +static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_in_label, NULL, 2);
> +static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_in_label, NULL, 3);
> +static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_in_label, NULL, 4);
> +static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_in_label, NULL, 5);
> +static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_in_label, NULL, 6);
> +static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_in_label, NULL, 7);
> +static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_in_label, NULL, 8);
> +static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_in_label, NULL, 9);
> +static SENSOR_DEVICE_ATTR(in10_label, S_IRUGO, show_in_label, NULL, 10);
> +static SENSOR_DEVICE_ATTR(in11_label, S_IRUGO, show_in_label, NULL, 11);
> +
> +static struct attribute *it85xx_volt_attrs[] = {
> +	&sensor_dev_attr_in0_label.dev_attr.attr,
> +	&sensor_dev_attr_in0_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_in1_label.dev_attr.attr,
> +	&sensor_dev_attr_in1_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_in2_label.dev_attr.attr,
> +	&sensor_dev_attr_in2_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_in3_label.dev_attr.attr,
> +	&sensor_dev_attr_in3_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_in4_label.dev_attr.attr,
> +	&sensor_dev_attr_in4_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_in5_label.dev_attr.attr,
> +	&sensor_dev_attr_in5_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_in6_label.dev_attr.attr,
> +	&sensor_dev_attr_in6_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_in7_label.dev_attr.attr,
> +	&sensor_dev_attr_in7_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_in8_label.dev_attr.attr,
> +	&sensor_dev_attr_in8_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_in9_label.dev_attr.attr,
> +	&sensor_dev_attr_in9_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_in10_label.dev_attr.attr,
> +	&sensor_dev_attr_in10_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_in11_label.dev_attr.attr,
> +	&sensor_dev_attr_in11_input.dev_attr.attr,
> +
> +	NULL
> +};
> +
> +static umode_t it85xx_volt_mode(struct kobject *kobj, struct attribute *attr,
> +				int index)
> +{
> +	struct sensor_device_attribute *sensor = container_of(
> +			attr, struct sensor_device_attribute, dev_attr.attr);
> +
> +	if (ec_volt_table[sensor->index].visible == 0)
> +		return 0;
> +
> +	return attr->mode;
> +}
> +
> +static const struct attribute_group it85xx_volt_group = {
> +	.attrs = it85xx_volt_attrs,
> +	.is_visible = it85xx_volt_mode,
> +};
> +
> +/* Current */
> +static int ec_get_current_adc(struct it85xx *ec, int index,
> +			      u32 *curr_milliampere)
> +{
> +	int ret;
> +	u8 i, tmp[5];
> +	u16 value, factor;
> +	u32 baseunit;
> +
> +	*curr_milliampere = 0;
> +
> +	spin_lock(&ec->lock);
> +
> +	if ((ec->flag & EC_F_MAILBOX) != 0)
> +		ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
> +					     ec_curr_table[index].did,
> +					     tmp, ARRAY_SIZE(tmp));
> +	else
> +		ret = -EOPNOTSUPP;
> +

If the operation is not supported in this case, why is the attribute
visible in the first place ?


> +	spin_unlock(&ec->lock);
> +
> +	if (ret != 0)
> +		return ret;
> +
> +	value = (tmp[0] << 8 | tmp[1]) & EC_ADC_RESOLUTION_MAX;
> +	factor = tmp[2] << 8 | tmp[3];

I would suggest to use ( ) around the shift operations. Technically
not necessary but makes it more obvious what is intended, and you do
it elsewhere.

> +	baseunit = 1;
> +	for (i = 1; i < tmp[4]; i++)
> +		baseunit *= 10;
> +
> +	*curr_milliampere = value * factor * baseunit *
> +			    EC_ADC_CURRENT_VALUE_MAX / EC_ADC_RESOLUTION_MAX;
> +
Would it make sense to use DIV_ROUND_CLOSEST() for those operations
to improve accuracy ?

> +	return 0;
> +}
> +
> +static void ec_current_init_once(struct it85xx *ec)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(ec_curr_table); i++)
> +		if (ec->table.devid2itemnum[ec_curr_table[i].did] !=
> +							EC_TABLE_ITEM_UNUSED)
> +			ec_curr_table[i].visible = 1;
> +		else
> +			ec_curr_table[i].visible = 0;

		ec_curr_table[i].visible = ec->table.devid2itemnum[ec_curr_table[i].did] !=
							EC_TABLE_ITEM_UNUSED;

would be a bit simpler. Either case, there should be no need to ever set visible
to 0.

> +}
> +
> +static ssize_t show_curr(struct device *dev, struct device_attribute *dev_attr,
> +			 char *buf)
> +{
> +	struct it85xx *ec = dev_get_drvdata(dev);
> +	u32 val;
> +	int i = to_sensor_dev_attr(dev_attr)->index;
> +	int ret = ec_get_current_adc(ec, i, &val);
> +
> +	if (ret != 0)
> +		return (ssize_t)ret;
> +
> +	return sprintf(buf, "%u\n", val);
> +}
> +
> +static ssize_t show_curr_label(struct device *dev,
> +			       struct device_attribute *dev_attr, char *buf)
> +{
> +	int i = to_sensor_dev_attr(dev_attr)->index;
> +	return sprintf(buf, "%s\n", ec_curr_table[i].name);
> +}
> +
> +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, show_curr, NULL, 0);
> +static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_curr_label, NULL, 0);
> +
> +static struct attribute *it85xx_curr_attrs[] = {
> +	&sensor_dev_attr_curr1_label.dev_attr.attr,
> +	&sensor_dev_attr_curr1_input.dev_attr.attr,
> +
> +	NULL
> +};
> +
> +static umode_t it85xx_curr_mode(struct kobject *kobj, struct attribute *attr,
> +				int index)
> +{
> +	struct sensor_device_attribute *sensor = container_of(
> +			attr, struct sensor_device_attribute, dev_attr.attr);
> +
> +	if (ec_curr_table[sensor->index].visible == 0)
> +		return 0;
> +
> +	return attr->mode;
> +}
> +
> +static const struct attribute_group it85xx_curr_group = {
> +	.attrs = it85xx_curr_attrs,
> +	.is_visible = it85xx_curr_mode,
> +};
> +
> +/* Temperature */
> +static int ec_get_thermal_temperature(struct it85xx *ec, int index,
> +				      int *temp_millicelsius)

thermal_temperature seems to be a bit redundant. Is there another
non-thermal temperature ? Just use 'temperature'.

> +{
> +	int ret;
> +	u8 tmp;
> +
> +	*temp_millicelsius = 0;
> +
> +	spin_lock(&ec->lock);
> +	ret = ec_acpiram_read_byte(ec, ec_temp_table[index].zonetype_acpireg,
> +				   &tmp);
> +	spin_unlock(&ec->lock);
> +
> +	if (ret != 0)
> +		return ret;
> +
> +	*temp_millicelsius = ((signed)tmp) * 1000;

Does this mean that tmp is really s8 and can be negative.
If so, I don't think the 'signed' typecase will help;
you might need (s8) instead. Please test.

> +
> +	return 0;
> +}
> +
> +static void ec_temp_init_once(struct it85xx *ec)
> +{
> +	int i, j, ret;
> +	u8 tmltype;
> +	struct ec_thermalzone zone;
> +
> +	for (i = 0; i < ARRAY_SIZE(ec_temp_table); i++)
> +		ec_temp_table[i].visible = 0;
> +
> +	for (i = 0; i < EC_MAX_THERMAL_ZONE; i++) {
> +		spin_lock(&ec->lock);
> +
> +		if ((ec->flag & EC_F_MAILBOX) != 0) {
> +			int len = sizeof(struct ec_thermalzone);
> +			ret = ec_read_thermalzone(ec, i, NULL, NULL,
> +						  (u8 *)&zone, &len);
> +		} else {
> +			ret = ec_io_read(
> +				EC_CMD_HWRAM_READ,
> +				EC_HWRAM_ADDR_THERMAL_SOURCE_SMB_STATUS(i),
> +				&zone.status, 1);
> +		}
> +
> +		spin_unlock(&ec->lock);
> +
> +		if (ret != 0)
> +			continue;
> +
> +		tmltype = (zone.status >> 5);
> +
> +		for (j = 0; j < ARRAY_SIZE(ec_temp_table); j++) {
> +			if (tmltype == ec_temp_table[j].zonetype_value) {
> +				ec_temp_table[j].visible = 1;
> +				break;
> +			}
> +		}
> +	}
> +}
> +
> +static ssize_t show_temp(struct device *dev, struct device_attribute *dev_attr,
> +			 char *buf)
> +{
> +	struct it85xx *ec = dev_get_drvdata(dev);
> +	int i = to_sensor_dev_attr(dev_attr)->index;
> +	int val, ret;
> +	ret = ec_get_thermal_temperature(ec, i, &val);
> +
> +	if (ret != 0)
> +		return (ssize_t)ret;
> +
> +	return sprintf(buf, "%d\n", val);
> +}
> +
> +static ssize_t show_temp_label(struct device *dev,
> +			       struct device_attribute *dev_attr, char *buf)
> +{
> +	int i = to_sensor_dev_attr(dev_attr)->index;
> +	return sprintf(buf, "%s\n", ec_temp_table[i].name);
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
> +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
> +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
> +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
> +
> +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0);
> +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1);
> +static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2);
> +static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3);
> +
> +static struct attribute *it85xx_temp_attrs[] = {
> +	&sensor_dev_attr_temp1_label.dev_attr.attr,
> +	&sensor_dev_attr_temp1_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_temp2_label.dev_attr.attr,
> +	&sensor_dev_attr_temp2_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_temp3_label.dev_attr.attr,
> +	&sensor_dev_attr_temp3_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_temp4_label.dev_attr.attr,
> +	&sensor_dev_attr_temp4_input.dev_attr.attr,
> +
> +	NULL
> +};
> +
> +static umode_t it85xx_temp_mode(struct kobject *kobj, struct attribute *attr,
> +				int index)
> +{
> +	struct sensor_device_attribute *sensor = container_of(
> +			attr, struct sensor_device_attribute, dev_attr.attr);
> +
> +	if (ec_temp_table[sensor->index].visible == 0)
> +		return 0;
> +
> +	return attr->mode;
> +}
> +
> +static const struct attribute_group it85xx_temp_group = {
> +	.attrs = it85xx_temp_attrs,
> +	.is_visible = it85xx_temp_mode,
> +};
> +
> +/* Fan Speed */
> +static int ec_get_fan_speed(struct it85xx *ec, int index, u32 *speed_rpm)
> +{
> +	int ret;
> +	u8 tmp[2];
> +
> +	*speed_rpm = 0;
> +
> +	spin_lock(&ec->lock);
> +
> +	if ((ec->flag & EC_F_MAILBOX) != 0)
> +		ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
> +					     ec_fan_table[index].did,
> +					     &tmp[0], 2);
> +	else
> +		ret = ec_io_read(EC_CMD_ACPIRAM_READ,
> +				 ec_fan_table[index].fspeed_acpireg,
> +				 &tmp[0], 2);
> +

For exported functions those function names are quite generic.
Might make sense to pick something a bit less generic.

> +	spin_unlock(&ec->lock);
> +
> +	if (ret != 0)
> +		return ret;
> +
> +	if (tmp[0] == 0xFF && tmp[1] == 0xFF)
> +		return -ENODEV;
> +
> +	*speed_rpm = (tmp[0] << 8) | tmp[1];
> +
> +	return 0;
> +}
> +
> +static void ec_fan_init_once(struct it85xx *ec)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(ec_fan_table); i++)
> +		ec_fan_table[i].visible = 0;
> +
> +	if ((ec->flag & EC_F_MAILBOX) != 0) {
> +		for (i = 0; i < ARRAY_SIZE(ec_fan_table); i++)
> +			if (ec->table.devid2itemnum[ec_fan_table[i].did] !=
> +							EC_TABLE_ITEM_UNUSED)
> +				ec_fan_table[i].visible = 1;
> +	} else {
> +		int fnum, ret;
> +		u8 tmp, fscr;
> +
> +		for (fnum = 0; fnum < EC_MAX_IO_FAN; fnum++) {
> +			spin_lock(&ec->lock);
> +			ret = ec_io_read(EC_CMD_HWRAM_READ,
> +					 EC_HWRAM_ADDR_FAN_CONTROL(fnum),
> +					 &tmp, 1);
> +			spin_unlock(&ec->lock);
> +
> +			if (ret != 0)
> +				continue;
> +
> +			fscr = (tmp >> 4) & 0x03;
> +
> +			switch (fscr) {
> +			case 1:	/* tacho0 */
> +			case 2:	/* tacho1 */
> +			case 3:	/* tacho2 */
> +				i = fscr - 1;
> +				break;
> +			default:
> +				continue;
> +			}
> +
			if (!fscr)
				continue;
			i = fscr - 1;

would simpler and do the same. Actually you don't even need 'i';
you could just use fscr as index, after
			fscr--;


> +			if (ec_fan_table[i].visible == 1)
> +				continue;
> +

Can you explain this logic a bit ?

> +			ec_fan_table[i].fspeed_acpireg =
> +					EC_ACPIRAM_ADDR_FAN_SPEED_BASE(i);
> +
> +			ec_fan_table[i].visible = 1;
> +		}
> +	}
> +}
> +
> +static ssize_t show_fan(struct device *dev, struct device_attribute *dev_attr,
> +			char *buf)
> +{
> +	struct it85xx *ec = dev_get_drvdata(dev);
> +	int i = to_sensor_dev_attr(dev_attr)->index;
> +	u32 val;
> +	int ret = ec_get_fan_speed(ec, i, &val);
> +
> +	if (ret != 0)
> +		return (ssize_t)ret;
> +
> +	return sprintf(buf, "%u\n", val);
> +}
> +
> +static ssize_t show_fan_label(struct device *dev,
> +			      struct device_attribute *dev_attr, char *buf)

The second-line alignment differs from function to function.
Please make it consistent, preferably aligned with ( as suggested
in coding style.

> +{
> +	int i = to_sensor_dev_attr(dev_attr)->index;
> +	return sprintf(buf, "%s\n", ec_fan_table[i].name);
> +}
> +
> +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
> +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
> +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
> +
> +static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, show_fan_label, NULL, 0);
> +static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, show_fan_label, NULL, 1);
> +static SENSOR_DEVICE_ATTR(fan3_label, S_IRUGO, show_fan_label, NULL, 2);
> +
> +static struct attribute *it85xx_fan_attrs[] = {
> +	&sensor_dev_attr_fan1_label.dev_attr.attr,
> +	&sensor_dev_attr_fan1_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_fan2_label.dev_attr.attr,
> +	&sensor_dev_attr_fan2_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_fan3_label.dev_attr.attr,
> +	&sensor_dev_attr_fan3_input.dev_attr.attr,
> +
> +	NULL
> +};
> +
> +static umode_t it85xx_fan_mode(struct kobject *kobj, struct attribute *attr,
> +			       int index)
> +{
> +	struct sensor_device_attribute *sensor = container_of(
> +			attr, struct sensor_device_attribute, dev_attr.attr);
> +
> +	if (ec_fan_table[sensor->index].visible == 0)

the '== 0' is really unnecessary here.
	if (!ec_fan_table[sensor->index].visible)
would be a better fit (and visible could be a boolean).

> +		return 0;
> +
> +	return attr->mode;
> +}
> +
> +static const struct attribute_group it85xx_fan_group = {
> +	.attrs = it85xx_fan_attrs,
> +	.is_visible = it85xx_fan_mode,
> +};
> +
> +/* HWM groups */
> +static const struct attribute_group *it85xx_hwmon_groups[] = {
> +	&it85xx_volt_group,
> +	&it85xx_curr_group,
> +	&it85xx_temp_group,
> +	&it85xx_fan_group,
> +
> +	NULL
> +};
> +
> +/* Module */
> +static int it85xx_hwmon_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct it85xx *hwmon_ec;
> +	struct device *hwmon_dev;
> +
> +	hwmon_ec = devm_kzalloc(dev, sizeof(struct it85xx), GFP_KERNEL);
> +
> +	if (hwmon_ec == NULL)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, hwmon_ec);
> +
> +	hwmon_ec = dev->parent->platform_data;
> +
You lost me here. What is this for ? Why do you allocate this structure
locally just to drop it and replace it with the one you got from the parent
platform data ?

> +	ec_volt_init_once(hwmon_ec, 0);
> +	ec_temp_init_once(hwmon_ec);
> +	ec_current_init_once(hwmon_ec);
> +	ec_fan_init_once(hwmon_ec);
> +
> +	hwmon_dev = devm_hwmon_device_register_with_groups(dev, CHIP_NAME,
> +							   hwmon_ec,
> +							   it85xx_hwmon_groups);
> +
> +	if (IS_ERR(hwmon_dev) != 0) {
> +		pr_err("Could not register it85xx hwmon device\n");
> +		return PTR_ERR(hwmon_dev);
> +	}
> +
> +	pr_info("HWM driver v%s loaded\n", DRV_VERSION);
> +

Can you drop all this noise ? Besides, you can use dev_info and dev_err
in all cases and don't need to use pr_ functions.

> +	return 0;
> +}
> +
> +static int it85xx_hwmon_remove(struct platform_device *pdev)
> +{
> +	pr_info("HWM driver removed\n");
> +
> +	return 0;
> +}

Please drop the remove function.

> +
> +static struct platform_driver it85xx_hwmon_driver = {
> +	.probe = it85xx_hwmon_probe,
> +	.remove = it85xx_hwmon_remove,
> +	.driver = {
> +		.owner = THIS_MODULE,
> +		.name  = DRV_NAME,
> +	},
> +};
> +
> +module_platform_driver(it85xx_hwmon_driver);
> +
> +MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
> +MODULE_DESCRIPTION("HW Monitoring interface for Advantech EC IT8516/18/28");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION(DRV_VERSION);
> diff --git a/drivers/hwmon/imanager2_hwm.h b/drivers/hwmon/imanager2_hwm.h
> new file mode 100755
> index 0000000..ccf4a4c
> --- /dev/null
> +++ b/drivers/hwmon/imanager2_hwm.h
> @@ -0,0 +1,118 @@
> +/* imanager2_hwm.h - HW Monitoring interface for Advantech EC IT8516/18/28
> + * Copyright (C) 2014  Richard Vidal-Dorsch <richard.dorsch@...antech.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 3 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, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __IMANAGER2_HWM_H__
> +#define __IMANAGER2_HWM_H__
> +
> +/* ADC */
> +#define EC_ADC_RESOLUTION_MAX		0x03FF	/* 10-bit */
> +#define EC_ADC_VOLTAGE_VALUE_MAX	3000	/* max: 3.0 V */
> +#define EC_ADC_CURRENT_VALUE_MAX	3000	/* max: 3.0 A */
> +
> +struct volt_item {
> +	u8 did;
> +	const char *name;
> +	int factor;
> +	int visible;

Any reason for not using bool for the 'visible' variables ?

> +};
> +
> +struct curr_item {
> +	const u8 did;
> +	const char *name;
> +	int visible;
> +};
> +
> +static struct volt_item ec_volt_table[] = {
> +	{.did = adcmosbat, .name = "BAT CMOS", .factor = 0, .visible = 0},
> +	{.did = adcbat, .name = "BAT", .factor = 0, .visible = 0},
> +	{.did = adc5vs0, .name = "5V S0", .factor = 0, .visible = 0},
> +	{.did = adv5vs5, .name = "5V S5", .factor = 0, .visible = 0},
> +	{.did = adc33vs0, .name = "3V3 S0", .factor = 0, .visible = 0},
> +	{.did = adc33vs5, .name = "3V3 S5", .factor = 0, .visible = 0},
> +	{.did = adv12vs0, .name = "12V S0", .factor = 0, .visible = 0},
> +	{.did = adcvcorea, .name = "Vcore A", .factor = 0, .visible = 0},
> +	{.did = adcvcoreb, .name = "Vcore B", .factor = 0, .visible = 0},
> +	{.did = adcdc, .name = "DC", .factor = 0, .visible = 0},
> +	{.did = adcdcstby, .name = "DC Standby", .factor = 0, .visible = 0},
> +	{.did = adcdcother, .name = "DC Other", .factor = 0, .visible = 0}
> +};
> +
> +static struct curr_item ec_curr_table[] = {
> +	{.did = adccurrent, .name = "IMON", .visible = 0}
> +};
> +
> +/* Thermal */
> +#define EC_MAX_THERMAL_ZONE	4
> +
> +#define EC_THERMAL_TYPE_NONE	0
> +#define EC_THERMAL_TYPE_SYS1	1
> +#define EC_THERMAL_TYPE_CPU	2
> +#define EC_THERMAL_TYPE_SYS3	3
> +#define EC_THERMAL_TYPE_SYS2	4
> +
> +struct temp_item {
> +	const char *name;
> +	const u8 zonetype_value;
> +	u8 zonetype_acpireg;
> +	int visible;
> +};
> +
> +static struct temp_item ec_temp_table[] = {
> +	{.name = "Temp SYS",
> +	 .zonetype_value = EC_THERMAL_TYPE_SYS1,
> +	 .zonetype_acpireg = EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(1),
> +	 .visible = 0},
> +	{.name = "Temp CPU",
> +	 .zonetype_value = EC_THERMAL_TYPE_CPU,
> +	 .zonetype_acpireg = EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(1),
> +	 .visible = 0},
> +	{.name = "Temp SYS3",
> +	 .zonetype_value = EC_THERMAL_TYPE_SYS3,
> +	 .zonetype_acpireg = EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(2),
> +	 .visible = 0},
> +	{.name = "Temp SYS2",
> +	 .zonetype_value = EC_THERMAL_TYPE_SYS2,
> +	 .zonetype_acpireg = EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(2),
> +	 .visible = 0},
> +};
> +
> +struct ec_thermalzone {
> +	u8 channel,
> +	   addr,
> +	   cmd,
> +	   status,
> +	   fancode,
> +	   temp;
> +};
> +
> +/* Tacho */
> +#define EC_MAX_IO_FAN	3
> +
> +struct fan_item {
> +	const u8 did;
> +	const char *name;
> +	u8 fspeed_acpireg;
> +	int visible;
> +};
> +
> +static struct fan_item ec_fan_table[] = {
> +	{.did = tacho0, .name = "Fan CPU", .fspeed_acpireg = 0, .visible = 0},
> +	{.did = tacho1, .name = "Fan SYS", .fspeed_acpireg = 0, .visible = 0},
> +	{.did = tacho2, .name = "Fan SYS2", .fspeed_acpireg = 0, .visible = 0},

No need to initialize static variables with 0.

> +};
> +
> +#endif /* __IMANAGER2_HWM_H__ */
>
Please merge the include file into the source. There is no benefit of having it extra,
and variable declarations in include files is a no-go.

Thanks,
Guenter

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ