2019-01-29 21:32:03

by Jeremy Gebben

[permalink] [raw]
Subject: [PATCH 0/3] hwmon: (lm85) add LM96000 high freqency pwm support

Hi,

This patch adds support for the PWM frequencies from 22.5 to 30 kHz
available on the LM96000.

It looks like this chip has been supported for a long time, but wasn't
mentioned in the docs (which I have updated).

Also, it has been using the generic 'lm85' prefix, which I have not
changed to avoid breaking userspace or device tree files. AFAICT,
lm85_detect() will only return 'lm85' for LM96000 chips, so it doesn't
look like you can get a bare 'lm85' prefix with any other chips.

I stumbled on to a 10 year old thread discussing a patch which looks
like an early attempt to add support for this chip, which may be of
interest:
https://lm-sensors.lm-sensors.narkive.com/1SIwaMDT/patch-hwmon-lm96000-support

Thanks for reviewing,

Jeremy


Jeremy Gebben (3):
hwmon: (lm85) remove freq_map size hardcodes
hwmon: (lm85) Document the LM96000 as supported
hwmon: (lm85) add support for LM96000 high frequencies

Documentation/hwmon/lm85 | 9 ++++++++-
drivers/hwmon/lm85.c | 32 ++++++++++++++++++++++----------
2 files changed, 30 insertions(+), 11 deletions(-)

--
2.17.1



2019-01-29 23:39:19

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 0/3] hwmon: (lm85) add LM96000 high freqency pwm support

On Tue, Jan 29, 2019 at 02:29:17PM -0700, Jeremy Gebben wrote:
> Hi,
>
> This patch adds support for the PWM frequencies from 22.5 to 30 kHz
> available on the LM96000.
>
> It looks like this chip has been supported for a long time, but wasn't
> mentioned in the docs (which I have updated).
>
> Also, it has been using the generic 'lm85' prefix, which I have not
> changed to avoid breaking userspace or device tree files. AFAICT,
> lm85_detect() will only return 'lm85' for LM96000 chips, so it doesn't
> look like you can get a bare 'lm85' prefix with any other chips.
>
> I stumbled on to a 10 year old thread discussing a patch which looks
> like an early attempt to add support for this chip, which may be of
> interest:
> https://lm-sensors.lm-sensors.narkive.com/1SIwaMDT/patch-hwmon-lm96000-support
>
> Thanks for reviewing,
>
Where are the actual patches ?

Guenter

2019-01-30 16:15:28

by Jeremy Gebben

[permalink] [raw]
Subject: [PATCH resend 1/3] hwmon: (lm85) remove freq_map size hardcodes


Allow support for chips that support more than 8 frequencies.
The size still must be a power of two.

Signed-off-by: Jeremy Gebben <[email protected]>
---
drivers/hwmon/lm85.c | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 0a325878e8f5..48f59667a88c 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -198,13 +198,13 @@ static int RANGE_TO_REG(long range)
#define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f]

/* These are the PWM frequency encodings */
-static const int lm85_freq_map[8] = { /* 1 Hz */
+static const int lm85_freq_map[] = { /* 1 Hz */
10, 15, 23, 30, 38, 47, 61, 94
};
-static const int adm1027_freq_map[8] = { /* 1 Hz */
+
+static const int adm1027_freq_map[] = { /* 1 Hz */
11, 15, 22, 29, 35, 44, 59, 88
};
-#define FREQ_MAP_LEN 8

static int FREQ_TO_REG(const int *map,
unsigned int map_size, unsigned long freq)
@@ -212,9 +212,9 @@ static int FREQ_TO_REG(const int *map,
return find_closest(freq, map, map_size);
}

-static int FREQ_FROM_REG(const int *map, u8 reg)
+static int FREQ_FROM_REG(const int *map, unsigned int map_size, u8 reg)
{
- return map[reg & 0x07];
+ return map[reg & (map_size - 1)];
}

/*
@@ -296,6 +296,8 @@ struct lm85_data {
struct i2c_client *client;
const struct attribute_group *groups[6];
const int *freq_map;
+ unsigned int freq_map_mask;
+
enum chips type;

bool has_vid5; /* true if VID5 is configured for ADT7463 or ADT7468 */
@@ -514,7 +516,7 @@ static struct lm85_data *lm85_update_device(struct device *dev)
data->autofan[i].config =
lm85_read_value(client, LM85_REG_AFAN_CONFIG(i));
val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i));
- data->pwm_freq[i] = val & 0x07;
+ data->pwm_freq[i] = val & data->freq_map_mask;
data->zone[i].range = val >> 4;
data->autofan[i].min_pwm =
lm85_read_value(client, LM85_REG_AFAN_MINPWM(i));
@@ -791,7 +793,8 @@ static ssize_t show_pwm_freq(struct device *dev,
if (IS_ADT7468_HFPWM(data))
freq = 22500;
else
- freq = FREQ_FROM_REG(data->freq_map, data->pwm_freq[nr]);
+ freq = FREQ_FROM_REG(data->freq_map, data->freq_map_mask + 1,
+ data->pwm_freq[nr]);

return sprintf(buf, "%d\n", freq);
}
@@ -820,7 +823,7 @@ static ssize_t set_pwm_freq(struct device *dev,
lm85_write_value(client, ADT7468_REG_CFG5, data->cfg5);
} else { /* Low freq. mode */
data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map,
- FREQ_MAP_LEN, val);
+ data->freq_map_mask + 1, val);
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
(data->zone[nr].range << 4)
| data->pwm_freq[nr]);
@@ -1196,7 +1199,7 @@ static ssize_t set_temp_auto_temp_min(struct device *dev,
TEMP_FROM_REG(data->zone[nr].limit));
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
((data->zone[nr].range & 0x0f) << 4)
- | (data->pwm_freq[nr] & 0x07));
+ | data->pwm_freq[nr]);

mutex_unlock(&data->update_lock);
return count;
@@ -1232,7 +1235,7 @@ static ssize_t set_temp_auto_temp_max(struct device *dev,
val - min);
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
((data->zone[nr].range & 0x0f) << 4)
- | (data->pwm_freq[nr] & 0x07));
+ | data->pwm_freq[nr]);
mutex_unlock(&data->update_lock);
return count;
}
@@ -1569,9 +1572,11 @@ static int lm85_probe(struct i2c_client *client, const struct i2c_device_id *id)
case emc6d103:
case emc6d103s:
data->freq_map = adm1027_freq_map;
+ data->freq_map_mask = ARRAY_SIZE(adm1027_freq_map) - 1;
break;
default:
data->freq_map = lm85_freq_map;
+ data->freq_map_mask = ARRAY_SIZE(lm85_freq_map) - 1;
}

/* Set the VRM version */
--
2.17.1


2019-01-30 16:15:31

by Jeremy Gebben

[permalink] [raw]
Subject: [PATCH resend 3/3] hwmon: (lm85) add support for LM96000 high frequencies

This chip expands the freqency field from 3 to 4 bits, to
add more frequencies in the 22.5 to 30 kHz ranges.

Signed-off-by: Jeremy Gebben <[email protected]>
---
Documentation/hwmon/lm85 | 3 +++
drivers/hwmon/lm85.c | 9 +++++++++
2 files changed, 12 insertions(+)

diff --git a/Documentation/hwmon/lm85 b/Documentation/hwmon/lm85
index 9f3a945d1b80..264874f09196 100644
--- a/Documentation/hwmon/lm85
+++ b/Documentation/hwmon/lm85
@@ -140,6 +140,9 @@ of voltage and temperature channels.
SMSC EMC6D103S is similar to EMC6D103, but does not support pwm#_auto_pwm_minctl
and temp#_auto_temp_off.

+The LM96000 supports additional high frequency PWM modes (22.5 kHz, 24 kHz,
+25.7 kHz, 27.7 kHz and 30 kHz), which can be configured on a per-PWM basis.
+
Hardware Configurations
-----------------------

diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 48f59667a88c..37bfa7d105a2 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -202,6 +202,11 @@ static const int lm85_freq_map[] = { /* 1 Hz */
10, 15, 23, 30, 38, 47, 61, 94
};

+static const int lm85_high_freq_map[] = { /* 1 Hz */
+ 10, 15, 23, 30, 38, 47, 61, 94,
+ 22500, 24000, 25700, 25700, 27700, 27700, 30000, 30000
+};
+
static const int adm1027_freq_map[] = { /* 1 Hz */
11, 15, 22, 29, 35, 44, 59, 88
};
@@ -1574,6 +1579,10 @@ static int lm85_probe(struct i2c_client *client, const struct i2c_device_id *id)
data->freq_map = adm1027_freq_map;
data->freq_map_mask = ARRAY_SIZE(adm1027_freq_map) - 1;
break;
+ case lm85:
+ data->freq_map = lm85_high_freq_map;
+ data->freq_map_mask = ARRAY_SIZE(lm85_high_freq_map) - 1;
+ break;
default:
data->freq_map = lm85_freq_map;
data->freq_map_mask = ARRAY_SIZE(lm85_freq_map) - 1;
--
2.17.1


2019-01-30 16:17:57

by Jeremy Gebben

[permalink] [raw]
Subject: [PATCH resend 2/3] hwmon: (lm85) Document the LM96000 as supported

It has been supported as a generic lm85 for quite some time.

Signed-off-by: Jeremy Gebben <[email protected]>
---
Documentation/hwmon/lm85 | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/hwmon/lm85 b/Documentation/hwmon/lm85
index 7c49feaa79d2..9f3a945d1b80 100644
--- a/Documentation/hwmon/lm85
+++ b/Documentation/hwmon/lm85
@@ -3,9 +3,13 @@ Kernel driver lm85

Supported chips:
* National Semiconductor LM85 (B and C versions)
- Prefix: 'lm85'
+ Prefix: 'lm85b' or 'lm85c'
Addresses scanned: I2C 0x2c, 0x2d, 0x2e
Datasheet: http://www.national.com/pf/LM/LM85.html
+ * Texas Instruments LM96000
+ Prefix: 'lm85'
+ Addresses scanned: I2C 0x2c, 0x2d, 0x2e
+ Datasheet: http://www.ti.com/lit/ds/symlink/lm96000.pdf
* Analog Devices ADM1027
Prefix: 'adm1027'
Addresses scanned: I2C 0x2c, 0x2d, 0x2e
--
2.17.1


2019-01-30 16:21:21

by Jeremy Gebben

[permalink] [raw]
Subject: Re: [PATCH 0/3] hwmon: (lm85) add LM96000 high freqency pwm support

On Tue, Jan 29, 2019 at 4:38 PM Guenter Roeck <[email protected]> wrote:
>
> On Tue, Jan 29, 2019 at 02:29:17PM -0700, Jeremy Gebben wrote:
> > Hi,
> >
> > This patch adds support for the PWM frequencies from 22.5 to 30 kHz
> > available on the LM96000.
> >
> > It looks like this chip has been supported for a long time, but wasn't
> > mentioned in the docs (which I have updated).
> >
> > Also, it has been using the generic 'lm85' prefix, which I have not
> > changed to avoid breaking userspace or device tree files. AFAICT,
> > lm85_detect() will only return 'lm85' for LM96000 chips, so it doesn't
> > look like you can get a bare 'lm85' prefix with any other chips.
> >
> > I stumbled on to a 10 year old thread discussing a patch which looks
> > like an early attempt to add support for this chip, which may be of
> > interest:
> > https://lm-sensors.lm-sensors.narkive.com/1SIwaMDT/patch-hwmon-lm96000-support
> >
> > Thanks for reviewing,
> >
> Where are the actual patches ?

Oops! It looks like they didn't send correctly. Hopefully they should
be showing up shortly.

Jeremy

>
> Guenter

2019-01-30 17:25:58

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH resend 1/3] hwmon: (lm85) remove freq_map size hardcodes

On Wed, Jan 30, 2019 at 09:14:45AM -0700, Jeremy Gebben wrote:
>
> Allow support for chips that support more than 8 frequencies.
> The size still must be a power of two.
>
This is risky. Someone later may not know/remember and
expand the table without maintaining that restriction.
I would suggest to use map_size instead of map_mask.
While that adds some divide operations, none of those are
in a critical path, and the code would be much more robust.

> Signed-off-by: Jeremy Gebben <[email protected]>
> ---
> drivers/hwmon/lm85.c | 25 +++++++++++++++----------
> 1 file changed, 15 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
> index 0a325878e8f5..48f59667a88c 100644
> --- a/drivers/hwmon/lm85.c
> +++ b/drivers/hwmon/lm85.c
> @@ -198,13 +198,13 @@ static int RANGE_TO_REG(long range)
> #define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f]
>
> /* These are the PWM frequency encodings */
> -static const int lm85_freq_map[8] = { /* 1 Hz */
> +static const int lm85_freq_map[] = { /* 1 Hz */
> 10, 15, 23, 30, 38, 47, 61, 94
> };
> -static const int adm1027_freq_map[8] = { /* 1 Hz */
> +
> +static const int adm1027_freq_map[] = { /* 1 Hz */
> 11, 15, 22, 29, 35, 44, 59, 88
> };
> -#define FREQ_MAP_LEN 8
>
> static int FREQ_TO_REG(const int *map,
> unsigned int map_size, unsigned long freq)
> @@ -212,9 +212,9 @@ static int FREQ_TO_REG(const int *map,
> return find_closest(freq, map, map_size);
> }
>
> -static int FREQ_FROM_REG(const int *map, u8 reg)
> +static int FREQ_FROM_REG(const int *map, unsigned int map_size, u8 reg)
> {
> - return map[reg & 0x07];
> + return map[reg & (map_size - 1)];

Passing in map_size (calculated as map_mask + 1) just to subtract one
doesn't make sense.

> }
>
> /*
> @@ -296,6 +296,8 @@ struct lm85_data {
> struct i2c_client *client;
> const struct attribute_group *groups[6];
> const int *freq_map;
> + unsigned int freq_map_mask;
> +
> enum chips type;
>
> bool has_vid5; /* true if VID5 is configured for ADT7463 or ADT7468 */
> @@ -514,7 +516,7 @@ static struct lm85_data *lm85_update_device(struct device *dev)
> data->autofan[i].config =
> lm85_read_value(client, LM85_REG_AFAN_CONFIG(i));
> val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i));
> - data->pwm_freq[i] = val & 0x07;
> + data->pwm_freq[i] = val & data->freq_map_mask;
> data->zone[i].range = val >> 4;
> data->autofan[i].min_pwm =
> lm85_read_value(client, LM85_REG_AFAN_MINPWM(i));
> @@ -791,7 +793,8 @@ static ssize_t show_pwm_freq(struct device *dev,
> if (IS_ADT7468_HFPWM(data))
> freq = 22500;
> else
> - freq = FREQ_FROM_REG(data->freq_map, data->pwm_freq[nr]);
> + freq = FREQ_FROM_REG(data->freq_map, data->freq_map_mask + 1,
> + data->pwm_freq[nr]);
>
> return sprintf(buf, "%d\n", freq);
> }
> @@ -820,7 +823,7 @@ static ssize_t set_pwm_freq(struct device *dev,
> lm85_write_value(client, ADT7468_REG_CFG5, data->cfg5);
> } else { /* Low freq. mode */
> data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map,
> - FREQ_MAP_LEN, val);
> + data->freq_map_mask + 1, val);
> lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
> (data->zone[nr].range << 4)
> | data->pwm_freq[nr]);
> @@ -1196,7 +1199,7 @@ static ssize_t set_temp_auto_temp_min(struct device *dev,
> TEMP_FROM_REG(data->zone[nr].limit));
> lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
> ((data->zone[nr].range & 0x0f) << 4)
> - | (data->pwm_freq[nr] & 0x07));
> + | data->pwm_freq[nr]);
>
> mutex_unlock(&data->update_lock);
> return count;
> @@ -1232,7 +1235,7 @@ static ssize_t set_temp_auto_temp_max(struct device *dev,
> val - min);
> lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
> ((data->zone[nr].range & 0x0f) << 4)
> - | (data->pwm_freq[nr] & 0x07));
> + | data->pwm_freq[nr]);
> mutex_unlock(&data->update_lock);
> return count;
> }
> @@ -1569,9 +1572,11 @@ static int lm85_probe(struct i2c_client *client, const struct i2c_device_id *id)
> case emc6d103:
> case emc6d103s:
> data->freq_map = adm1027_freq_map;
> + data->freq_map_mask = ARRAY_SIZE(adm1027_freq_map) - 1;
> break;
> default:
> data->freq_map = lm85_freq_map;
> + data->freq_map_mask = ARRAY_SIZE(lm85_freq_map) - 1;
> }

You'd have to make sure that freq_map_mask is indeed a mask, ie that
ARRAY_SIZE() is a power of two. I would suggest to use freq_map_size
instead.

>
> /* Set the VRM version */
> --
> 2.17.1
>