aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm/chips/ds125df410.py
blob: 5cea7e67abb8c3112c0198c782b65fd1681e7e73 (plain)
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
#
# Copyright 2018 Ettus Research, a National Instruments Company
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
DS125DF410 driver class

For use with TI's retimer chip
"""

import math
from builtins import object
from usrp_mpm.mpmlog import get_logger

LINE_RATE_PRESETS = {'Ethernet': 0xF6, 'CPRI1': 0x36, 'CPRI2': 0x46}
ALL_CHANS = range(4)


class DS125DF410(object):
    """
    Driver class for DS125DF410 access.

    Inputs:
    regs_iface : regs_iface bus driver to access device
    parent_log : logger of parent
    """

    # (deemphasis, swing)
    DRIVER_PRESETS = {}

    def __init__(self, regs_iface, parent_log=None):
        self.log = \
            parent_log.getChild("DS125DF410") if parent_log is not None \
            else get_logger("DS125DF410")
        self.regs_iface = regs_iface
        # Set channel select register to control set
        self.regs_iface.poke8(0xFF, 0)
        # Probe chip ID
        chip_id = self.regs_iface.peek8(0x01)
        assert chip_id == 0xd1
        self.log.debug("Probed DS125DF410 retimer")
        for chan in ALL_CHANS:
            self._set_chan_select(chan)
            # Reset channel registers
            self.regs_iface.poke8(0x00, 0x04)
            # Enable DFE mode
            self.regs_iface.poke8(0x1E, 0xE1)
            self.regs_iface.poke8(0x31, 0x40)

    def _rmw(self, addr, data, mask):
        """ Read, modify, write """
        base = self.regs_iface.peek8(addr) & ~mask
        data = (data & mask) | base
        self.regs_iface.poke8(addr, data)

    def _set_chan_select(self, chan):
        """
        Channel select
        """
        assert chan in ALL_CHANS
        self.regs_iface.poke8(0xFF, chan + 4)

    def set_rate_preset(self, preset, channels=None):
        """
        Set rate preset
        """
        channels = channels or ALL_CHANS
        assert preset in LINE_RATE_PRESETS.keys()
        for chan in channels:
            self._set_chan_select(chan)
            self.regs_iface.poke8(0x2F, LINE_RATE_PRESETS[preset])

    def set_rate(self, rate, channels=None):
        """
        Set rate
        """
        channels = channels or ALL_CHANS
        self.log.trace("Writing custom line rate {}".format(rate))
        ppm_val = int(math.ceil(rate*1280.0))
        assert ppm_val <= 0x7FFF
        for chan in channels:
            assert chan in ALL_CHANS
            self._set_chan_select(chan)
            # Set VCO divider to 1
            self.regs_iface.poke8(0x2F, 0xC6)
            # Set frequency range detection
            self.regs_iface.poke8(0x60, (ppm_val & 0x00FF))
            self.regs_iface.poke8(0x62, (ppm_val & 0x00FF))
            self.regs_iface.poke8(0x61, 0x80 | ((ppm_val >> 8) & 0x00FF))
            self.regs_iface.poke8(0x63, 0x80 | ((ppm_val >> 8) & 0x00FF))
            # Set VCO tolerance range to max
            self.regs_iface.poke8(0x64, 0xFF)

    def set_driver_preset(self, preset, channels=None):
        """
        Set driver preset
        """
        channels = channels or ALL_CHANS
        assert preset in self.DRIVER_PRESETS.keys()
        self.log.trace("Setting retimer's driver for " + preset + " preset")
        deemphasis, swing = self.DRIVER_PRESETS[preset]
        for chan in channels:
            self._set_chan_select(chan)
            self._rmw(0x15, deemphasis, 0x47)
            self._rmw(0x2D, swing, 0x07)