1#
2# Copyright 2017 Ettus Research, a National Instruments Company
3#
4# SPDX-License-Identifier: GPL-3.0-or-later
5#
6"""
7LMK04828 parent driver class
8"""
9
10import math
11from builtins import object
12from usrp_mpm.mpmlog import get_logger
13
14class LMK04828(object):
15    """
16    Generic driver class for LMK04828 access.
17    """
18    LMK_CHIP_ID = 6
19
20    def __init__(self, regs_iface, parent_log=None):
21        self.log = \
22            parent_log.getChild("LMK04828") if parent_log is not None \
23            else get_logger("LMK04828")
24        self.regs_iface = regs_iface
25        assert hasattr(self.regs_iface, 'peek8')
26        assert hasattr(self.regs_iface, 'poke8')
27        self.poke8 = regs_iface.poke8
28        self.peek8 = regs_iface.peek8
29
30    def pokes8(self, addr_vals):
31        """
32        Apply a series of pokes.
33        pokes8((0,1),(0,2)) is the same as calling poke8(0,1), poke8(0,2).
34        """
35        for addr, val in addr_vals:
36            self.poke8(addr, val)
37
38    def get_chip_id(self):
39        """
40        Read back the chip ID
41        """
42        chip_id = self.peek8(0x03)
43        self.log.trace("Chip ID Readback: {}".format(chip_id))
44        return chip_id
45
46    def verify_chip_id(self):
47        """
48        Returns True if the chip ID matches what we expect, False otherwise.
49        """
50        chip_id = self.get_chip_id()
51        if chip_id != self.LMK_CHIP_ID:
52            self.log.error("Wrong Chip ID 0x{:X}".format(chip_id))
53            return False
54        return True
55
56    def check_plls_locked(self):
57        """
58        Returns True if both PLLs are locked, False otherwise.
59        """
60        def check_pll_lock(pll_id, addr):
61            """
62            pll_id -- A string defining the PLL (e.g. 'PLL1')
63            addr -- The address to peek to see if it's locked
64            """
65            pll_lock_status = self.regs_iface.peek8(addr)
66            if (pll_lock_status & 0x7) != 0x02:
67                self.log.debug("{} reporting unlocked... Status: 0x{:x}"
68                               .format(pll_id, pll_lock_status))
69                return False
70            return True
71        lock_status = \
72                check_pll_lock("PLL1", 0x182) and \
73                check_pll_lock("PLL2", 0x183)
74        return lock_status
75
76
77## Register bitfield definitions ##
78
79    def divide_to_cnth_cntl_reg(self, divide_val):
80        """
81        From the divider value, returns the CNTL and CNTH register value.
82        Split divider value in half. If odd, round up for the CNTL and down
83        for the CNTH based on the datasheet recommendation.
84        """
85        cntl = int(math.ceil( divide_val/2.0))
86        cnth = int(math.floor(divide_val/2.0))
87        reg_val = ((cnth & 0xF) << 4) | (cntl & 0xF)
88        self.log.trace("From divider value 0d{}, writing CNTH/L as 0x{:02X}."
89                       .format(divide_val, reg_val))
90        return reg_val
91
92    def divide_to_reg(self, divide_val, in_drive=0x1, out_drive=0x1):
93        """
94        From the divider value, returns the register value combined with the other
95        register fields.
96        """
97        reg_val = (divide_val & 0x1F) | ((in_drive & 0x1) << 5) | ((out_drive & 0x1) << 6)
98        self.log.trace("From divider value 0d{}, writing divider register as 0x{:02X}."
99                       .format(divide_val, reg_val))
100        return reg_val
101
102    def pll2_pre_to_reg(self, prescaler, osc_field=0x01, xtal_en=0x0, ref_2x_en=0x0):
103        """
104        From the prescaler value, returns the register value combined with the other
105        register fields.
106        """
107        # valid prescaler values are 2-8, where 8 is represented as 0x00.
108        assert prescaler in range(2, 8+1)
109        reg_val = ((prescaler & 0x07) << 5) \
110                  | ((osc_field & 0x7) << 2) \
111                  | ((xtal_en & 0x1) << 1) \
112                  | ((ref_2x_en & 0x1) << 0)
113        self.log.trace("From prescaler value 0d{}, writing register as 0x{:02X}."
114                       .format(prescaler, reg_val))
115        return reg_val
116
117