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"); 270