// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020 MaxLinear, Inc. * * This driver is a hardware monitoring driver for PVT controller * (MR75203) which is used to configure & control Moortec embedded * analog IP to enable multiple embedded temperature sensor(TS), * voltage monitor(VM) & process detector(PD) modules.
*/ #include <linux/bits.h> #include <linux/clk.h> #include <linux/debugfs.h> #include <linux/hwmon.h> #include <linux/kstrtox.h> #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/regmap.h> #include <linux/reset.h> #include <linux/slab.h> #include <linux/units.h>
/** * struct voltage_device - VM single input parameters. * @vm_map: Map channel number to VM index. * @ch_map: Map channel number to channel index. * @pre_scaler: Pre scaler value (1 or 2) used to normalize the voltage output * result. * * The structure provides mapping between channel-number (0..N-1) to VM-index * (0..num_vm-1) and channel-index (0..ch_num-1) where N = num_vm * ch_num. * It also provides normalization factor for the VM equation.
*/ struct voltage_device {
u32 vm_map;
u32 ch_map;
u32 pre_scaler;
};
/** * struct voltage_channels - VM channel count. * @total: Total number of channels in all VMs. * @max: Maximum number of channels among all VMs. * * The structure provides channel count information across all VMs.
*/ struct voltage_channels {
u32 total;
u8 max;
};
staticint pvt_read_temp(struct device *dev, u32 attr, int channel, long *val)
{ struct pvt_device *pvt = dev_get_drvdata(dev); struct regmap *t_map = pvt->t_map;
u32 stat, nbs; int ret;
switch (attr) { case hwmon_temp_input:
ret = regmap_read_poll_timeout(t_map, SDIF_DONE(channel),
stat, stat & SDIF_SMPL_DONE,
PVT_POLL_DELAY_US,
PVT_POLL_TIMEOUT_US); if (ret) return ret;
ret = regmap_read(t_map, SDIF_DATA(channel), &nbs); if (ret < 0) return ret;
nbs &= SAMPLE_DATA_MSK;
/* * Convert the register value to * degrees centigrade temperature
*/
*val = pvt_calc_temp(pvt, nbs);
return 0; default: return -EOPNOTSUPP;
}
}
staticint pvt_read_in(struct device *dev, u32 attr, int channel, long *val)
{ struct pvt_device *pvt = dev_get_drvdata(dev); struct regmap *v_map = pvt->v_map;
u32 n, stat, pre_scaler;
u8 vm_idx, ch_idx; int ret;
if (channel >= pvt->vm_channels.total) return -EINVAL;
switch (attr) { case hwmon_in_input:
ret = regmap_read_poll_timeout(v_map, VM_SDIF_DONE(vm_idx),
stat, stat & SDIF_SMPL_DONE,
PVT_POLL_DELAY_US,
PVT_POLL_TIMEOUT_US); if (ret) return ret;
ret = regmap_read(v_map, VM_SDIF_DATA(vm_idx, ch_idx), &n); if (ret < 0) return ret;
n &= SAMPLE_DATA_MSK;
pre_scaler = pvt->vd[channel].pre_scaler; /* * Convert the N bitstream count into voltage. * To support negative voltage calculation for 64bit machines * n must be cast to long, since n and *val differ both in * signedness and in size. * Division is used instead of right shift, because for signed * numbers, the sign bit is used to fill the vacated bit * positions, and if the number is negative, 1 is used. * BIT(x) may not be used instead of (1 << x) because it's * unsigned.
*/
*val = pre_scaler * (PVT_N_CONST * (long)n - PVT_R_CONST) /
(1 << PVT_CONV_BITS);
return 0; default: return -EOPNOTSUPP;
}
}
staticint pvt_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{ switch (type) { case hwmon_temp: return pvt_read_temp(dev, attr, channel, val); case hwmon_in: return pvt_read_in(dev, attr, channel, val); default: return -EOPNOTSUPP;
}
}
/* * The system supports 'clk_sys' to 'clk_ip' frequency ratios * from 2:1 to 512:1
*/
key = clamp_val(key, CLK_SYS_CYCLES_MIN, CLK_SYS_CYCLES_MAX) - 2;
ret = device_property_read_u8_array(dev, "moortec,vm-active-channels",
vm_active_ch, vm_num); if (ret) { /* * Incase "moortec,vm-active-channels" property is not defined, * we assume each VM sensor has all of its channels active.
*/
memset(vm_active_ch, ch_num, vm_num);
pvt->vm_channels.max = ch_num;
pvt->vm_channels.total = ch_num * vm_num;
} else { for (i = 0; i < vm_num; i++) { if (vm_active_ch[i] > ch_num) {
dev_err(dev, "invalid active channels: %u\n",
vm_active_ch[i]); return -EINVAL;
}
pvt->vm_channels.total += vm_active_ch[i];
if (vm_active_ch[i] > pvt->vm_channels.max)
pvt->vm_channels.max = vm_active_ch[i];
}
}
/* * Map between the channel-number to VM-index and channel-index. * Example - 3 VMs, "moortec,vm_active_ch" = <5 2 4>: * vm_map = [0 0 0 0 0 1 1 2 2 2 2] * ch_map = [0 1 2 3 4 0 1 0 1 2 3]
*/
pvt->vd = devm_kcalloc(dev, pvt->vm_channels.total, sizeof(*pvt->vd),
GFP_KERNEL); if (!pvt->vd) return -ENOMEM;
k = 0; for (i = 0; i < vm_num; i++) { for (j = 0; j < vm_active_ch[i]; j++) {
pvt->vd[k].vm_map = vm_idx[i];
pvt->vd[k].ch_map = j;
k++;
}
}
return 0;
}
staticint pvt_get_pre_scaler(struct device *dev, struct pvt_device *pvt)
{
u8 *pre_scaler_ch_list; int i, ret, num_ch;
u32 channel;
/* Set default pre-scaler value to be 1. */ for (i = 0; i < pvt->vm_channels.total; i++)
pvt->vd[i].pre_scaler = PRE_SCALER_X1;
/* Get number of channels configured in "moortec,vm-pre-scaler-x2". */
num_ch = device_property_count_u8(dev, "moortec,vm-pre-scaler-x2"); if (num_ch <= 0) return 0;
pre_scaler_ch_list = kcalloc(num_ch, sizeof(*pre_scaler_ch_list),
GFP_KERNEL); if (!pre_scaler_ch_list) return -ENOMEM;
/* Get list of all channels that have pre-scaler of 2. */
ret = device_property_read_u8_array(dev, "moortec,vm-pre-scaler-x2",
pre_scaler_ch_list, num_ch); if (ret) goto out;
for (i = 0; i < num_ch; i++) {
channel = pre_scaler_ch_list[i];
pvt->vd[channel].pre_scaler = PRE_SCALER_X2;
}
/* Incase ts-series property is not defined, use default 5. */
ret = device_property_read_u32(dev, "moortec,ts-series", &series); if (ret)
series = TEMPERATURE_SENSOR_SERIES_5;
pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL); if (!pvt) return -ENOMEM;
ret = pvt_get_regmap(pdev, "common", pvt); if (ret) return ret;
pvt->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(pvt->clk)) return dev_err_probe(dev, PTR_ERR(pvt->clk), "failed to get clock\n");
pvt->rst = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(pvt->rst)) return dev_err_probe(dev, PTR_ERR(pvt->rst), "failed to get reset control\n");
if (pvt->rst) {
ret = pvt_reset_control_deassert(dev, pvt); if (ret) return dev_err_probe(dev, ret, "cannot deassert reset control\n");
}
ret = regmap_read(pvt->c_map, PVT_IP_CONFIG, &val); if (ret < 0) 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.