/* cold junction for thermocouples and rsense for rtd's and thermistor's */ #define LTC2983_CHAN_ASSIGN_MASK GENMASK(26, 22) #define LTC2983_CHAN_ASSIGN(x) FIELD_PREP(LTC2983_CHAN_ASSIGN_MASK, x)
/* * Convert to Q format numbers. These number's are integers where * the number of integer and fractional bits are specified. The resolution * is given by 1/@resolution and tell us the number of fractional bits. For * instance a resolution of 2^-10 means we have 10 fractional bits.
*/ static u32 __convert_to_raw(const u64 val, const u32 resolution)
{
u64 __res = val * resolution;
/* all values are multiplied by 1000000 to remove the fraction */
do_div(__res, 1000000);
staticint __ltc2983_chan_custom_sensor_assign(struct ltc2983_data *st, struct ltc2983_custom_sensor *custom,
u32 *chan_val)
{
u32 reg;
u8 mult = custom->is_steinhart ? LTC2983_CUSTOM_STEINHART_ENTRY_SZ :
LTC2983_CUSTOM_SENSOR_ENTRY_SZ; conststruct device *dev = &st->spi->dev; /* * custom->size holds the raw size of the table. However, when * configuring the sensor channel, we must write the number of * entries of the table minus 1. For steinhart sensors 0 is written * since the size is constant!
*/ const u8 len = custom->is_steinhart ? 0 :
(custom->size / LTC2983_CUSTOM_SENSOR_ENTRY_SZ) - 1; /* * Check if the offset was assigned already. It should be for steinhart * sensors. When coming from sleep, it should be assigned for all.
*/ if (custom->offset < 0) { /* * This needs to be done again here because, from the moment * when this test was done (successfully) for this custom * sensor, a steinhart sensor might have been added changing * custom_table_size...
*/ if (st->custom_table_size + custom->size >
(LTC2983_CUST_SENS_TBL_END_REG -
LTC2983_CUST_SENS_TBL_START_REG) + 1) {
dev_err(dev, "Not space left(%d) for new custom sensor(%zu)",
st->custom_table_size,
custom->size); return -EINVAL;
}
staticstruct ltc2983_custom_sensor *
__ltc2983_custom_sensor_new(struct ltc2983_data *st, conststruct fwnode_handle *fn, constchar *propname, constbool is_steinhart, const u32 resolution, constbool has_signed)
{ struct ltc2983_custom_sensor *new_custom; struct device *dev = &st->spi->dev; /* * For custom steinhart, the full u32 is taken. For all the others * the MSB is discarded.
*/ const u8 n_size = is_steinhart ? 4 : 3;
u8 index, n_entries; int ret;
if (is_steinhart)
n_entries = fwnode_property_count_u32(fn, propname); else
n_entries = fwnode_property_count_u64(fn, propname); /* n_entries must be an even number */ if (!n_entries || (n_entries % 2) != 0) return dev_err_ptr_probe(dev, -EINVAL, "Number of entries either 0 or not even\n");
new_custom = devm_kzalloc(dev, sizeof(*new_custom), GFP_KERNEL); if (!new_custom) return ERR_PTR(-ENOMEM);
new_custom->size = n_entries * n_size; /* check Steinhart size */ if (is_steinhart && new_custom->size != LTC2983_CUSTOM_STEINHART_SIZE) return dev_err_ptr_probe(dev, -EINVAL, "Steinhart sensors size(%zu) must be %u\n",
new_custom->size, LTC2983_CUSTOM_STEINHART_SIZE);
/* Check space on the table. */ if (st->custom_table_size + new_custom->size >
(LTC2983_CUST_SENS_TBL_END_REG - LTC2983_CUST_SENS_TBL_START_REG) + 1) return dev_err_ptr_probe(dev, -EINVAL, "No space left(%d) for new custom sensor(%zu)\n",
st->custom_table_size, new_custom->size);
/* allocate the table */ if (is_steinhart)
new_custom->table = devm_kcalloc(dev, n_entries, sizeof(u32), GFP_KERNEL); else
new_custom->table = devm_kcalloc(dev, n_entries, sizeof(u64), GFP_KERNEL); if (!new_custom->table) return ERR_PTR(-ENOMEM);
/* * Steinhart sensors are configured with raw values in the firmware * node. For the other sensors we must convert the value to raw. * The odd index's correspond to temperatures and always have 1/1024 * of resolution. Temperatures also come in Kelvin, so signed values * are not possible.
*/ if (is_steinhart) {
ret = fwnode_property_read_u32_array(fn, propname, new_custom->table, n_entries); if (ret < 0) return ERR_PTR(ret);
cpu_to_be32_array(new_custom->table, new_custom->table, n_entries);
} else {
ret = fwnode_property_read_u64_array(fn, propname, new_custom->table, n_entries); if (ret < 0) return ERR_PTR(ret);
for (index = 0; index < n_entries; index++) {
u64 temp = ((u64 *)new_custom->table)[index];
put_unaligned_be24(temp, new_custom->table + index * 3);
}
}
new_custom->is_steinhart = is_steinhart; /* * This is done to first add all the steinhart sensors to the table, * in order to maximize the table usage. If we mix adding steinhart * with the other sensors, we might have to do some roundup to make * sure that sensor_addr - 0x250(start address) is a multiple of 4 * (for steinhart), and a multiple of 6 for all the other sensors. * Since we have const 24 bytes for steinhart sensors and 24 is * also a multiple of 6, we guarantee that the first non-steinhart * sensor will sit in a correct address without the need of filling * addresses.
*/ if (is_steinhart) {
new_custom->offset = st->custom_table_size /
LTC2983_CUSTOM_STEINHART_ENTRY_SZ;
st->custom_table_size += new_custom->size;
} else { /* mark as unset. This is checked later on the assign phase */
new_custom->offset = -1;
}
thermo = devm_kzalloc(&st->spi->dev, sizeof(*thermo), GFP_KERNEL); if (!thermo) return ERR_PTR(-ENOMEM);
if (fwnode_property_read_bool(child, "adi,single-ended"))
thermo->sensor_config = LTC2983_THERMOCOUPLE_SGL(1);
ret = fwnode_property_read_u32(child, "adi,sensor-oc-current-microamp", &oc_current); if (!ret) { switch (oc_current) { case 10:
thermo->sensor_config |=
LTC2983_THERMOCOUPLE_OC_CURR(0); break; case 100:
thermo->sensor_config |=
LTC2983_THERMOCOUPLE_OC_CURR(1); break; case 500:
thermo->sensor_config |=
LTC2983_THERMOCOUPLE_OC_CURR(2); break; case 1000:
thermo->sensor_config |=
LTC2983_THERMOCOUPLE_OC_CURR(3); break; default: return dev_err_ptr_probe(&st->spi->dev, -EINVAL, "Invalid open circuit current:%u\n",
oc_current);
}
thermo->sensor_config |= LTC2983_THERMOCOUPLE_OC_CHECK(1);
} /* validate channel index */ if (!(thermo->sensor_config & LTC2983_THERMOCOUPLE_DIFF_MASK) &&
sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) return dev_err_ptr_probe(&st->spi->dev, -EINVAL, "Invalid chann:%d for differential thermocouple\n",
sensor->chan);
struct fwnode_handle *ref __free(fwnode_handle) =
fwnode_find_reference(child, "adi,cold-junction-handle", 0); if (IS_ERR(ref)) {
ref = NULL;
} else {
ret = fwnode_property_read_u32(ref, "reg", &thermo->cold_junction_chan); if (ret) /* * This would be catched later but we can just return * the error right away.
*/ return dev_err_ptr_probe(&st->spi->dev, ret, "Property reg must be given\n");
}
rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL); if (!rtd) return ERR_PTR(-ENOMEM);
struct fwnode_handle *ref __free(fwnode_handle) =
fwnode_find_reference(child, "adi,rsense-handle", 0); if (IS_ERR(ref)) return dev_err_cast_probe(dev, ref, "Property adi,rsense-handle missing or invalid\n");
ret = fwnode_property_read_u32(ref, "reg", &rtd->r_sense_chan); if (ret) return dev_err_ptr_probe(dev, ret, "Property reg must be given\n");
ret = fwnode_property_read_u32(child, "adi,number-of-wires", &n_wires); if (!ret) { switch (n_wires) { case 2:
rtd->sensor_config = LTC2983_RTD_N_WIRES(0); break; case 3:
rtd->sensor_config = LTC2983_RTD_N_WIRES(1); break; case 4:
rtd->sensor_config = LTC2983_RTD_N_WIRES(2); break; case 5: /* 4 wires, Kelvin Rsense */
rtd->sensor_config = LTC2983_RTD_N_WIRES(3); break; default: return dev_err_ptr_probe(dev, -EINVAL, "Invalid number of wires:%u\n",
n_wires);
}
}
if (fwnode_property_read_bool(child, "adi,rsense-share")) { /* Current rotation is only available with rsense sharing */ if (fwnode_property_read_bool(child, "adi,current-rotate")) { if (n_wires == 2 || n_wires == 3) return dev_err_ptr_probe(dev, -EINVAL, "Rotation not allowed for 2/3 Wire RTDs\n");
rtd->sensor_config |= LTC2983_RTD_C_ROTATE(1);
} else {
rtd->sensor_config |= LTC2983_RTD_R_SHARE(1);
}
} /* * rtd channel indexes are a bit more complicated to validate. * For 4wire RTD with rotation, the channel selection cannot be * >=19 since the chann + 1 is used in this configuration. * For 4wire RTDs with kelvin rsense, the rsense channel cannot be * <=1 since chanel - 1 and channel - 2 are used.
*/ if (rtd->sensor_config & LTC2983_RTD_4_WIRE_MASK) { /* 4-wire */
u8 min = LTC2983_DIFFERENTIAL_CHAN_MIN,
max = st->info->max_channels_nr;
if (rtd->sensor_config & LTC2983_RTD_ROTATION_MASK)
max = st->info->max_channels_nr - 1;
if (((rtd->sensor_config & LTC2983_RTD_KELVIN_R_SENSE_MASK)
== LTC2983_RTD_KELVIN_R_SENSE_MASK) &&
(rtd->r_sense_chan <= min)) /* kelvin rsense*/ return dev_err_ptr_probe(dev, -EINVAL, "Invalid rsense chann:%d to use in kelvin rsense\n",
rtd->r_sense_chan);
if (sensor->chan < min || sensor->chan > max) return dev_err_ptr_probe(dev, -EINVAL, "Invalid chann:%d for the rtd config\n",
sensor->chan);
} else { /* same as differential case */ if (sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) return dev_err_ptr_probe(&st->spi->dev, -EINVAL, "Invalid chann:%d for RTD\n",
sensor->chan);
}
thermistor = devm_kzalloc(dev, sizeof(*thermistor), GFP_KERNEL); if (!thermistor) return ERR_PTR(-ENOMEM);
struct fwnode_handle *ref __free(fwnode_handle) =
fwnode_find_reference(child, "adi,rsense-handle", 0); if (IS_ERR(ref)) return dev_err_cast_probe(dev, ref, "Property adi,rsense-handle missing or invalid\n");
ret = fwnode_property_read_u32(ref, "reg", &thermistor->r_sense_chan); if (ret) return dev_err_ptr_probe(dev, ret, "rsense channel must be configured...\n");
if (fwnode_property_read_bool(child, "adi,single-ended")) {
thermistor->sensor_config = LTC2983_THERMISTOR_SGL(1);
} elseif (fwnode_property_read_bool(child, "adi,rsense-share")) { /* rotation is only possible if sharing rsense */ if (fwnode_property_read_bool(child, "adi,current-rotate"))
thermistor->sensor_config =
LTC2983_THERMISTOR_C_ROTATE(1); else
thermistor->sensor_config =
LTC2983_THERMISTOR_R_SHARE(1);
} /* validate channel index */ if (!(thermistor->sensor_config & LTC2983_THERMISTOR_DIFF_MASK) &&
sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) return dev_err_ptr_probe(&st->spi->dev, -EINVAL, "Invalid chann:%d for differential thermistor\n",
sensor->chan);
thermistor->custom = __ltc2983_custom_sensor_new(st, child,
propname,
steinhart,
64, false); if (IS_ERR(thermistor->custom)) return ERR_CAST(thermistor->custom);
} /* set common parameters */
thermistor->sensor.fault_handler = ltc2983_common_fault_handler;
thermistor->sensor.assign_chan = ltc2983_thermistor_assign_chan;
ret = fwnode_property_read_u32(child, "adi,excitation-current-nanoamp",
&excitation_current); if (ret) { /* Auto range is not allowed for custom sensors */ if (sensor->type >= LTC2983_SENSOR_THERMISTOR_STEINHART) /* default to 1uA */
thermistor->excitation_current = 0x03; else /* default to auto-range */
thermistor->excitation_current = 0x0c;
} else { switch (excitation_current) { case 0: /* auto range */ if (sensor->type >= LTC2983_SENSOR_THERMISTOR_STEINHART) return dev_err_ptr_probe(&st->spi->dev, -EINVAL, "Auto Range not allowed for custom sensors\n");
thermistor->excitation_current = 0x0c; break; case 250:
thermistor->excitation_current = 0x01; break; case 500:
thermistor->excitation_current = 0x02; break; case 1000:
thermistor->excitation_current = 0x03; break; case 5000:
thermistor->excitation_current = 0x04; break; case 10000:
thermistor->excitation_current = 0x05; break; case 25000:
thermistor->excitation_current = 0x06; break; case 50000:
thermistor->excitation_current = 0x07; break; case 100000:
thermistor->excitation_current = 0x08; break; case 250000:
thermistor->excitation_current = 0x09; break; case 500000:
thermistor->excitation_current = 0x0a; break; case 1000000:
thermistor->excitation_current = 0x0b; break; default: return dev_err_ptr_probe(&st->spi->dev, -EINVAL, "Invalid value for excitation current(%u)\n",
excitation_current);
}
}
rsense = devm_kzalloc(&st->spi->dev, sizeof(*rsense), GFP_KERNEL); if (!rsense) return ERR_PTR(-ENOMEM);
/* validate channel index */ if (sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) return dev_err_ptr_probe(&st->spi->dev, -EINVAL, "Invalid chann:%d for r_sense\n",
sensor->chan);
ret = fwnode_property_read_u32(child, "adi,rsense-val-milli-ohms", &temp); if (ret) return dev_err_ptr_probe(&st->spi->dev, -EINVAL, "Property adi,rsense-val-milli-ohms missing\n"); /* * Times 1000 because we have milli-ohms and __convert_to_raw * expects scales of 1000000 which are used for all other * properties. * 2^10 resolution
*/
rsense->r_sense_val = __convert_to_raw((u64)temp * 1000, 1024);
/* set common parameters */
rsense->sensor.assign_chan = ltc2983_r_sense_assign_chan;
/* set common parameters */
temp->sensor.assign_chan = ltc2983_temp_assign_chan;
temp->sensor.fault_handler = ltc2983_common_fault_handler;
return &temp->sensor;
}
staticint ltc2983_chan_read(struct ltc2983_data *st, conststruct ltc2983_sensor *sensor, int *val)
{
u32 start_conversion = 0; int ret; unsignedlong time;
start_conversion = LTC2983_STATUS_START(true);
start_conversion |= LTC2983_STATUS_CHAN_SEL(sensor->chan);
dev_dbg(&st->spi->dev, "Start conversion on chan:%d, status:%02X\n",
sensor->chan, start_conversion); /* start conversion */
ret = regmap_write(st->regmap, LTC2983_STATUS_REG, start_conversion); if (ret) return ret;
reinit_completion(&st->completion); /* * wait for conversion to complete. * 300 ms should be more than enough to complete the conversion. * Depending on the sensor configuration, there are 2/3 conversions * cycles of 82ms.
*/
time = wait_for_completion_timeout(&st->completion,
msecs_to_jiffies(300)); if (!time) {
dev_warn(&st->spi->dev, "Conversion timed out\n"); return -ETIMEDOUT;
}
/* read the converted data */
ret = regmap_bulk_read(st->regmap, LTC2983_CHAN_RES_ADDR(sensor->chan),
&st->temp, sizeof(st->temp)); if (ret) return ret;
st->num_channels = device_get_child_node_count(dev); if (!st->num_channels) return dev_err_probe(&st->spi->dev, -EINVAL, "At least one channel must be given!\n");
st->sensors = devm_kcalloc(dev, st->num_channels, sizeof(*st->sensors),
GFP_KERNEL); if (!st->sensors) return -ENOMEM;
ret = fwnode_property_read_u32(child, "reg", &sensor.chan); if (ret) return dev_err_probe(dev, ret, "reg property must given for child nodes\n");
/* check if we have a valid channel */ if (sensor.chan < LTC2983_MIN_CHANNELS_NR ||
sensor.chan > st->info->max_channels_nr) return dev_err_probe(dev, -EINVAL, "chan:%d must be from %u to %u\n",
sensor.chan,
LTC2983_MIN_CHANNELS_NR,
st->info->max_channels_nr);
if (channel_avail_mask & BIT(sensor.chan)) return dev_err_probe(dev, -EINVAL, "chan:%d already in use\n",
sensor.chan);
ret = fwnode_property_read_u32(child, "adi,sensor-type", &sensor.type); if (ret) return dev_err_probe(dev, ret, "adi,sensor-type property must given for child nodes\n");
dev_dbg(dev, "Create new sensor, type %u, chann %u",
sensor.type, sensor.chan);
/* make sure the device is up: start bit (7) is 0 and done bit (6) is 1 */
ret = regmap_read_poll_timeout(st->regmap, LTC2983_STATUS_REG, status,
LTC2983_STATUS_UP(status) == 1, 25000,
25000 * 10); if (ret) return dev_err_probe(&st->spi->dev, ret, "Device startup timed out\n");
ret = regmap_update_bits(st->regmap, LTC2983_GLOBAL_CONFIG_REG,
LTC2983_NOTCH_FREQ_MASK,
LTC2983_NOTCH_FREQ(st->filter_notch_freq)); if (ret) return ret;
ret = regmap_write(st->regmap, LTC2983_MUX_CONFIG_REG,
st->mux_delay_config); if (ret) return ret;
if (st->info->has_eeprom && !assign_iio) {
ret = ltc2983_eeprom_cmd(st, LTC2983_EEPROM_READ_CMD,
LTC2983_EEPROM_READ_TIME_MS,
LTC2983_EEPROM_READ_STATUS_REG,
LTC2983_EEPROM_READ_FAILURE_MASK); if (!ret) return 0;
}
ret = st->sensors[chan]->assign_chan(st, st->sensors[chan]); if (ret) return ret; /* * The assign_iio flag is necessary for when the device is * coming out of sleep. In that case, we just need to * re-configure the device channels. * We also don't assign iio channels for rsense.
*/ if (st->sensors[chan]->type == LTC2983_SENSOR_SENSE_RESISTOR ||
!assign_iio) continue;
/* * add chan as the iio .address so that, we can directly * reference the sensor given the iio_chan_spec
*/
st->iio_chan[iio_idx++] = LTC2983_CHAN(chan_type, (*iio_chan)++,
chan);
}
ret = ltc2983_setup(st, true); if (ret) return ret;
ret = devm_request_irq(&spi->dev, spi->irq, ltc2983_irq_handler,
IRQF_TRIGGER_RISING, st->info->name, st); if (ret) return dev_err_probe(&spi->dev, ret, "failed to request an irq\n");
if (st->info->has_eeprom) {
ret = ltc2983_eeprom_cmd(st, LTC2983_EEPROM_WRITE_CMD,
LTC2983_EEPROM_WRITE_TIME_MS,
LTC2986_EEPROM_STATUS_REG,
LTC2983_EEPROM_STATUS_FAILURE_MASK); if (ret) return ret;
}
/* dummy read to bring the device out of sleep */
regmap_read(st->regmap, LTC2983_STATUS_REG, &dummy); /* we need to re-assign the channels */ return ltc2983_setup(st, false);
}
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.