diff options
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 |