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