diff options
author | Toni Jones <toni.jones@ni.com> | 2019-01-08 17:23:24 +0000 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2021-02-22 10:15:13 -0600 |
commit | 2e441d296b1f98fd4939d4008430e8814e777e0c (patch) | |
tree | 582c03600dd20a9443a90a9d9fbd9a3291526a0c /mpm | |
parent | ecbb24505dc2662ecee9da1de95cf8f99dec0c9e (diff) | |
download | uhd-2e441d296b1f98fd4939d4008430e8814e777e0c.tar.gz uhd-2e441d296b1f98fd4939d4008430e8814e777e0c.tar.bz2 uhd-2e441d296b1f98fd4939d4008430e8814e777e0c.zip |
mpm: Add an LMK04832 base chip driver
Added an LMK04832 base chip driver which does basic register access, ID
validation, and PLL lock validation. This will act as the base class for
device specific drivers which control the chip. The code is similar to
the LMK04828 base driver class, but has a different register map
structure.
Diffstat (limited to 'mpm')
-rw-r--r-- | mpm/python/usrp_mpm/chips/CMakeLists.txt | 1 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/chips/__init__.py | 1 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/chips/lmk04832.py | 163 |
3 files changed, 165 insertions, 0 deletions
diff --git a/mpm/python/usrp_mpm/chips/CMakeLists.txt b/mpm/python/usrp_mpm/chips/CMakeLists.txt index 13704abf1..9bd7b9372 100644 --- a/mpm/python/usrp_mpm/chips/CMakeLists.txt +++ b/mpm/python/usrp_mpm/chips/CMakeLists.txt @@ -8,6 +8,7 @@ set(USRP_MPM_FILES ${USRP_MPM_FILES}) set(USRP_MPM_CHIP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk04828.py + ${CMAKE_CURRENT_SOURCE_DIR}/lmk04832.py ${CMAKE_CURRENT_SOURCE_DIR}/adf400x.py ${CMAKE_CURRENT_SOURCE_DIR}/ds125df410.py ) diff --git a/mpm/python/usrp_mpm/chips/__init__.py b/mpm/python/usrp_mpm/chips/__init__.py index 15b4a3704..60368ad6a 100644 --- a/mpm/python/usrp_mpm/chips/__init__.py +++ b/mpm/python/usrp_mpm/chips/__init__.py @@ -9,3 +9,4 @@ Chips submodule from .adf400x import ADF400x from .lmk04828 import LMK04828 +from .lmk04832 import LMK04832 diff --git a/mpm/python/usrp_mpm/chips/lmk04832.py b/mpm/python/usrp_mpm/chips/lmk04832.py new file mode 100644 index 000000000..f4b4482b1 --- /dev/null +++ b/mpm/python/usrp_mpm/chips/lmk04832.py @@ -0,0 +1,163 @@ +# +# Copyright 2019 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +LMK04832 parent driver class +""" + +from usrp_mpm.mpmlog import get_logger + +class LMK04832(): + """ + Generic driver class for LMK04832 access. + """ + LMK_CHIP_ID = 6 + LMK_PROD_ID = 0xD163 + + def __init__(self, regs_iface, parent_log=None): + self.log = \ + parent_log.getChild("LMK04832") if parent_log is not None \ + else get_logger("LMK04832") + self.regs_iface = regs_iface + assert hasattr(self.regs_iface, 'peek8') + assert hasattr(self.regs_iface, 'poke8') + self.poke8 = regs_iface.poke8 + self.peek8 = regs_iface.peek8 + self.enable_3wire_spi = False + + def pokes8(self, addr_vals): + """ + Apply a series of pokes. + pokes8([(0,1),(0,2)]) is the same as calling poke8(0,1), poke8(0,2). + """ + for addr, val in addr_vals: + self.poke8(addr, val) + + def get_chip_id(self): + """ + Read back the chip ID + """ + chip_id = self.peek8(0x03) + self.log.trace("Chip ID Readback: {}".format(chip_id)) + return chip_id + + def get_product_id(self): + """ + Read back the product ID + """ + prod_id_0 = self.peek8(0x04) + prod_id_1 = self.peek8(0x05) + prod_id = (prod_id_1 << 8) \ + | prod_id_0 + self.log.trace("Product ID Readback: 0x{:X}".format(prod_id)) + return prod_id + + def verify_chip_id(self): + """ + Returns True if the chip ID and product ID matches what we expect, False otherwise. + """ + chip_id = self.get_chip_id() + prod_id = self.get_product_id() + if chip_id != self.LMK_CHIP_ID: + self.log.error("Wrong Chip ID 0x{:X}".format(chip_id)) + return False + if prod_id != self.LMK_PROD_ID: + self.log.error("Wrong Product ID 0x{:X}".format(prod_id)) + return False + return True + + def check_plls_locked(self, pll='BOTH'): + """ + Returns True if the specified PLLs are locked, False otherwise. + """ + pll = pll.upper() + assert pll in ('BOTH', 'PLL1', 'PLL2'), 'Cannot check lock for invalid PLL' + result = True + pll_lock_status = self.peek8(0x183) + + if pll == 'BOTH' or pll == 'PLL1': + # Lock status for PLL1 should be 0x01 on bits [3:2] + if (pll_lock_status & 0xC) != 0x04: + self.log.debug("PLL1 reporting unlocked... Status: 0x{:x}" + .format(pll_lock_status)) + result = False + if pll == 'BOTH' or pll == 'PLL2': + # Lock status for PLL2 should be 0x01 on bits [1:0] + if (pll_lock_status & 0x3) != 0x01: + self.log.debug("PLL2 reporting unlocked... Status: 0x{:x}" + .format(pll_lock_status)) + result = False + return result + + def clr_ld_lost_sticky(self): + """ + Sets and clears the CLR_PLLX_LD_LOST for PLL1 and PLL2 + """ + self.poke8(0x182, 0x03) + self.poke8(0x182, 0x00) + + def soft_reset(self, value=True): + """ + Performs a soft reset of the LMK04832 by setting or unsetting the reset register. + """ + reset_addr = 0 + if value: # Reset + reset_byte = 0x80 + else: # Clear Reset + reset_byte = 0x00 + if not self.enable_3wire_spi: + reset_byte |= 0x10 + self.poke8(reset_addr, reset_byte) + + + def pll1_r_divider_sync(self, sync_pin_callback): + """ + Run PLL1 R Divider sync according to + http://www.ti.com/lit/ds/snas688c/snas688c.pdf chapter 8.3.1.1 + + Rising edge on sync pin is done by an callback which has to return it's + success state. If the sync callback was successful, returns PLL1 lock + state as overall success otherwise the method fails. + """ + + # 1) Setup device for synchronizing PLL1 R + self.poke8(0x145, 0x50) # PLL1R_SYNC_EN (6) = 1 + # PLL1R_SYNC_SRC (5,4) = Sync pin + # PLL2R_SYNC_EN (3) = 0 + + # Do NOT change clkin0_TYPE and Clkin[0,1]_DEMUX. + # Both are set in initialization and remain static. + + # 2) Arm PLL1 R divider for synchronization + self.poke8(0x177, 0x20) + self.poke8(0x177, 0) + + # 3) Send rising edge on SYNC pin + result = sync_pin_callback() + + # reset 0x145 to safe value (no sync enable set, sync src invalidated + self.poke8(0x145, 0) + + if result: + return self.wait_for_pll_lock("PLL1") + else: + return False + + ## Register bitfield definitions ## + + def pll2_pre_to_reg(self, prescaler, osc_field=0x01, xtal_en=0x0, ref_2x_en=0x0): + """ + From the prescaler value, returns the register value combined with the other + register fields. + """ + # valid prescaler values are 2-8, where 8 is represented as 0x00. + assert prescaler in range(2, 8+1) + reg_val = ((prescaler & 0x07) << 5) \ + | ((osc_field & 0x3) << 2) \ + | ((xtal_en & 0x1) << 1) \ + | ((ref_2x_en & 0x1) << 0) + self.log.trace("From prescaler value 0d{}, writing register as 0x{:02X}." + .format(prescaler, reg_val)) + return reg_val |