aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py
diff options
context:
space:
mode:
authorLars Amsel <lars.amsel@ni.com>2021-06-04 08:27:50 +0200
committerAaron Rossetto <aaron.rossetto@ni.com>2021-06-10 12:01:53 -0500
commit2a575bf9b5a4942f60e979161764b9e942699e1e (patch)
tree2f0535625c30025559ebd7494a4b9e7122550a73 /mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py
parente17916220cc955fa219ae37f607626ba88c4afe3 (diff)
downloaduhd-2a575bf9b5a4942f60e979161764b9e942699e1e.tar.gz
uhd-2a575bf9b5a4942f60e979161764b9e942699e1e.tar.bz2
uhd-2a575bf9b5a4942f60e979161764b9e942699e1e.zip
uhd: Add support for the USRP X410
Co-authored-by: Lars Amsel <lars.amsel@ni.com> Co-authored-by: Michael Auchter <michael.auchter@ni.com> Co-authored-by: Martin Braun <martin.braun@ettus.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Cristina Fuentes <cristina.fuentes-curiel@ni.com> Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com> Co-authored-by: Virendra Kakade <virendra.kakade@ni.com> Co-authored-by: Lane Kolbly <lane.kolbly@ni.com> Co-authored-by: Max Köhler <max.koehler@ni.com> Co-authored-by: Andrew Lynch <andrew.lynch@ni.com> Co-authored-by: Grant Meyerhoff <grant.meyerhoff@ni.com> Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com> Co-authored-by: Thomas Vogel <thomas.vogel@ni.com>
Diffstat (limited to 'mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py')
-rw-r--r--mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py204
1 files changed, 204 insertions, 0 deletions
diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py b/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py
new file mode 100644
index 000000000..927db3360
--- /dev/null
+++ b/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py
@@ -0,0 +1,204 @@
+#
+# Copyright 2021 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+"""
+X4xx motherboard CPLD control
+"""
+
+from usrp_mpm import lib # Pulls in everything from C++-land
+
+def parse_encoded_git_hash(encoded):
+ git_hash = encoded & 0x0FFFFFFF
+ tree_dirty = ((encoded & 0xF0000000) > 0)
+ dirtiness_qualifier = 'dirty' if tree_dirty else 'clean'
+ return (git_hash, dirtiness_qualifier)
+
+class MboardCPLD:
+ """
+ Control for the CPLD.
+ """
+ # pylint: disable=bad-whitespace
+ SIGNATURE_OFFSET = 0x0000
+ COMPAT_REV_OFFSET = 0x0004
+ OLDEST_COMPAT_REV_OFFSET = 0x0008
+ GIT_HASH_OFFSET = 0x0010
+ DB_ENABLE_OFFSET = 0x0020
+ SERIAL_NO_LO_OFFSET = 0x0034
+ SERIAL_NO_HI_OFFSET = 0x0038
+ CMI_OFFSET = 0x003C
+
+ # change these revisions only on breaking changes
+ OLDEST_REQ_COMPAT_REV = 0x20122114
+ REQ_COMPAT_REV = 0x20122114
+ SIGNATURE = 0x0A522D27
+ PLL_REF_CLOCK_ENABLED = 1 << 2
+ ENABLE_CLK_DB0 = 1 << 8
+ ENABLE_CLK_DB1 = 1 << 9
+ ENABLE_PRC = 1 << 10
+ DISABLE_CLK_DB0 = 1 << 12
+ DISABLE_CLK_DB1 = 1 << 13
+ DISABLE_PRC = 1 << 14
+ RELEASE_RST_DB0 = 1 << 16
+ RELEASE_RST_DB1 = 1 << 17
+ ASSERT_RST_DB0 = 1 << 20
+ ASSERT_RST_DB1 = 1 << 21
+ # pylint: enable=bad-whitespace
+
+ def __init__(self, spi_dev_node, log):
+ self.log = log.getChild("CPLD")
+ self.regs = lib.spi.make_spidev_regs_iface(
+ spi_dev_node,
+ 1000000, # Speed (Hz)
+ 0, # SPI mode
+ 32, # Addr shift
+ 0, # Data shift
+ 0, # Read flag
+ 1<<47 # Write flag
+ )
+ self.poke32 = self.regs.poke32
+ self.peek32 = self.regs.peek32
+
+ def enable_pll_ref_clk(self, enable=True):
+ """
+ Enables or disables the PLL reference clock.
+
+ This makes no assumptions on the prior state of the clock. It does check
+ if the clock-enable was successful by polling the CPLD, and throws if
+ not.
+ """
+ def check_pll_enabled():
+ return self.peek32(self.DB_ENABLE_OFFSET) & self.PLL_REF_CLOCK_ENABLED
+ if enable:
+ self.poke32(self.DB_ENABLE_OFFSET, self.ENABLE_PRC)
+ if not check_pll_enabled():
+ self.log.error("PRC enable failed!")
+ raise RuntimeError('PRC enable failed!')
+ return
+ # Disable PRC:
+ self.poke32(self.DB_ENABLE_OFFSET, self.DISABLE_PRC)
+ if check_pll_enabled():
+ self.log.error('PRC reset failed!')
+ raise RuntimeError('PRC reset failed!')
+
+ def enable_daughterboard(self, db_id, enable=True):
+ """ Enable or disable clock forwarding to a given DB """
+ if db_id == 0:
+ release_reset = self.RELEASE_RST_DB0
+ assert_reset = self.ASSERT_RST_DB0
+ else:
+ release_reset = self.RELEASE_RST_DB1
+ assert_reset = self.ASSERT_RST_DB1
+ value = self.peek32(self.DB_ENABLE_OFFSET)
+ if enable:
+ # De-assert reset
+ value = (value | release_reset) & (~assert_reset)
+ else: #disable
+ # Assert reset
+ value = (value | assert_reset) & (~release_reset)
+ self.poke32(self.DB_ENABLE_OFFSET, value)
+
+ def enable_daughterboard_support_clock(self, db_id, enable=True):
+ """ Enable or disable clock forwarding to a given DB """
+ if db_id == 0:
+ clk_enable = self.ENABLE_CLK_DB0
+ clk_disable = self.DISABLE_CLK_DB0
+ else:
+ clk_enable = self.ENABLE_CLK_DB1
+ clk_disable = self.DISABLE_CLK_DB1
+ value = self.peek32(self.DB_ENABLE_OFFSET)
+ if enable:
+ # Enable clock
+ value = (value | clk_enable) & (~clk_disable)
+ else: #disable
+ # Disable clock
+ value = (value | clk_disable) & (~clk_enable)
+ self.poke32(self.DB_ENABLE_OFFSET, value)
+
+ def check_signature(self):
+ """
+ Assert that the CPLD signature is correct. If the CPLD 'signature'
+ register returns something unexpectected, throws a RuntimeError.
+ """
+ read_signature = self.peek32(self.SIGNATURE_OFFSET)
+ if self.SIGNATURE != read_signature:
+ self.log.error('MB PS CPLD signature {:X} does not match '
+ 'expected value {:X}'.format(read_signature, self.SIGNATURE))
+ raise RuntimeError('MB PS CPLD signature {:X} does not match '
+ 'expected value {:X}'.format(read_signature, self.SIGNATURE))
+
+ def check_compat_version(self):
+ """
+ Check oldest compatible revision offset of HW against required revision.
+ The value has to match as the register offsets depends on them.
+ Furthermore there needs to be a minimum revision to check for existence
+ of functionality.
+ """
+ cpld_image_compat_revision = self.peek32(self.OLDEST_COMPAT_REV_OFFSET)
+ if cpld_image_compat_revision < self.OLDEST_REQ_COMPAT_REV:
+ error_message = (
+ 'MB CPLD oldest compatible revision'
+ f' 0x{cpld_image_compat_revision:08x} is out of date. Update'
+ f' your CPLD image to 0x{self.OLDEST_REQ_COMPAT_REV:08x}.')
+ self.log.error(error_message)
+ raise RuntimeError(error_message)
+ if cpld_image_compat_revision > self.OLDEST_REQ_COMPAT_REV:
+ error_message = (
+ 'MB CPLD oldest compatible revision'
+ f' 0x{cpld_image_compat_revision:08x} is unknown. Downgrade'
+ f' your CPLD image to 0x{self.OLDEST_REQ_COMPAT_REV:08x}.')
+ self.log.error(error_message)
+ raise RuntimeError(error_message)
+
+ if not self.has_compat_version(self.REQ_COMPAT_REV):
+ error_message = (
+ "MB CPLD compatible revision is too old. Update your CPLD"
+ f" image to at least 0x{self.REQ_COMPAT_REV:08x}.")
+ self.log.error(error_message)
+ raise RuntimeError(error_message)
+
+ def has_compat_version(self, min_required_version):
+ """
+ Check for a minimum required version.
+ """
+ if min_required_version < self.REQ_COMPAT_REV:
+ self.log.warning(
+ "Somebody called MB CPLD has_compat_version with revision"
+ f" 0x{min_required_version:x} which is older than the mandated"
+ f" version 0x{self.REQ_COMPAT_REV:x}.")
+ cpld_image_compat_revision = self.peek32(self.COMPAT_REV_OFFSET)
+ return cpld_image_compat_revision >= min_required_version
+
+ def trace_git_hash(self):
+ """
+ Trace build of MB CPLD
+ """
+ git_hash_rb = self.peek32(self.GIT_HASH_OFFSET)
+ (git_hash, dirtiness_qualifier) = parse_encoded_git_hash(git_hash_rb)
+ self.log.trace("MB CPLD build GIT Hash: {:07x} ({})".format(
+ git_hash, dirtiness_qualifier))
+
+ def set_serial_number(self, serial_number):
+ """
+ Set serial number register
+ """
+ assert len(serial_number) > 0
+ assert len(serial_number) <= 8
+ serial_number_string = str(serial_number, 'ascii')
+ serial_number_int = int(serial_number_string, 16)
+ self.poke32(self.SERIAL_NO_LO_OFFSET, serial_number_int & 0xFFFFFFFF)
+ self.poke32(self.SERIAL_NO_HI_OFFSET, serial_number_int >> 32)
+
+ def set_cmi_device_ready(self, ready=True):
+ """
+ Inform CMI partner that this device is ready for PCI-Express communication.
+ """
+ value = 1 if ready else 0
+ self.poke32(self.CMI_OFFSET, value)
+
+ def get_cmi_status(self):
+ """
+ Return true if upstream CMI device was found.
+ """
+ return bool(self.peek32(self.CMI_OFFSET))