Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iio: amplifiers: adl8113: add driver support

Add support for adl8113 10MHz to 12GHz Low Noise Amplifier with
10MHz to 14GHz bypass switches.

Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Antoniu Miclaus and committed by
Jonathan Cameron
b8c7340e 09140a72

+282
+12
drivers/iio/amplifiers/Kconfig
··· 36 36 To compile this driver as a module, choose M here: the 37 37 module will be called ada4250. 38 38 39 + config ADL8113 40 + tristate "Analog Devices ADL8113 Low Noise Amplifier" 41 + depends on GPIOLIB 42 + help 43 + Say yes here to build support for Analog Devices ADL8113 Low Noise 44 + Amplifier with integrated bypass switches. The device supports four 45 + operation modes controlled by GPIO pins: internal amplifier, 46 + internal bypass, and two external bypass modes. 47 + 48 + To compile this driver as a module, choose M here: the 49 + module will be called adl8113. 50 + 39 51 config HMC425 40 52 tristate "Analog Devices HMC425A and similar GPIO Gain Amplifiers" 41 53 depends on GPIOLIB
+1
drivers/iio/amplifiers/Makefile
··· 6 6 # When adding new entries keep the list in alphabetical order 7 7 obj-$(CONFIG_AD8366) += ad8366.o 8 8 obj-$(CONFIG_ADA4250) += ada4250.o 9 + obj-$(CONFIG_ADL8113) += adl8113.o 9 10 obj-$(CONFIG_HMC425) += hmc425a.o
+269
drivers/iio/amplifiers/adl8113.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * ADL8113 Low Noise Amplifier with integrated bypass switches 4 + * 5 + * Copyright 2025 Analog Devices Inc. 6 + */ 7 + 8 + #include <linux/array_size.h> 9 + #include <linux/bitmap.h> 10 + #include <linux/device/driver.h> 11 + #include <linux/dev_printk.h> 12 + #include <linux/err.h> 13 + #include <linux/gpio/consumer.h> 14 + #include <linux/iio/iio.h> 15 + #include <linux/mod_devicetable.h> 16 + #include <linux/module.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/property.h> 19 + #include <linux/regulator/consumer.h> 20 + #include <linux/types.h> 21 + 22 + enum adl8113_signal_path { 23 + ADL8113_INTERNAL_AMP, 24 + ADL8113_INTERNAL_BYPASS, 25 + ADL8113_EXTERNAL_A, 26 + ADL8113_EXTERNAL_B, 27 + }; 28 + 29 + struct adl8113_gain_config { 30 + enum adl8113_signal_path path; 31 + int gain_db; 32 + }; 33 + 34 + struct adl8113_state { 35 + struct gpio_descs *gpios; 36 + struct adl8113_gain_config *gain_configs; 37 + unsigned int num_gain_configs; 38 + enum adl8113_signal_path current_path; 39 + }; 40 + 41 + static const char * const adl8113_supply_names[] = { 42 + "vdd1", 43 + "vss2", 44 + "vdd2", 45 + }; 46 + 47 + static int adl8113_set_path(struct adl8113_state *st, 48 + enum adl8113_signal_path path) 49 + { 50 + DECLARE_BITMAP(values, 2); 51 + int ret; 52 + 53 + /* 54 + * Determine GPIO values based on signal path. 55 + * Va: bit 0, Vb: bit 1. 56 + */ 57 + switch (path) { 58 + case ADL8113_INTERNAL_AMP: 59 + bitmap_write(values, 0x00, 0, 2); 60 + break; 61 + case ADL8113_INTERNAL_BYPASS: 62 + bitmap_write(values, 0x03, 0, 2); 63 + break; 64 + case ADL8113_EXTERNAL_A: 65 + bitmap_write(values, 0x02, 0, 2); 66 + break; 67 + case ADL8113_EXTERNAL_B: 68 + bitmap_write(values, 0x01, 0, 2); 69 + break; 70 + default: 71 + return -EINVAL; 72 + } 73 + 74 + ret = gpiod_set_array_value_cansleep(st->gpios->ndescs, st->gpios->desc, 75 + st->gpios->info, values); 76 + if (ret) 77 + return ret; 78 + 79 + st->current_path = path; 80 + return 0; 81 + } 82 + 83 + static int adl8113_find_gain_config(struct adl8113_state *st, int gain_db) 84 + { 85 + unsigned int i; 86 + 87 + for (i = 0; i < st->num_gain_configs; i++) { 88 + if (st->gain_configs[i].gain_db == gain_db) 89 + return i; 90 + } 91 + return -EINVAL; 92 + } 93 + 94 + static const struct iio_chan_spec adl8113_channels[] = { 95 + { 96 + .type = IIO_VOLTAGE, 97 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN), 98 + }, 99 + }; 100 + 101 + static int adl8113_read_raw(struct iio_dev *indio_dev, 102 + struct iio_chan_spec const *chan, 103 + int *val, int *val2, long mask) 104 + { 105 + struct adl8113_state *st = iio_priv(indio_dev); 106 + unsigned int i; 107 + 108 + switch (mask) { 109 + case IIO_CHAN_INFO_HARDWAREGAIN: 110 + /* Find current gain configuration */ 111 + for (i = 0; i < st->num_gain_configs; i++) { 112 + if (st->gain_configs[i].path == st->current_path) { 113 + *val = st->gain_configs[i].gain_db; 114 + *val2 = 0; 115 + return IIO_VAL_INT_PLUS_MICRO_DB; 116 + } 117 + } 118 + return -EINVAL; 119 + default: 120 + return -EINVAL; 121 + } 122 + } 123 + 124 + static int adl8113_write_raw(struct iio_dev *indio_dev, 125 + struct iio_chan_spec const *chan, 126 + int val, int val2, long mask) 127 + { 128 + struct adl8113_state *st = iio_priv(indio_dev); 129 + int config_idx; 130 + 131 + switch (mask) { 132 + case IIO_CHAN_INFO_HARDWAREGAIN: 133 + if (val2 != 0) 134 + return -EINVAL; 135 + 136 + config_idx = adl8113_find_gain_config(st, val); 137 + if (config_idx < 0) 138 + return config_idx; 139 + 140 + return adl8113_set_path(st, st->gain_configs[config_idx].path); 141 + default: 142 + return -EINVAL; 143 + } 144 + } 145 + 146 + static const struct iio_info adl8113_info = { 147 + .read_raw = adl8113_read_raw, 148 + .write_raw = adl8113_write_raw, 149 + }; 150 + 151 + static int adl8113_init_gain_configs(struct device *dev, struct adl8113_state *st) 152 + { 153 + int external_a_gain, external_b_gain; 154 + unsigned int i; 155 + 156 + /* 157 + * Allocate for all 4 possible paths: 158 + * - Internal amp and bypass (always present) 159 + * - External bypass A and B (optional if configured) 160 + */ 161 + st->gain_configs = devm_kcalloc(dev, 4, sizeof(*st->gain_configs), 162 + GFP_KERNEL); 163 + if (!st->gain_configs) 164 + return -ENOMEM; 165 + 166 + /* Start filling the gain configurations with data */ 167 + i = 0; 168 + 169 + /* Always include internal amplifier (14dB) */ 170 + st->gain_configs[i++] = (struct adl8113_gain_config) { 171 + .path = ADL8113_INTERNAL_AMP, 172 + .gain_db = 14, 173 + }; 174 + 175 + /* Always include internal bypass (-2dB insertion loss) */ 176 + st->gain_configs[i++] = (struct adl8113_gain_config) { 177 + .path = ADL8113_INTERNAL_BYPASS, 178 + .gain_db = -2, 179 + }; 180 + 181 + /* Add external bypass A if configured */ 182 + if (!device_property_read_u32(dev, "adi,external-bypass-a-gain-db", 183 + &external_a_gain)) { 184 + st->gain_configs[i++] = (struct adl8113_gain_config) { 185 + .path = ADL8113_EXTERNAL_A, 186 + .gain_db = external_a_gain, 187 + }; 188 + } 189 + 190 + /* Add external bypass B if configured */ 191 + if (!device_property_read_u32(dev, "adi,external-bypass-b-gain-db", 192 + &external_b_gain)) { 193 + st->gain_configs[i++] = (struct adl8113_gain_config) { 194 + .path = ADL8113_EXTERNAL_B, 195 + .gain_db = external_b_gain, 196 + }; 197 + } 198 + 199 + st->num_gain_configs = i; 200 + 201 + return 0; 202 + } 203 + 204 + static int adl8113_probe(struct platform_device *pdev) 205 + { 206 + struct device *dev = &pdev->dev; 207 + struct adl8113_state *st; 208 + struct iio_dev *indio_dev; 209 + int ret; 210 + 211 + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); 212 + if (!indio_dev) 213 + return -ENOMEM; 214 + 215 + st = iio_priv(indio_dev); 216 + 217 + st->gpios = devm_gpiod_get_array(dev, "ctrl", GPIOD_OUT_LOW); 218 + if (IS_ERR(st->gpios)) 219 + return dev_err_probe(dev, PTR_ERR(st->gpios), 220 + "failed to get control GPIOs\n"); 221 + 222 + if (st->gpios->ndescs != 2) 223 + return dev_err_probe(dev, -EINVAL, 224 + "expected 2 control GPIOs, got %u\n", 225 + st->gpios->ndescs); 226 + 227 + ret = devm_regulator_bulk_get_enable(dev, 228 + ARRAY_SIZE(adl8113_supply_names), 229 + adl8113_supply_names); 230 + if (ret) 231 + return dev_err_probe(dev, ret, 232 + "failed to get and enable supplies\n"); 233 + 234 + /* Initialize gain configurations from devicetree */ 235 + ret = adl8113_init_gain_configs(dev, st); 236 + if (ret) 237 + return ret; 238 + 239 + /* Initialize to internal amplifier path (14dB) */ 240 + ret = adl8113_set_path(st, ADL8113_INTERNAL_AMP); 241 + if (ret) 242 + return ret; 243 + 244 + indio_dev->info = &adl8113_info; 245 + indio_dev->name = "adl8113"; 246 + indio_dev->channels = adl8113_channels; 247 + indio_dev->num_channels = ARRAY_SIZE(adl8113_channels); 248 + 249 + return devm_iio_device_register(dev, indio_dev); 250 + } 251 + 252 + static const struct of_device_id adl8113_of_match[] = { 253 + { .compatible = "adi,adl8113" }, 254 + { } 255 + }; 256 + MODULE_DEVICE_TABLE(of, adl8113_of_match); 257 + 258 + static struct platform_driver adl8113_driver = { 259 + .driver = { 260 + .name = "adl8113", 261 + .of_match_table = adl8113_of_match, 262 + }, 263 + .probe = adl8113_probe, 264 + }; 265 + module_platform_driver(adl8113_driver); 266 + 267 + MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>"); 268 + MODULE_DESCRIPTION("Analog Devices ADL8113 Low Noise Amplifier"); 269 + MODULE_LICENSE("GPL");