// SPDX-License-Identifier: GPL-2.0+ /* * IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor * * Copyright (C) 2017-2024 Microchip Technology Inc. and its subsidiaries * * Author: Bogdan Bolocan <bogdan.bolocan@microchip.com> * Author: Victor Tudose * Author: Marius Cristea <marius.cristea@microchip.com> * * Datasheet for PAC1931, PAC1932, PAC1933 and PAC1934 can be found here: * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/PAC1931-Family-Data-Sheet-DS20005850E.pdf
*/
/* * maximum accumulation time should be (17 * 60 * 1000) around 17 minutes@1024 sps * till PAC1934 accumulation registers starts to saturate
*/ #define PAC1934_MAX_RFSH_LIMIT_MS 60000 /* 50msec is the timeout for validity of the cached registers */ #define PAC1934_MIN_POLLING_TIME_MS 50 /* * 1000usec is the minimum wait time for normal conversions when sample * rate doesn't change
*/ #define PAC1934_MIN_UPDATE_WAIT_TIME_US 1000
/* 32000mV */ #define PAC1934_VOLTAGE_MILLIVOLTS_MAX 32000 /* voltage bits resolution when set for unsigned values */ #define PAC1934_VOLTAGE_U_RES 16 /* voltage bits resolution when set for signed values */ #define PAC1934_VOLTAGE_S_RES 15
/* * max signed value that can be stored on 32 bits and 8 digits fractional value * (2^31 - 1) * 10^8 + 99999999
*/ #define PAC_193X_MAX_POWER_ACC 214748364799999999LL /* * min signed value that can be stored on 32 bits and 8 digits fractional value * -(2^31) * 10^8 - 99999999
*/ #define PAC_193X_MIN_POWER_ACC -214748364899999999LL
/* * relative offsets when using multi-byte reads/writes even though these * bytes are read one after the other, they are not at adjacent memory * locations within the I2C memory map. The chip can skip some addresses
*/ #define PAC1934_CHANNEL_DIS_REG_OFF 0 #define PAC1934_NEG_PWR_REG_OFF 1
/* * when reading/writing multiple bytes from offset PAC1934_CHANNEL_DIS_REG_OFF, * the chip jumps over the 0x1E (REFRESH_G) and 0x1F (REFRESH_V) offsets
*/ #define PAC1934_SLOW_REG_OFF 2 #define PAC1934_CTRL_ACT_REG_OFF 3 #define PAC1934_CHANNEL_DIS_ACT_REG_OFF 4 #define PAC1934_NEG_PWR_ACT_REG_OFF 5 #define PAC1934_CTRL_LAT_REG_OFF 6 #define PAC1934_CHANNEL_DIS_LAT_REG_OFF 7 #define PAC1934_NEG_PWR_LAT_REG_OFF 8 #define PAC1934_PID_REG_OFF 9 #define PAC1934_MID_REG_OFF 10 #define PAC1934_REV_REG_OFF 11 #define PAC1934_CTRL_STATUS_INFO_LEN 12
/* * Universal Unique Identifier (UUID), * 033771E0-1705-47B4-9535-D1BBE14D9A09, * is reserved to Microchip for the PAC1934.
*/ #define PAC1934_DSM_UUID "033771E0-1705-47B4-9535-D1BBE14D9A09"
/* * these indexes are exactly describing the element order within a single * PAC1934 phys channel IIO channel descriptor; see the static const struct * iio_chan_spec pac1934_single_channel[] declaration
*/ enum pac1934_ch_idx {
PAC1934_CH_ENERGY,
PAC1934_CH_POWER,
PAC1934_CH_VOLTAGE,
PAC1934_CH_CURRENT,
PAC1934_CH_VOLTAGE_AVERAGE,
PAC1934_CH_CURRENT_AVERAGE
};
/** * struct pac1934_features - features of a pac1934 instance * @phys_channels: number of physical channels supported by the chip * @name: chip's name
*/ struct pac1934_features {
u8 phys_channels; constchar *name;
};
/** * struct reg_data - data from the registers * @meas_regs: snapshot of raw measurements registers * @ctrl_regs: snapshot of control registers * @energy_sec_acc: snapshot of energy values * @vpower_acc: accumulated vpower values * @vpower: snapshot of vpower registers * @vbus: snapshot of vbus registers * @vbus_avg: averages of vbus registers * @vsense: snapshot of vsense registers * @vsense_avg: averages of vsense registers * @num_enabled_channels: count of how many chip channels are currently enabled
*/ struct reg_data {
u8 meas_regs[PAC1934_MEAS_REG_LEN];
u8 ctrl_regs[PAC1934_CTRL_REG_LEN];
s64 energy_sec_acc[PAC1934_MAX_NUM_CHANNELS];
s64 vpower_acc[PAC1934_MAX_NUM_CHANNELS];
s32 vpower[PAC1934_MAX_NUM_CHANNELS];
s32 vbus[PAC1934_MAX_NUM_CHANNELS];
s32 vbus_avg[PAC1934_MAX_NUM_CHANNELS];
s32 vsense[PAC1934_MAX_NUM_CHANNELS];
s32 vsense_avg[PAC1934_MAX_NUM_CHANNELS];
u8 num_enabled_channels;
};
/** * struct pac1934_chip_info - information about the chip * @client: the i2c-client attached to the device * @lock: synchronize access to driver's state members * @work_chip_rfsh: work queue used for refresh commands * @phys_channels: phys channels count * @active_channels: array of values, true means that channel is active * @enable_energy: array of values, true means that channel energy is measured * @bi_dir: array of bools, true means that channel is bidirectional * @chip_variant: chip variant * @chip_revision: chip revision * @shunts: shunts * @chip_reg_data: chip reg data * @sample_rate_value: sampling frequency * @labels: table with channels labels * @iio_info: iio_info * @tstamp: chip's uptime
*/ struct pac1934_chip_info { struct i2c_client *client; struct mutex lock; /* synchronize access to driver's state members */ struct delayed_work work_chip_rfsh;
u8 phys_channels; bool active_channels[PAC1934_MAX_NUM_CHANNELS]; bool enable_energy[PAC1934_MAX_NUM_CHANNELS]; bool bi_dir[PAC1934_MAX_NUM_CHANNELS];
u8 chip_variant;
u8 chip_revision;
u32 shunts[PAC1934_MAX_NUM_CHANNELS]; struct reg_data chip_reg_data;
s32 sample_rate_value; char *labels[PAC1934_MAX_NUM_CHANNELS]; struct iio_info iio_info; unsignedlong tstamp;
};
ret = i2c_smbus_write_byte_data(client,
PAC1934_CTRL_STAT_REGS_ADDR +
PAC1934_NEG_PWR_REG_OFF,
bidir_reg); if (ret) return ret;
}
ret = i2c_smbus_write_byte(client, refresh_cmd); if (ret) {
dev_err(&client->dev, "%s - cannot send 0x%02X\n",
__func__, refresh_cmd); return ret;
}
if (revision_bug) { /* * chip rev 2 and 3 bug workaround - write again the same * register write the updated registers back
*/
ret = i2c_smbus_write_byte_data(client,
PAC1934_CTRL_STAT_REGS_ADDR +
PAC1934_NEG_PWR_REG_OFF, bidir_reg); if (ret) return ret;
}
/* register data retrieval timestamp */
info->tstamp = jiffies;
/* wait till the data is available */
usleep_range(wait_time, wait_time + 100);
/* read the data registers */
ret = pac1934_i2c_read(client, PAC1934_ACC_COUNT_REG_ADDR,
(u8 *)reg_data->meas_regs, PAC1934_MEAS_REG_LEN); if (ret) {
dev_err(&client->dev, "%s - cannot read ACC_COUNT register: %d:%d\n",
__func__, ret, PAC1934_MEAS_REG_LEN); return ret;
}
/* see how much shift is required by the sample rate */
samp_rate = samp_rate_map_tbl[((reg_data->ctrl_regs[PAC1934_CTRL_LAT_REG_OFF]) >> 6)];
samp_shift = get_count_order(samp_rate);
/* start with VPOWER_ACC */ for (cnt = 0; cnt < info->phys_channels; cnt++) { /* check if the channel is active, skip all fields if disabled */ if ((ctrl_regs_tmp << cnt) & 0x80) continue;
/* skip if the energy accumulation is disabled */ if (info->enable_energy[cnt]) {
curr_energy = info->chip_reg_data.energy_sec_acc[cnt];
if (info->bi_dir[cnt])
reg_data->vpower_acc[cnt] = sign_extend64(tmp_energy, 47); else
reg_data->vpower_acc[cnt] = tmp_energy;
/* * compute the scaled to 1 second accumulated energy value; * energy accumulator scaled to 1sec = VPOWER_ACC/2^samp_shift * the chip's sampling rate is 2^samp_shift samples/sec
*/
inc = (reg_data->vpower_acc[cnt] >> samp_shift);
/* VPOWER */ for (cnt = 0; cnt < info->phys_channels; cnt++) { if ((ctrl_regs_tmp << cnt) & 0x80) continue;
tmp = get_unaligned_be32(offset_reg_data_p) >> 4;
if (info->bi_dir[cnt])
reg_data->vpower[cnt] = sign_extend32(tmp, 27); else
reg_data->vpower[cnt] = tmp;
offset_reg_data_p += PAC1934_VPOWER_REG_LEN;
}
return 0;
}
staticint pac1934_retrieve_data(struct pac1934_chip_info *info,
u32 wait_time)
{ int ret = 0;
/* * check if the minimal elapsed time has passed and if so, * re-read the chip, otherwise the cached info is just fine
*/ if (time_after(jiffies, info->tstamp + msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS))) {
ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR,
wait_time);
/* * Re-schedule the work for the read registers on timeout * (to prevent chip registers saturation)
*/
mod_delayed_work(system_wq, &info->work_chip_rfsh,
msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS));
}
return ret;
}
staticint pac1934_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{ struct pac1934_chip_info *info = iio_priv(indio_dev);
s64 curr_energy; int ret, channel = chan->channel - 1;
/* * For AVG the index should be between 5 to 8. * To calculate PAC1934_CH_VOLTAGE_AVERAGE, * respectively PAC1934_CH_CURRENT real index, we need * to remove the added offset (PAC1934_MAX_NUM_CHANNELS).
*/ if (channel >= PAC1934_MAX_NUM_CHANNELS)
channel = channel - PAC1934_MAX_NUM_CHANNELS;
ret = pac1934_retrieve_data(info, PAC1934_MIN_UPDATE_WAIT_TIME_US); if (ret < 0) return ret;
switch (mask) { case IIO_CHAN_INFO_RAW: switch (chan->type) { case IIO_VOLTAGE:
*val = info->chip_reg_data.vbus[channel]; return IIO_VAL_INT; case IIO_CURRENT:
*val = info->chip_reg_data.vsense[channel]; return IIO_VAL_INT; case IIO_POWER:
*val = info->chip_reg_data.vpower[channel]; return IIO_VAL_INT; case IIO_ENERGY:
curr_energy = info->chip_reg_data.energy_sec_acc[channel];
*val = (u32)curr_energy;
*val2 = (u32)(curr_energy >> 32); return IIO_VAL_INT_64; default: return -EINVAL;
} case IIO_CHAN_INFO_AVERAGE_RAW: switch (chan->type) { case IIO_VOLTAGE:
*val = info->chip_reg_data.vbus_avg[channel]; return IIO_VAL_INT; case IIO_CURRENT:
*val = info->chip_reg_data.vsense_avg[channel]; return IIO_VAL_INT; default: return -EINVAL;
} case IIO_CHAN_INFO_SCALE: switch (chan->address) { /* Voltages - scale for millivolts */ case PAC1934_VBUS_1_ADDR: case PAC1934_VBUS_2_ADDR: case PAC1934_VBUS_3_ADDR: case PAC1934_VBUS_4_ADDR: case PAC1934_VBUS_AVG_1_ADDR: case PAC1934_VBUS_AVG_2_ADDR: case PAC1934_VBUS_AVG_3_ADDR: case PAC1934_VBUS_AVG_4_ADDR:
*val = PAC1934_VOLTAGE_MILLIVOLTS_MAX; if (chan->scan_type.sign == 'u')
*val2 = PAC1934_VOLTAGE_U_RES; else
*val2 = PAC1934_VOLTAGE_S_RES; return IIO_VAL_FRACTIONAL_LOG2; /* * Currents - scale for mA - depends on the * channel's shunt value * (100mV * 1000000) / (2^16 * shunt(uohm))
*/ case PAC1934_VSENSE_1_ADDR: case PAC1934_VSENSE_2_ADDR: case PAC1934_VSENSE_3_ADDR: case PAC1934_VSENSE_4_ADDR: case PAC1934_VSENSE_AVG_1_ADDR: case PAC1934_VSENSE_AVG_2_ADDR: case PAC1934_VSENSE_AVG_3_ADDR: case PAC1934_VSENSE_AVG_4_ADDR:
*val = PAC1934_MAX_VSENSE_RSHIFTED_BY_16B; if (chan->scan_type.sign == 'u')
*val2 = info->shunts[channel]; else
*val2 = info->shunts[channel] >> 1; return IIO_VAL_FRACTIONAL; /* * Power - uW - it will use the combined scale * for current and voltage * current(mA) * voltage(mV) = power (uW)
*/ case PAC1934_VPOWER_1_ADDR: case PAC1934_VPOWER_2_ADDR: case PAC1934_VPOWER_3_ADDR: case PAC1934_VPOWER_4_ADDR:
*val = PAC1934_MAX_VPOWER_RSHIFTED_BY_28B; if (chan->scan_type.sign == 'u')
*val2 = info->shunts[channel]; else
*val2 = info->shunts[channel] >> 1; return IIO_VAL_FRACTIONAL; case PAC1934_VPOWER_ACC_1_ADDR: case PAC1934_VPOWER_ACC_2_ADDR: case PAC1934_VPOWER_ACC_3_ADDR: case PAC1934_VPOWER_ACC_4_ADDR: /* * expresses the 32 bit scale value here compute * the scale for energy (miliWatt-second or miliJoule)
*/
*val = PAC1934_SCALE_CONSTANT;
/* * now, force a snapshot with refresh - call retrieve * data in order to update the refresh timer * alter the timestamp in order to force trigger a * register snapshot and a timestamp update
*/
info->tstamp -= msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS);
ret = pac1934_retrieve_data(info, (1024 / old_samp_rate) * 1000); if (ret < 0) {
dev_err(&client->dev, "%s - cannot snapshot ctrl and measurement regs\n",
__func__); return ret;
}
return 0; case IIO_CHAN_INFO_ENABLE:
scoped_guard(mutex, &info->lock) {
info->enable_energy[chan->channel - 1] = val ? true : false; if (!val)
info->chip_reg_data.energy_sec_acc[chan->channel - 1] = 0;
}
switch (chan->address) { case PAC1934_VBUS_1_ADDR: case PAC1934_VBUS_2_ADDR: case PAC1934_VBUS_3_ADDR: case PAC1934_VBUS_4_ADDR: return sysfs_emit(label, "%s_VBUS_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1); case PAC1934_VBUS_AVG_1_ADDR: case PAC1934_VBUS_AVG_2_ADDR: case PAC1934_VBUS_AVG_3_ADDR: case PAC1934_VBUS_AVG_4_ADDR: return sysfs_emit(label, "%s_VBUS_AVG_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1); case PAC1934_VSENSE_1_ADDR: case PAC1934_VSENSE_2_ADDR: case PAC1934_VSENSE_3_ADDR: case PAC1934_VSENSE_4_ADDR: return sysfs_emit(label, "%s_IBUS_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1); case PAC1934_VSENSE_AVG_1_ADDR: case PAC1934_VSENSE_AVG_2_ADDR: case PAC1934_VSENSE_AVG_3_ADDR: case PAC1934_VSENSE_AVG_4_ADDR: return sysfs_emit(label, "%s_IBUS_AVG_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1); case PAC1934_VPOWER_1_ADDR: case PAC1934_VPOWER_2_ADDR: case PAC1934_VPOWER_3_ADDR: case PAC1934_VPOWER_4_ADDR: return sysfs_emit(label, "%s_POWER_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1); case PAC1934_VPOWER_ACC_1_ADDR: case PAC1934_VPOWER_ACC_2_ADDR: case PAC1934_VPOWER_ACC_3_ADDR: case PAC1934_VPOWER_ACC_4_ADDR: return sysfs_emit(label, "%s_ENERGY_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1);
}
for (i = 0; i < rez->package.count; i += 2) {
idx = i / 2;
info->labels[idx] =
devm_kmemdup(dev, rez->package.elements[i].string.pointer,
(size_t)rez->package.elements[i].string.length + 1,
GFP_KERNEL);
info->labels[idx][rez->package.elements[i].string.length] = '\0';
info->shunts[idx] = rez->package.elements[i + 1].integer.value * 1000;
info->active_channels[idx] = (info->shunts[idx] != 0);
}
ACPI_FREE(rez);
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_UOHMS_VALS, NULL); if (!rez) { /* * initializing with default values * we assume all channels are unidirectional(the mask is zero) * and assign the default sampling rate
*/
info->sample_rate_value = PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ; return 0;
}
for (i = 0; i < rez->package.count; i++) {
idx = i;
info->shunts[idx] = rez->package.elements[i].integer.value;
info->active_channels[idx] = (info->shunts[idx] != 0);
}
info->chip_reg_data.num_enabled_channels = 0; for (cnt = 0; cnt < info->phys_channels; cnt++) { if (info->active_channels[cnt])
info->chip_reg_data.num_enabled_channels++;
}
/* * read whatever information was gathered before the driver was loaded * establish which channels are enabled/disabled and then establish the * information retrieval mode (using SKIP or no). * Read the chip ID values
*/
ret = i2c_smbus_read_i2c_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR,
ARRAY_SIZE(regs),
(u8 *)regs); if (ret < 0) {
dev_err_probe(&client->dev, ret, "%s - cannot read regs from 0x%02X\n",
__func__, PAC1934_CTRL_STAT_REGS_ADDR); return ret;
}
/* no SLOW triggered REFRESH, clear POR */
regs[PAC1934_SLOW_REG_OFF] = 0;
/* * Write the three bytes sequentially, as the device does not support * block write.
*/
ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_STAT_REGS_ADDR,
regs[PAC1934_CHANNEL_DIS_REG_OFF]); if (ret) return ret;
ret = i2c_smbus_write_byte_data(client,
PAC1934_CTRL_STAT_REGS_ADDR + PAC1934_NEG_PWR_REG_OFF,
regs[PAC1934_NEG_PWR_REG_OFF]); if (ret) return ret;
ret = i2c_smbus_write_byte_data(client, PAC1934_SLOW_REG_ADDR,
regs[PAC1934_SLOW_REG_OFF]); if (ret) return ret;
ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_REG_ADDR, ctrl_reg); if (ret) return ret;
/* * send a REFRESH to the chip, so the new settings take place * as well as resetting the accumulators
*/
ret = i2c_smbus_write_byte(client, PAC1934_REFRESH_REG_ADDR); if (ret) {
dev_err(&client->dev, "%s - cannot send 0x%02X\n",
__func__, PAC1934_REFRESH_REG_ADDR); return ret;
}
/* * get the current(in the chip) sampling speed and compute the * required timeout based on its value * the timeout is 1/sampling_speed
*/
idx = regs[PAC1934_CTRL_ACT_REG_OFF] >> PAC1934_SAMPLE_RATE_SHIFT;
wait_time = (1024 / samp_rate_map_tbl[idx]) * 1000;
/* * wait the maximum amount of time to be on the safe side * the maximum wait time is for 8sps
*/
usleep_range(wait_time, wait_time + 100);
INIT_DELAYED_WORK(&info->work_chip_rfsh, pac1934_work_periodic_rfsh); /* Setup the latest moment for reading the regs before saturation */
schedule_delayed_work(&info->work_chip_rfsh,
msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS));
/* find out dynamically how many IIO channels we need */
attribute_count = 0;
channel_size = 0; for (cnt = 0; cnt < info->phys_channels; cnt++) { if (!info->active_channels[cnt]) continue;
/* add the size of the properties of one chip physical channel */
channel_size += sizeof(pac1934_single_channel); /* count how many enabled channels we have */
attribute_count += ARRAY_SIZE(pac1934_single_channel);
dev_dbg(dev, ":%s: Channel %d active\n", __func__, cnt + 1);
}
dyn_ch_struct = devm_kzalloc(dev, channel_size, GFP_KERNEL); if (!dyn_ch_struct) return -EINVAL;
tmp_data = dyn_ch_struct;
/* populate the dynamic channels and make all the adjustments */ for (cnt = 0; cnt < info->phys_channels; cnt++) { if (!info->active_channels[cnt]) continue;
/* * In order to be able to use labels for PAC1934_CH_VOLTAGE, and * PAC1934_CH_VOLTAGE_AVERAGE,respectively PAC1934_CH_CURRENT * and PAC1934_CH_CURRENT_AVERAGE we need to use different * channel numbers. We will add +5 (+1 to maximum PAC channels).
*/
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].channel = cnt + 5;
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_index = cnt;
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].address = cnt + PAC1934_VBUS_AVG_1_ADDR;
ch_sp[PAC1934_CH_CURRENT_AVERAGE].channel = cnt + 5;
ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_index = cnt;
ch_sp[PAC1934_CH_CURRENT_AVERAGE].address = cnt + PAC1934_VSENSE_AVG_1_ADDR;
/* * now modify the parameters in all channels if the * whole chip rail(channel) is bi-directional
*/ if (info->bi_dir[cnt]) {
ch_sp[PAC1934_CH_ENERGY].scan_type.sign = 's';
ch_sp[PAC1934_CH_ENERGY].scan_type.realbits = 47;
ch_sp[PAC1934_CH_POWER].scan_type.sign = 's';
ch_sp[PAC1934_CH_POWER].scan_type.realbits = 27;
ch_sp[PAC1934_CH_VOLTAGE].scan_type.sign = 's';
ch_sp[PAC1934_CH_VOLTAGE].scan_type.realbits = 15;
ch_sp[PAC1934_CH_CURRENT].scan_type.sign = 's';
ch_sp[PAC1934_CH_CURRENT].scan_type.realbits = 15;
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.sign = 's';
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.realbits = 15;
ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.sign = 's';
ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.realbits = 15;
}
tmp_data += sizeof(pac1934_single_channel);
}
/* * send the updated dynamic channel structure information towards IIO * prepare the required field for IIO class registration
*/
indio_dev->num_channels = attribute_count;
indio_dev->channels = (conststruct iio_chan_spec *)dyn_ch_struct;
indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); if (!indio_dev) return -ENOMEM;
info = iio_priv(indio_dev);
info->client = client;
/* always start with energy accumulation enabled */ for (cnt = 0; cnt < PAC1934_MAX_NUM_CHANNELS; cnt++)
info->enable_energy[cnt] = true;
ret = pac1934_chip_identify(info); if (ret < 0) { /* * If failed to identify the hardware based on internal * registers, try using fallback compatible in device tree * to deal with some newer part number.
*/
chip = i2c_get_match_data(client); if (!chip) return -EINVAL;
if (is_acpi_device_node(dev_fwnode(dev)))
ret = pac1934_acpi_parse_channel_config(client, info); else /* * This makes it possible to use also ACPI PRP0001 for * registering the device using device tree properties.
*/
ret = pac1934_fw_parse_channel_config(client, info);
if (ret) return dev_err_probe(dev, ret, "parameter parsing returned an error\n");
mutex_init(&info->lock);
ret = devm_add_action_or_reset(dev, pac1934_mutex_destroy,
&info->lock); if (ret < 0) return ret;
/* * do now any chip specific initialization (e.g. read/write * some registers), enable/disable certain channels, change the sampling * rate to the requested value
*/
ret = pac1934_chip_configure(info); if (ret < 0) return ret;
/* prepare the channel information */
ret = pac1934_prep_iio_channels(info, indio_dev); if (ret < 0) return ret;
ret = pac1934_prep_custom_attributes(info, indio_dev); if (ret < 0) return dev_err_probe(dev, ret, "Can't configure custom attributes for PAC1934 device\n");
/* * read whatever has been accumulated in the chip so far * and reset the accumulators
*/
ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR,
PAC1934_MIN_UPDATE_WAIT_TIME_US); if (ret < 0) return ret;
ret = devm_iio_device_register(dev, indio_dev); if (ret < 0) return dev_err_probe(dev, ret, "Can't register IIO device\n");
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.