The ADAR1000 is a 4-channel, X and Ku frequency band, beamforming core chip for phased arrays. This device operates in half-duplex between receive and transmit modes. In receive mode, input signals pass through four receive channels and are combined in a common RF_IO pin. In transmit mode, the RF_IO input signal is split and passes through the four transmit channels. In both modes, the ADAR1000 provides a ≥31 dB gain adjustment range and a full 360° phase adjustment range in the radio frequency (RF) path, with 6-bit resolution (less than ≤0.5 dB and 2.8°, respectively).
This is a Linux industrial I/O (IIO) subsystem driver, targeting RF Transceivers. The industrial I/O subsystem provides a unified framework for drivers for many different types of converters and sensors using a number of different physical interfaces (i2c, spi, etc). See IIO for more information.
Function | File |
---|---|
driver | drivers/iio/beamformer/adar1000.c |
devicetree bindings | Documentation/devicetree/bindings/iio/adc/adi,adar1000.yaml |
The ADAR1000 driver is a spi-bus driver and can currently only be instantiated via device tree.
Required devicetree properties:
Function | File |
---|---|
RPI Device Tree | rpi-adar1000-overlay.dts |
Configure kernel with “make menuconfig” (alternatively use “make xconfig” or “make qconfig”)
The ADAR1000 driver depends on CONFIG_SPI
Configure kernel with “make menuconfig” (alternatively use “make xconfig” or “make qconfig”)
Linux Kernel Configuration Device Drivers ---> <*> Industrial I/O support ---> --- Industrial I/O support -*- Enable ring buffer support within IIO -*- Industrial I/O lock free software ring -*- Enable triggered sampling support *** Analog to digital converters *** [--snip--] <*> Analog Devices ADAR1000 Beamformer driver [--snip--]
Each and every IIO device, typically a hardware chip, has a device folder under /sys/bus/iio/devices/iio:deviceX. Where X is the IIO index of the device. Under every of these directory folders reside a set of files, depending on the characteristics and features of the hardware device in question. These files are consistently generalized and documented in the IIO ABI documentation. In order to determine which IIO deviceX corresponds to which hardware device, the user can read the name file /sys/bus/iio/devices/iio:deviceX/name. In case the sequence in which the iio device drivers are loaded/registered is constant, the numbering is constant and may be known in advance.
General attribute naming convention:
in_voltage0_[…]: targets RX1
in_voltage1_[…]: targets RX2
out_voltage0_[…]: targets TX1
out_voltage1_[…]: targets TX2
This specifies any shell prompt running on the target
root:/> cd /sys/bus/iio/devices/ root:/sys/bus/iio/devices> ls iio:device0 iio:device1 iio:device2 iio:device3 root:/sys/bus/iio/devices> cd iio:device0 root:/sys/bus/iio/devices/iio:device1# ls -l total 0 -rw-rw-rw- 1 root root 4096 Oct 27 09:10 bias_current_rx -rw-rw-rw- 1 root root 4096 Oct 27 09:10 bias_current_rx_lna -rw-rw-rw- 1 root root 4096 Oct 27 09:10 bias_current_tx -rw-rw-rw- 1 root root 4096 Oct 27 09:10 bias_current_tx_drv -rw-rw-rw- 1 root root 4096 Oct 27 09:10 dev -rw-rw-rw- 1 root root 4096 Oct 27 09:10 gen_clk_cycles -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_temp0_raw -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage0_RX_beam_pos_load -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage0_RX_beam_pos_save -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage0_RX_hardwaregain -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage0_RX_phase -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage0_RX_powerdown -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage1_RX_beam_pos_load -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage1_RX_beam_pos_save -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage1_RX_hardwaregain -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage1_RX_phase -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage1_RX_powerdown -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage2_RX_beam_pos_load -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage2_RX_beam_pos_save -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage2_RX_hardwaregain -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage2_RX_phase -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage2_RX_powerdown -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage3_RX_beam_pos_load -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage3_RX_beam_pos_save -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage3_RX_hardwaregain -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage3_RX_phase -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage3_RX_powerdown -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage_bias_set_load -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage_bias_set_save -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage_sequence_end -rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage_sequence_start -rw-rw-rw- 1 root root 4096 Oct 27 09:10 label -rw-rw-rw- 1 root root 4096 Oct 27 09:10 lna_bias_off -rw-rw-rw- 1 root root 4096 Oct 27 09:10 lna_bias_on -rw-rw-rw- 1 root root 4096 Oct 27 09:10 name lrwxrwxrwx 1 root root 0 Oct 27 09:10 of_node -> ../../../../../../../../firmware/devicetree/base/fpga-axi@0/axi_quad_spi@85200000/adar1000@1/dev@0 -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_beam_pos_load -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_beam_pos_save -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_detector_en -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_hardwaregain -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_pa_bias_off -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_pa_bias_on -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_phase -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_powerdown -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_raw -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_beam_pos_load -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_beam_pos_save -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_detector_en -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_hardwaregain -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_pa_bias_off -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_pa_bias_on -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_phase -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_powerdown -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_raw -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_beam_pos_load -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_beam_pos_save -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_detector_en -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_hardwaregain -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_pa_bias_off -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_pa_bias_on -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_phase -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_powerdown -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_raw -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_beam_pos_load -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_beam_pos_save -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_detector_en -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_hardwaregain -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_pa_bias_off -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_pa_bias_on -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_phase -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_powerdown -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_raw -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage_bias_set_load -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage_bias_set_save -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage_sequence_end -rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage_sequence_start -rw-rw-rw- 1 root root 4096 Oct 27 09:10 phase_table_config drwxrwxrwx 2 root root 0 Oct 27 09:10 power -rw-rw-rw- 1 root root 4096 Oct 27 09:10 reset -rw-rw-rw- 1 root root 4096 Oct 27 09:10 rx_lna_enable -rw-rw-rw- 1 root root 4096 Oct 27 09:10 rx_vga_enable -rw-rw-rw- 1 root root 4096 Oct 27 09:10 rx_vm_enable -rw-rw-rw- 1 root root 4096 Oct 27 09:10 sequencer_enable lrwxrwxrwx 1 root root 0 Oct 27 09:10 subsystem -> ../../../../../../../../bus/iio -rw-rw-rw- 1 root root 4096 Oct 27 09:10 tx_lna_enable -rw-rw-rw- 1 root root 4096 Oct 27 09:10 tx_vga_enable -rw-rw-rw- 1 root root 4096 Oct 27 09:10 tx_vm_enable -rw-rw-rw- 1 root root 4096 Oct 27 09:10 uevent root:/sys/bus/iio/devices/iio:device0#
This specifies any shell prompt running on the target
root:/sys/bus/iio/devices/iio:device0> cat name adar1000
The default phase control is provided by table 13 of the ADAR1000 datasheet. This is the configuration used at device startup.
Standard Table:
Phase (Degrees) | I Reg (Hex) | Q Reg (Hex) |
---|---|---|
0 … 360 | 0x3F | 0x20 |
Custom phase tables can be loaded automatically during driver probe or anytime later via the phase_table_config sysfs attribute. Tables must be stored in the /firmware folder, or compiled into the kernel using the CONFIG_FIRMWARE_IN_KERNEL, CONFIG_EXTRA_FIRMWARE config options. The table loaded during driver probe can be specified using following device tree property:
adi,phasetable-name = “adar1000_std_phasetable”;
In case no table is specified or loaded, the driver will continue to use the provided standard phase table.
Phase table is stored in a human readable file, with the format specified below.
Example: adar1000_std_phasetable
<phasetable [ADAR1000] table entries=no_of_entries> phase_in_degrees, reg_I, reg_Q phase_in_degrees, reg_I, reg_Q phase_in_degrees, reg_I, reg_Q … </phasetable>
Assumptions:
This specifies any shell prompt running on the target
root@analog:/sys/bus/iio/devices/iio:device0# ls /firmware/adar* adar1000_std_phasetable root@analog:/sys/bus/iio/devices/iio:device0# cat /firmware/adar1000_std_phasetable > phase_table_config
Reading the phase_table_config attribute returns the current phase table that is loaded into the IC. The printed format matches the format used in the phasetable file.
This specifies any shell prompt running on the target
root@analog:/sys/bus/iio/devices/iio:device0# cat phase_table_config <phasetable ADAR1000 table entries=64> 0.000, 0x3F, 0x20 2.812, 0x3F, 0x21 5.625, 0x3F, 0x23 8.437, 0x3F, 0x24 11.250, 0x3F, 0x26 14.040, 0x3E, 0x27 16.875, 0x3E, 0x28 19.687, 0x3D, 0x2A 22.500, 0x3D, 0x2B 25.312, 0x3C, 0x2D [--snip--] 165.937, 0x1E, 0x27 168.750, 0x1E, 0x26 171.562, 0x1F, 0x24 174.375, 0x1F, 0x23 177.187, 0x1F, 0x21 </phasetable>
On-chip RAM is provided for storing phase and amplitude settings for up to 121 beam positions and seven bias settings for both transmit and receive modes.
A beam position can be written to ram and loaded afterwards from that memory location (index).
echo 0, -11.0, 15.4 > /sys/bus/iio/devices/iio\:device0/in_voltage1_RX_beam_pos_save
This command can be decoded using this enumeration:
Values for gain_value and phase_value must be in integer and fractional part. beam_position can take values from 0 to 120.
echo 10 > in_voltage1_RX_beam_pos_load
To load a position just write the position number in the load attribute for the required channel.
Seven memory locations are also provided for storing bias settings for all the transmit and receive channel subcircuits.
echo 0, 1, 2, 3, 4 > in_voltage_bias_set_save
The values written here correspond the the following diagram:
echo 1, 10, 2, 3, 5, 6, 7, 8, 9, 10, 11 > out_voltage_bias_set_save
The values written here correspond the the following diagram:
echo 5 > out_voltage_bias_set_load echo > in_voltage_bias_set_load
For loading a different bias setting the operations is the same for RX and TX channels.
The beam can be stepped sequentially through the positions stored in memory. To use this feature the following steps must be executed:
echo 10 > in_voltage_sequence_start echo 20 > in_voltage_sequence_end echo 1 > sequencer_enable echo 1 > gen_clk_cycles
Some IIO drivers feature an optional debug facility, allowing users to read or write registers directly. Special care needs to be taken when using this feature, since you can modify registers on the back of the driver.
Accessing debugfs requires root privileges.
In order to identify if the IIO device in question feature this option you first need to identify the IIO device number.
Therefore read the name attribute of each IIO device
This specifies any shell prompt running on the target
root@analog:~# grep "" /sys/bus/iio/devices/iio\:device*/name /sys/bus/iio/devices/iio:device0/name:ad7291 /sys/bus/iio/devices/iio:device1/name:ad9361-phy /sys/bus/iio/devices/iio:device2/name:xadc /sys/bus/iio/devices/iio:device3/name:adf4351-udc-rx-pmod /sys/bus/iio/devices/iio:device4/name:adf4351-udc-tx-pmod /sys/bus/iio/devices/iio:device5/name:cf-ad9361-dds-core-lpc /sys/bus/iio/devices/iio:device6/name:cf-ad9361-lpc root@analog:~#
Change directory to /sys/kernel/debug/iio/ iio:deviceX and check if the direct_reg_access file exists.
This specifies any shell prompt running on the target
root@analog:~# cd /sys/kernel/debug/iio/iio\:device1 root@analog:/sys/kernel/debug/iio/iio:device1# ls direct_reg_access direct_reg_access
Reading
This specifies any shell prompt running on the target
root@analog:/sys/kernel/debug/iio/iio:device1# echo 0x7 > direct_reg_access root@analog:/sys/kernel/debug/iio/iio:device1# cat direct_reg_access 0x40
Writing
Write ADDRESS VALUE
This specifies any shell prompt running on the target
root@analog:/sys/kernel/debug/iio/iio:device1# echo 0x7 0x50 > direct_reg_access root@analog:/sys/kernel/debug/iio/iio:device1# cat direct_reg_access 0x50
Accessing HDL CORE registers
Special ADI device driver convention for devices that have both:
In this case when accessing the HDL Core Registers always set BIT31.
The register map for typical ADI HDL cores can be found here: Register Map
This specifies any shell prompt running on the target
root@analog:/sys/kernel/debug/iio/iio:device6# echo 0x80000000 > direct_reg_access root@analog:/sys/kernel/debug/iio/iio:device6# cat direct_reg_access 0x80062