struct ltc4282_cache {
u32 in_max_raw;
u32 in_min_raw; long in_highest; long in_lowest; bool en;
};
struct ltc4282_state { struct regmap *map; /* Protect against multiple accesses to the device registers */ struct mutex lock; struct clk_hw clk_hw; /* * Used to cache values for VDD/VSOURCE depending which will be used * when hwmon is not enabled for that channel. Needed because they share * the same registers.
*/ struct ltc4282_cache in0_1_cache[LTC4282_CHAN_VGPIO];
u32 vsense_max; long power_max;
u32 rsense;
u16 vdd;
u16 vfs_out; bool energy_en;
};
/* * Note the 15HZ conversion rate assumes 12bit ADC which is what we are * supporting for now.
*/ staticconstunsignedint ltc4282_out_rates[] = {
LTC4282_CLKOUT_CNV, LTC4282_CLKOUT_SYSTEM
};
staticint ltc4282_read_voltage_word(conststruct ltc4282_state *st, u32 reg,
u32 fs, long *val)
{
__be16 in; int ret;
ret = regmap_bulk_read(st->map, reg, &in, sizeof(in)); if (ret) return ret;
/* * This is also used to calculate current in which case fs comes in * 10 * uV. Hence the ULL usage.
*/
*val = DIV_ROUND_CLOSEST_ULL(be16_to_cpu(in) * (u64)fs, U16_MAX); return 0;
}
staticint ltc4282_read_voltage_byte_cached(conststruct ltc4282_state *st,
u32 reg, u32 fs, long *val,
u32 *cached_raw)
{ int ret;
u32 in;
if (cached_raw) {
in = *cached_raw;
} else {
ret = regmap_read(st->map, reg, &in); if (ret) return ret;
}
staticint __ltc4282_read_alarm(struct ltc4282_state *st, u32 reg, u32 mask, long *val)
{
u32 alarm; int ret;
ret = regmap_read(st->map, reg, &alarm); if (ret) return ret;
*val = !!(alarm & mask);
/* if not status/fault logs, clear the alarm after reading it */ if (reg != LTC4282_STATUS_LSB && reg != LTC4282_FAULT_LOG) return regmap_clear_bits(st->map, reg, mask);
staticint ltc4282_vdd_source_read_alm(struct ltc4282_state *st, u32 mask,
u32 channel, long *val)
{
guard(mutex)(&st->lock); if (!st->in0_1_cache[channel].en) { /* * Do this otherwise alarms can get confused because we clear * them after reading them. So, if someone mistakenly reads * VSOURCE right before VDD (or the other way around), we might * get no alarm just because it was cleared when reading VSOURCE * and had no time for a new conversion and thus having the * alarm again.
*/
*val = 0; return 0;
}
staticint ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val,
u32 channel)
{ switch (attr) { case hwmon_in_input: if (channel == LTC4282_CHAN_VGPIO) return ltc4282_read_voltage_word(st, LTC4282_VGPIO,
1280, val);
return ltc4282_vdd_source_read_in(st, channel, val); case hwmon_in_highest: if (channel == LTC4282_CHAN_VGPIO) return ltc4282_read_voltage_word(st,
LTC4282_VGPIO_HIGHEST,
1280, val);
return ltc4282_vdd_source_read_hist(st, LTC4282_VSOURCE_HIGHEST,
channel,
&st->in0_1_cache[channel].in_highest, val); case hwmon_in_lowest: if (channel == LTC4282_CHAN_VGPIO) return ltc4282_read_voltage_word(st, LTC4282_VGPIO_LOWEST,
1280, val);
return ltc4282_vdd_source_read_hist(st, LTC4282_VSOURCE_LOWEST,
channel,
&st->in0_1_cache[channel].in_lowest, val); case hwmon_in_max_alarm: if (channel == LTC4282_CHAN_VGPIO) return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
LTC4282_GPIO_ALARM_H_MASK,
val);
return ltc4282_vdd_source_read_alm(st,
LTC4282_VSOURCE_ALARM_H_MASK,
channel, val); case hwmon_in_min_alarm: if (channel == LTC4282_CHAN_VGPIO)
ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
LTC4282_GPIO_ALARM_L_MASK, val);
return ltc4282_vdd_source_read_alm(st,
LTC4282_VSOURCE_ALARM_L_MASK,
channel, val); case hwmon_in_crit_alarm: return ltc4282_read_alarm(st, LTC4282_STATUS_LSB,
LTC4282_OV_STATUS_MASK, val); case hwmon_in_lcrit_alarm: return ltc4282_read_alarm(st, LTC4282_STATUS_LSB,
LTC4282_UV_STATUS_MASK, val); case hwmon_in_max: if (channel == LTC4282_CHAN_VGPIO) return ltc4282_read_voltage_byte(st, LTC4282_VGPIO_MAX,
1280, val);
return ltc4282_vdd_source_read_lim(st, LTC4282_VSOURCE_MAX,
channel,
&st->in0_1_cache[channel].in_max_raw, val); case hwmon_in_min: if (channel == LTC4282_CHAN_VGPIO) return ltc4282_read_voltage_byte(st, LTC4282_VGPIO_MIN,
1280, val);
return ltc4282_vdd_source_read_lim(st, LTC4282_VSOURCE_MIN,
channel,
&st->in0_1_cache[channel].in_min_raw, val); case hwmon_in_enable:
scoped_guard(mutex, &st->lock) {
*val = st->in0_1_cache[channel].en;
} return 0; case hwmon_in_fault: /* * We report failure if we detect either a fer_bad or a * fet_short in the status register.
*/ return ltc4282_read_alarm(st, LTC4282_STATUS_LSB,
LTC4282_FET_FAILURE_MASK, val); default: return -EOPNOTSUPP;
}
}
staticint ltc4282_read_current_word(conststruct ltc4282_state *st, u32 reg, long *val)
{ long in; int ret;
/* * We pass in full scale in 10 * micro (note that 40 is already * millivolt) so we have better approximations to calculate current.
*/
ret = ltc4282_read_voltage_word(st, reg, DECA * 40 * MILLI, &in); if (ret) return ret;
*val = DIV_ROUND_CLOSEST(in * MILLI, st->rsense);
return 0;
}
staticint ltc4282_read_current_byte(conststruct ltc4282_state *st, u32 reg, long *val)
{ long in; int ret;
ret = ltc4282_read_voltage_byte(st, reg, DECA * 40 * MILLI, &in); if (ret) return ret;
*val = DIV_ROUND_CLOSEST(in * MILLI, st->rsense);
return 0;
}
staticint ltc4282_read_curr(struct ltc4282_state *st, const u32 attr, long *val)
{ switch (attr) { case hwmon_curr_input: return ltc4282_read_current_word(st, LTC4282_VSENSE, val); case hwmon_curr_highest: return ltc4282_read_current_word(st, LTC4282_VSENSE_HIGHEST,
val); case hwmon_curr_lowest: return ltc4282_read_current_word(st, LTC4282_VSENSE_LOWEST,
val); case hwmon_curr_max: return ltc4282_read_current_byte(st, LTC4282_VSENSE_MAX, val); case hwmon_curr_min: return ltc4282_read_current_byte(st, LTC4282_VSENSE_MIN, val); case hwmon_curr_max_alarm: return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
LTC4282_VSENSE_ALARM_H_MASK, val); case hwmon_curr_min_alarm: return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
LTC4282_VSENSE_ALARM_L_MASK, val); case hwmon_curr_crit_alarm: return ltc4282_read_alarm(st, LTC4282_STATUS_LSB,
LTC4282_OC_STATUS_MASK, val); default: return -EOPNOTSUPP;
}
}
ret = regmap_bulk_read(st->map, LTC4282_ENERGY, &raw, 6); if (ret) return ret;
energy = be64_to_cpu(raw) >> 16; /* * The formula for energy is given by: * E = CODE(48b) * 0.040 * Vfs(out) * Tconv * 256 / * ((2^16 - 1)^2 * Rsense) * * Since we only support 12bit ADC, Tconv = 0.065535s. Passing Vfs(out) * and 0.040 to mV and Tconv to us, we can simplify the formula to: * E = CODE(48b) * 40 * Vfs(out) * 256 / (U16_MAX * Rsense) * * As Rsense can have tenths of micro-ohm resolution, we need to * multiply by DECA to get microujoule.
*/ if (check_mul_overflow(DECA * st->vfs_out * 40 * BIT(8), energy, &temp)) {
temp = DIV_ROUND_CLOSEST(DECA * st->vfs_out * 40 * BIT(8), U16_MAX);
*val = DIV_ROUND_CLOSEST_ULL(temp * energy, st->rsense); return 0;
}
/* * We are also clearing possible fault logs in reset_history. Clearing * the logs might be important when the auto retry bits are not enabled * as the chip only enables the output again after having these logs * cleared. As some of these logs are related to limits, it makes sense * to clear them in here. For VDD, we need to clear under/over voltage * events. For VSOURCE, fet_short and fet_bad...
*/ if (channel == LTC4282_CHAN_VSOURCE) return regmap_clear_bits(st->map, LTC4282_FAULT_LOG,
LTC4282_FET_FAILURE_FAULT_MASK);
/* * We need to mux between VSOURCE and VDD which means they are mutually * exclusive. Moreover, we can't really disable both VDD and VSOURCE as the ADC * is continuously running (we cannot independently halt it without also * stopping VGPIO). Hence, the logic is that disabling or enabling VDD will * automatically have the reverse effect on VSOURCE and vice-versa.
*/ staticint ltc4282_vdd_source_enable(struct ltc4282_state *st, int channel, long val)
{ int ret, other_chan = ~channel & 0x1;
u8 __val = val;
guard(mutex)(&st->lock); if (st->in0_1_cache[channel].en == !!val) return 0;
/* clearing the bit makes the ADC to monitor VDD */ if (channel == LTC4282_CHAN_VDD)
__val = !__val;
ret = regmap_update_bits(st->map, LTC4282_ILIM_ADJUST,
LTC4282_VDD_MONITOR_MASK,
FIELD_PREP(LTC4282_VDD_MONITOR_MASK, !!__val)); if (ret) return ret;
if (st->in0_1_cache[channel].en) { /* * Then, we are disabling @other_chan. Let's save it's current * history.
*/
ret = ltc4282_cache_history(st, other_chan); if (ret) return ret;
return ltc4282_cache_sync(st, channel);
} /* * Then, we are enabling @other_chan. We need to do the opposite from * above.
*/
ret = ltc4282_cache_history(st, channel); if (ret) return ret;
return ltc4282_cache_sync(st, other_chan);
}
staticint ltc4282_write_in(struct ltc4282_state *st, u32 attr, long val, int channel)
{ switch (attr) { case hwmon_in_max: if (channel == LTC4282_CHAN_VGPIO) return ltc4282_write_voltage_byte(st, LTC4282_VGPIO_MAX,
1280, val);
return ltc4282_vdd_source_write_lim(st, LTC4282_VSOURCE_MAX,
channel,
&st->in0_1_cache[channel].in_max_raw, val); case hwmon_in_min: if (channel == LTC4282_CHAN_VGPIO) return ltc4282_write_voltage_byte(st, LTC4282_VGPIO_MIN,
1280, val);
return ltc4282_vdd_source_write_lim(st, LTC4282_VSOURCE_MIN,
channel,
&st->in0_1_cache[channel].in_min_raw, val); case hwmon_in_reset_history: if (channel == LTC4282_CHAN_VGPIO) return ltc4282_in_write_history(st,
LTC4282_VGPIO_LOWEST,
1280, 0, 1280);
staticint ltc4282_curr_reset_hist(struct ltc4282_state *st)
{ int ret;
guard(mutex)(&st->lock);
ret = __ltc4282_in_write_history(st, LTC4282_VSENSE_LOWEST,
st->vsense_max, 0, 40 * MILLI); if (ret) return ret;
/* now, let's also clear possible overcurrent fault logs */ return regmap_clear_bits(st->map, LTC4282_FAULT_LOG,
LTC4282_OC_FAULT_MASK);
}
staticint ltc4282_write_curr(struct ltc4282_state *st, u32 attr, long val)
{ /* need to pass it in millivolt */
u32 in = DIV_ROUND_CLOSEST_ULL((u64)val * st->rsense, DECA * MICRO);
switch (attr) { case hwmon_curr_max: return ltc4282_write_voltage_byte(st, LTC4282_VSENSE_MAX, 40,
in); case hwmon_curr_min: return ltc4282_write_voltage_byte(st, LTC4282_VSENSE_MIN, 40,
in); case hwmon_curr_reset_history: return ltc4282_curr_reset_hist(st); default: return -EOPNOTSUPP;
}
}
staticint ltc4282_energy_enable_set(struct ltc4282_state *st, long val)
{ int ret;
guard(mutex)(&st->lock); /* setting the bit halts the meter */
ret = regmap_update_bits(st->map, LTC4282_ADC_CTRL,
LTC4282_METER_HALT_MASK,
FIELD_PREP(LTC4282_METER_HALT_MASK, !val)); if (ret) return ret;
st->energy_en = !!val;
return 0;
}
staticint ltc4282_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{ struct ltc4282_state *st = dev_get_drvdata(dev);
switch (type) { case hwmon_power: return ltc4282_write_power(st, attr, val); case hwmon_in: return ltc4282_write_in(st, attr, val, channel); case hwmon_curr: return ltc4282_write_curr(st, attr, val); case hwmon_energy: return ltc4282_energy_enable_set(st, val); default: return -EOPNOTSUPP;
}
}
static umode_t ltc4282_in_is_visible(conststruct ltc4282_state *st, u32 attr)
{ switch (attr) { case hwmon_in_input: case hwmon_in_highest: case hwmon_in_lowest: case hwmon_in_max_alarm: case hwmon_in_min_alarm: case hwmon_in_label: case hwmon_in_lcrit_alarm: case hwmon_in_crit_alarm: case hwmon_in_fault: return 0444; case hwmon_in_max: case hwmon_in_min: case hwmon_in_enable: case hwmon_in_reset_history: return 0644; default: return 0;
}
}
static umode_t ltc4282_curr_is_visible(u32 attr)
{ switch (attr) { case hwmon_curr_input: case hwmon_curr_highest: case hwmon_curr_lowest: case hwmon_curr_max_alarm: case hwmon_curr_min_alarm: case hwmon_curr_crit_alarm: case hwmon_curr_label: return 0444; case hwmon_curr_max: case hwmon_curr_min: case hwmon_curr_reset_history: return 0644; default: return 0;
}
}
static umode_t ltc4282_power_is_visible(u32 attr)
{ switch (attr) { case hwmon_power_input: case hwmon_power_input_highest: case hwmon_power_input_lowest: case hwmon_power_label: case hwmon_power_max_alarm: case hwmon_power_min_alarm: return 0444; case hwmon_power_max: case hwmon_power_min: case hwmon_power_reset_history: return 0644; default: return 0;
}
}
static umode_t ltc4282_is_visible(constvoid *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{ switch (type) { case hwmon_in: return ltc4282_in_is_visible(data, attr); case hwmon_curr: return ltc4282_curr_is_visible(attr); case hwmon_power: return ltc4282_power_is_visible(attr); case hwmon_energy: /* hwmon_energy_enable */ return 0644; default: return 0;
}
}
ret = ltc428_clk_provider_setup(st, dev); if (ret) return ret;
clkin = devm_clk_get_optional_enabled(dev, NULL); if (IS_ERR(clkin)) return dev_err_probe(dev, PTR_ERR(clkin), "Failed to get clkin"); if (!clkin) return 0;
/* * Clocks faster than 250KHZ should be reduced to 250KHZ. The clock * frequency is divided by twice the value in the register.
*/
val = rate / (2 * LTC4282_CLKIN_MIN);
/* * Set max limits for ISENSE and Power as that depends on the max voltage on * rsense that is defined in ILIM_ADJUST. This is specially important for power * because for some rsense and vfsout values, if we allow the default raw 255 * value, that would overflow long in 32bit archs when reading back the max * power limit. * * Also set meaningful historic values for VDD and VSOURCE * (0 would not mean much).
*/ staticint ltc4282_set_max_limits(struct ltc4282_state *st)
{ int ret;
ret = ltc4282_write_voltage_byte(st, LTC4282_VSENSE_MAX, 40 * MILLI,
st->vsense_max); if (ret) return ret;
/* Power is given by ISENSE * Vout. */
st->power_max = DIV_ROUND_CLOSEST(st->vsense_max * DECA * MILLI, st->rsense) * st->vfs_out;
ret = ltc4282_write_power_byte(st, LTC4282_POWER_MAX, st->power_max); if (ret) return ret;
ret = device_property_read_string(dev, "adi,gpio1-mode", &func); if (!ret) {
ret = match_string(ltc4282_gpio1_modes,
ARRAY_SIZE(ltc4282_gpio1_modes), func); if (ret < 0) return dev_err_probe(dev, ret, "Invalid func(%s) for gpio1\n",
func);
ret = regmap_update_bits(st->map, LTC4282_GPIO_CONFIG,
LTC4282_GPIO_1_CONFIG_MASK,
FIELD_PREP(LTC4282_GPIO_1_CONFIG_MASK, ret)); if (ret) return ret;
}
ret = device_property_read_string(dev, "adi,gpio2-mode", &func); if (!ret) {
ret = match_string(ltc4282_gpio2_modes,
ARRAY_SIZE(ltc4282_gpio2_modes), func); if (ret < 0) return dev_err_probe(dev, ret, "Invalid func(%s) for gpio2\n",
func); if (!ret) { /* setting the bit to 1 so the ADC to monitors GPIO2 */
ret = regmap_set_bits(st->map, LTC4282_ILIM_ADJUST,
LTC4282_GPIO_MODE_MASK);
} else {
ret = regmap_update_bits(st->map, LTC4282_GPIO_CONFIG,
LTC4282_GPIO_2_FET_STRESS_MASK,
FIELD_PREP(LTC4282_GPIO_2_FET_STRESS_MASK, 1));
}
if (ret) return ret;
}
if (!device_property_read_bool(dev, "adi,gpio3-monitor-enable")) return 0;
if (func && !strcmp(func, "adc_input")) return dev_err_probe(dev, -EINVAL, "Cannot have both gpio2 and gpio3 muxed into the ADC");
/* The part has an eeprom so let's get the needed defaults from it */
ret = ltc4282_get_defaults(st, &vin_mode); if (ret) return ret;
ret = device_property_read_u32(dev, "adi,rsense-nano-ohms",
&st->rsense); if (ret) return dev_err_probe(dev, ret, "Failed to read adi,rsense-nano-ohms\n"); if (st->rsense < CENTI) return dev_err_probe(dev, -EINVAL, "adi,rsense-nano-ohms too small (< %lu)\n",
CENTI);
/* * The resolution for rsense is tenths of micro (eg: 62.5 uOhm) which * means we need nano in the bindings. However, to make things easier to * handle (with respect to overflows) we divide it by 100 as we don't * really need the last two digits.
*/
st->rsense /= CENTI;
val = vin_mode;
ret = device_property_read_u32(dev, "adi,vin-mode-microvolt", &val); if (!ret) { switch (val) { case 3300000:
val = LTC4282_VIN_3_3V; break; case 5000000:
val = LTC4282_VIN_5V; break; case 12000000:
val = LTC4282_VIN_12V; break; case 24000000:
val = LTC4282_VIN_24V; break; default: return dev_err_probe(dev, -EINVAL, "Invalid val(%u) for vin-mode-microvolt\n",
val);
}
ret = regmap_update_bits(st->map, LTC4282_CTRL_MSB,
LTC4282_CTRL_VIN_MODE_MASK,
FIELD_PREP(LTC4282_CTRL_VIN_MODE_MASK, val)); if (ret) return ret;
/* Foldback mode should also be set to the input voltage */
ret = regmap_update_bits(st->map, LTC4282_ILIM_ADJUST,
LTC4282_FOLDBACK_MODE_MASK,
FIELD_PREP(LTC4282_FOLDBACK_MODE_MASK, val)); if (ret) return ret;
}
ret = device_property_read_u32(dev, "adi,current-limit-sense-microvolt",
&st->vsense_max); if (!ret) { int reg_val;
switch (val) { case 12500:
reg_val = 0; break; case 15625:
reg_val = 1; break; case 18750:
reg_val = 2; break; case 21875:
reg_val = 3; break; case 25000:
reg_val = 4; break; case 28125:
reg_val = 5; break; case 31250:
reg_val = 6; break; case 34375:
reg_val = 7; break; default: return dev_err_probe(dev, -EINVAL, "Invalid val(%u) for adi,current-limit-microvolt\n",
st->vsense_max);
}
ret = regmap_update_bits(st->map, LTC4282_ILIM_ADJUST,
LTC4282_ILIM_ADJUST_MASK,
FIELD_PREP(LTC4282_ILIM_ADJUST_MASK, reg_val)); if (ret) return ret;
}
ret = ltc4282_set_max_limits(st); if (ret) return ret;
ret = device_property_read_string(dev, "adi,overvoltage-dividers",
÷r); if (!ret) { int div = match_string(ltc4282_dividers,
ARRAY_SIZE(ltc4282_dividers), divider); if (div < 0) return dev_err_probe(dev, -EINVAL, "Invalid val(%s) for adi,overvoltage-divider\n",
divider);
ret = regmap_update_bits(st->map, LTC4282_CTRL_MSB,
LTC4282_CTRL_OV_MODE_MASK,
FIELD_PREP(LTC4282_CTRL_OV_MODE_MASK, div));
}
ret = device_property_read_string(dev, "adi,undervoltage-dividers",
÷r); if (!ret) { int div = match_string(ltc4282_dividers,
ARRAY_SIZE(ltc4282_dividers), divider); if (div < 0) return dev_err_probe(dev, -EINVAL, "Invalid val(%s) for adi,undervoltage-divider\n",
divider);
ret = regmap_update_bits(st->map, LTC4282_CTRL_MSB,
LTC4282_CTRL_UV_MODE_MASK,
FIELD_PREP(LTC4282_CTRL_UV_MODE_MASK, div));
}
if (device_property_read_bool(dev, "adi,overcurrent-retry")) {
ret = regmap_set_bits(st->map, LTC4282_CTRL_LSB,
LTC4282_CTRL_OC_RETRY_MASK); if (ret) return ret;
}
if (device_property_read_bool(dev, "adi,overvoltage-retry-disable")) {
ret = regmap_clear_bits(st->map, LTC4282_CTRL_LSB,
LTC4282_CTRL_OV_RETRY_MASK); if (ret) return ret;
}
if (device_property_read_bool(dev, "adi,undervoltage-retry-disable")) {
ret = regmap_clear_bits(st->map, LTC4282_CTRL_LSB,
LTC4282_CTRL_UV_RETRY_MASK); if (ret) return ret;
}
if (device_property_read_bool(dev, "adi,fault-log-enable")) {
ret = regmap_set_bits(st->map, LTC4282_ADC_CTRL, LTC4282_FAULT_LOG_EN_MASK); if (ret) return ret;
}
ret = device_property_read_u32(dev, "adi,fet-bad-timeout-ms", &val); if (!ret) { if (val > LTC4282_FET_BAD_MAX_TIMEOUT) return dev_err_probe(dev, -EINVAL, "Invalid value(%u) for adi,fet-bad-timeout-ms",
val);
ret = regmap_write(st->map, LTC4282_FET_BAD_FAULT_TIMEOUT, val); if (ret) return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.