1# 2# Copyright 2018 Ettus Research, a National Instruments Company 3# Copyright 2019 Ettus Research, a National Instruments Brand 4# 5# SPDX-License-Identifier: GPL-3.0-or-later 6# 7""" 8N3xx peripherals 9""" 10 11from usrp_mpm import lib 12from usrp_mpm.sys_utils.sysfs_gpio import SysFSGPIO, GPIOBank 13from usrp_mpm.sys_utils import i2c_dev 14from usrp_mpm.chips.ds125df410 import DS125DF410 15from usrp_mpm.periph_manager.common import MboardRegsCommon 16 17# Map register values to SFP transport types 18N3XX_SFP_TYPES = { 19 0: "", # Port not connected 20 1: "1G", 21 2: "10G", 22 3: "A", # Aurora 23 4: "W" # White Rabbit 24} 25 26N3XX_FPGA_TYPES_BY_SFP = { 27 ("", ""): "", 28 ("1G", "10G"): "HG", 29 ("10G", "10G"): "XG", 30 ("10G", "A"): "XA", 31 ("A", "A"): "AA", 32 ("W", "10G"): "WX", 33} 34 35class TCA6424(object): 36 """ 37 Abstraction layer for the port/gpio expander 38 pins_list is an array of different version of TCA6424 pins map. 39 First element of this array corresponding to revC, second is revD etc... 40 """ 41 pins_list = [ 42 ( 43 'PWREN-CLK-MGT156MHz', 44 'NETCLK-CE', #revC name: 'PWREN-CLK-WB-CDCM', 45 'NETCLK-RESETn', #revC name: 'WB-CDCM-RESETn', 46 'NETCLK-PR0', #revC name: 'WB-CDCM-PR0', 47 'NETCLK-PR1', #revC name: 'WB-CDCM-PR1', 48 'NETCLK-OD0', #revC name: 'WB-CDCM-OD0', 49 'NETCLK-OD1', #revC name: 'WB-CDCM-OD1', 50 'NETCLK-OD2', #revC name: 'WB-CDCM-OD2', 51 'PWREN-CLK-MAINREF', 52 'CLK-MAINSEL-25MHz', #revC name: 'CLK-MAINREF-SEL1', 53 'CLK-MAINSEL-EX_B', #revC name: 'CLK-MAINREF-SEL0', 54 '12', 55 'CLK-MAINSEL-GPS', #revC name: '13', 56 'FPGA-GPIO-EN', 57 'PWREN-CLK-WB-20MHz', 58 'PWREN-CLK-WB-25MHz', 59 'GPS-PHASELOCK', 60 'GPS-nINITSURV', 61 'GPS-nRESET', 62 'GPS-WARMUP', 63 'GPS-SURVEY', 64 'GPS-LOCKOK', 65 'GPS-ALARM', 66 'PWREN-GPS', 67 ), 68 ( 69 'NETCLK-PR1', 70 'NETCLK-PR0', 71 'NETCLK-CE', 72 'NETCLK-RESETn', 73 'NETCLK-OD2', 74 'NETCLK-OD1', 75 'NETCLK-OD0', 76 'PWREN-CLK-MGT156MHz', 77 'PWREN-CLK-MAINREF', 78 'CLK-MAINSEL-25MHz', 79 'CLK-MAINSEL-EX_B', 80 '12', 81 'CLK-MAINSEL-GPS', 82 'FPGA-GPIO-EN', 83 'PWREN-CLK-WB-20MHz', 84 'PWREN-CLK-WB-25MHz', 85 'GPS-PHASELOCK', 86 'GPS-nINITSURV', 87 'GPS-nRESET', 88 'GPS-WARMUP', 89 'GPS-SURVEY', 90 'GPS-LOCKOK', 91 'GPS-ALARM', 92 'PWREN-GPS', 93 )] 94 95 def __init__(self, rev): 96 # Default state: Turn on GPS power, take GPS out of reset or 97 # init-survey, turn on 156.25 MHz clock 98 # min Support from revC or rev = 2 99 if rev == 2: 100 self.pins = self.pins_list[0] 101 else: 102 self.pins = self.pins_list[1] 103 104 default_val = 0x860101 if rev == 2 else 0x860780 105 self._gpios = SysFSGPIO({'device/name': 'tca6424', 'device/of_node/name': 'gpio'}, 0xFFF7FF, 0x86F7FF, default_val) 106 107 def set(self, name, value=None): 108 """ 109 Assert a pin by name 110 """ 111 assert name in self.pins 112 self._gpios.set(self.pins.index(name), value=value) 113 114 def reset(self, name): 115 """ 116 Deassert a pin by name 117 """ 118 self.set(name, value=0) 119 120 def get(self, name): 121 """ 122 Read back a pin by name 123 """ 124 assert name in self.pins 125 return self._gpios.get(self.pins.index(name)) 126 127 128class FrontpanelGPIO(GPIOBank): 129 """ 130 Abstraction layer for the front panel GPIO 131 """ 132 EMIO_BASE = 54 133 FP_GPIO_OFFSET = 32 # Bit offset within the ps_gpio_* pins 134 135 def __init__(self, ddr): 136 GPIOBank.__init__( 137 self, 138 {'label': 'zynq_gpio'}, 139 self.FP_GPIO_OFFSET + self.EMIO_BASE, 140 0xFFF, # use_mask 141 ddr 142 ) 143 144class BackpanelGPIO(GPIOBank): 145 """ 146 Abstraction layer for the back panel GPIO 147 """ 148 EMIO_BASE = 54 149 BP_GPIO_OFFSET = 45 150 LED_LINK = 0 151 LED_REF = 1 152 LED_GPS = 2 153 154 def __init__(self): 155 GPIOBank.__init__( 156 self, 157 {'label': 'zynq_gpio'}, 158 self.BP_GPIO_OFFSET + self.EMIO_BASE, 159 0x7, # use_mask 160 0x7, # ddr 161 ) 162 163class MboardRegsControl(MboardRegsCommon): 164 """ 165 Control the FPGA Motherboard registers 166 """ 167 # pylint: disable=bad-whitespace 168 # Motherboard registers (on top of the ones in MboardRegsCommon) 169 MB_CLOCK_CTRL = 0x0018 170 MB_XADC_RB = 0x001C 171 MB_BUS_CLK_RATE = 0x0020 172 MB_BUS_COUNTER = 0x0024 173 MB_SFP0_INFO = 0x0028 174 MB_SFP1_INFO = 0x002C 175 MB_GPIO_MASTER = 0x0030 176 MB_GPIO_RADIO_SRC = 0x0034 177 178 # Bitfield locations for the MB_CLOCK_CTRL register. 179 MB_CLOCK_CTRL_PPS_SEL_INT_10 = 0 # pps_sel is one-hot encoded! 180 MB_CLOCK_CTRL_PPS_SEL_INT_25 = 1 181 MB_CLOCK_CTRL_PPS_SEL_EXT = 2 182 MB_CLOCK_CTRL_PPS_SEL_GPSDO = 3 183 MB_CLOCK_CTRL_PPS_SEL_SFP0 = 5 184 MB_CLOCK_CTRL_PPS_SEL_SFP1 = 6 185 MB_CLOCK_CTRL_PPS_OUT_EN = 4 # output enabled = 1 186 MB_CLOCK_CTRL_MEAS_CLK_RESET = 12 # set to 1 to reset mmcm, default is 0 187 MB_CLOCK_CTRL_MEAS_CLK_LOCKED = 13 # locked indication for meas_clk mmcm 188 MB_CLOCK_CTRL_DISABLE_REF_CLK = 16 # to disable the ref_clk, write a '1' 189 # pylint: enable=bad-whitespace 190 191 def __init__(self, label, log): 192 MboardRegsCommon.__init__(self, label, log) 193 194 def set_fp_gpio_master(self, value): 195 """set driver for front panel GPIO 196 Arguments: 197 value {unsigned} -- value is a single bit bit mask of 12 pins GPIO 198 0: means the pin is driven by PL 199 1: means the pin is driven by PS 200 """ 201 with self.regs: 202 return self.poke32(self.MB_GPIO_MASTER, value) 203 204 def get_fp_gpio_master(self): 205 """get "who" is driving front panel gpio 206 The return value is a bit mask of 12 pins GPIO. 207 0: means the pin is driven by PL 208 1: means the pin is driven by PS 209 """ 210 with self.regs: 211 return self.peek32(self.MB_GPIO_MASTER) & 0xfff 212 213 def set_fp_gpio_radio_src(self, value): 214 """set driver for front panel GPIO 215 Arguments: 216 value {unsigned} -- value is 2x12 bits, two bits per GPIO pin 217 00: means the pin is driven by radio 0 218 01: means the pin is driven by radio 1 219 10: means the pin is driven by radio 2 220 11: means the pin is driven by radio 3 221 """ 222 with self.regs: 223 return self.poke32(self.MB_GPIO_RADIO_SRC, value) 224 225 def get_fp_gpio_radio_src(self): 226 """get which radio is driving front panel gpio 227 The return value is 2-bit bit mask of 12 pins GPIO. 228 00: means the pin is driven by radio 0 229 01: means the pin is driven by radio 1 230 10: means the pin is driven by radio 2 231 11: means the pin is driven by radio 3 232 """ 233 with self.regs: 234 return self.peek32(self.MB_GPIO_RADIO_SRC) & 0xffffff 235 236 def set_time_source(self, time_source, ref_clk_freq): 237 """ 238 Set time source 239 """ 240 pps_sel_val = 0x0 241 if time_source == 'internal': 242 assert ref_clk_freq in (10e6, 25e6), \ 243 "Invalid reference frequency for time source 'internal'. Must " \ 244 "be either 10 MHz or 25 MHz. Check clock and time source match." 245 if ref_clk_freq == 10e6: 246 self.log.debug("Setting time source to internal " 247 "(10 MHz reference)...") 248 pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_INT_10 249 elif ref_clk_freq == 25e6: 250 self.log.debug("Setting time source to internal " 251 "(25 MHz reference)...") 252 pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_INT_25 253 elif time_source == 'external': 254 self.log.debug("Setting time source to external...") 255 pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_EXT 256 elif time_source == 'gpsdo': 257 self.log.debug("Setting time source to gpsdo...") 258 pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_GPSDO 259 elif time_source == 'sfp0': 260 self.log.debug("Setting time source to sfp0...") 261 pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_SFP0 262 elif time_source == 'sfp1': 263 self.log.debug("Setting time source to sfp1...") 264 pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_SFP1 265 else: 266 raise RuntimeError("Invalid time source: {}".format(time_source)) 267 268 with self.regs: 269 reg_val = self.peek32(self.MB_CLOCK_CTRL) & 0xFFFFFF90 270 # prevent glitches by writing a cleared value first, then the final value. 271 self.poke32(self.MB_CLOCK_CTRL, reg_val) 272 reg_val = reg_val | (pps_sel_val & 0x6F) 273 self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val)) 274 self.poke32(self.MB_CLOCK_CTRL, reg_val) 275 276 def enable_pps_out(self, enable): 277 """ 278 Enables the PPS/Trig output on the back panel 279 """ 280 self.log.trace("%s PPS/Trig output!", 281 "Enabling" if enable else "Disabling") 282 mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN) 283 with self.regs: 284 # mask the bit to clear it: 285 reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask 286 if enable: 287 # set the bit if desired: 288 reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN) 289 self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val)) 290 self.poke32(self.MB_CLOCK_CTRL, reg_val) 291 292 def enable_ref_clk(self, enable): 293 """ 294 Enables the reference clock internal to the FPGA 295 """ 296 self.log.trace("%s the Reference Clock!", 297 "Enabling" if enable else "Disabling") 298 mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_DISABLE_REF_CLK) 299 with self.regs: 300 # mask the bit to clear it and therefore enable the clock: 301 reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask 302 if not enable: 303 # set the bit if not enabled (note this is a DISABLE bit when = 1): 304 reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_DISABLE_REF_CLK) 305 self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val)) 306 self.poke32(self.MB_CLOCK_CTRL, reg_val) 307 308 def reset_meas_clk_mmcm(self, reset=True): 309 """ 310 Reset or unreset the MMCM for the measurement clock in the FPGA TDC. 311 """ 312 self.log.trace("%s measurement clock MMCM reset...", 313 "Asserting" if reset else "Clearing") 314 mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET) 315 with self.regs: 316 # mask the bit to clear it 317 reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask 318 if reset: 319 # set the bit if desired 320 reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET) 321 self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val)) 322 self.poke32(self.MB_CLOCK_CTRL, reg_val) 323 324 def get_meas_clock_mmcm_lock(self): 325 """ 326 Check the status of the MMCM for the measurement clock in the FPGA TDC. 327 """ 328 mask = 0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_LOCKED 329 with self.regs: 330 reg_val = self.peek32(self.MB_CLOCK_CTRL) 331 locked = (reg_val & mask) > 0 332 if not locked: 333 self.log.warning("Measurement clock MMCM reporting unlocked. " 334 "MB_CLOCK_CTRL reg: 0x{:08X}".format(reg_val)) 335 else: 336 self.log.trace("Measurement clock MMCM locked!") 337 return locked 338 339 def get_fpga_type(self): 340 """ 341 Reads the type of the FPGA image currently loaded 342 Returns a string with the type (ie HG, XG, AA, etc.) 343 """ 344 with self.regs: 345 sfp0_info_rb = self.peek32(self.MB_SFP0_INFO) 346 sfp1_info_rb = self.peek32(self.MB_SFP1_INFO) 347 # Print the registers values as 32-bit hex values 348 self.log.trace("SFP0 Info: 0x{0:0{1}X}".format(sfp0_info_rb, 8)) 349 self.log.trace("SFP1 Info: 0x{0:0{1}X}".format(sfp1_info_rb, 8)) 350 sfp0_type = N3XX_SFP_TYPES.get((sfp0_info_rb & 0x0000FF00) >> 8, "") 351 sfp1_type = N3XX_SFP_TYPES.get((sfp1_info_rb & 0x0000FF00) >> 8, "") 352 self.log.trace("SFP types: ({}, {})".format(sfp0_type, sfp1_type)) 353 try: 354 return N3XX_FPGA_TYPES_BY_SFP[(sfp0_type, sfp1_type)] 355 except KeyError: 356 self.log.warning("Unrecognized SFP type combination: ({}, {})" 357 .format(sfp0_type, sfp1_type)) 358 return "" 359 360 361class RetimerQSFP(DS125DF410): 362 """ 363 Thin wrapper around an I2C device that controls the QSFP retimer 364 """ 365 # (deemphasis, swing) 366 DRIVER_PRESETS = { 367 '1m': (0x00, 0x07), '3m': (0x41, 0x06), 'Optical': (0x41, 0x04) 368 } 369 370 def __init__(self, i2c_bus): 371 regs_iface = lib.i2c.make_i2cdev_regs_iface( 372 i2c_bus, 373 0x18, 374 False, 375 100, 376 1 377 ) 378 super(RetimerQSFP, self).__init__(regs_iface) 379