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
|
#
# Copyright 2020 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
LMK05318 parent driver class
"""
import time
import datetime
from usrp_mpm.mpmlog import get_logger
class LMK05318:
"""
Generic driver class for LMK05318 access.
"""
LMK_VENDOR_ID = 0x100B
LMK_PROD_ID = 0x35
LMK_EEPROM_REG_COMMIT = "register_commit"
LMK_EEPROM_DIRECT_WRITE = "direct_write"
def __init__(self, regs_iface, parent_log=None):
self.log = \
parent_log.getChild("LMK05318") if parent_log is not None \
else get_logger("LMK05318")
self.regs_iface = regs_iface
assert hasattr(self.regs_iface, 'peek8')
assert hasattr(self.regs_iface, 'poke8')
self.peek8 = regs_iface.peek8
def poke8(self, addr, val, overwrite_mask=False):
"""
Write val to addr via register interface
"""
# TI LMK UserGuide chapter 9.5.5 states that some register require bit masks
# to be applied to bits to avoid writing to them
# mask is in the form that a 1 means that the bit shall not be modified
# in order to write to the address without the mask applied
# the overwrite_mask parameter can be set to True
if not overwrite_mask:
mask = None
if addr == 0x0C:
mask = 0xA7
elif addr == 0x9D:
mask = 0xFF
elif addr == 0xA4:
mask = 0xFF
elif addr in range(0x161, 0x1B3):
mask = 0xFF
if mask is not None:
current_val = self.peek8(addr)
val = val & ~mask
val = val | current_val
self.log.trace(
"Attention: writing to register {:02x} with masked bits, "
"mask 0x{:02x} was applied, resulting in value {:02x}"
.format(addr, mask, val))
self.regs_iface.poke8(addr, val)
def pokes8(self, addr_vals, overwrite_mask=False):
"""
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, overwrite_mask)
def get_vendor_id(self):
""" Read back the chip's vendor ID"""
vendor_id_high = self.peek8(0x00)
vendor_id_low = self.peek8(0x01)
vendor_id = (vendor_id_high << 8) \
| vendor_id_low
self.log.trace("Vendor ID Readback: 0x{:X}".format(vendor_id))
return vendor_id
def get_product_id(self):
"""
Read back the chip's product ID
"""
prod_id = self.peek8(0x02)
self.log.trace("Product ID Readback: 0x{:X}".format(prod_id))
return prod_id
def is_chip_id_valid(self):
"""
Returns True if the chip ID and product ID matches what we expect,
False otherwise.
"""
vendor_id = self.get_vendor_id()
prod_id = self.get_product_id()
if vendor_id != self.LMK_VENDOR_ID:
self.log.error("Wrong Vendor ID 0x{:X}".format(vendor_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 soft_reset(self, value=True):
"""
Performs a soft reset of the LMK05318 by setting or unsetting
the reset register
"""
reset_addr = 0xC #DEV_CTL
if value: # Reset
reset_byte = 0x80
else: # Clear Reset
reset_byte = 0x7F & self.peek8(reset_addr)
self.poke8(reset_addr, reset_byte, overwrite_mask=True)
def write_cfg_regs_to_eeprom(self, method, eeprom_data=None):
"""
program the current register config to LMK eeprom
"""
def _wait_for_busy(self, value):
wait_until = datetime.datetime.now() + datetime.timedelta(seconds=2)
while datetime.datetime.now() < wait_until:
self.log.trace("wait till busy bit becomes {}".format(value))
busy = (self.peek8(0x9D) >> 2) & 1 # check if busy bit is cleared
if busy == value:
return True
time.sleep(0.01)
return False
if method == self.LMK_EEPROM_REG_COMMIT:
self.log.trace("write current device register content to EEPROM")
#store current cfg to SRAM
self.poke8(0x9D, 0x40, overwrite_mask=True)
time.sleep(0.01)
#unlock EEPROM
self.poke8(0xA4, 0xEA, overwrite_mask=True)
time.sleep(0.01)
#store SRAM into EEPROM
self.poke8(0x9D, 0x03, overwrite_mask=True)
#the actual programming takes about 230ms, poll the busy bit to see when it's done
if not _wait_for_busy(self, 1):
self.log.error("EEPROM does not start programming, something went wrong")
if not _wait_for_busy(self, 0):
self.log.error("EEPROM is still busy after programming, something went wrong")
elif method == self.LMK_EEPROM_DIRECT_WRITE:
raise RuntimeError("direct LMK05318 EEPROM programming not implemented")
else:
raise RuntimeError("Invalid method for LMK05318 EEPROM programming")
#lock EEPROM
self.poke8(0xA4, 0x00, overwrite_mask=True)
self.log.trace("programming EEPROM done, power-cycle or hard-reset to take effect")
def write_eeprom_to_cfg_regs(self):
"""
read register cfg from eeprom and store it into registers
"""
self.poke8(0x9D, 0x08, overwrite_mask=True)
def get_eeprom_prog_cycles(self):
"""
returns the number of eeprom programming cycles
note:
the actual counter only increases after programming AND power-cycle/hard-reset
so multiple programming cycles without power cycle will lead to wrong counter values
"""
return self.peek8(0x9C)
def get_status_dpll(self):
"""
returns the status register of the DPLL as human readable string
"""
status = self.peek8(0x0E)
return f"""
Loss of phase lock: {status>>7 & 1}
Loss of freq. lock: {status>>6 & 1}
Tuning word update: {status>>5 & 1}
Holdover Event: {status>>4 & 1}
Reference Switch Event: {status>>3 & 1}
Active ref. missing clk: {status>>2 & 1}
Active ref. loss freq.: {status>>1 & 1}
Active ref. loss ampl.: {status & 1}
"""
def get_status_pll_xo(self):
"""
returns the status register of the PLLs and XO as human readable string
"""
status = self.peek8(0x0D)
return f"""
Loss of freq. detection XO: {status>>4 & 1}
Loss of lock APLL2: {status>>3 & 1}
Loss of lock APLL1: {status>>2 & 1}
Loss of source XO: {status & 1}
"""
|