138416c28SMatti Vaittinen // SPDX-License-Identifier: GPL-2.0-only 238416c28SMatti Vaittinen /* gain-time-scale conversion helpers for IIO light sensors 338416c28SMatti Vaittinen * 438416c28SMatti Vaittinen * Copyright (c) 2023 Matti Vaittinen <mazziesaccount@gmail.com> 538416c28SMatti Vaittinen */ 638416c28SMatti Vaittinen 738416c28SMatti Vaittinen #include <linux/device.h> 838416c28SMatti Vaittinen #include <linux/errno.h> 938416c28SMatti Vaittinen #include <linux/export.h> 1038416c28SMatti Vaittinen #include <linux/minmax.h> 1138416c28SMatti Vaittinen #include <linux/module.h> 1238416c28SMatti Vaittinen #include <linux/overflow.h> 1338416c28SMatti Vaittinen #include <linux/slab.h> 1438416c28SMatti Vaittinen #include <linux/sort.h> 1538416c28SMatti Vaittinen #include <linux/types.h> 1638416c28SMatti Vaittinen #include <linux/units.h> 1738416c28SMatti Vaittinen 1838416c28SMatti Vaittinen #include <linux/iio/iio-gts-helper.h> 1938416c28SMatti Vaittinen #include <linux/iio/types.h> 2038416c28SMatti Vaittinen 2138416c28SMatti Vaittinen /** 2238416c28SMatti Vaittinen * iio_gts_get_gain - Convert scale to total gain 2338416c28SMatti Vaittinen * 2438416c28SMatti Vaittinen * Internal helper for converting scale to total gain. 2538416c28SMatti Vaittinen * 2638416c28SMatti Vaittinen * @max: Maximum linearized scale. As an example, when scale is created 2738416c28SMatti Vaittinen * in magnitude of NANOs and max scale is 64.1 - The linearized 2838416c28SMatti Vaittinen * scale is 64 100 000 000. 2938416c28SMatti Vaittinen * @scale: Linearized scale to compute the gain for. 3038416c28SMatti Vaittinen * 3138416c28SMatti Vaittinen * Return: (floored) gain corresponding to the scale. -EINVAL if scale 3238416c28SMatti Vaittinen * is invalid. 3338416c28SMatti Vaittinen */ 3438416c28SMatti Vaittinen static int iio_gts_get_gain(const u64 max, const u64 scale) 3538416c28SMatti Vaittinen { 3638416c28SMatti Vaittinen u64 full = max; 3738416c28SMatti Vaittinen 3838416c28SMatti Vaittinen if (scale > full || !scale) 3938416c28SMatti Vaittinen return -EINVAL; 4038416c28SMatti Vaittinen 41bb76cc45SMatti Vaittinen return div64_u64(full, scale); 4238416c28SMatti Vaittinen } 4338416c28SMatti Vaittinen 4438416c28SMatti Vaittinen /** 4538416c28SMatti Vaittinen * gain_get_scale_fraction - get the gain or time based on scale and known one 4638416c28SMatti Vaittinen * 4738416c28SMatti Vaittinen * @max: Maximum linearized scale. As an example, when scale is created 4838416c28SMatti Vaittinen * in magnitude of NANOs and max scale is 64.1 - The linearized 4938416c28SMatti Vaittinen * scale is 64 100 000 000. 5038416c28SMatti Vaittinen * @scale: Linearized scale to compute the gain/time for. 5138416c28SMatti Vaittinen * @known: Either integration time or gain depending on which one is known 5238416c28SMatti Vaittinen * @unknown: Pointer to variable where the computed gain/time is stored 5338416c28SMatti Vaittinen * 5438416c28SMatti Vaittinen * Internal helper for computing unknown fraction of total gain. 5538416c28SMatti Vaittinen * Compute either gain or time based on scale and either the gain or time 5638416c28SMatti Vaittinen * depending on which one is known. 5738416c28SMatti Vaittinen * 5838416c28SMatti Vaittinen * Return: 0 on success. 5938416c28SMatti Vaittinen */ 6038416c28SMatti Vaittinen static int gain_get_scale_fraction(const u64 max, u64 scale, int known, 6138416c28SMatti Vaittinen int *unknown) 6238416c28SMatti Vaittinen { 6338416c28SMatti Vaittinen int tot_gain; 6438416c28SMatti Vaittinen 6538416c28SMatti Vaittinen tot_gain = iio_gts_get_gain(max, scale); 6638416c28SMatti Vaittinen if (tot_gain < 0) 6738416c28SMatti Vaittinen return tot_gain; 6838416c28SMatti Vaittinen 6938416c28SMatti Vaittinen *unknown = tot_gain / known; 7038416c28SMatti Vaittinen 7138416c28SMatti Vaittinen /* We require total gain to be exact multiple of known * unknown */ 7238416c28SMatti Vaittinen if (!*unknown || *unknown * known != tot_gain) 7338416c28SMatti Vaittinen return -EINVAL; 7438416c28SMatti Vaittinen 7538416c28SMatti Vaittinen return 0; 7638416c28SMatti Vaittinen } 7738416c28SMatti Vaittinen 7838416c28SMatti Vaittinen static int iio_gts_delinearize(u64 lin_scale, unsigned long scaler, 7938416c28SMatti Vaittinen int *scale_whole, int *scale_nano) 8038416c28SMatti Vaittinen { 8138416c28SMatti Vaittinen int frac; 8238416c28SMatti Vaittinen 8338416c28SMatti Vaittinen if (scaler > NANO) 8438416c28SMatti Vaittinen return -EOVERFLOW; 8538416c28SMatti Vaittinen 8638416c28SMatti Vaittinen if (!scaler) 8738416c28SMatti Vaittinen return -EINVAL; 8838416c28SMatti Vaittinen 8938416c28SMatti Vaittinen frac = do_div(lin_scale, scaler); 9038416c28SMatti Vaittinen 9138416c28SMatti Vaittinen *scale_whole = lin_scale; 9238416c28SMatti Vaittinen *scale_nano = frac * (NANO / scaler); 9338416c28SMatti Vaittinen 9438416c28SMatti Vaittinen return 0; 9538416c28SMatti Vaittinen } 9638416c28SMatti Vaittinen 9738416c28SMatti Vaittinen static int iio_gts_linearize(int scale_whole, int scale_nano, 9838416c28SMatti Vaittinen unsigned long scaler, u64 *lin_scale) 9938416c28SMatti Vaittinen { 10038416c28SMatti Vaittinen /* 10138416c28SMatti Vaittinen * Expect scale to be (mostly) NANO or MICRO. Divide divider instead of 10238416c28SMatti Vaittinen * multiplication followed by division to avoid overflow. 10338416c28SMatti Vaittinen */ 10438416c28SMatti Vaittinen if (scaler > NANO || !scaler) 10538416c28SMatti Vaittinen return -EINVAL; 10638416c28SMatti Vaittinen 10738416c28SMatti Vaittinen *lin_scale = (u64)scale_whole * (u64)scaler + 10838416c28SMatti Vaittinen (u64)(scale_nano / (NANO / scaler)); 10938416c28SMatti Vaittinen 11038416c28SMatti Vaittinen return 0; 11138416c28SMatti Vaittinen } 11238416c28SMatti Vaittinen 11338416c28SMatti Vaittinen /** 11438416c28SMatti Vaittinen * iio_gts_total_gain_to_scale - convert gain to scale 11538416c28SMatti Vaittinen * @gts: Gain time scale descriptor 11638416c28SMatti Vaittinen * @total_gain: the gain to be converted 11738416c28SMatti Vaittinen * @scale_int: Pointer to integral part of the scale (typically val1) 11838416c28SMatti Vaittinen * @scale_nano: Pointer to fractional part of the scale (nano or ppb) 11938416c28SMatti Vaittinen * 12038416c28SMatti Vaittinen * Convert the total gain value to scale. NOTE: This does not separate gain 12138416c28SMatti Vaittinen * generated by HW-gain or integration time. It is up to caller to decide what 12238416c28SMatti Vaittinen * part of the total gain is due to integration time and what due to HW-gain. 12338416c28SMatti Vaittinen * 12438416c28SMatti Vaittinen * Return: 0 on success. Negative errno on failure. 12538416c28SMatti Vaittinen */ 12638416c28SMatti Vaittinen int iio_gts_total_gain_to_scale(struct iio_gts *gts, int total_gain, 12738416c28SMatti Vaittinen int *scale_int, int *scale_nano) 12838416c28SMatti Vaittinen { 12938416c28SMatti Vaittinen u64 tmp; 13038416c28SMatti Vaittinen 13138416c28SMatti Vaittinen tmp = gts->max_scale; 13238416c28SMatti Vaittinen 13338416c28SMatti Vaittinen do_div(tmp, total_gain); 13438416c28SMatti Vaittinen 13538416c28SMatti Vaittinen return iio_gts_delinearize(tmp, NANO, scale_int, scale_nano); 13638416c28SMatti Vaittinen } 13738416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_total_gain_to_scale, IIO_GTS_HELPER); 13838416c28SMatti Vaittinen 13938416c28SMatti Vaittinen /** 14038416c28SMatti Vaittinen * iio_gts_purge_avail_scale_table - free-up the available scale tables 14138416c28SMatti Vaittinen * @gts: Gain time scale descriptor 14238416c28SMatti Vaittinen * 14338416c28SMatti Vaittinen * Free the space reserved by iio_gts_build_avail_scale_table(). 14438416c28SMatti Vaittinen */ 14538416c28SMatti Vaittinen static void iio_gts_purge_avail_scale_table(struct iio_gts *gts) 14638416c28SMatti Vaittinen { 14738416c28SMatti Vaittinen int i; 14838416c28SMatti Vaittinen 14938416c28SMatti Vaittinen if (gts->per_time_avail_scale_tables) { 15038416c28SMatti Vaittinen for (i = 0; i < gts->num_itime; i++) 15138416c28SMatti Vaittinen kfree(gts->per_time_avail_scale_tables[i]); 15238416c28SMatti Vaittinen 15338416c28SMatti Vaittinen kfree(gts->per_time_avail_scale_tables); 15438416c28SMatti Vaittinen gts->per_time_avail_scale_tables = NULL; 15538416c28SMatti Vaittinen } 15638416c28SMatti Vaittinen 15738416c28SMatti Vaittinen kfree(gts->avail_all_scales_table); 15838416c28SMatti Vaittinen gts->avail_all_scales_table = NULL; 15938416c28SMatti Vaittinen 16038416c28SMatti Vaittinen gts->num_avail_all_scales = 0; 16138416c28SMatti Vaittinen } 16238416c28SMatti Vaittinen 16338416c28SMatti Vaittinen static int iio_gts_gain_cmp(const void *a, const void *b) 16438416c28SMatti Vaittinen { 16538416c28SMatti Vaittinen return *(int *)a - *(int *)b; 16638416c28SMatti Vaittinen } 16738416c28SMatti Vaittinen 16838416c28SMatti Vaittinen static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales) 16938416c28SMatti Vaittinen { 17038416c28SMatti Vaittinen int ret, i, j, new_idx, time_idx; 17138416c28SMatti Vaittinen int *all_gains; 17238416c28SMatti Vaittinen size_t gain_bytes; 17338416c28SMatti Vaittinen 17438416c28SMatti Vaittinen for (i = 0; i < gts->num_itime; i++) { 17538416c28SMatti Vaittinen /* 17638416c28SMatti Vaittinen * Sort the tables for nice output and for easier finding of 17738416c28SMatti Vaittinen * unique values. 17838416c28SMatti Vaittinen */ 17938416c28SMatti Vaittinen sort(gains[i], gts->num_hwgain, sizeof(int), iio_gts_gain_cmp, 18038416c28SMatti Vaittinen NULL); 18138416c28SMatti Vaittinen 18238416c28SMatti Vaittinen /* Convert gains to scales */ 18338416c28SMatti Vaittinen for (j = 0; j < gts->num_hwgain; j++) { 18438416c28SMatti Vaittinen ret = iio_gts_total_gain_to_scale(gts, gains[i][j], 18538416c28SMatti Vaittinen &scales[i][2 * j], 18638416c28SMatti Vaittinen &scales[i][2 * j + 1]); 18738416c28SMatti Vaittinen if (ret) 18838416c28SMatti Vaittinen return ret; 18938416c28SMatti Vaittinen } 19038416c28SMatti Vaittinen } 19138416c28SMatti Vaittinen 19238416c28SMatti Vaittinen gain_bytes = array_size(gts->num_hwgain, sizeof(int)); 19338416c28SMatti Vaittinen all_gains = kcalloc(gts->num_itime, gain_bytes, GFP_KERNEL); 19438416c28SMatti Vaittinen if (!all_gains) 19538416c28SMatti Vaittinen return -ENOMEM; 19638416c28SMatti Vaittinen 19738416c28SMatti Vaittinen /* 19838416c28SMatti Vaittinen * We assume all the gains for same integration time were unique. 19938416c28SMatti Vaittinen * It is likely the first time table had greatest time multiplier as 20038416c28SMatti Vaittinen * the times are in the order of preference and greater times are 20138416c28SMatti Vaittinen * usually preferred. Hence we start from the last table which is likely 20238416c28SMatti Vaittinen * to have the smallest total gains. 20338416c28SMatti Vaittinen */ 20438416c28SMatti Vaittinen time_idx = gts->num_itime - 1; 20538416c28SMatti Vaittinen memcpy(all_gains, gains[time_idx], gain_bytes); 20638416c28SMatti Vaittinen new_idx = gts->num_hwgain; 20738416c28SMatti Vaittinen 20838416c28SMatti Vaittinen while (time_idx--) { 20938416c28SMatti Vaittinen for (j = 0; j < gts->num_hwgain; j++) { 21038416c28SMatti Vaittinen int candidate = gains[time_idx][j]; 21138416c28SMatti Vaittinen int chk; 21238416c28SMatti Vaittinen 21338416c28SMatti Vaittinen if (candidate > all_gains[new_idx - 1]) { 21438416c28SMatti Vaittinen all_gains[new_idx] = candidate; 21538416c28SMatti Vaittinen new_idx++; 21638416c28SMatti Vaittinen 21738416c28SMatti Vaittinen continue; 21838416c28SMatti Vaittinen } 21938416c28SMatti Vaittinen for (chk = 0; chk < new_idx; chk++) 22038416c28SMatti Vaittinen if (candidate <= all_gains[chk]) 22138416c28SMatti Vaittinen break; 22238416c28SMatti Vaittinen 22338416c28SMatti Vaittinen if (candidate == all_gains[chk]) 22438416c28SMatti Vaittinen continue; 22538416c28SMatti Vaittinen 22638416c28SMatti Vaittinen memmove(&all_gains[chk + 1], &all_gains[chk], 22738416c28SMatti Vaittinen (new_idx - chk) * sizeof(int)); 22838416c28SMatti Vaittinen all_gains[chk] = candidate; 22938416c28SMatti Vaittinen new_idx++; 23038416c28SMatti Vaittinen } 23138416c28SMatti Vaittinen } 23238416c28SMatti Vaittinen 23338416c28SMatti Vaittinen gts->avail_all_scales_table = kcalloc(new_idx, 2 * sizeof(int), 23438416c28SMatti Vaittinen GFP_KERNEL); 23538416c28SMatti Vaittinen if (!gts->avail_all_scales_table) { 23638416c28SMatti Vaittinen ret = -ENOMEM; 23738416c28SMatti Vaittinen goto free_out; 23838416c28SMatti Vaittinen } 23938416c28SMatti Vaittinen gts->num_avail_all_scales = new_idx; 24038416c28SMatti Vaittinen 24138416c28SMatti Vaittinen for (i = 0; i < gts->num_avail_all_scales; i++) { 24238416c28SMatti Vaittinen ret = iio_gts_total_gain_to_scale(gts, all_gains[i], 24338416c28SMatti Vaittinen >s->avail_all_scales_table[i * 2], 24438416c28SMatti Vaittinen >s->avail_all_scales_table[i * 2 + 1]); 24538416c28SMatti Vaittinen 24638416c28SMatti Vaittinen if (ret) { 24738416c28SMatti Vaittinen kfree(gts->avail_all_scales_table); 24838416c28SMatti Vaittinen gts->num_avail_all_scales = 0; 24938416c28SMatti Vaittinen goto free_out; 25038416c28SMatti Vaittinen } 25138416c28SMatti Vaittinen } 25238416c28SMatti Vaittinen 25338416c28SMatti Vaittinen free_out: 25438416c28SMatti Vaittinen kfree(all_gains); 25538416c28SMatti Vaittinen 25638416c28SMatti Vaittinen return ret; 25738416c28SMatti Vaittinen } 25838416c28SMatti Vaittinen 25938416c28SMatti Vaittinen /** 26038416c28SMatti Vaittinen * iio_gts_build_avail_scale_table - create tables of available scales 26138416c28SMatti Vaittinen * @gts: Gain time scale descriptor 26238416c28SMatti Vaittinen * 26338416c28SMatti Vaittinen * Build the tables which can represent the available scales based on the 26438416c28SMatti Vaittinen * originally given gain and time tables. When both time and gain tables are 26538416c28SMatti Vaittinen * given this results: 26638416c28SMatti Vaittinen * 1. A set of tables representing available scales for each supported 26738416c28SMatti Vaittinen * integration time. 26838416c28SMatti Vaittinen * 2. A single table listing all the unique scales that any combination of 26938416c28SMatti Vaittinen * supported gains and times can provide. 27038416c28SMatti Vaittinen * 27138416c28SMatti Vaittinen * NOTE: Space allocated for the tables must be freed using 27238416c28SMatti Vaittinen * iio_gts_purge_avail_scale_table() when the tables are no longer needed. 27338416c28SMatti Vaittinen * 27438416c28SMatti Vaittinen * Return: 0 on success. 27538416c28SMatti Vaittinen */ 27638416c28SMatti Vaittinen static int iio_gts_build_avail_scale_table(struct iio_gts *gts) 27738416c28SMatti Vaittinen { 27838416c28SMatti Vaittinen int **per_time_gains, **per_time_scales, i, j, ret = -ENOMEM; 27938416c28SMatti Vaittinen 28038416c28SMatti Vaittinen per_time_gains = kcalloc(gts->num_itime, sizeof(*per_time_gains), GFP_KERNEL); 28138416c28SMatti Vaittinen if (!per_time_gains) 28238416c28SMatti Vaittinen return ret; 28338416c28SMatti Vaittinen 28438416c28SMatti Vaittinen per_time_scales = kcalloc(gts->num_itime, sizeof(*per_time_scales), GFP_KERNEL); 28538416c28SMatti Vaittinen if (!per_time_scales) 28638416c28SMatti Vaittinen goto free_gains; 28738416c28SMatti Vaittinen 28838416c28SMatti Vaittinen for (i = 0; i < gts->num_itime; i++) { 28938416c28SMatti Vaittinen per_time_scales[i] = kcalloc(gts->num_hwgain, 2 * sizeof(int), 29038416c28SMatti Vaittinen GFP_KERNEL); 29138416c28SMatti Vaittinen if (!per_time_scales[i]) 29238416c28SMatti Vaittinen goto err_free_out; 29338416c28SMatti Vaittinen 29438416c28SMatti Vaittinen per_time_gains[i] = kcalloc(gts->num_hwgain, sizeof(int), 29538416c28SMatti Vaittinen GFP_KERNEL); 29638416c28SMatti Vaittinen if (!per_time_gains[i]) { 29738416c28SMatti Vaittinen kfree(per_time_scales[i]); 29838416c28SMatti Vaittinen goto err_free_out; 29938416c28SMatti Vaittinen } 30038416c28SMatti Vaittinen 30138416c28SMatti Vaittinen for (j = 0; j < gts->num_hwgain; j++) 30238416c28SMatti Vaittinen per_time_gains[i][j] = gts->hwgain_table[j].gain * 30338416c28SMatti Vaittinen gts->itime_table[i].mul; 30438416c28SMatti Vaittinen } 30538416c28SMatti Vaittinen 30638416c28SMatti Vaittinen ret = gain_to_scaletables(gts, per_time_gains, per_time_scales); 30738416c28SMatti Vaittinen if (ret) 30838416c28SMatti Vaittinen goto err_free_out; 30938416c28SMatti Vaittinen 31038416c28SMatti Vaittinen kfree(per_time_gains); 31138416c28SMatti Vaittinen gts->per_time_avail_scale_tables = per_time_scales; 31238416c28SMatti Vaittinen 31338416c28SMatti Vaittinen return 0; 31438416c28SMatti Vaittinen 31538416c28SMatti Vaittinen err_free_out: 31638416c28SMatti Vaittinen for (i--; i; i--) { 31738416c28SMatti Vaittinen kfree(per_time_scales[i]); 31838416c28SMatti Vaittinen kfree(per_time_gains[i]); 31938416c28SMatti Vaittinen } 32038416c28SMatti Vaittinen kfree(per_time_scales); 32138416c28SMatti Vaittinen free_gains: 32238416c28SMatti Vaittinen kfree(per_time_gains); 32338416c28SMatti Vaittinen 32438416c28SMatti Vaittinen return ret; 32538416c28SMatti Vaittinen } 32638416c28SMatti Vaittinen 327e6506513SMatti Vaittinen static void iio_gts_us_to_int_micro(int *time_us, int *int_micro_times, 328e6506513SMatti Vaittinen int num_times) 329e6506513SMatti Vaittinen { 330e6506513SMatti Vaittinen int i; 331e6506513SMatti Vaittinen 332e6506513SMatti Vaittinen for (i = 0; i < num_times; i++) { 333e6506513SMatti Vaittinen int_micro_times[i * 2] = time_us[i] / 1000000; 334e6506513SMatti Vaittinen int_micro_times[i * 2 + 1] = time_us[i] % 1000000; 335e6506513SMatti Vaittinen } 336e6506513SMatti Vaittinen } 337e6506513SMatti Vaittinen 33838416c28SMatti Vaittinen /** 33938416c28SMatti Vaittinen * iio_gts_build_avail_time_table - build table of available integration times 34038416c28SMatti Vaittinen * @gts: Gain time scale descriptor 34138416c28SMatti Vaittinen * 34238416c28SMatti Vaittinen * Build the table which can represent the available times to be returned 34338416c28SMatti Vaittinen * to users using the read_avail-callback. 34438416c28SMatti Vaittinen * 34538416c28SMatti Vaittinen * NOTE: Space allocated for the tables must be freed using 34638416c28SMatti Vaittinen * iio_gts_purge_avail_time_table() when the tables are no longer needed. 34738416c28SMatti Vaittinen * 34838416c28SMatti Vaittinen * Return: 0 on success. 34938416c28SMatti Vaittinen */ 35038416c28SMatti Vaittinen static int iio_gts_build_avail_time_table(struct iio_gts *gts) 35138416c28SMatti Vaittinen { 352e6506513SMatti Vaittinen int *times, i, j, idx = 0, *int_micro_times; 35338416c28SMatti Vaittinen 35438416c28SMatti Vaittinen if (!gts->num_itime) 35538416c28SMatti Vaittinen return 0; 35638416c28SMatti Vaittinen 35738416c28SMatti Vaittinen times = kcalloc(gts->num_itime, sizeof(int), GFP_KERNEL); 35838416c28SMatti Vaittinen if (!times) 35938416c28SMatti Vaittinen return -ENOMEM; 36038416c28SMatti Vaittinen 36138416c28SMatti Vaittinen /* Sort times from all tables to one and remove duplicates */ 36238416c28SMatti Vaittinen for (i = gts->num_itime - 1; i >= 0; i--) { 36338416c28SMatti Vaittinen int new = gts->itime_table[i].time_us; 36438416c28SMatti Vaittinen 365*5acc3f97SChenyuan Yang if (idx == 0 || times[idx - 1] < new) { 36638416c28SMatti Vaittinen times[idx++] = new; 36738416c28SMatti Vaittinen continue; 36838416c28SMatti Vaittinen } 36938416c28SMatti Vaittinen 370*5acc3f97SChenyuan Yang for (j = 0; j < idx; j++) { 371*5acc3f97SChenyuan Yang if (times[j] == new) 372*5acc3f97SChenyuan Yang break; 37338416c28SMatti Vaittinen if (times[j] > new) { 37438416c28SMatti Vaittinen memmove(×[j + 1], ×[j], 37538416c28SMatti Vaittinen (idx - j) * sizeof(int)); 37638416c28SMatti Vaittinen times[j] = new; 37738416c28SMatti Vaittinen idx++; 378*5acc3f97SChenyuan Yang break; 37938416c28SMatti Vaittinen } 38038416c28SMatti Vaittinen } 38138416c28SMatti Vaittinen } 382e6506513SMatti Vaittinen 383e6506513SMatti Vaittinen /* create a list of times formatted as list of IIO_VAL_INT_PLUS_MICRO */ 384e6506513SMatti Vaittinen int_micro_times = kcalloc(idx, sizeof(int) * 2, GFP_KERNEL); 385e6506513SMatti Vaittinen if (int_micro_times) { 38638416c28SMatti Vaittinen /* 387e6506513SMatti Vaittinen * This is just to survive a unlikely corner-case where times in 388e6506513SMatti Vaittinen * the given time table were not unique. Else we could just 389e6506513SMatti Vaittinen * trust the gts->num_itime. 39038416c28SMatti Vaittinen */ 39138416c28SMatti Vaittinen gts->num_avail_time_tables = idx; 392e6506513SMatti Vaittinen iio_gts_us_to_int_micro(times, int_micro_times, idx); 393e6506513SMatti Vaittinen } 394e6506513SMatti Vaittinen 395e6506513SMatti Vaittinen gts->avail_time_tables = int_micro_times; 396e6506513SMatti Vaittinen kfree(times); 397e6506513SMatti Vaittinen 398e6506513SMatti Vaittinen if (!int_micro_times) 399e6506513SMatti Vaittinen return -ENOMEM; 40038416c28SMatti Vaittinen 40138416c28SMatti Vaittinen return 0; 40238416c28SMatti Vaittinen } 40338416c28SMatti Vaittinen 40438416c28SMatti Vaittinen /** 40538416c28SMatti Vaittinen * iio_gts_purge_avail_time_table - free-up the available integration time table 40638416c28SMatti Vaittinen * @gts: Gain time scale descriptor 40738416c28SMatti Vaittinen * 40838416c28SMatti Vaittinen * Free the space reserved by iio_gts_build_avail_time_table(). 40938416c28SMatti Vaittinen */ 41038416c28SMatti Vaittinen static void iio_gts_purge_avail_time_table(struct iio_gts *gts) 41138416c28SMatti Vaittinen { 41238416c28SMatti Vaittinen if (gts->num_avail_time_tables) { 41338416c28SMatti Vaittinen kfree(gts->avail_time_tables); 41438416c28SMatti Vaittinen gts->avail_time_tables = NULL; 41538416c28SMatti Vaittinen gts->num_avail_time_tables = 0; 41638416c28SMatti Vaittinen } 41738416c28SMatti Vaittinen } 41838416c28SMatti Vaittinen 41938416c28SMatti Vaittinen /** 42038416c28SMatti Vaittinen * iio_gts_build_avail_tables - create tables of available scales and int times 42138416c28SMatti Vaittinen * @gts: Gain time scale descriptor 42238416c28SMatti Vaittinen * 42338416c28SMatti Vaittinen * Build the tables which can represent the available scales and available 42438416c28SMatti Vaittinen * integration times. Availability tables are built based on the originally 42538416c28SMatti Vaittinen * given gain and given time tables. 42638416c28SMatti Vaittinen * 42738416c28SMatti Vaittinen * When both time and gain tables are 42838416c28SMatti Vaittinen * given this results: 42938416c28SMatti Vaittinen * 1. A set of sorted tables representing available scales for each supported 43038416c28SMatti Vaittinen * integration time. 43138416c28SMatti Vaittinen * 2. A single sorted table listing all the unique scales that any combination 43238416c28SMatti Vaittinen * of supported gains and times can provide. 43338416c28SMatti Vaittinen * 3. A sorted table of supported integration times 43438416c28SMatti Vaittinen * 43538416c28SMatti Vaittinen * After these tables are built one can use the iio_gts_all_avail_scales(), 43638416c28SMatti Vaittinen * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to 43738416c28SMatti Vaittinen * implement the read_avail operations. 43838416c28SMatti Vaittinen * 43938416c28SMatti Vaittinen * NOTE: Space allocated for the tables must be freed using 44038416c28SMatti Vaittinen * iio_gts_purge_avail_tables() when the tables are no longer needed. 44138416c28SMatti Vaittinen * 44238416c28SMatti Vaittinen * Return: 0 on success. 44338416c28SMatti Vaittinen */ 44438416c28SMatti Vaittinen static int iio_gts_build_avail_tables(struct iio_gts *gts) 44538416c28SMatti Vaittinen { 44638416c28SMatti Vaittinen int ret; 44738416c28SMatti Vaittinen 44838416c28SMatti Vaittinen ret = iio_gts_build_avail_scale_table(gts); 44938416c28SMatti Vaittinen if (ret) 45038416c28SMatti Vaittinen return ret; 45138416c28SMatti Vaittinen 45238416c28SMatti Vaittinen ret = iio_gts_build_avail_time_table(gts); 45338416c28SMatti Vaittinen if (ret) 45438416c28SMatti Vaittinen iio_gts_purge_avail_scale_table(gts); 45538416c28SMatti Vaittinen 45638416c28SMatti Vaittinen return ret; 45738416c28SMatti Vaittinen } 45838416c28SMatti Vaittinen 45938416c28SMatti Vaittinen /** 46038416c28SMatti Vaittinen * iio_gts_purge_avail_tables - free-up the availability tables 46138416c28SMatti Vaittinen * @gts: Gain time scale descriptor 46238416c28SMatti Vaittinen * 46338416c28SMatti Vaittinen * Free the space reserved by iio_gts_build_avail_tables(). Frees both the 46438416c28SMatti Vaittinen * integration time and scale tables. 46538416c28SMatti Vaittinen */ 46638416c28SMatti Vaittinen static void iio_gts_purge_avail_tables(struct iio_gts *gts) 46738416c28SMatti Vaittinen { 46838416c28SMatti Vaittinen iio_gts_purge_avail_time_table(gts); 46938416c28SMatti Vaittinen iio_gts_purge_avail_scale_table(gts); 47038416c28SMatti Vaittinen } 47138416c28SMatti Vaittinen 47238416c28SMatti Vaittinen static void devm_iio_gts_avail_all_drop(void *res) 47338416c28SMatti Vaittinen { 47438416c28SMatti Vaittinen iio_gts_purge_avail_tables(res); 47538416c28SMatti Vaittinen } 47638416c28SMatti Vaittinen 47738416c28SMatti Vaittinen /** 47838416c28SMatti Vaittinen * devm_iio_gts_build_avail_tables - manged add availability tables 47938416c28SMatti Vaittinen * @dev: Pointer to the device whose lifetime tables are bound 48038416c28SMatti Vaittinen * @gts: Gain time scale descriptor 48138416c28SMatti Vaittinen * 48238416c28SMatti Vaittinen * Build the tables which can represent the available scales and available 48338416c28SMatti Vaittinen * integration times. Availability tables are built based on the originally 48438416c28SMatti Vaittinen * given gain and given time tables. 48538416c28SMatti Vaittinen * 48638416c28SMatti Vaittinen * When both time and gain tables are given this results: 48738416c28SMatti Vaittinen * 1. A set of sorted tables representing available scales for each supported 48838416c28SMatti Vaittinen * integration time. 48938416c28SMatti Vaittinen * 2. A single sorted table listing all the unique scales that any combination 49038416c28SMatti Vaittinen * of supported gains and times can provide. 49138416c28SMatti Vaittinen * 3. A sorted table of supported integration times 49238416c28SMatti Vaittinen * 49338416c28SMatti Vaittinen * After these tables are built one can use the iio_gts_all_avail_scales(), 49438416c28SMatti Vaittinen * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to 49538416c28SMatti Vaittinen * implement the read_avail operations. 49638416c28SMatti Vaittinen * 49738416c28SMatti Vaittinen * The tables are automatically released upon device detach. 49838416c28SMatti Vaittinen * 49938416c28SMatti Vaittinen * Return: 0 on success. 50038416c28SMatti Vaittinen */ 50138416c28SMatti Vaittinen static int devm_iio_gts_build_avail_tables(struct device *dev, 50238416c28SMatti Vaittinen struct iio_gts *gts) 50338416c28SMatti Vaittinen { 50438416c28SMatti Vaittinen int ret; 50538416c28SMatti Vaittinen 50638416c28SMatti Vaittinen ret = iio_gts_build_avail_tables(gts); 50738416c28SMatti Vaittinen if (ret) 50838416c28SMatti Vaittinen return ret; 50938416c28SMatti Vaittinen 51038416c28SMatti Vaittinen return devm_add_action_or_reset(dev, devm_iio_gts_avail_all_drop, gts); 51138416c28SMatti Vaittinen } 51238416c28SMatti Vaittinen 51338416c28SMatti Vaittinen static int sanity_check_time(const struct iio_itime_sel_mul *t) 51438416c28SMatti Vaittinen { 51538416c28SMatti Vaittinen if (t->sel < 0 || t->time_us < 0 || t->mul <= 0) 51638416c28SMatti Vaittinen return -EINVAL; 51738416c28SMatti Vaittinen 51838416c28SMatti Vaittinen return 0; 51938416c28SMatti Vaittinen } 52038416c28SMatti Vaittinen 52138416c28SMatti Vaittinen static int sanity_check_gain(const struct iio_gain_sel_pair *g) 52238416c28SMatti Vaittinen { 52338416c28SMatti Vaittinen if (g->sel < 0 || g->gain <= 0) 52438416c28SMatti Vaittinen return -EINVAL; 52538416c28SMatti Vaittinen 52638416c28SMatti Vaittinen return 0; 52738416c28SMatti Vaittinen } 52838416c28SMatti Vaittinen 52938416c28SMatti Vaittinen static int iio_gts_sanity_check(struct iio_gts *gts) 53038416c28SMatti Vaittinen { 53138416c28SMatti Vaittinen int g, t, ret; 53238416c28SMatti Vaittinen 53338416c28SMatti Vaittinen if (!gts->num_hwgain && !gts->num_itime) 53438416c28SMatti Vaittinen return -EINVAL; 53538416c28SMatti Vaittinen 53638416c28SMatti Vaittinen for (t = 0; t < gts->num_itime; t++) { 53738416c28SMatti Vaittinen ret = sanity_check_time(>s->itime_table[t]); 53838416c28SMatti Vaittinen if (ret) 53938416c28SMatti Vaittinen return ret; 54038416c28SMatti Vaittinen } 54138416c28SMatti Vaittinen 54238416c28SMatti Vaittinen for (g = 0; g < gts->num_hwgain; g++) { 54338416c28SMatti Vaittinen ret = sanity_check_gain(>s->hwgain_table[g]); 54438416c28SMatti Vaittinen if (ret) 54538416c28SMatti Vaittinen return ret; 54638416c28SMatti Vaittinen } 54738416c28SMatti Vaittinen 54838416c28SMatti Vaittinen for (g = 0; g < gts->num_hwgain; g++) { 54938416c28SMatti Vaittinen for (t = 0; t < gts->num_itime; t++) { 55038416c28SMatti Vaittinen int gain, mul, res; 55138416c28SMatti Vaittinen 55238416c28SMatti Vaittinen gain = gts->hwgain_table[g].gain; 55338416c28SMatti Vaittinen mul = gts->itime_table[t].mul; 55438416c28SMatti Vaittinen 55538416c28SMatti Vaittinen if (check_mul_overflow(gain, mul, &res)) 55638416c28SMatti Vaittinen return -EOVERFLOW; 55738416c28SMatti Vaittinen } 55838416c28SMatti Vaittinen } 55938416c28SMatti Vaittinen 56038416c28SMatti Vaittinen return 0; 56138416c28SMatti Vaittinen } 56238416c28SMatti Vaittinen 56338416c28SMatti Vaittinen static int iio_init_iio_gts(int max_scale_int, int max_scale_nano, 56438416c28SMatti Vaittinen const struct iio_gain_sel_pair *gain_tbl, int num_gain, 56538416c28SMatti Vaittinen const struct iio_itime_sel_mul *tim_tbl, int num_times, 56638416c28SMatti Vaittinen struct iio_gts *gts) 56738416c28SMatti Vaittinen { 56838416c28SMatti Vaittinen int ret; 56938416c28SMatti Vaittinen 57038416c28SMatti Vaittinen memset(gts, 0, sizeof(*gts)); 57138416c28SMatti Vaittinen 57238416c28SMatti Vaittinen ret = iio_gts_linearize(max_scale_int, max_scale_nano, NANO, 57338416c28SMatti Vaittinen >s->max_scale); 57438416c28SMatti Vaittinen if (ret) 57538416c28SMatti Vaittinen return ret; 57638416c28SMatti Vaittinen 57738416c28SMatti Vaittinen gts->hwgain_table = gain_tbl; 57838416c28SMatti Vaittinen gts->num_hwgain = num_gain; 57938416c28SMatti Vaittinen gts->itime_table = tim_tbl; 58038416c28SMatti Vaittinen gts->num_itime = num_times; 58138416c28SMatti Vaittinen 58238416c28SMatti Vaittinen return iio_gts_sanity_check(gts); 58338416c28SMatti Vaittinen } 58438416c28SMatti Vaittinen 58538416c28SMatti Vaittinen /** 58638416c28SMatti Vaittinen * devm_iio_init_iio_gts - Initialize the gain-time-scale helper 58738416c28SMatti Vaittinen * @dev: Pointer to the device whose lifetime gts resources are 58838416c28SMatti Vaittinen * bound 58938416c28SMatti Vaittinen * @max_scale_int: integer part of the maximum scale value 59038416c28SMatti Vaittinen * @max_scale_nano: fraction part of the maximum scale value 59138416c28SMatti Vaittinen * @gain_tbl: table describing supported gains 59238416c28SMatti Vaittinen * @num_gain: number of gains in the gain table 59338416c28SMatti Vaittinen * @tim_tbl: table describing supported integration times. Provide 59438416c28SMatti Vaittinen * the integration time table sorted so that the preferred 59538416c28SMatti Vaittinen * integration time is in the first array index. The search 59638416c28SMatti Vaittinen * functions like the 59738416c28SMatti Vaittinen * iio_gts_find_time_and_gain_sel_for_scale() start search 59838416c28SMatti Vaittinen * from first provided time. 59938416c28SMatti Vaittinen * @num_times: number of times in the time table 60038416c28SMatti Vaittinen * @gts: pointer to the helper struct 60138416c28SMatti Vaittinen * 60238416c28SMatti Vaittinen * Initialize the gain-time-scale helper for use. Note, gains, times, selectors 60338416c28SMatti Vaittinen * and multipliers must be positive. Negative values are reserved for error 60438416c28SMatti Vaittinen * checking. The total gain (maximum gain * maximum time multiplier) must not 60538416c28SMatti Vaittinen * overflow int. The allocated resources will be released upon device detach. 60638416c28SMatti Vaittinen * 60738416c28SMatti Vaittinen * Return: 0 on success. 60838416c28SMatti Vaittinen */ 60938416c28SMatti Vaittinen int devm_iio_init_iio_gts(struct device *dev, int max_scale_int, int max_scale_nano, 61038416c28SMatti Vaittinen const struct iio_gain_sel_pair *gain_tbl, int num_gain, 61138416c28SMatti Vaittinen const struct iio_itime_sel_mul *tim_tbl, int num_times, 61238416c28SMatti Vaittinen struct iio_gts *gts) 61338416c28SMatti Vaittinen { 61438416c28SMatti Vaittinen int ret; 61538416c28SMatti Vaittinen 61638416c28SMatti Vaittinen ret = iio_init_iio_gts(max_scale_int, max_scale_nano, gain_tbl, 61738416c28SMatti Vaittinen num_gain, tim_tbl, num_times, gts); 61838416c28SMatti Vaittinen if (ret) 61938416c28SMatti Vaittinen return ret; 62038416c28SMatti Vaittinen 62138416c28SMatti Vaittinen return devm_iio_gts_build_avail_tables(dev, gts); 62238416c28SMatti Vaittinen } 62338416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(devm_iio_init_iio_gts, IIO_GTS_HELPER); 62438416c28SMatti Vaittinen 62538416c28SMatti Vaittinen /** 62638416c28SMatti Vaittinen * iio_gts_all_avail_scales - helper for listing all available scales 62738416c28SMatti Vaittinen * @gts: Gain time scale descriptor 62838416c28SMatti Vaittinen * @vals: Returned array of supported scales 62938416c28SMatti Vaittinen * @type: Type of returned scale values 63038416c28SMatti Vaittinen * @length: Amount of returned values in array 63138416c28SMatti Vaittinen * 63238416c28SMatti Vaittinen * Return: a value suitable to be returned from read_avail or a negative error. 63338416c28SMatti Vaittinen */ 63438416c28SMatti Vaittinen int iio_gts_all_avail_scales(struct iio_gts *gts, const int **vals, int *type, 63538416c28SMatti Vaittinen int *length) 63638416c28SMatti Vaittinen { 63738416c28SMatti Vaittinen if (!gts->num_avail_all_scales) 63838416c28SMatti Vaittinen return -EINVAL; 63938416c28SMatti Vaittinen 64038416c28SMatti Vaittinen *vals = gts->avail_all_scales_table; 64138416c28SMatti Vaittinen *type = IIO_VAL_INT_PLUS_NANO; 64238416c28SMatti Vaittinen *length = gts->num_avail_all_scales * 2; 64338416c28SMatti Vaittinen 64438416c28SMatti Vaittinen return IIO_AVAIL_LIST; 64538416c28SMatti Vaittinen } 64638416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_all_avail_scales, IIO_GTS_HELPER); 64738416c28SMatti Vaittinen 64838416c28SMatti Vaittinen /** 64938416c28SMatti Vaittinen * iio_gts_avail_scales_for_time - list scales for integration time 65038416c28SMatti Vaittinen * @gts: Gain time scale descriptor 65138416c28SMatti Vaittinen * @time: Integration time for which the scales are listed 65238416c28SMatti Vaittinen * @vals: Returned array of supported scales 65338416c28SMatti Vaittinen * @type: Type of returned scale values 65438416c28SMatti Vaittinen * @length: Amount of returned values in array 65538416c28SMatti Vaittinen * 65638416c28SMatti Vaittinen * Drivers which do not allow scale setting to change integration time can 65738416c28SMatti Vaittinen * use this helper to list only the scales which are valid for given integration 65838416c28SMatti Vaittinen * time. 65938416c28SMatti Vaittinen * 66038416c28SMatti Vaittinen * Return: a value suitable to be returned from read_avail or a negative error. 66138416c28SMatti Vaittinen */ 66238416c28SMatti Vaittinen int iio_gts_avail_scales_for_time(struct iio_gts *gts, int time, 66338416c28SMatti Vaittinen const int **vals, int *type, int *length) 66438416c28SMatti Vaittinen { 66538416c28SMatti Vaittinen int i; 66638416c28SMatti Vaittinen 66738416c28SMatti Vaittinen for (i = 0; i < gts->num_itime; i++) 66838416c28SMatti Vaittinen if (gts->itime_table[i].time_us == time) 66938416c28SMatti Vaittinen break; 67038416c28SMatti Vaittinen 67138416c28SMatti Vaittinen if (i == gts->num_itime) 67238416c28SMatti Vaittinen return -EINVAL; 67338416c28SMatti Vaittinen 67438416c28SMatti Vaittinen *vals = gts->per_time_avail_scale_tables[i]; 67538416c28SMatti Vaittinen *type = IIO_VAL_INT_PLUS_NANO; 67638416c28SMatti Vaittinen *length = gts->num_hwgain * 2; 67738416c28SMatti Vaittinen 67838416c28SMatti Vaittinen return IIO_AVAIL_LIST; 67938416c28SMatti Vaittinen } 68038416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_avail_scales_for_time, IIO_GTS_HELPER); 68138416c28SMatti Vaittinen 68238416c28SMatti Vaittinen /** 68338416c28SMatti Vaittinen * iio_gts_avail_times - helper for listing available integration times 68438416c28SMatti Vaittinen * @gts: Gain time scale descriptor 68538416c28SMatti Vaittinen * @vals: Returned array of supported times 68638416c28SMatti Vaittinen * @type: Type of returned scale values 68738416c28SMatti Vaittinen * @length: Amount of returned values in array 68838416c28SMatti Vaittinen * 68938416c28SMatti Vaittinen * Return: a value suitable to be returned from read_avail or a negative error. 69038416c28SMatti Vaittinen */ 69138416c28SMatti Vaittinen int iio_gts_avail_times(struct iio_gts *gts, const int **vals, int *type, 69238416c28SMatti Vaittinen int *length) 69338416c28SMatti Vaittinen { 69438416c28SMatti Vaittinen if (!gts->num_avail_time_tables) 69538416c28SMatti Vaittinen return -EINVAL; 69638416c28SMatti Vaittinen 69738416c28SMatti Vaittinen *vals = gts->avail_time_tables; 698e6506513SMatti Vaittinen *type = IIO_VAL_INT_PLUS_MICRO; 699e6506513SMatti Vaittinen *length = gts->num_avail_time_tables * 2; 70038416c28SMatti Vaittinen 70138416c28SMatti Vaittinen return IIO_AVAIL_LIST; 70238416c28SMatti Vaittinen } 70338416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_avail_times, IIO_GTS_HELPER); 70438416c28SMatti Vaittinen 70538416c28SMatti Vaittinen /** 70638416c28SMatti Vaittinen * iio_gts_find_sel_by_gain - find selector corresponding to a HW-gain 70738416c28SMatti Vaittinen * @gts: Gain time scale descriptor 70838416c28SMatti Vaittinen * @gain: HW-gain for which matching selector is searched for 70938416c28SMatti Vaittinen * 71038416c28SMatti Vaittinen * Return: a selector matching given HW-gain or -EINVAL if selector was 71138416c28SMatti Vaittinen * not found. 71238416c28SMatti Vaittinen */ 71338416c28SMatti Vaittinen int iio_gts_find_sel_by_gain(struct iio_gts *gts, int gain) 71438416c28SMatti Vaittinen { 71538416c28SMatti Vaittinen int i; 71638416c28SMatti Vaittinen 71738416c28SMatti Vaittinen for (i = 0; i < gts->num_hwgain; i++) 71838416c28SMatti Vaittinen if (gts->hwgain_table[i].gain == gain) 71938416c28SMatti Vaittinen return gts->hwgain_table[i].sel; 72038416c28SMatti Vaittinen 72138416c28SMatti Vaittinen return -EINVAL; 72238416c28SMatti Vaittinen } 72338416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_find_sel_by_gain, IIO_GTS_HELPER); 72438416c28SMatti Vaittinen 72538416c28SMatti Vaittinen /** 72638416c28SMatti Vaittinen * iio_gts_find_gain_by_sel - find HW-gain corresponding to a selector 72738416c28SMatti Vaittinen * @gts: Gain time scale descriptor 72838416c28SMatti Vaittinen * @sel: selector for which matching HW-gain is searched for 72938416c28SMatti Vaittinen * 73038416c28SMatti Vaittinen * Return: a HW-gain matching given selector or -EINVAL if HW-gain was not 73138416c28SMatti Vaittinen * found. 73238416c28SMatti Vaittinen */ 73338416c28SMatti Vaittinen int iio_gts_find_gain_by_sel(struct iio_gts *gts, int sel) 73438416c28SMatti Vaittinen { 73538416c28SMatti Vaittinen int i; 73638416c28SMatti Vaittinen 73738416c28SMatti Vaittinen for (i = 0; i < gts->num_hwgain; i++) 73838416c28SMatti Vaittinen if (gts->hwgain_table[i].sel == sel) 73938416c28SMatti Vaittinen return gts->hwgain_table[i].gain; 74038416c28SMatti Vaittinen 74138416c28SMatti Vaittinen return -EINVAL; 74238416c28SMatti Vaittinen } 74338416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_by_sel, IIO_GTS_HELPER); 74438416c28SMatti Vaittinen 74538416c28SMatti Vaittinen /** 74638416c28SMatti Vaittinen * iio_gts_get_min_gain - find smallest valid HW-gain 74738416c28SMatti Vaittinen * @gts: Gain time scale descriptor 74838416c28SMatti Vaittinen * 74938416c28SMatti Vaittinen * Return: The smallest HW-gain -EINVAL if no HW-gains were in the tables. 75038416c28SMatti Vaittinen */ 75138416c28SMatti Vaittinen int iio_gts_get_min_gain(struct iio_gts *gts) 75238416c28SMatti Vaittinen { 75338416c28SMatti Vaittinen int i, min = -EINVAL; 75438416c28SMatti Vaittinen 75538416c28SMatti Vaittinen for (i = 0; i < gts->num_hwgain; i++) { 75638416c28SMatti Vaittinen int gain = gts->hwgain_table[i].gain; 75738416c28SMatti Vaittinen 75838416c28SMatti Vaittinen if (min == -EINVAL) 75938416c28SMatti Vaittinen min = gain; 76038416c28SMatti Vaittinen else 76138416c28SMatti Vaittinen min = min(min, gain); 76238416c28SMatti Vaittinen } 76338416c28SMatti Vaittinen 76438416c28SMatti Vaittinen return min; 76538416c28SMatti Vaittinen } 76638416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_get_min_gain, IIO_GTS_HELPER); 76738416c28SMatti Vaittinen 76838416c28SMatti Vaittinen /** 76938416c28SMatti Vaittinen * iio_find_closest_gain_low - Find the closest lower matching gain 77038416c28SMatti Vaittinen * @gts: Gain time scale descriptor 77138416c28SMatti Vaittinen * @gain: HW-gain for which the closest match is searched 77238416c28SMatti Vaittinen * @in_range: indicate if the @gain was actually in the range of 77338416c28SMatti Vaittinen * supported gains. 77438416c28SMatti Vaittinen * 77538416c28SMatti Vaittinen * Search for closest supported gain that is lower than or equal to the 77638416c28SMatti Vaittinen * gain given as a parameter. This is usable for drivers which do not require 77738416c28SMatti Vaittinen * user to request exact matching gain but rather for rounding to a supported 77838416c28SMatti Vaittinen * gain value which is equal or lower (setting lower gain is typical for 77938416c28SMatti Vaittinen * avoiding saturation) 78038416c28SMatti Vaittinen * 78138416c28SMatti Vaittinen * Return: The closest matching supported gain or -EINVAL if @gain 78238416c28SMatti Vaittinen * was smaller than the smallest supported gain. 78338416c28SMatti Vaittinen */ 78438416c28SMatti Vaittinen int iio_find_closest_gain_low(struct iio_gts *gts, int gain, bool *in_range) 78538416c28SMatti Vaittinen { 78638416c28SMatti Vaittinen int i, diff = 0; 78738416c28SMatti Vaittinen int best = -1; 78838416c28SMatti Vaittinen 78938416c28SMatti Vaittinen *in_range = false; 79038416c28SMatti Vaittinen 79138416c28SMatti Vaittinen for (i = 0; i < gts->num_hwgain; i++) { 79238416c28SMatti Vaittinen if (gain == gts->hwgain_table[i].gain) { 79338416c28SMatti Vaittinen *in_range = true; 79438416c28SMatti Vaittinen return gain; 79538416c28SMatti Vaittinen } 79638416c28SMatti Vaittinen 79738416c28SMatti Vaittinen if (gain > gts->hwgain_table[i].gain) { 79838416c28SMatti Vaittinen if (!diff) { 79938416c28SMatti Vaittinen diff = gain - gts->hwgain_table[i].gain; 80038416c28SMatti Vaittinen best = i; 80138416c28SMatti Vaittinen } else { 80238416c28SMatti Vaittinen int tmp = gain - gts->hwgain_table[i].gain; 80338416c28SMatti Vaittinen 80438416c28SMatti Vaittinen if (tmp < diff) { 80538416c28SMatti Vaittinen diff = tmp; 80638416c28SMatti Vaittinen best = i; 80738416c28SMatti Vaittinen } 80838416c28SMatti Vaittinen } 80938416c28SMatti Vaittinen } else { 81038416c28SMatti Vaittinen /* 81138416c28SMatti Vaittinen * We found valid HW-gain which is greater than 81238416c28SMatti Vaittinen * reference. So, unless we return a failure below we 81338416c28SMatti Vaittinen * will have found an in-range gain 81438416c28SMatti Vaittinen */ 81538416c28SMatti Vaittinen *in_range = true; 81638416c28SMatti Vaittinen } 81738416c28SMatti Vaittinen } 81838416c28SMatti Vaittinen /* The requested gain was smaller than anything we support */ 81938416c28SMatti Vaittinen if (!diff) { 82038416c28SMatti Vaittinen *in_range = false; 82138416c28SMatti Vaittinen 82238416c28SMatti Vaittinen return -EINVAL; 82338416c28SMatti Vaittinen } 82438416c28SMatti Vaittinen 82538416c28SMatti Vaittinen return gts->hwgain_table[best].gain; 82638416c28SMatti Vaittinen } 82738416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_find_closest_gain_low, IIO_GTS_HELPER); 82838416c28SMatti Vaittinen 82938416c28SMatti Vaittinen static int iio_gts_get_int_time_gain_multiplier_by_sel(struct iio_gts *gts, 83038416c28SMatti Vaittinen int sel) 83138416c28SMatti Vaittinen { 83238416c28SMatti Vaittinen const struct iio_itime_sel_mul *time; 83338416c28SMatti Vaittinen 83438416c28SMatti Vaittinen time = iio_gts_find_itime_by_sel(gts, sel); 83538416c28SMatti Vaittinen if (!time) 83638416c28SMatti Vaittinen return -EINVAL; 83738416c28SMatti Vaittinen 83838416c28SMatti Vaittinen return time->mul; 83938416c28SMatti Vaittinen } 84038416c28SMatti Vaittinen 84138416c28SMatti Vaittinen /** 84238416c28SMatti Vaittinen * iio_gts_find_gain_for_scale_using_time - Find gain by time and scale 84338416c28SMatti Vaittinen * @gts: Gain time scale descriptor 84438416c28SMatti Vaittinen * @time_sel: Integration time selector corresponding to the time gain is 84538416c28SMatti Vaittinen * searched for 84638416c28SMatti Vaittinen * @scale_int: Integral part of the scale (typically val1) 84738416c28SMatti Vaittinen * @scale_nano: Fractional part of the scale (nano or ppb) 84838416c28SMatti Vaittinen * @gain: Pointer to value where gain is stored. 84938416c28SMatti Vaittinen * 85038416c28SMatti Vaittinen * In some cases the light sensors may want to find a gain setting which 85138416c28SMatti Vaittinen * corresponds given scale and integration time. Sensors which fill the 85238416c28SMatti Vaittinen * gain and time tables may use this helper to retrieve the gain. 85338416c28SMatti Vaittinen * 85438416c28SMatti Vaittinen * Return: 0 on success. -EINVAL if gain matching the parameters is not 85538416c28SMatti Vaittinen * found. 85638416c28SMatti Vaittinen */ 85738416c28SMatti Vaittinen static int iio_gts_find_gain_for_scale_using_time(struct iio_gts *gts, int time_sel, 85838416c28SMatti Vaittinen int scale_int, int scale_nano, 85938416c28SMatti Vaittinen int *gain) 86038416c28SMatti Vaittinen { 86138416c28SMatti Vaittinen u64 scale_linear; 86238416c28SMatti Vaittinen int ret, mul; 86338416c28SMatti Vaittinen 86438416c28SMatti Vaittinen ret = iio_gts_linearize(scale_int, scale_nano, NANO, &scale_linear); 86538416c28SMatti Vaittinen if (ret) 86638416c28SMatti Vaittinen return ret; 86738416c28SMatti Vaittinen 86838416c28SMatti Vaittinen ret = iio_gts_get_int_time_gain_multiplier_by_sel(gts, time_sel); 86938416c28SMatti Vaittinen if (ret < 0) 87038416c28SMatti Vaittinen return ret; 87138416c28SMatti Vaittinen 87238416c28SMatti Vaittinen mul = ret; 87338416c28SMatti Vaittinen 87438416c28SMatti Vaittinen ret = gain_get_scale_fraction(gts->max_scale, scale_linear, mul, gain); 87538416c28SMatti Vaittinen if (ret) 87638416c28SMatti Vaittinen return ret; 87738416c28SMatti Vaittinen 87838416c28SMatti Vaittinen if (!iio_gts_valid_gain(gts, *gain)) 87938416c28SMatti Vaittinen return -EINVAL; 88038416c28SMatti Vaittinen 88138416c28SMatti Vaittinen return 0; 88238416c28SMatti Vaittinen } 88338416c28SMatti Vaittinen 88438416c28SMatti Vaittinen /** 88538416c28SMatti Vaittinen * iio_gts_find_gain_sel_for_scale_using_time - Fetch gain selector. 88638416c28SMatti Vaittinen * @gts: Gain time scale descriptor 88738416c28SMatti Vaittinen * @time_sel: Integration time selector corresponding to the time gain is 88838416c28SMatti Vaittinen * searched for 88938416c28SMatti Vaittinen * @scale_int: Integral part of the scale (typically val1) 89038416c28SMatti Vaittinen * @scale_nano: Fractional part of the scale (nano or ppb) 89138416c28SMatti Vaittinen * @gain_sel: Pointer to value where gain selector is stored. 89238416c28SMatti Vaittinen * 89338416c28SMatti Vaittinen * See iio_gts_find_gain_for_scale_using_time() for more information 89438416c28SMatti Vaittinen */ 89538416c28SMatti Vaittinen int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel, 89638416c28SMatti Vaittinen int scale_int, int scale_nano, 89738416c28SMatti Vaittinen int *gain_sel) 89838416c28SMatti Vaittinen { 89938416c28SMatti Vaittinen int gain, ret; 90038416c28SMatti Vaittinen 90138416c28SMatti Vaittinen ret = iio_gts_find_gain_for_scale_using_time(gts, time_sel, scale_int, 90238416c28SMatti Vaittinen scale_nano, &gain); 90338416c28SMatti Vaittinen if (ret) 90438416c28SMatti Vaittinen return ret; 90538416c28SMatti Vaittinen 90638416c28SMatti Vaittinen ret = iio_gts_find_sel_by_gain(gts, gain); 90738416c28SMatti Vaittinen if (ret < 0) 90838416c28SMatti Vaittinen return ret; 90938416c28SMatti Vaittinen 91038416c28SMatti Vaittinen *gain_sel = ret; 91138416c28SMatti Vaittinen 91238416c28SMatti Vaittinen return 0; 91338416c28SMatti Vaittinen } 91438416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_sel_for_scale_using_time, IIO_GTS_HELPER); 91538416c28SMatti Vaittinen 91638416c28SMatti Vaittinen static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time) 91738416c28SMatti Vaittinen { 91838416c28SMatti Vaittinen const struct iio_itime_sel_mul *itime; 91938416c28SMatti Vaittinen 92038416c28SMatti Vaittinen if (!iio_gts_valid_gain(gts, gain)) 92138416c28SMatti Vaittinen return -EINVAL; 92238416c28SMatti Vaittinen 92338416c28SMatti Vaittinen if (!gts->num_itime) 92438416c28SMatti Vaittinen return gain; 92538416c28SMatti Vaittinen 92638416c28SMatti Vaittinen itime = iio_gts_find_itime_by_time(gts, time); 92738416c28SMatti Vaittinen if (!itime) 92838416c28SMatti Vaittinen return -EINVAL; 92938416c28SMatti Vaittinen 93038416c28SMatti Vaittinen return gain * itime->mul; 93138416c28SMatti Vaittinen } 93238416c28SMatti Vaittinen 93338416c28SMatti Vaittinen static int iio_gts_get_scale_linear(struct iio_gts *gts, int gain, int time, 93438416c28SMatti Vaittinen u64 *scale) 93538416c28SMatti Vaittinen { 93638416c28SMatti Vaittinen int total_gain; 93738416c28SMatti Vaittinen u64 tmp; 93838416c28SMatti Vaittinen 93938416c28SMatti Vaittinen total_gain = iio_gts_get_total_gain(gts, gain, time); 94038416c28SMatti Vaittinen if (total_gain < 0) 94138416c28SMatti Vaittinen return total_gain; 94238416c28SMatti Vaittinen 94338416c28SMatti Vaittinen tmp = gts->max_scale; 94438416c28SMatti Vaittinen 94538416c28SMatti Vaittinen do_div(tmp, total_gain); 94638416c28SMatti Vaittinen 94738416c28SMatti Vaittinen *scale = tmp; 94838416c28SMatti Vaittinen 94938416c28SMatti Vaittinen return 0; 95038416c28SMatti Vaittinen } 95138416c28SMatti Vaittinen 95238416c28SMatti Vaittinen /** 95338416c28SMatti Vaittinen * iio_gts_get_scale - get scale based on integration time and HW-gain 95438416c28SMatti Vaittinen * @gts: Gain time scale descriptor 95538416c28SMatti Vaittinen * @gain: HW-gain for which the scale is computed 95638416c28SMatti Vaittinen * @time: Integration time for which the scale is computed 95738416c28SMatti Vaittinen * @scale_int: Integral part of the scale (typically val1) 95838416c28SMatti Vaittinen * @scale_nano: Fractional part of the scale (nano or ppb) 95938416c28SMatti Vaittinen * 96038416c28SMatti Vaittinen * Compute scale matching the integration time and HW-gain given as parameter. 96138416c28SMatti Vaittinen * 96238416c28SMatti Vaittinen * Return: 0 on success. 96338416c28SMatti Vaittinen */ 96438416c28SMatti Vaittinen int iio_gts_get_scale(struct iio_gts *gts, int gain, int time, int *scale_int, 96538416c28SMatti Vaittinen int *scale_nano) 96638416c28SMatti Vaittinen { 96738416c28SMatti Vaittinen u64 lin_scale; 96838416c28SMatti Vaittinen int ret; 96938416c28SMatti Vaittinen 97038416c28SMatti Vaittinen ret = iio_gts_get_scale_linear(gts, gain, time, &lin_scale); 97138416c28SMatti Vaittinen if (ret) 97238416c28SMatti Vaittinen return ret; 97338416c28SMatti Vaittinen 97438416c28SMatti Vaittinen return iio_gts_delinearize(lin_scale, NANO, scale_int, scale_nano); 97538416c28SMatti Vaittinen } 97638416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_get_scale, IIO_GTS_HELPER); 97738416c28SMatti Vaittinen 97838416c28SMatti Vaittinen /** 97938416c28SMatti Vaittinen * iio_gts_find_new_gain_sel_by_old_gain_time - compensate for time change 98038416c28SMatti Vaittinen * @gts: Gain time scale descriptor 98138416c28SMatti Vaittinen * @old_gain: Previously set gain 98238416c28SMatti Vaittinen * @old_time_sel: Selector corresponding previously set time 98338416c28SMatti Vaittinen * @new_time_sel: Selector corresponding new time to be set 98438416c28SMatti Vaittinen * @new_gain: Pointer to value where new gain is to be written 98538416c28SMatti Vaittinen * 98638416c28SMatti Vaittinen * We may want to mitigate the scale change caused by setting a new integration 98738416c28SMatti Vaittinen * time (for a light sensor) by also updating the (HW)gain. This helper computes 98838416c28SMatti Vaittinen * new gain value to maintain the scale with new integration time. 98938416c28SMatti Vaittinen * 99038416c28SMatti Vaittinen * Return: 0 if an exactly matching supported new gain was found. When a 99138416c28SMatti Vaittinen * non-zero value is returned, the @new_gain will be set to a negative or 99238416c28SMatti Vaittinen * positive value. The negative value means that no gain could be computed. 99338416c28SMatti Vaittinen * Positive value will be the "best possible new gain there could be". There 99438416c28SMatti Vaittinen * can be two reasons why finding the "best possible" new gain is not deemed 99538416c28SMatti Vaittinen * successful. 1) This new value cannot be supported by the hardware. 2) The new 99638416c28SMatti Vaittinen * gain required to maintain the scale would not be an integer. In this case, 99738416c28SMatti Vaittinen * the "best possible" new gain will be a floored optimal gain, which may or 99838416c28SMatti Vaittinen * may not be supported by the hardware. 99938416c28SMatti Vaittinen */ 100038416c28SMatti Vaittinen int iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts *gts, 100138416c28SMatti Vaittinen int old_gain, int old_time_sel, 100238416c28SMatti Vaittinen int new_time_sel, int *new_gain) 100338416c28SMatti Vaittinen { 100438416c28SMatti Vaittinen const struct iio_itime_sel_mul *itime_old, *itime_new; 100538416c28SMatti Vaittinen u64 scale; 100638416c28SMatti Vaittinen int ret; 100738416c28SMatti Vaittinen 100838416c28SMatti Vaittinen *new_gain = -1; 100938416c28SMatti Vaittinen 101038416c28SMatti Vaittinen itime_old = iio_gts_find_itime_by_sel(gts, old_time_sel); 101138416c28SMatti Vaittinen if (!itime_old) 101238416c28SMatti Vaittinen return -EINVAL; 101338416c28SMatti Vaittinen 101438416c28SMatti Vaittinen itime_new = iio_gts_find_itime_by_sel(gts, new_time_sel); 101538416c28SMatti Vaittinen if (!itime_new) 101638416c28SMatti Vaittinen return -EINVAL; 101738416c28SMatti Vaittinen 101838416c28SMatti Vaittinen ret = iio_gts_get_scale_linear(gts, old_gain, itime_old->time_us, 101938416c28SMatti Vaittinen &scale); 102038416c28SMatti Vaittinen if (ret) 102138416c28SMatti Vaittinen return ret; 102238416c28SMatti Vaittinen 102338416c28SMatti Vaittinen ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul, 102438416c28SMatti Vaittinen new_gain); 102538416c28SMatti Vaittinen if (ret) 102638416c28SMatti Vaittinen return ret; 102738416c28SMatti Vaittinen 102838416c28SMatti Vaittinen if (!iio_gts_valid_gain(gts, *new_gain)) 102938416c28SMatti Vaittinen return -EINVAL; 103038416c28SMatti Vaittinen 103138416c28SMatti Vaittinen return 0; 103238416c28SMatti Vaittinen } 103338416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_sel_by_old_gain_time, IIO_GTS_HELPER); 103438416c28SMatti Vaittinen 103538416c28SMatti Vaittinen /** 103638416c28SMatti Vaittinen * iio_gts_find_new_gain_by_old_gain_time - compensate for time change 103738416c28SMatti Vaittinen * @gts: Gain time scale descriptor 103838416c28SMatti Vaittinen * @old_gain: Previously set gain 103938416c28SMatti Vaittinen * @old_time: Selector corresponding previously set time 104038416c28SMatti Vaittinen * @new_time: Selector corresponding new time to be set 104138416c28SMatti Vaittinen * @new_gain: Pointer to value where new gain is to be written 104238416c28SMatti Vaittinen * 104338416c28SMatti Vaittinen * We may want to mitigate the scale change caused by setting a new integration 104438416c28SMatti Vaittinen * time (for a light sensor) by also updating the (HW)gain. This helper computes 104538416c28SMatti Vaittinen * new gain value to maintain the scale with new integration time. 104638416c28SMatti Vaittinen * 104738416c28SMatti Vaittinen * Return: 0 if an exactly matching supported new gain was found. When a 104838416c28SMatti Vaittinen * non-zero value is returned, the @new_gain will be set to a negative or 104938416c28SMatti Vaittinen * positive value. The negative value means that no gain could be computed. 105038416c28SMatti Vaittinen * Positive value will be the "best possible new gain there could be". There 105138416c28SMatti Vaittinen * can be two reasons why finding the "best possible" new gain is not deemed 105238416c28SMatti Vaittinen * successful. 1) This new value cannot be supported by the hardware. 2) The new 105338416c28SMatti Vaittinen * gain required to maintain the scale would not be an integer. In this case, 105438416c28SMatti Vaittinen * the "best possible" new gain will be a floored optimal gain, which may or 105538416c28SMatti Vaittinen * may not be supported by the hardware. 105638416c28SMatti Vaittinen */ 105738416c28SMatti Vaittinen int iio_gts_find_new_gain_by_old_gain_time(struct iio_gts *gts, int old_gain, 105838416c28SMatti Vaittinen int old_time, int new_time, 105938416c28SMatti Vaittinen int *new_gain) 106038416c28SMatti Vaittinen { 106138416c28SMatti Vaittinen const struct iio_itime_sel_mul *itime_new; 106238416c28SMatti Vaittinen u64 scale; 106338416c28SMatti Vaittinen int ret; 106438416c28SMatti Vaittinen 106538416c28SMatti Vaittinen *new_gain = -1; 106638416c28SMatti Vaittinen 106738416c28SMatti Vaittinen itime_new = iio_gts_find_itime_by_time(gts, new_time); 106838416c28SMatti Vaittinen if (!itime_new) 106938416c28SMatti Vaittinen return -EINVAL; 107038416c28SMatti Vaittinen 107138416c28SMatti Vaittinen ret = iio_gts_get_scale_linear(gts, old_gain, old_time, &scale); 107238416c28SMatti Vaittinen if (ret) 107338416c28SMatti Vaittinen return ret; 107438416c28SMatti Vaittinen 107538416c28SMatti Vaittinen ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul, 107638416c28SMatti Vaittinen new_gain); 107738416c28SMatti Vaittinen if (ret) 107838416c28SMatti Vaittinen return ret; 107938416c28SMatti Vaittinen 108038416c28SMatti Vaittinen if (!iio_gts_valid_gain(gts, *new_gain)) 108138416c28SMatti Vaittinen return -EINVAL; 108238416c28SMatti Vaittinen 108338416c28SMatti Vaittinen return 0; 108438416c28SMatti Vaittinen } 108538416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_old_gain_time, IIO_GTS_HELPER); 108638416c28SMatti Vaittinen 108738416c28SMatti Vaittinen MODULE_LICENSE("GPL"); 108838416c28SMatti Vaittinen MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>"); 108938416c28SMatti Vaittinen MODULE_DESCRIPTION("IIO light sensor gain-time-scale helpers"); 1090