1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
#
# Copyright 2019-2021 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
LMK04832 parent driver class
"""
import time
from usrp_mpm.mpmlog import get_logger
class LMK04832:
"""
Generic driver class for LMK04832 access.
"""
LMK_CHIP_ID = 6
LMK_PROD_ID = 0xD163
VCXO_FREQUENCIES = [122.88e6, 100.00e6]
LMK_VCO0_RANGE_MIN = 2440e6
LMK_VCO0_RANGE_MAX = 2580e6
LMK_VCO1_RANGE_MIN = 2945e6
LMK_VCO1_RANGE_MAX = 3255e6
# PLL2 Prescaler is in range from 2, 8
PLL2_PRESCALER = range(2, 9)
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 enable_4wire_spi(self):
"""Enable 4-wire SPI readback from the CLKin_SEL0 pin"""
self.poke8(0x148, 0x33)
self.enable_3wire_spi = False
def get_status(self):
"""
Returns PLL lock status as a dictionary
"""
pll1_status = self.check_plls_locked(pll="PLL1")
pll2_status = self.check_plls_locked(pll="PLL2")
return {"PLL1 lock": pll1_status, "PLL2 lock": pll2_status}
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"), "Invalid PLL specified"
result = True
pll_lock_status = self.peek8(0x183)
if pll in ("BOTH", "PLL1"):
# Lock status for PLL1 is 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 in ("BOTH", "PLL2"):
# Lock status for PLL2 is 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 wait_for_pll_lock(self, pll="BOTH", timeout=2000):
"""
Waits for the PLL(s) to lock.
Returns False if the PLL(s) do not lock before the timeout (in ms)
"""
# Sets and clears the CLR_PLLX_LD_LOST for PLL1 and PLL2
self.poke8(0x182, 0x03)
self.poke8(0x182, 0x00)
# Now poll lock status until timeout
end_time = time.monotonic() + (timeout / 1000)
while time.monotonic() < end_time:
time.sleep(0.1)
if self.check_plls_locked(pll):
return True
pll = "PLL1 or PLL2" if pll.upper() == "BOTH" else pll
self.log.debug("{} not reporting locked after {} ms wait".format(pll, timeout))
return False
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 its
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
# PLL1R_SYNC_EN (6) = 1
# PLL1R_SYNC_SRC (5,4) = Sync pin
# PLL2R_SYNC_EN (3) = 0
self.poke8(0x145, 0x50)
# 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) wait for the writes to complete by triggering a read
self.get_chip_id()
# 4) Send rising edge on SYNC pin
result = sync_pin_callback()
# 5) reset 0x145 to safe value (no sync enable set, sync src invalidated)
self.poke8(0x145, 0)
# 6) wait for PLL1 to lock
if result:
return self.wait_for_pll_lock("PLL1")
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
|