[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20240416171720.2875916-2-naresh.solanki@9elements.com>
Date: Tue, 16 Apr 2024 22:47:15 +0530
From: Naresh Solanki <naresh.solanki@...ements.com>
To: Guenter Roeck <linux@...ck-us.net>,
krzysztof.kozlowski+dt@...aro.org,
u.kleine-koenig@...gutronix.de,
Jean Delvare <jdelvare@...e.com>
Cc: Naresh Solanki <naresh.solanki@...ements.com>,
linux-hwmon@...r.kernel.org,
linux-kernel@...r.kernel.org,
linux-pwm@...r.kernel.org
Subject: [PATCH 2/4] hwmon: (max6639) : Utilise pwm subsystem
Utilise pwm subsystem for fan pwm handling
Signed-off-by: Naresh Solanki <naresh.solanki@...ements.com>
---
drivers/hwmon/Kconfig | 1 +
drivers/hwmon/max6639.c | 200 ++++++++++++++++++++++++++++++++++++++--
2 files changed, 191 insertions(+), 10 deletions(-)
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 257ec5360e35..c9cc74f8c807 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1224,6 +1224,7 @@ config SENSORS_MAX6639
tristate "Maxim MAX6639 sensor chip"
depends on I2C
select REGMAP_I2C
+ depends on PWM
help
If you say yes here you get support for the MAX6639
sensor chips.
diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c
index 1af93fc53cb5..f37fdd161154 100644
--- a/drivers/hwmon/max6639.c
+++ b/drivers/hwmon/max6639.c
@@ -20,6 +20,7 @@
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/platform_data/max6639.h>
+#include <linux/pwm.h>
#include <linux/regmap.h>
/* Addresses to scan */
@@ -55,6 +56,9 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END };
#define MAX6639_GCONFIG_PWM_FREQ_HI 0x08
#define MAX6639_FAN_CONFIG1_PWM 0x80
+#define MAX6639_REG_FAN_CONFIG2a_PWM_POL 0x02
+#define MAX6639_FAN_CONFIG3_FREQ_MASK 0x03
+#define MAX6639_REG_TARGTDUTY_SLOT 120
#define MAX6639_FAN_CONFIG3_THERM_FULL_SPEED 0x40
@@ -62,6 +66,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END };
static const int rpm_ranges[] = { 2000, 4000, 8000, 16000 };
+/* Supported PWM frequency */
+static const unsigned int freq_table[] = { 20, 33, 50, 100, 5000, 8333, 12500,
+ 25000 };
+
#define FAN_FROM_REG(val, rpm_range) ((val) == 0 || (val) == 255 ? \
0 : (rpm_ranges[rpm_range] * 30) / (val))
#define TEMP_LIMIT_TO_REG(val) clamp_val((val) / 1000, 0, 255)
@@ -93,6 +101,9 @@ struct max6639_data {
/* Optional regulator for FAN supply */
struct regulator *reg;
+ /* max6639 pwm chip */
+ struct pwm_chip chip;
+ struct pwm_device *pwmd[MAX6639_NDEV]; /* max6639 has two pwm device */
};
static struct max6639_data *max6639_update_device(struct device *dev)
@@ -271,8 +282,11 @@ static ssize_t pwm_show(struct device *dev, struct device_attribute *dev_attr,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
struct max6639_data *data = dev_get_drvdata(dev);
+ struct pwm_state state;
+
+ pwm_get_state(data->pwmd[attr->index], &state);
- return sprintf(buf, "%d\n", data->pwm[attr->index] * 255 / 120);
+ return sprintf(buf, "%d\n", pwm_get_relative_duty_cycle(&state, 255));
}
static ssize_t pwm_store(struct device *dev,
@@ -281,6 +295,7 @@ static ssize_t pwm_store(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
struct max6639_data *data = dev_get_drvdata(dev);
+ struct pwm_state state;
unsigned long val;
int res;
@@ -290,10 +305,10 @@ static ssize_t pwm_store(struct device *dev,
val = clamp_val(val, 0, 255);
- mutex_lock(&data->update_lock);
- data->pwm[attr->index] = (u8)(val * 120 / 255);
- regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(attr->index), data->pwm[attr->index]);
- mutex_unlock(&data->update_lock);
+ pwm_get_state(data->pwmd[attr->index], &state);
+ pwm_set_relative_duty_cycle(&state, val, 255);
+ pwm_apply_state(data->pwmd[attr->index], &state);
+
return count;
}
@@ -373,6 +388,158 @@ static struct attribute *max6639_attrs[] = {
};
ATTRIBUTE_GROUPS(max6639);
+static struct max6639_data *to_max6639_pwm(struct pwm_chip *chip)
+{
+ return container_of(chip, struct max6639_data, chip);
+}
+
+static int max6639_pwm_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct max6639_data *data = to_max6639_pwm(chip);
+ int value, i = pwm->hwpwm, x, err;
+ unsigned int freq;
+
+ mutex_lock(&data->update_lock);
+
+ err = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG1(i), &value);
+ if (err < 0)
+ goto abort;
+
+ if (value & MAX6639_FAN_CONFIG1_PWM) {
+ state->enabled = true;
+
+ /* Determine frequency from respective registers */
+ err = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG3(i), &value);
+ if (err < 0)
+ goto abort;
+ x = value & MAX6639_FAN_CONFIG3_FREQ_MASK;
+
+ err = regmap_read(data->regmap, MAX6639_REG_GCONFIG, &value);
+ if (err < 0)
+ goto abort;
+ if (value & MAX6639_GCONFIG_PWM_FREQ_HI)
+ x |= 0x4;
+ x &= 0x7;
+ freq = freq_table[x];
+
+ state->period = DIV_ROUND_UP(NSEC_PER_SEC, freq);
+
+ err = regmap_read(data->regmap, MAX6639_REG_TARGTDUTY(i), &value);
+ if (err < 0)
+ goto abort;
+ /* max6639 supports 120 slots only */
+ state->duty_cycle = mul_u64_u32_div(state->period, value, 120);
+
+ err = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), &value);
+ if (err < 0)
+ goto abort;
+ value &= MAX6639_REG_FAN_CONFIG2a_PWM_POL;
+ state->polarity = (value != 0);
+ } else {
+ state->enabled = false;
+ }
+
+abort:
+ mutex_unlock(&data->update_lock);
+ return value;
+}
+
+static int max6639_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct max6639_data *data = to_max6639_pwm(chip);
+ int value, i = pwm->hwpwm, x, err;
+ unsigned int freq;
+ struct pwm_state cstate;
+
+ cstate = pwm->state;
+
+ mutex_lock(&data->update_lock);
+
+ if (state->period != cstate.period) {
+ /* Configure frequency */
+ freq = DIV_ROUND_UP_ULL(NSEC_PER_SEC, state->period);
+
+ /* Chip supports limited number of frequency */
+ for (x = 0; x < sizeof(freq_table); x++)
+ if (freq <= freq_table[x])
+ break;
+
+ err = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG3(i), &value);
+ if (err < 0)
+ goto abort;
+
+ value &= ~MAX6639_FAN_CONFIG3_FREQ_MASK;
+ value |= (x & MAX6639_FAN_CONFIG3_FREQ_MASK);
+ err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG3(i), value);
+ if (err < 0)
+ goto abort;
+
+ err = regmap_read(data->regmap, MAX6639_REG_GCONFIG, &value);
+ if (err < 0)
+ goto abort;
+
+ if (x >> 2)
+ value &= ~MAX6639_GCONFIG_PWM_FREQ_HI;
+ else
+ value |= MAX6639_GCONFIG_PWM_FREQ_HI;
+ err = regmap_write(data->regmap, MAX6639_REG_GCONFIG, value);
+ if (err < 0)
+ goto abort;
+ }
+
+ /* Configure dutycycle */
+ if (state->duty_cycle != cstate.duty_cycle ||
+ state->period != cstate.period) {
+ value = DIV_ROUND_DOWN_ULL(state->duty_cycle * MAX6639_REG_TARGTDUTY_SLOT,
+ state->period);
+ err = regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(i), value);
+ if (err < 0)
+ goto abort;
+ }
+
+ /* Configure polarity */
+ if (state->polarity != cstate.polarity) {
+ err = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), &value);
+ if (err < 0)
+ goto abort;
+ if (state->polarity == PWM_POLARITY_NORMAL)
+ value |= MAX6639_REG_FAN_CONFIG2a_PWM_POL;
+ else
+ value &= ~MAX6639_REG_FAN_CONFIG2a_PWM_POL;
+ err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), value);
+ if (err < 0)
+ goto abort;
+ }
+
+ if (state->enabled != cstate.enabled) {
+ err = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG1(i), &value);
+ if (err < 0)
+ goto abort;
+ if (state->enabled)
+ value |= MAX6639_FAN_CONFIG1_PWM;
+ else
+ value &= ~MAX6639_FAN_CONFIG1_PWM;
+
+ err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG1(i), value);
+ if (err < 0)
+ goto abort;
+ }
+ value = 0;
+
+abort:
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static const struct pwm_ops max6639_pwm_ops = {
+ .apply = max6639_pwm_apply,
+ .get_state = max6639_pwm_get_state,
+};
+
/*
* returns respective index in rpm_ranges table
* 1 by default on invalid range
@@ -396,6 +563,7 @@ static int max6639_init_client(struct i2c_client *client,
dev_get_platdata(&client->dev);
int i;
int rpm_range = 1; /* default: 4000 RPM */
+ struct pwm_state state;
int err;
/* Reset chip to default values, see below for GCONFIG setup */
@@ -459,11 +627,15 @@ static int max6639_init_client(struct i2c_client *client,
if (err)
goto exit;
- /* PWM 120/120 (i.e. 100%) */
- data->pwm[i] = 120;
- err = regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(i), data->pwm[i]);
- if (err)
- goto exit;
+ dev_dbg(&client->dev, "Using chip default PWM");
+ data->pwmd[i] = pwm_request_from_chip(&data->chip, i, NULL);
+ if (IS_ERR(data->pwmd[i]))
+ return PTR_ERR(data->pwmd[i]);
+ pwm_get_state(data->pwmd[i], &state);
+ state.period = DIV_ROUND_UP(NSEC_PER_SEC, 25000);
+ state.polarity = PWM_POLARITY_NORMAL;
+ pwm_set_relative_duty_cycle(&state, 0, 255);
+ pwm_apply_state(data->pwmd[i], &state);
}
/* Start monitoring */
err = regmap_write(data->regmap, MAX6639_REG_GCONFIG,
@@ -540,6 +712,14 @@ static int max6639_probe(struct i2c_client *client)
PTR_ERR(data->regmap),
"regmap initialization failed\n");
+ /* Add PWM controller of max6639 */
+ data->chip.dev = dev;
+ data->chip.ops = &max6639_pwm_ops;
+ data->chip.npwm = MAX6639_NDEV;
+ err = devm_pwmchip_add(dev, &data->chip);
+ if (err < 0)
+ return dev_err_probe(dev, err, "failed to add PWM chip\n");
+
data->reg = devm_regulator_get_optional(dev, "fan");
if (IS_ERR(data->reg)) {
if (PTR_ERR(data->reg) != -ENODEV)
--
2.42.0
Powered by blists - more mailing lists