struct ltc2947_data { struct regmap *map; struct device *dev; /* * The mutex is needed because the device has 2 memory pages. When * reading/writing the correct page needs to be set so that, the * complete sequence select_page->read/write needs to be protected.
*/ struct mutex lock;
u32 lsb_energy; bool gpio_out;
};
switch (size) { case 2:
ret = __ltc2947_val_read16(st, reg, &__val); break; case 3:
ret = __ltc2947_val_read24(st, reg, &__val); break; case 6:
ret = __ltc2947_val_read64(st, reg, &__val); break; default:
ret = -EINVAL; break;
}
mutex_lock(&st->lock); /* set device on correct page */
ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page); if (ret) {
mutex_unlock(&st->lock); return ret;
}
switch (size) { case 2:
ret = __ltc2947_val_write16(st, reg, val); break; case 6:
ret = __ltc2947_val_write64(st, reg, val); break; default:
ret = -EINVAL; break;
}
mutex_unlock(&st->lock);
return ret;
}
staticint ltc2947_reset_history(struct ltc2947_data *st, const u8 reg_h, const u8 reg_l)
{ int ret; /* * let's reset the tracking register's. Tracking register's have all * 2 bytes size
*/
ret = ltc2947_val_write(st, reg_h, LTC2947_PAGE0, 2, 0x8000U); if (ret) return ret;
staticint ltc2947_alarm_read(struct ltc2947_data *st, const u8 reg, const u32 mask, long *val)
{
u8 offset = reg - LTC2947_REG_STATUS; /* +1 to include status reg */ char alarms[LTC2947_ALERTS_SIZE + 1]; int ret = 0;
memset(alarms, 0, sizeof(alarms));
mutex_lock(&st->lock);
ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, LTC2947_PAGE0); if (ret) goto unlock;
dev_dbg(st->dev, "Read alarm, reg:%02X, mask:%02X\n", reg, mask); /* * As stated in the datasheet, when Threshold and Overflow registers * are used, the status and all alert registers must be read in one * multi-byte transaction.
*/
ret = regmap_bulk_read(st->map, LTC2947_REG_STATUS, alarms, sizeof(alarms)); if (ret) goto unlock;
/* get the alarm */
*val = !!(alarms[offset] & mask);
unlock:
mutex_unlock(&st->lock); return ret;
}
staticint ltc2947_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{ switch (type) { case hwmon_in: return ltc2947_write_in(dev, attr, val, channel); case hwmon_curr: return ltc2947_write_curr(dev, attr, val); case hwmon_power: return ltc2947_write_power(dev, attr, val); case hwmon_temp: return ltc2947_write_temp(dev, attr, val, channel); default: return -ENOTSUPP;
}
}
staticint ltc2947_read_labels(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, constchar **str)
{ switch (type) { case hwmon_in: if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
*str = "DVCC"; else
*str = "VP-VM"; return 0; case hwmon_curr:
*str = "IP-IM"; return 0; case hwmon_temp: if (channel == LTC2947_TEMP_FAN_CHAN)
*str = "TEMPFAN"; else
*str = "Ambient"; return 0; case hwmon_power:
*str = "Power"; return 0; default: return -ENOTSUPP;
}
}
staticint ltc2947_in_is_visible(const 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: return 0444; case hwmon_in_reset_history: return 0200; case hwmon_in_max: case hwmon_in_min: return 0644; default: return 0;
}
}
staticint ltc2947_curr_is_visible(const 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_label: return 0444; case hwmon_curr_reset_history: return 0200; case hwmon_curr_max: case hwmon_curr_min: return 0644; default: return 0;
}
}
staticint ltc2947_power_is_visible(const 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_reset_history: return 0200; case hwmon_power_max: case hwmon_power_min: return 0644; default: return 0;
}
}
staticint ltc2947_temp_is_visible(const u32 attr)
{ switch (attr) { case hwmon_temp_input: case hwmon_temp_highest: case hwmon_temp_lowest: case hwmon_temp_max_alarm: case hwmon_temp_min_alarm: case hwmon_temp_label: return 0444; case hwmon_temp_reset_history: return 0200; case hwmon_temp_max: case hwmon_temp_min: return 0644; default: return 0;
}
}
static umode_t ltc2947_is_visible(constvoid *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{ switch (type) { case hwmon_in: return ltc2947_in_is_visible(attr); case hwmon_curr: return ltc2947_curr_is_visible(attr); case hwmon_power: return ltc2947_power_is_visible(attr); case hwmon_temp: return ltc2947_temp_is_visible(attr); default: return 0;
}
}
/* energy attributes are 6bytes wide so we need u64 */ static SENSOR_DEVICE_ATTR(energy1_input, 0444, ltc2947_show_value, NULL,
LTC2947_REG_ENERGY1); static SENSOR_DEVICE_ATTR(energy2_input, 0444, ltc2947_show_value, NULL,
LTC2947_REG_ENERGY2);
/* clear status register by reading it */
ret = regmap_read(st->map, LTC2947_REG_STATUS, &dummy); if (ret) return ret; /* * Set max/min for power here since the default values x scale * would overflow on 32bit arch
*/
ret = ltc2947_val_write(st, LTC2947_REG_POWER_THRE_H, LTC2947_PAGE1, 2,
POWER_MAX / 200000); if (ret) return ret;
ret = ltc2947_val_write(st, LTC2947_REG_POWER_THRE_L, LTC2947_PAGE1, 2,
POWER_MIN / 200000); if (ret) return ret;
/* check external clock presence */
extclk = devm_clk_get_optional_enabled(st->dev, NULL); if (IS_ERR(extclk)) return dev_err_probe(st->dev, PTR_ERR(extclk), "Failed to get external clock\n");
if (extclk) { unsignedlong rate_hz;
u8 pre = 0, div, tbctl;
u64 aux;
/* let's calculate and set the right valus in TBCTL */
rate_hz = clk_get_rate(extclk); if (rate_hz < LTC2947_CLK_MIN || rate_hz > LTC2947_CLK_MAX) {
dev_err(st->dev, "Invalid rate:%lu for external clock",
rate_hz); return -EINVAL;
}
/* as in table 1 of the datasheet */ if (rate_hz >= LTC2947_CLK_MIN && rate_hz <= 1000000)
pre = 0; elseif (rate_hz > 1000000 && rate_hz <= 2000000)
pre = 1; elseif (rate_hz > 2000000 && rate_hz <= 4000000)
pre = 2; elseif (rate_hz > 4000000 && rate_hz <= 8000000)
pre = 3; elseif (rate_hz > 8000000 && rate_hz <= 16000000)
pre = 4; elseif (rate_hz > 16000000 && rate_hz <= LTC2947_CLK_MAX)
pre = 5; /* * Div is given by: * floor(fref / (2^PRE * 32768))
*/
div = rate_hz / ((1 << pre) * 32768);
tbctl = LTC2947_PRE(pre) | LTC2947_DIV(div);
ret = regmap_write(st->map, LTC2947_REG_TBCTL, tbctl); if (ret) return ret; /* * The energy lsb is given by (in W*s): * 06416 * (1/fref) * 2^PRE * (DIV + 1) * The value is multiplied by 10E9
*/
aux = (div + 1) * ((1 << pre) * 641600000ULL);
st->lsb_energy = DIV_ROUND_CLOSEST_ULL(aux, rate_hz);
} else { /* 19.89E-6 * 10E9 */
st->lsb_energy = 19890;
}
ret = device_property_read_u32_array(st->dev, "adi,accumulator-ctl-pol",
accum, ARRAY_SIZE(accum)); if (!ret) {
u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) |
LTC2947_ACCUM_POL_2(accum[1]);
ret = regmap_write(st->map, LTC2947_REG_ACCUM_POL, accum_reg); if (ret) return ret;
}
ret = device_property_read_u32(st->dev, "adi,accumulation-deadband-microamp",
&deadband); if (!ret) { /* the LSB is the same as the current, so 3mA */
ret = regmap_write(st->map, LTC2947_REG_ACCUM_DEADBAND,
deadband / (1000 * 3)); if (ret) return ret;
} /* check gpio cfg */
ret = device_property_read_u32(st->dev, "adi,gpio-out-pol", &pol); if (!ret) { /* setup GPIO as output */
u32 gpio_ctl = LTC2947_GPIO_EN(1) | LTC2947_GPIO_FAN_EN(1) |
LTC2947_GPIO_FAN_POL(pol);
st->gpio_out = true;
ret = regmap_write(st->map, LTC2947_REG_GPIOSTATCTL, gpio_ctl); if (ret) return ret;
}
ret = device_property_read_u32_array(st->dev, "adi,gpio-in-accum",
accum, ARRAY_SIZE(accum)); if (!ret) { /* * Setup the accum options. The gpioctl is already defined as * input by default.
*/
u32 accum_val = LTC2947_ACCUM_POL_1(accum[0]) |
LTC2947_ACCUM_POL_2(accum[1]);
if (st->gpio_out) {
dev_err(st->dev, "Cannot have input gpio config if already configured as output"); return -EINVAL;
}
ret = regmap_write(st->map, LTC2947_REG_GPIO_ACCUM, accum_val); if (ret) return ret;
}
/* dummy read to wake the device */
ret = regmap_read(st->map, LTC2947_REG_CTRL, &ctrl); if (ret) return ret; /* * Wait for the device. It takes 100ms to wake up so, 10ms extra * should be enough.
*/
msleep(110);
ret = regmap_read(st->map, LTC2947_REG_CTRL, &ctrl); if (ret) return ret; /* ctrl should be 0 */ if (ctrl != 0) {
dev_err(st->dev, "Device failed to wake up, ctl:%02X\n", ctrl); return -ETIMEDOUT;
}
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.