Commit ea6fc5c7 authored by Peter Jansweijer's avatar Peter Jansweijer

added support for LTC6950 PLL (SPEC7 Version 2)

parent 59775109
......@@ -8,6 +8,7 @@ obj-$(CONFIG_EMBEDDED_NODE) += \
dev/endpoint.o \
dev/ep_pfilter.o \
dev/i2c.o \
dev/ltc6950.o \
dev/minic.o \
dev/syscon.o \
dev/sfp.o \
......
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2020 CERN/Nikhef (www.cern.ch, www.nikhef.nl)
* Author: Peter Jansweijer <peterj@nikhef.nl>, based on work from
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
/*
* Trivial pll programmer using an spi controller.
* PLL is LTC6950, SPI is opencores
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <wrc.h>
#include "board.h"
#include "syscon.h"
#ifdef CONFIG_WR_NODE
#else /* CONFIG_WR_SWITCH */
#include "gpio-wrs.h"
#endif
#include "rt_ipc.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
#endif
static inline void writel(uint32_t data, void *where)
{
* (volatile uint32_t *)where = data;
}
static inline uint32_t readl(void *where)
{
return * (volatile uint32_t *)where;
}
struct ltc6950_reg {
uint16_t reg;
uint8_t val;
};
#include "ltc6950_config.h"
/*
* SPI stuff, used by later code
*/
#define SPI_REG_RX0 0
#define SPI_REG_TX0 0
#define SPI_REG_RX1 4
#define SPI_REG_TX1 4
#define SPI_REG_RX2 8
#define SPI_REG_TX2 8
#define SPI_REG_RX3 12
#define SPI_REG_TX3 12
#define SPI_REG_CTRL 16
#define SPI_REG_DIVIDER 20
#define SPI_REG_SS 24
#define SPI_CTRL_ASS (1<<13)
#define SPI_CTRL_IE (1<<12)
#define SPI_CTRL_LSB (1<<11)
#define SPI_CTRL_TXNEG (1<<10)
#define SPI_CTRL_RXNEG (1<<9)
#define SPI_CTRL_GO_BSY (1<<8)
#define SPI_CTRL_CHAR_LEN(x) ((x) & 0x7f)
#define CS_PLL 0 /* LTC6950 on SPI CS0 */
static int oc_spi_init(void *base_addr)
{
writel(100, base_addr + SPI_REG_DIVIDER);
return 0;
}
static int oc_spi_txrx(void *base, int ss, int nbits, uint32_t in, uint32_t *out)
{
uint32_t rval;
if (!out)
out = &rval;
writel(SPI_CTRL_ASS | SPI_CTRL_CHAR_LEN(nbits)
| SPI_CTRL_TXNEG,
base + SPI_REG_CTRL);
writel(in, base + SPI_REG_TX0);
writel((1 << ss), base + SPI_REG_SS);
writel(SPI_CTRL_ASS | SPI_CTRL_CHAR_LEN(nbits)
| SPI_CTRL_TXNEG | SPI_CTRL_GO_BSY,
base + SPI_REG_CTRL);
while(readl(base + SPI_REG_CTRL) & SPI_CTRL_GO_BSY)
;
*out = readl(base + SPI_REG_RX0);
return 0;
}
/*
* LTC6950 stuff, using SPI, used by later code.
* "reg" is 7 bits, "val" is 8 bits, but both are better used as int
* Serial port sequence:
* A6|A5|A4|A3|A2|A1|A0|RW|D7|D6|D5|D4|D3|D2|D1|D0
*/
static void ltc6950_write_reg(void *base, int reg, int val)
{
oc_spi_txrx(base, CS_PLL, 16, (reg << 9) | val, NULL);
}
static int ltc6950_read_reg(void *base, int reg)
{
uint32_t rval;
oc_spi_txrx(base, CS_PLL, 16, (reg << 9) | (1 << 8), &rval);
return rval & 0xff;
}
static void ltc6950_load_regset(void *base, const struct ltc6950_reg *regs, int n_regs, int commit)
{
int i;
for(i=0; i<n_regs; i++)
ltc6950_write_reg(base, regs[i].reg, regs[i].val);
}
static void ltc6950_wait_lock(void *base)
{
while ((ltc6950_read_reg(base, 0x00) & 4) == 0);
}
static void pll_wr_mode_init(int mode)
{
gpio_out(GPIO_PLL_WR_MODE0, mode & 0x01);
pp_printf("GPIO_PLL_WR_MODE0 %x\n", mode & 0x01);
gpio_out(GPIO_PLL_WR_MODE1, mode & 0x02);
pp_printf("GPIO_PLL_WR_MODE1 %x\n", mode & 0x02);
}
#ifdef CONFIG_WR_NODE
int spec7_ltc6950_init(int pll_wr_mode)
{
pp_printf("Initializing SPEC7 LTC6950 PLL...\n");
oc_spi_init((void *)BASE_SPI);
void *spi_base = (void *)BASE_SPI;
/* reset the PLL (RES6950 clears itself)*/
ltc6950_write_reg(spi_base, 0x03, 4);
timer_delay(10);
/* Check the presence of the chip */
if (ltc6950_read_reg(spi_base, 0x16) != 0x65) {
pp_printf("Error: ltc6950 PLL not responding.\n");
return -1;
}
pll_wr_mode_init(pll_wr_mode);
if (pll_wr_mode == PLL_WR_MODE_MASTER | pll_wr_mode == PLL_WR_MODE_GM ) {
/* Configuration for the SPEC7:
External 10 MHZ In (Bulls-Eye B03/B04) => 125 MHz (PLL_WR_MODE_GM) OR
10 MHZ from TCXO (PLL_WR_MODE_MASTER) on outputs 0, 1, 2
*/
ltc6950_load_regset(spi_base, ltc6950_10mhz_base_config_spec7, ARRAY_SIZE(ltc6950_10mhz_base_config_spec7), 0);
ltc6950_wait_lock(spi_base);
pp_printf("ltc6950 locked.\n");
} else {
/* Configuration for the SPEC7: Forward 125 MHz VCXO_REFCLK at CLK input to outputs 0, 1, 2 */
ltc6950_load_regset(spi_base, ltc6950_base_config_spec7, ARRAY_SIZE(ltc6950_base_config_spec7), 0);
timer_delay(10);
}
pp_printf("Switch clk_sys source from free running clk_dmtd to ltc6950 output.\n");
/* ltc6950 now initialized so switch clk_sys from free running clk_dmtd to ltc6950 output */
gpio_out(GPIO_PLL_CLK_SEL, 1);
timer_delay(1000);
pp_printf("now running on ref clock.\n");
}
#endif
/* Configuration for the SPEC7: Forward 125 MHz VCXO_REFCLK at CLK input to outputs 0, 1, 2 */
const struct ltc6950_reg ltc6950_base_config_spec7[] = {
//{0x0000, 0x08}, /* Reg 0 = status info, read only */
{0x0001, 0x00}, /* STAT1 mask */
{0x0002, 0x00}, /* STAT2 mask */
{0x0003, 0x70}, /* Power Down PLL, VCO and REF, no LKEN, Enable OUT[0] */
{0x0004, 0xf0}, /* Power Down OUT[4:3]; Enable OUT[2:1]*/
{0x0005, 0x98}, /* LKWIN = 30ns; LKCT = 128 cycles; cp = 4mA */
{0x0006, 0x00}, /* No ChargePump intervention */
{0x0007, 0x00}, /* No REST_R = 0 */
{0x0008, 0x02}, /* R divider = 2 */
{0x0009, 0x00}, /* No REST_N = 0 */
{0x000A, 0x19}, /* N divider = 25 */
{0x000B, 0x41}, /* SYNCMD = StandAlone; No FILTV/R */
{0x000C, 0x80}, /* set SYNC_EN0; DEL0=0 */
{0x000D, 0x81}, /* set IBIAS0; output divider M0 = 1 */
{0x000E, 0x80}, /* set SYNC_EN1; DEL1=0 */
{0x000F, 0x81}, /* set IBIAS1; output divider M1 = 1 */
{0x0010, 0x80}, /* set SYNC_EN2; DEL2=0 */
{0x0011, 0x81}, /* set IBIAS2; output divider M2 = 1 */
{0x0012, 0x00}, /* no SYNC_EN2; DEL3=0 */
{0x0013, 0x01}, /* no IBIAS3; output divider M3 = 1 */
{0x0014, 0x00}, /* no SYNC_EN4; DEL0=0 */
{0x0015, 0x01} /* no RDIVOUT; output divider M4 = 1 */
//{0x0016, 0x65} /* Reg 16 = REVision and PARTnumber, read only => 0x65 */
};
/* Configuration for the SPEC7: External 10 MHZ In (Bulls-Eye B03/B04) => 125 MHz on outputs 0, 1, 2 */
const struct ltc6950_reg ltc6950_10mhz_base_config_spec7[] = {
//{0x0000, 0x04}, /* Reg 0 = status info, read only */
{0x0001, 0x04}, /* STAT1 mask LOCK */
{0x0002, 0x3b}, /* STAT2 mask NO_VCO, NO_REF, UNLOCK, THI, TLO*/
{0x0003, 0x08}, /* Power PLL, VCO and REF, set LKEN, Enable OUT[0] */
{0x0004, 0xf0}, /* Power Down OUT[4:3]; Enable OUT[2:1]*/
{0x0005, 0x98}, /* LKWIN = 30ns; LKCT = 128 cycles; cp = 4mA */
{0x0006, 0x00}, /* No ChargePump intervention */
{0x0007, 0x00}, /* No REST_R = 0 */
{0x0008, 0x02}, /* R divider = 2 */
{0x0009, 0x00}, /* No REST_N = 0 */
{0x000A, 0x19}, /* N divider = 25 */
{0x000B, 0x41}, /* SYNCMD = StandAlone; No FILTV/R */
{0x000C, 0x80}, /* set SYNC_EN0; DEL0=0 */
{0x000D, 0x81}, /* set IBIAS0; output divider M0 = 1 */
{0x000E, 0x80}, /* set SYNC_EN1; DEL1=0 */
{0x000F, 0x81}, /* set IBIAS1; output divider M1 = 1 */
{0x0010, 0x80}, /* set SYNC_EN2; DEL2=0 */
{0x0011, 0x81}, /* set IBIAS2; output divider M2 = 1 */
{0x0012, 0x00}, /* no SYNC_EN2; DEL3=0 */
{0x0013, 0x01}, /* no IBIAS3; output divider M3 = 1 */
{0x0014, 0x00}, /* no SYNC_EN4; DEL0=0 */
{0x0015, 0x01} /* no RDIVOUT; output divider M4 = 1 */
//{0x0016, 0x65} /* Reg 16 = REVision and PARTnumber, read only => 0x65 */
};
......@@ -3,7 +3,7 @@
* File : wrc_syscon_regs.h
* Author : auto-generated by wbgen2 from wrc_syscon_wb.wb
* Created : 11/22/19 16:40:26
* Created : 01/20/20 09:14:48
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE wrc_syscon_wb.wb
......@@ -98,6 +98,12 @@
/* definitions for field: PLL_CLK_SEL in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_PLL_CLK_SEL WBGEN2_GEN_MASK(17, 1)
/* definitions for field: PLL_WR_MODE0 in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_PLL_WR_MODE0 WBGEN2_GEN_MASK(18, 1)
/* definitions for field: PLL_WR_MODE1 in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_PLL_WR_MODE1 WBGEN2_GEN_MASK(19, 1)
/* definitions for register: GPIO Clear Register */
/* definitions for field: Status LED in reg: GPIO Clear Register */
......@@ -133,6 +139,12 @@
/* definitions for field: PLL_CLK_SEL in reg: GPIO Clear Register */
#define SYSC_GPCR_PLL_CLK_SEL WBGEN2_GEN_MASK(17, 1)
/* definitions for field: PLL_WR_MODE0 in reg: GPIO Clear Register */
#define SYSC_GPCR_PLL_WR_MODE0 WBGEN2_GEN_MASK(18, 1)
/* definitions for field: PLL_WR_MODE1 in reg: GPIO Clear Register */
#define SYSC_GPCR_PLL_WR_MODE1 WBGEN2_GEN_MASK(19, 1)
/* definitions for register: Hardware Feature Register */
/* definitions for field: Memory size in reg: Hardware Feature Register */
......
......@@ -92,6 +92,8 @@ struct SYSCON_WB {
#define GPIO_PLL_LOCK SYSC_GPSR_PLL_LOCK
#define GPIO_PLL_STATUS SYSC_GPSR_PLL_STATUS
#define GPIO_PLL_CLK_SEL SYSC_GPSR_PLL_CLK_SEL
#define GPIO_PLL_WR_MODE0 SYSC_GPSR_PLL_WR_MODE0
#define GPIO_PLL_WR_MODE1 SYSC_GPSR_PLL_WR_MODE1
#define WRPC_FMC_I2C 0
#define WRPC_SFP_I2C 1
......
......@@ -48,6 +48,11 @@
# define IS_WR_NODE_SIM 0
#endif
// PLL WR_MODE options:
# define PLL_WR_MODE_MASTER 1
# define PLL_WR_MODE_SLAVE 2
# define PLL_WR_MODE_GM 3
extern int wrc_vlan_number;
int wrc_mon_gui(void);
......@@ -72,6 +77,7 @@ extern int wrc_ui_refperiod;
/* Init functions and defaults for the wrs build */
int ad9516_init(int scb_ver, int ljd_present);
int spec7_ad9516_init(int ext_10mhz);
int spec7_ltc6950_init(int pll_wr_mode);
int ljd_ad9516_init(void);
void rts_init(void);
int rtipc_init(void);
......
......@@ -52,7 +52,9 @@ static uint32_t prev_nanos_for_profile;
static uint32_t prev_ticks_for_profile;
uint32_t print_task_time_threshold = CONFIG_DEFAULT_PRINT_TASK_TIME_THRESHOLD;
int ext_10mhz = 0;
//int pll_wr_mode = PLL_WR_MODE_MASTER;
//int pll_wr_mode = PLL_WR_MODE_SLAVE;
int pll_wr_mode = PLL_WR_MODE_GM;
static void wrc_initialize(void)
{
......@@ -94,7 +96,8 @@ static void wrc_initialize(void)
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
mac_addr[4], mac_addr[5]);
spec7_ad9516_init(ext_10mhz);
//spec7_ad9516_init(ext_10mhz);
spec7_ltc6950_init(pll_wr_mode);
net_rst();
ep_init(mac_addr);
/* Sleep for 1s to make sure WRS v4.2 always realizes that
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment