diff --git a/doc/sphinx/source/drivers/admfm2000.rst b/doc/sphinx/source/drivers/admfm2000.rst new file mode 100644 index 00000000000..020e41ce1ab --- /dev/null +++ b/doc/sphinx/source/drivers/admfm2000.rst @@ -0,0 +1 @@ +.. include:: ../../../../drivers/frequency/admfm2000/README.rst \ No newline at end of file diff --git a/doc/sphinx/source/drivers_doc.rst b/doc/sphinx/source/drivers_doc.rst index 9136c2571d2..a2a2b721b3a 100644 --- a/doc/sphinx/source/drivers_doc.rst +++ b/doc/sphinx/source/drivers_doc.rst @@ -56,6 +56,7 @@ FREQUENCY GENERATORS drivers/adf4382 drivers/adf4368 drivers/adf5611 + drivers/admfm2000 INERTIAL MEASUREMENT UNITS ========================== diff --git a/drivers/frequency/admfm2000/README.rst b/drivers/frequency/admfm2000/README.rst new file mode 100644 index 00000000000..16465f482b6 --- /dev/null +++ b/drivers/frequency/admfm2000/README.rst @@ -0,0 +1,410 @@ +ADMFM2000 no-OS driver +====================== + +Supported Devices +----------------- + +`ADMFM2000 `_ + +Overview +-------- + +The `ADMFM2000 `_ is a dual-channel +microwave downconverter, with +input RF and local oscillator (LO) frequency ranges covering 5 GHz +to 32 GHz, with an output intermediate frequency (IF) frequency +range from 0.5 GHz to 8 GHz. The downconverting mixer can +also be bypassed allowing direct access to the 0.5 to 8 GHz IF +path. A common LO input signal is split to feed two separate +buffer amplifiers to drive the mixer in each channel. Each down +conversion path consists of a low noise amplifier (LNA), a mixer, an +IF filter, a digital step attenuator (DSA), and an IF amplifier + +Fabricated using a combination of surface mount and bare die +components, the `ADMFM2000 `_ +provides precise gain adjustment capabilities with low distortion performance. +The ADMFM2000 comes in a compact, shielded 20.00 mm × 14.00 mm, 179-ball +chip scale package ball grid array (CSP_BGA) and operates over a temperature +range of −40°C to +85°C. + +Applications +------------ + +* Phased array radar receivers +* Satellite communications (satcom) receivers +* Electronic warfare +* Electronic test and measurement equipment +* Automatic test equipmen + +ADMFM2000 Device Configuration +-------------------------------- + +Driver Initialization +--------------------- + +In order to be able to use the device, you will have to provide the gpios +connected in order to control the device. + +The first API to be called is **admfm2000_init**. Make sure that it returns 0, +which means that the driver was initialized correctly. This function will also +set the channel to mixer_mode/direct if and set the gain for both channels +according to the initialization parameter. + +Switch configutation +----------------------------- + +The ADMFM2000 has a broadband SPDT RF switch in each channel that can be used to +bypass the mixer. epending on the logic level applied to those control pins, the +IF path is either connected to the mixer or to the RF_BYPASS_IN pin. This can be +done using **admfm2000_set_channel_mode**. The current configuration can be read +out using **admfm2000_get_channel_mode**.The channels can only be set +simultaneously, meaning when one channel is set to mixer mode, the other one will +be set to bypass mode. + +ADMFM2000 Driver Initialization Example +----------------------------------------- + +Platform Communication Example +----------------------------------------- + +.. code-block:: bash + + struct admfm2000_dev *dev; + int ret; + + struct no_os_uart_init_param admfm2000_uart_ip = { + .device_id = UART_DEVICE_ID, + .irq_id = UART_IRQ_ID, + .asynchronous_rx = true, + .baud_rate = UART_BAUDRATE, + .size = NO_OS_UART_CS_8, + .parity = NO_OS_UART_PAR_NO, + .stop = NO_OS_UART_STOP_1_BIT, + .platform_ops = UART_OPS, + .extra = &xuip, + }; + + struct no_os_gpio_init_param admfm2000_gpio_ip[2][2] = { + { + { + .port = 2, + .number = 10, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 2, + .number = 6, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + }, + { + { + .port = 0, + .number = 12, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 0, + .number = 14, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + } + }; + + struct no_os_gpio_init_param admfm2000_gpio_dsa_ip[2][5] = { + { + { + .port = 2, + .number = 1, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 0, + .number = 11, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 2, + .number = 7, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 2, + .number = 8, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 2, + .number = 9, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + }, + { + { + .port = 2, + .number = 20, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 2, + .number = 21, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 3, + .number = 8, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 4, + .number = 1, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 0, + .number = 13, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + } + }; + + struct admfm2000_init_param admfm2000_ip = { + .mixer_mode = ADMFM2000_DIRECT_IF_MODE, + .dsa_gain = 0, + .gpio_sw_param = &admfm2000_gpio_ip, + .gpio_dsa_param = &admfm2000_gpio_dsa_ip, + }; + + ret = admfm2000_init(&dev, &admfm2000_ip); + if (ret) + goto error; + + ret = admfm2000_set_channel_config(dev, ADMFM2000_MIXER_MODE); + if (ret) + goto error; + + ret = admfm2000_set_gain(dev, 0, 24); + if (ret) + goto error; + +ADMFM2000 no-OS IIO support +---------------------------- + +The ADMFM2000 IIO driver comes on top of ADMFM2000 driver and offers support for +interfacing IIO clients through IIO lib. + +ADMFM2000 IIO Device Configuration +----------------------------------- + +Device Attributes +----------------- + +The ADMFM2000 only has two channels, each having a configurable gain. + +Device Channels +--------------- + +ADMFM2000 IIO device has 2 output channels. + +The channels are: + +* output voltage0 - corresponding to channel 1 on the device +* output voltage1 - corresponding to channel 2 on the device + +Each channel has 1 individual attribute: + +* hardwaregain - is the dsa gain of the channel. The gain can be set between 0 + (reference) and 31. + +ADMFM2000 IIO Driver Initialization Example +-------------------------------------------- + +.. code-block:: bash + + struct admfm2000_iio_dev *admfm2000_iio_dev; + struct admfm2000_iio_dev_init_param admfm2000_iio_ip; + struct iio_app_desc *app; + struct iio_app_init_param app_init_param = { 0 }; + int ret; + + struct no_os_uart_init_param admfm2000_uart_ip = { + .device_id = UART_DEVICE_ID, + .irq_id = UART_IRQ_ID, + .asynchronous_rx = true, + .baud_rate = UART_BAUDRATE, + .size = NO_OS_UART_CS_8, + .parity = NO_OS_UART_PAR_NO, + .stop = NO_OS_UART_STOP_1_BIT, + .platform_ops = UART_OPS, + .extra = &xuip, + }; + + struct no_os_gpio_init_param admfm2000_gpio_ip[2][2] = { + { + { + .port = 2, + .number = 10, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 2, + .number = 6, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + }, + { + { + .port = 0, + .number = 12, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 0, + .number = 14, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + } + }; + + struct no_os_gpio_init_param admfm2000_gpio_dsa_ip[2][5] = { + { + { + .port = 2, + .number = 1, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 0, + .number = 11, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 2, + .number = 7, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 2, + .number = 8, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 2, + .number = 9, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + }, + { + { + .port = 2, + .number = 20, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 2, + .number = 21, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 3, + .number = 8, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 4, + .number = 1, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + { + .port = 0, + .number = 13, + .pull = NO_OS_PULL_NONE, + .platform_ops = GPIO_OPS, + .extra = GPIO_EXTRA, + }, + } + }; + + struct admfm2000_init_param admfm2000_ip = { + .mixer_mode = ADMFM2000_DIRECT_IF_MODE, + .dsa_gain = 0, + .gpio_sw_param = &admfm2000_gpio_ip, + .gpio_dsa_param = &admfm2000_gpio_dsa_ip + }; + + admfm2000_iio_ip.admfm2000_dev_init = &admfm2000_ip; + ret = admfm2000_iio_init(&admfm2000_iio_dev, &admfm2000_iio_ip); + if (ret) + return ret; + + struct iio_app_device iio_devices[] = { + { + .name = "admfm2000", + .dev = admfm2000_iio_dev, + .dev_descriptor = admfm2000_iio_dev->iio_dev, + } + }; + + app_init_param.devices = iio_devices; + app_init_param.nb_devices = NO_OS_ARRAY_SIZE(iio_devices); + app_init_param.uart_init_params = admfm2000_uart_ip; + + ret = iio_app_init(&app, app_init_param); + if (ret) + return ret; + + return iio_app_run(app); diff --git a/drivers/frequency/admfm2000/admfm2000.c b/drivers/frequency/admfm2000/admfm2000.c new file mode 100644 index 00000000000..92132d83bcf --- /dev/null +++ b/drivers/frequency/admfm2000/admfm2000.c @@ -0,0 +1,209 @@ +/***************************************************************************//** + * @file admfm2000.c + * @brief Implementation of admfm2000 Driver. + * @author Ramona Nechita (ramona.nechita@analog.com) +******************************************************************************** + * Copyright 2025(c) Analog Devices, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. “AS IS” AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ANALOG DEVICES, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#include "admfm2000.h" +#include "no_os_alloc.h" +#include "no_os_error.h" +#include "no_os_print_log.h" + +/** + * @brief Get the value of the dsa pins of the device. + * @param dev - The device structure. + * @param chan - The channel for which the value is read. + * @param dsa_raw - The raw value from DSA gpios. + * @return Returns 0 in case of success or negative error code otherwise. + */ +int admfm2000_get_dsa_raw(struct admfm2000_dev *dev, uint8_t chan, + int32_t *dsa_raw) +{ + int32_t ret; + uint8_t i; + uint8_t bit; + int32_t tmp = 0; + + if (chan > 1) + return -EINVAL; + + for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) { + ret = no_os_gpio_get_value(dev->gpio_dsa[chan][i], &bit); + if (ret != 0) + return ret; + tmp |= (bit << i); + } + + *dsa_raw = tmp; + + return 0; +} + +/** + * @brief Set the DSA GPIOs of the device. + * @param dev - The device structure. + * @param chan - The channel for which the value is set. + * @param dsa_raw - The raw value for GPIOs. + * @return Returns 0 in case of success or negative error code otherwise. + */ +int admfm2000_set_dsa_raw(struct admfm2000_dev *dev, uint8_t chan, + int32_t dsa_raw) +{ + int32_t ret; + uint8_t i; + + if (dsa_raw > ADMFM2000_MAX_GAIN_RAW || dsa_raw < ADMFM2000_MIN_GAIN_RAW) + return -EINVAL; + + for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) { + ret = no_os_gpio_set_value(dev->gpio_dsa[chan][i], + (dsa_raw & (1 << i)) >> i); + if (ret != 0) + return ret; + } + + return 0; +} + +/** + * @brief Set the channel configuration. + * @param dev - The device structure. + * @param config - The config + * @return Returns 0 in case of success or negative error code otherwise. + */ +int admfm2000_set_channel_config(struct admfm2000_dev *dev, uint8_t config) +{ + int32_t ret; + int32_t i; + + if (config > 1) + return -EINVAL; + + if (config) { + ret = no_os_gpio_set_value(dev->gpio_sw[0][0], 0); + ret = no_os_gpio_set_value(dev->gpio_sw[0][1], 1); + ret = no_os_gpio_set_value(dev->gpio_sw[1][0], 1); + ret = no_os_gpio_set_value(dev->gpio_sw[1][1], 0); + } else { + ret = no_os_gpio_set_value(dev->gpio_sw[0][0], 1); + ret = no_os_gpio_set_value(dev->gpio_sw[0][1], 0); + ret = no_os_gpio_set_value(dev->gpio_sw[1][0], 0); + ret = no_os_gpio_set_value(dev->gpio_sw[1][1], 1); + } + + return ret; +} + +/** + * @brief Initialize the admfm2000 device. + * @param device - The device structure. + * @param init_param - The structure that contains the device initial parameters. + * @return Returns 0 in case of success or negative error code otherwise. + */ +int admfm2000_init(struct admfm2000_dev **device, + struct admfm2000_init_param *init_param) +{ + int32_t ret; + uint8_t i, j; + struct admfm2000_dev *dev; + + dev = (struct admfm2000_dev *)no_os_calloc(1, sizeof(*dev)); + if (!dev) + return -ENOMEM; + + for (i = 0; i < ADMFM2000_NUM_CHANNELS; i++) { + for (j = 0; j < ADMFM2000_MODE_GPIOS; j++) { + ret = no_os_gpio_get(&dev->gpio_sw[i][j], + init_param->gpio_sw_param[i][j]); + if (ret) + goto error; + ret = no_os_gpio_direction_output(dev->gpio_sw[i][j], + NO_OS_GPIO_HIGH); + if (ret) + goto error; + } + } + + for (i = 0; i < ADMFM2000_NUM_CHANNELS; i++) { + for (j = 0; j < ADMFM2000_DSA_GPIOS; j++) { + ret = no_os_gpio_get(&dev->gpio_dsa[i][j], + init_param->gpio_dsa_param[i][j]); + if (ret) + goto error; + ret = no_os_gpio_direction_output(dev->gpio_dsa[i][j], + NO_OS_GPIO_HIGH); + if (ret) + goto error; + } + } + + ret = admfm2000_set_channel_config(dev, init_param->mixer_mode); + if (ret) + goto error; + + ret = admfm2000_set_dsa_raw(dev, 0, init_param->dsa_gain); + if (ret) + goto error; + + ret = admfm2000_set_dsa_raw(dev, 1, init_param->dsa_gain); + if (ret) + goto error; + + *device = dev; + + return 0; + +error: + no_os_free(dev); + + return ret; +} + +/** + * @brief Free the resources allocated by admfm2000_init(). + * @param dev - The device structure. + * @return ret - Result of the remove procedure. + */ +int admfm2000_remove(struct admfm2000_dev *dev) +{ + uint8_t i, j; + + for (i = 0; i < ADMFM2000_NUM_CHANNELS; i++) + for (j = 0; j < ADMFM2000_MODE_GPIOS; j++) + no_os_gpio_remove(dev->gpio_sw[i][j]); + + for (i = 0; i < ADMFM2000_NUM_CHANNELS; i++) + for (j = 0; j < ADMFM2000_DSA_GPIOS; j++) + no_os_gpio_remove(dev->gpio_dsa[i][j]); + + no_os_free(dev); + + return 0; +} diff --git a/drivers/frequency/admfm2000/admfm2000.h b/drivers/frequency/admfm2000/admfm2000.h new file mode 100644 index 00000000000..9fbca4fa3f1 --- /dev/null +++ b/drivers/frequency/admfm2000/admfm2000.h @@ -0,0 +1,84 @@ +/***************************************************************************//** + * @file admfm2000.h + * @brief Header file for admfm2000 Driver. + * @author Ramona Nechita (ramona.nechita@analog.com) +******************************************************************************** + * Copyright 2025(c) Analog Devices, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. “AS IS” AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ANALOG DEVICES, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef SRC_ADMFM2000_H_ +#define SRC_ADMFM2000_H_ + +#include +#include "no_os_gpio.h" + +#define ADMFM2000_MIXER_MODE 0 +#define ADMFM2000_DIRECT_IF_MODE 1 +#define ADMFM2000_DSA_GPIOS 5 +#define ADMFM2000_MODE_GPIOS 2 +#define ADMFM2000_MAX_GAIN 0 +#define ADMFM2000_MIN_GAIN -31000 +#define ADMFM2000_MAX_GAIN_RAW 31 +#define ADMFM2000_MIN_GAIN_RAW 0 +#define ADMFM2000_DEFAULT_GAIN -0x20 +#define ADMFM2000_NUM_CHANNELS 2 + +struct admfm2000_init_param { + /* Mixer Mode */ + uint8_t mixer_mode; + /* GAIN */ + int32_t dsa_gain; + /* GPIO Control Switch chan 0&1 */ + struct no_os_gpio_init_param *gpio_sw_param[2][2]; + /* GPIO Control DSA chan 0&1 */ + struct no_os_gpio_init_param *gpio_dsa_param[2][5]; +}; + +struct admfm2000_dev { + /* GPIO Control Switch chan 0&1 */ + struct no_os_gpio_desc *gpio_sw[2][2]; + /* GPIO Control DSA chan 0&1 */ + struct no_os_gpio_desc *gpio_dsa[2][5]; +}; + +int admfm2000_init(struct admfm2000_dev **device, + struct admfm2000_init_param *init_param); + +int admfm2000_remove(struct admfm2000_dev *dev); + +int admfm2000_set_dsa_raw(struct admfm2000_dev *dev, uint8_t chan, + int32_t dsa_raw); + +int admfm2000_get_dsa_raw(struct admfm2000_dev *dev, uint8_t chan, + int32_t *dsa_raw); + +int admfm2000_get_channel_mode(struct admfm2000_dev *dev, uint8_t mode); + +int admfm2000_set_channel_config(struct admfm2000_dev *dev, uint8_t config); + +#endif /* SRC_ADMFM2000_H_ */ diff --git a/drivers/frequency/admfm2000/iio_admfm2000.c b/drivers/frequency/admfm2000/iio_admfm2000.c new file mode 100644 index 00000000000..f0503090b37 --- /dev/null +++ b/drivers/frequency/admfm2000/iio_admfm2000.c @@ -0,0 +1,206 @@ +/***************************************************************************//** + * @file iio_admfm2000.c + * @brief Implementation of admfm2000 IIO Driver. + * @author Ramona Nechita (ramona.nechita@analog.com) +******************************************************************************** + * Copyright 2025(c) Analog Devices, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. “AS IS” AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ANALOG DEVICES, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#include +#include +#include +#include "no_os_error.h" +#include "no_os_util.h" +#include "iio_admfm2000.h" +#include "no_os_alloc.h" + +/** + * @brief Get the channel gain. + * @param dev - The iio device structure. + * @param buf - Command buffer to be filled with the data to be written. + * @param len - Length of the received command buffer in bytes. + * @param channel - Command channel info. + * @param priv - Command attribute id. + * @return - The size of the read data in case of success, error code + * otherwise. + */ +static int admfm2000_iio_read_gain(void *dev, char *buf, uint32_t len, + const struct iio_ch_info *channel, + intptr_t priv) +{ + struct admfm2000_iio_dev *iio_admfm2000 = (struct admfm2000_iio_dev *)dev; + struct admfm2000_dev *admfm2000_dev; + int32_t gain; + int32_t vals[2]; + int32_t tmp; + int32_t ret; + + if (!iio_admfm2000) + return -EINVAL; + + admfm2000_dev = iio_admfm2000->admfm2000_dev; + + if (!admfm2000_dev) + return -EINVAL; + + ret = admfm2000_get_dsa_raw(admfm2000_dev, channel->ch_num, &gain); + if (ret) + return ret; + tmp = ~(gain) * -1000; + vals[0] = tmp / 1000; + vals[1] = (tmp % 1000) * 1000; + return iio_format_value(buf, len, IIO_VAL_INT_PLUS_MICRO_DB, 1, + (int32_t *)vals); +} + +/** + * @brief Set the channel gain. + * @param dev - The iio device structure. + * @param buf - Command buffer to be filled with the data to be written. + * @param len - Length of the received command buffer in bytes. + * @param channel - Command channel info. + * @param priv - Command attribute id. + * @return - The size of the read data in case of success, error code + * otherwise. + */ +static int admfm2000_iio_write_gain(void *dev, char *buf, uint32_t len, + const struct iio_ch_info *channel, + intptr_t priv) +{ + struct admfm2000_iio_dev *iio_admfm2000 = (struct admfm2000_iio_dev *)dev; + struct admfm2000_dev *admfm2000_dev; + int32_t val, val2; + int32_t gain, tmp; + int32_t ret; + + if (!iio_admfm2000) + return -EINVAL; + + admfm2000_dev = iio_admfm2000->admfm2000_dev; + + if (!admfm2000_dev) + return -EINVAL; + + ret = iio_parse_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, &val, &val2); + if (ret) + return ret; + + if (val < 0) + tmp = (val * 1000) - (val2 / 1000); + else + tmp = (val * 1000) + (val2 / 1000); + gain = ~((abs(tmp) / 1000)) & 0x1F; + ret = admfm2000_set_dsa_raw(admfm2000_dev, channel->ch_num, gain); + if (ret) + return ret; + + return len; +} + +static struct iio_attribute admfm2000_iio_ch_attrs[] = { + { + .name = "hardwaregain", + .shared = IIO_SEPARATE, + .show = admfm2000_iio_read_gain, + .store = admfm2000_iio_write_gain, + }, + END_ATTRIBUTES_ARRAY +}; + +static struct iio_channel admfm2000_channels[] = { + { + .ch_type = IIO_VOLTAGE, + .channel = 0, + .ch_out = true, + .indexed = true, + .attributes = admfm2000_iio_ch_attrs, + .scan_type = NULL, + }, + { + .ch_type = IIO_VOLTAGE, + .channel = 1, + .ch_out = true, + .indexed = true, + .attributes = admfm2000_iio_ch_attrs, + .scan_type = NULL, + }, +}; + +static struct iio_device admfm2000_iio_dev = { + .num_ch = NO_OS_ARRAY_SIZE(admfm2000_channels), + .channels = admfm2000_channels, +}; + +/** + * @brief Initialize the iio device. + * @param iio_dev - The iio device structure. + * @param init_param - The iio device initialization structure. + * @return SUCCESS in case of success, FAILURE otherwise. + */ +int admfm2000_iio_init(struct admfm2000_iio_dev **iio_dev, + struct admfm2000_iio_dev_init_param *init_param) +{ + struct admfm2000_iio_dev *iio_admfm2000; + struct admfm2000_dev *admfm2000_dev; + int32_t ret; + + iio_admfm2000 = (struct admfm2000_iio_dev *)no_os_calloc(1, + sizeof(*iio_admfm2000)); + if (!iio_admfm2000) + return -ENOMEM; + + iio_admfm2000->iio_dev = &admfm2000_iio_dev; + + ret = admfm2000_init(&iio_admfm2000->admfm2000_dev, + init_param->admfm2000_dev_init); + if (ret) + return ret; + + *iio_dev = iio_admfm2000; + + return 0; +} + +/** + * @brief Free the resources allocated by admfm2000_iio_init(). + * @param desc - The iio device structure. + * @return SUCCESS in case of success, FAILURE otherwise. + */ +int admfm2000_iio_remove(struct admfm2000_iio_dev *desc) +{ + int32_t ret; + + ret = admfm2000_remove(desc->admfm2000_dev); + if (ret) + return ret; + + no_os_free(desc->admfm2000_dev); + no_os_free(desc); + + return 0; +} diff --git a/drivers/frequency/admfm2000/iio_admfm2000.h b/drivers/frequency/admfm2000/iio_admfm2000.h new file mode 100644 index 00000000000..69c80457df6 --- /dev/null +++ b/drivers/frequency/admfm2000/iio_admfm2000.h @@ -0,0 +1,55 @@ +/***************************************************************************//** + * @file iio_admfm2000.h + * @brief Header file for admfm2000 IIO Driver. + * @author Ramona Nechita (ramona.nechita@analog.com) +******************************************************************************** + * Copyright 2025(c) Analog Devices, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. “AS IS” AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ANALOG DEVICES, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef SRC_IIO_ADMFM2000_H_ +#define SRC_IIO_ADMFM2000_H_ + +#include +#include "admfm2000.h" +#include "iio.h" + +struct admfm2000_iio_dev { + struct admfm2000_dev *admfm2000_dev; + struct iio_device *iio_dev; +}; + +struct admfm2000_iio_dev_init_param { + struct admfm2000_init_param *admfm2000_dev_init; +}; + +int admfm2000_iio_init(struct admfm2000_iio_dev **iio_dev, + struct admfm2000_iio_dev_init_param *init_param); + +int admfm2000_iio_remove(struct admfm2000_iio_dev *desc); + +#endif /* SRC_IIO_ADMFM2000_H_ */