From 1b0ae107cbb6227ad93ccc54d8ad56d4698835ba Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 1 Nov 2022 16:07:03 +0100 Subject: [PATCH] fw/e1-tracer: Initialize IDT82V2081 from within firmware in e1d mode Initially it was a good choice to keep the LIU driver in the host software (easier debugging/changes). However, we never really needed it to do anything but initialization of the LIU. So in order to avoid having to teach osmo-e1d or other software the details about the LIU initialization, let's do this from the firmware *if* the e1d compatible USB configuration is used. Closes: OS#5733 Change-Id: Id2217ff4573c4eebd816318128f256e85fb3c3bd --- firmware/ice40-riscv/e1-tracer/Makefile | 4 + firmware/ice40-riscv/e1-tracer/idt82v2081.c | 140 ++++++++++++++++++ firmware/ice40-riscv/e1-tracer/idt82v2081.h | 35 +++++ .../ice40-riscv/e1-tracer/idt82v2081_no2spi.c | 48 ++++++ .../ice40-riscv/e1-tracer/idt82v2081_regs.h | 81 ++++++++++ firmware/ice40-riscv/e1-tracer/usb_e1.c | 6 + 6 files changed, 314 insertions(+) create mode 100644 firmware/ice40-riscv/e1-tracer/idt82v2081.c create mode 100644 firmware/ice40-riscv/e1-tracer/idt82v2081.h create mode 100644 firmware/ice40-riscv/e1-tracer/idt82v2081_no2spi.c create mode 100644 firmware/ice40-riscv/e1-tracer/idt82v2081_regs.h diff --git a/firmware/ice40-riscv/e1-tracer/Makefile b/firmware/ice40-riscv/e1-tracer/Makefile index 030501e..5fa38fa 100644 --- a/firmware/ice40-riscv/e1-tracer/Makefile +++ b/firmware/ice40-riscv/e1-tracer/Makefile @@ -47,6 +47,8 @@ SOURCES_common += $(SOURCES_no2usb) HEADERS_app=\ config.h \ e1.h \ + idt82v2081.h \ + idt82v2081_regs.h \ misc.h \ usb_str_app.gen.h \ $(NULL) @@ -54,6 +56,8 @@ HEADERS_app=\ SOURCES_app=\ e1.c \ fw_app.c \ + idt82v2081.c \ + idt82v2081_no2spi.c \ misc.c \ usb_desc_app.c \ usb_e1.c \ diff --git a/firmware/ice40-riscv/e1-tracer/idt82v2081.c b/firmware/ice40-riscv/e1-tracer/idt82v2081.c new file mode 100644 index 0000000..9233292 --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/idt82v2081.c @@ -0,0 +1,140 @@ +/* (C) 2019 by Harald Welte + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include "idt82v2081.h" +#include "idt82v2081_regs.h" + +/*! \brief Set or clear some (masked) bits inside a register + * \param[in] e4k reference to the tuner + * \param[in] reg number of the register + * \param[in] mask bit-mask of the value + * \param[in] val data value to be written to register + * \returns 0 on success, negative in case of error + */ +static int idt82_reg_set_bit_mask(struct idt82 *idt, uint8_t reg, + uint8_t mask, uint8_t val) +{ + uint8_t tmp = idt82_reg_read(idt, reg); + + if ((tmp & mask) == val) + return 0; + + return idt82_reg_write(idt, reg, (tmp & ~mask) | (val & mask)); +} + +int idt82_termination(struct idt82 *idt, enum idt82_term term) +{ + uint8_t puls, scal; + + idt82_reg_set_bit_mask(idt, IDT_REG_TERM, term | (term << IDT_TERM_T_SHIFT), + IDT_TERM_T_MASK | IDT_TERM_R_MASK); + + switch (idt->mode) { + case IDT_MODE_E1: + if (term == IDT_TERM_INT_75) + puls = 0; + else + puls = 1; + scal = 0x21; + break; + case IDT_MODE_T1: + /* FIXME: different length! */ + puls = 2; + scal = 0x36; + break; + case IDT_MODE_J1: + puls = 7; + scal = 0x36; + break; + default: + return -1; + } + + idt82_reg_set_bit_mask(idt, IDT_REG_TCF1, puls, IDT_TCF1_PULS_MASK); + idt82_reg_set_bit_mask(idt, IDT_REG_TCF2, scal, IDT_TCF2_SCAL_MASK); + + idt->term = term; + + return 0; +} + +int idt82_mode(struct idt82 *idt, enum idt82_mode mode) +{ + switch (mode) { + case IDT_MODE_E1: + idt82_reg_set_bit_mask(idt, IDT_REG_GCF, IDT_GCF_T1E1_E1, + IDT_GCF_T1E1_MASK); + break; + case IDT_MODE_T1: + case IDT_MODE_J1: + idt82_reg_set_bit_mask(idt, IDT_REG_GCF, IDT_GCF_T1E1_T1, + IDT_GCF_T1E1_MASK); + break; + } + idt->mode = mode; + + return 0; +} + +int idt82_get_errcount(struct idt82 *idt) +{ + uint16_t ret; + int rc; + + rc = idt82_reg_read(idt, IDT_REG_CNT0); + if (rc < 0) + return rc; + + ret = rc; + + rc = idt82_reg_read(idt, IDT_REG_CNT1); + if (rc < 0) + return rc; + + ret |= (rc << 8); + + return ret; +} + +/* return in dB, range is return value ... (value + 2) */ +int idt82_get_line_att(struct idt82 *idt) +{ + int rc; + + rc = idt82_reg_read(idt, IDT_REG_STAT1); + if (rc < 0) + return rc; + + return (rc & IDT_STAT1_ATT_MASK)*2; +} + +int idt82_init(struct idt82 *idt, bool monitor) +{ + idt82_reg_write(idt, IDT_REG_RST, 0x00); /* Reset to defaults */ + + idt82_mode(idt, IDT_MODE_E1); + idt82_termination(idt, IDT_TERM_INT_120); + + idt82_reg_write(idt, IDT_REG_TCF0, 0x10); /* Disable TX */ + + if (monitor) + idt82_reg_write(idt, IDT_REG_RCF2, 0x19); /* 22 dB monitor mode */ + + return 0; +} + diff --git a/firmware/ice40-riscv/e1-tracer/idt82v2081.h b/firmware/ice40-riscv/e1-tracer/idt82v2081.h new file mode 100644 index 0000000..1c930b1 --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/idt82v2081.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +enum idt82_term { + IDT_TERM_INT_75 = 0, + IDT_TERM_INT_120, + IDT_TERM_INT_100, + IDT_TERM_INT_110, + IDT_TERM_EXT, +}; + +enum idt82_mode { + IDT_MODE_E1 = 0, + IDT_MODE_T1, + IDT_MODE_J1, +}; + +struct idt82 { + enum idt82_mode mode; + enum idt82_term term; + void *priv; + uint8_t cs; +}; + +int idt82_termination(struct idt82 *idt, enum idt82_term term); +int idt82_mode(struct idt82 *idt, enum idt82_mode mode); +int idt82_get_errcount(struct idt82 *idt); +int idt82_get_line_att(struct idt82 *idt); +int idt82_init(struct idt82 *idt, bool monitor); + +/* callbacks into transport */ +int idt82_reg_write(struct idt82 *idt, uint8_t reg, uint8_t val); +int idt82_reg_read(struct idt82 *idt, uint8_t reg); diff --git a/firmware/ice40-riscv/e1-tracer/idt82v2081_no2spi.c b/firmware/ice40-riscv/e1-tracer/idt82v2081_no2spi.c new file mode 100644 index 0000000..66e5c5b --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/idt82v2081_no2spi.c @@ -0,0 +1,48 @@ +/* (C) 2019 by Harald Welte + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "idt82v2081.h" +#include "spi.h" + +/* Adaption layer between idt82 driver and no2fpga SPI */ + +/* backend function for core idt82 driver */ +int idt82_reg_read(struct idt82 *idt, uint8_t reg) +{ + uint8_t cmd = reg | 0x20; + uint8_t rv; + struct spi_xfer_chunk xfer[2] = { + { .data = (void*)&cmd, .len = 1, .read = false, .write = true, }, + { .data = (void*)&rv, .len = 1, .read = true, .write = false, }, + }; + spi_xfer(SPI_CS_LIU(idt->cs), xfer, 2); + return rv; +} + +/* backend function for core idt82 driver */ +int idt82_reg_write(struct idt82 *idt, uint8_t reg, uint8_t val) +{ + uint8_t cmd[2] = { reg, val }; + struct spi_xfer_chunk xfer[2] = { + { .data = (void*)cmd, .len = 2, .read = false, .write = true, }, + }; + spi_xfer(SPI_CS_LIU(idt->cs), xfer, 1); + return 0; +} diff --git a/firmware/ice40-riscv/e1-tracer/idt82v2081_regs.h b/firmware/ice40-riscv/e1-tracer/idt82v2081_regs.h new file mode 100644 index 0000000..679ed90 --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/idt82v2081_regs.h @@ -0,0 +1,81 @@ +#ifndef _IDT82_REGS_H +#define _IDT82_REGS_H + +/* Section 4.1 of Data Sheet */ +enum idt82v2081_reg { + IDT_REG_ID, /* control */ + IDT_REG_RST, + IDT_REG_GCF, + IDT_REG_TERM, + IDT_REG_JACF, + IDT_REG_TCF0, /* Tx path control */ + IDT_REG_TCF1, + IDT_REG_TCF2, + IDT_REG_TCF3, + IDT_REG_TCF4, + IDT_REG_RCF0, /* Rx path control */ + IDT_REG_RCF1, + IDT_REG_RCF2, + IDT_REG_MAINT0, /* Net Diag Ctrl */ + IDT_REG_MAINT1, + IDT_REG_MAINT2, + IDT_REG_MAINT3, + IDT_REG_MAINT4, + IDT_REG_MAINT5, + IDT_REG_MAINT6, + IDT_REG_INTM0, /* Interrupt Control */ + IDT_REG_INTM1, + IDT_REG_INTES, + IDT_REG_STAT0, /* Line Status */ + IDT_REG_STAT1, + IDT_REG_INTS0, /* Interrupt Status */ + IDT_REG_INTS1, + IDT_REG_CNT0, /* Counter */ + IDT_REG_CNT1, +}; + +#define IDT_GCF_T1E1_E1 (0 << 2) +#define IDT_GCF_T1E1_T1 (1 << 2) +#define IDT_GCF_T1E1_MASK (1 << 2) + +#define IDT_TERM_T_SHIFT 3 +#define IDT_TERM_T_MASK (7 << IDT_TERM_T_SHIFT) +#define IDT_TERM_R_SHIFT 0 +#define IDT_TERM_R_MASK (7 << IDT_TERM_R_SHIFT) + +#define IDT_TCF1_PULS_MASK 0xF + +#define IDT_TCF2_SCAL_MASK 0x3F + +#define IDT_RCF2_MG_MASK 3 +#define IDT_RCF2_UPDW_SHIFT 2 +#define IDT_RCF2_UPDW_MASK (3 << IDT_TERM_INT_75) +#define IDT_RCF2_SLICE_SHIFT 4 +#define IDT_RCF2_SLICE_MASK (3 << IDT_RCF2_SLICE_SHIFT) + +#define IDT_INTM0_EQ (1 << 7) /* equalizer out of range */ +#define IDT_INTM0_IBLBA (1 << 6) /* in-band LB act detect */ +#define IDT_INTM0_IBLBD (1 << 5) /* in-band LB deact detect */ +#define IDT_INTM0_PRBS (1 << 4) /* prbs sync signal detect */ +#define IDT_INTM0_TCLK (1 << 3) /* tclk loss */ +#define IDT_INTM0_DF (1 << 2) /* driver failure */ +#define IDT_INTM0_AIS (1 << 1) /* Alarm Indication Signal */ +#define IDT_INTM0_LOS (1 << 0) /* Loss Of Signal */ + +#define IDT_INTM1_DAC_OV (1 << 7) /* DAC arithmetic overflow */ +#define IDT_INTM1_JA_OV (1 << 6) /* JA overflow */ +#define IDT_INTM1_JA_UD (1 << 5) /* JA underflow */ +#define IDT_INTM1_ERR (1 << 4) /* PRBS/QRBS logic error detect */ +#define IDT_INTM1_EXZ (1 << 3) /* Receive excess zeros */ +#define IDT_INTM1_CV (1 << 2) /* Receive error */ +#define IDT_INTM1_TIMER (1 << 1) /* One second timer expiration */ +#define IDT_INTM1_CNT (1 << 0) /* Counter overflow */ + +/* STAT0 == INTES == INTS0 == INTM0 */ + +/* INTS1 == INTM1 */ + +#define IDT_STAT1_RLP (1 << 5) +#define IDT_STAT1_ATT_MASK 0x1F + +#endif /* _IDT82_REGS_H */ diff --git a/firmware/ice40-riscv/e1-tracer/usb_e1.c b/firmware/ice40-riscv/e1-tracer/usb_e1.c index 728dc3a..160d2b5 100644 --- a/firmware/ice40-riscv/e1-tracer/usb_e1.c +++ b/firmware/ice40-riscv/e1-tracer/usb_e1.c @@ -16,10 +16,12 @@ #include "console.h" #include "e1.h" #include "misc.h" +#include "idt82v2081.h" struct { bool running[2]; int in_bdi[2]; + struct idt82 idt82[2]; } g_usb_e1; @@ -218,6 +220,7 @@ _e1_set_intf(const struct usb_intf_desc *base, const struct usb_intf_desc *sel) disable_chan(base->bInterfaceNumber); break; case 1: + idt82_init(&g_usb_e1.idt82[base->bInterfaceNumber], true); enable_chan(base->bInterfaceNumber); break; default: @@ -271,6 +274,9 @@ usb_e1_init(void) { /* Clear state */ memset(&g_usb_e1, 0x00, sizeof(g_usb_e1)); + /* make sure we use the right SPI channel for the respective IDT82 */ + g_usb_e1.idt82[0].cs = 0; + g_usb_e1.idt82[1].cs = 1; /* Install driver */ usb_register_function_driver(&_e1_drv);