Commit 16bc0c8a authored by Alessandro Rubini's avatar Alessandro Rubini

softpll-unify: add new files, from wr-switch-sw/rt

The commit is part of the effort in unifying softpll with
wr-switch-sw, and later remove the duplicated code there.

The files added by these commit are going to be built when configuring
wrpc-sw to build wr-switch rt_cpu (which is, basically, the softpll
code alone, with mini-rpc with the host and the basic glue code).  The
files are copied with the original name with two exceptions:

   wr-switch-sw::rt/main.c            becomes wrpc-sw::wrs_main.c
   wr-switch-sw::rt/arch/lm32/ram.ld  becomes wrpc-sw::arch/lm32/ram-wrs.ld

The files are copied from commit FIXME of wr-switch-sw.

This commit has no technical effect, as the files are not built for wrpc-sw.
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent aa2dd48e
/*
* Link script for Lattice Mico32. Very loosely based on
* code contributed by Jon Beniston <jon@beniston.com>
*
* Jon's license (BSD-style):
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
OUTPUT_FORMAT("elf32-lm32")
ENTRY(_start)
MEMORY
{
ram : ORIGIN = 0x00000000, LENGTH = 0x7000
mbox : ORIGIN = 0x7000, LENGTH = 0x1000
}
SECTIONS
{
.boot : { *(.boot) } > ram
.text : { *(.text .text.*) } > ram =0
.rodata : { *(.rodata .rodata.*) } > ram
.data : {
*(.data .data.*)
_gp = ALIGN(16) + 0x7ff0; /* FIXME: what is this? */
} > ram
.bss : {
_fbss = .;
*(.bss .bss.*)
*(COMMON)
_ebss = .;
} > ram
.mbox : {
. = ALIGN(4);
_fmbox = .;
*(.mbox)
} > mbox
/* First location in stack is highest address in RAM */
PROVIDE(_fstack = ORIGIN(ram) + LENGTH(ram) - 4);
}
/* We need to provide mprintf to ptp-noposix object files, if missing */
PROVIDE(mprintf = pp_printf);
/*
* Trivial pll programmer using an spi controoler.
* PLL is AD9516, SPI is opencores
* Tomasz Wlostowski, Alessandro Rubini, 2011, for CERN.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "board.h"
#include "syscon.h"
#include "gpio-wrs.h"
#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 ad9516_reg {
uint16_t reg;
uint8_t val;
};
#include "ad9516_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 GPIO_PLL_RESET_N 1
#define GPIO_SYS_CLK_SEL 0
#define GPIO_PERIPH_RESET_N 3
#define CS_PLL 0 /* AD9516 on SPI CS0 */
static void *oc_spi_base;
int oc_spi_init(void *base_addr)
{
oc_spi_base = base_addr;
writel(100, oc_spi_base + SPI_REG_DIVIDER);
return 0;
}
int oc_spi_txrx(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,
oc_spi_base + SPI_REG_CTRL);
writel(in, oc_spi_base + SPI_REG_TX0);
writel((1 << ss), oc_spi_base + SPI_REG_SS);
writel(SPI_CTRL_ASS | SPI_CTRL_CHAR_LEN(nbits)
| SPI_CTRL_TXNEG | SPI_CTRL_GO_BSY,
oc_spi_base + SPI_REG_CTRL);
while(readl(oc_spi_base + SPI_REG_CTRL) & SPI_CTRL_GO_BSY)
;
*out = readl(oc_spi_base + SPI_REG_RX0);
return 0;
}
/*
* AD9516 stuff, using SPI, used by later code.
* "reg" is 12 bits, "val" is 8 bits, but both are better used as int
*/
static void ad9516_write_reg(int reg, int val)
{
oc_spi_txrx(CS_PLL, 24, (reg << 8) | val, NULL);
}
static int ad9516_read_reg(int reg)
{
uint32_t rval;
oc_spi_txrx(CS_PLL, 24, (reg << 8) | (1 << 23), &rval);
return rval & 0xff;
}
static void ad9516_load_regset(const struct ad9516_reg *regs, int n_regs, int commit)
{
int i;
for(i=0; i<n_regs; i++)
ad9516_write_reg(regs[i].reg, regs[i].val);
if(commit)
ad9516_write_reg(0x232, 1);
}
static void ad9516_wait_lock()
{
while ((ad9516_read_reg(0x1f) & 1) == 0);
}
#define SECONDARY_DIVIDER 0x100
int ad9516_set_output_divider(int output, int ratio, int phase_offset)
{
uint8_t lcycles = (ratio/2) - 1;
uint8_t hcycles = (ratio - (ratio / 2)) - 1;
int secondary = (output & SECONDARY_DIVIDER) ? 1 : 0;
output &= 0xf;
if(output >= 0 && output < 6) /* LVPECL outputs */
{
uint16_t base = (output / 2) * 0x3 + 0x190;
if(ratio == 1) /* bypass the divider */
{
uint8_t div_ctl = ad9516_read_reg(base + 1);
ad9516_write_reg(base + 1, div_ctl | (1<<7) | (phase_offset & 0xf));
} else {
uint8_t div_ctl = ad9516_read_reg(base + 1);
TRACE("DivCtl: %x\n", div_ctl);
ad9516_write_reg(base + 1, (div_ctl & (~(1<<7))) | (phase_offset & 0xf)); /* disable bypass bit */
ad9516_write_reg(base, (lcycles << 4) | hcycles);
}
} else { /* LVDS/CMOS outputs */
uint16_t base = ((output - 6) / 2) * 0x5 + 0x199;
TRACE("Output [divider %d]: %d ratio: %d base %x lc %d hc %d\n", secondary, output, ratio, base, lcycles ,hcycles);
if(!secondary)
{
if(ratio == 1) /* bypass the divider 1 */
ad9516_write_reg(base + 3, ad9516_read_reg(base + 3) | 0x10);
else {
ad9516_write_reg(base, (lcycles << 4) | hcycles);
ad9516_write_reg(base + 1, phase_offset & 0xf);
}
} else {
if(ratio == 1) /* bypass the divider 2 */
ad9516_write_reg(base + 3, ad9516_read_reg(base + 3) | 0x20);
else {
ad9516_write_reg(base + 2, (lcycles << 4) | hcycles);
// ad9516_write_reg(base + 1, phase_offset & 0xf);
}
}
}
/* update */
ad9516_write_reg(0x232, 0x0);
ad9516_write_reg(0x232, 0x1);
ad9516_write_reg(0x232, 0x0);
}
int ad9516_set_vco_divider(int ratio) /* Sets the VCO divider (2..6) or 0 to enable static output */
{
if(ratio == 0)
ad9516_write_reg(0x1e0, 0x5); /* static mode */
else
ad9516_write_reg(0x1e0, (ratio-2));
ad9516_write_reg(0x232, 0x1);
}
void ad9516_sync_outputs()
{
/* VCO divider: static mode */
ad9516_write_reg(0x1E0, 0x7);
ad9516_write_reg(0x232, 0x1);
/* Sync the outputs when they're inactive to avoid +-1 cycle uncertainity */
ad9516_write_reg(0x230, 1);
ad9516_write_reg(0x232, 1);
ad9516_write_reg(0x230, 0);
ad9516_write_reg(0x232, 1);
}
int ad9516_init(int ref_source)
{
TRACE("Initializing AD9516 PLL...\n");
oc_spi_init((void *)BASE_SPI);
gpio_out(GPIO_SYS_CLK_SEL, 0); /* switch to the standby reference clock, since the PLL is off after reset */
/* reset the PLL */
gpio_out(GPIO_PLL_RESET_N, 0);
timer_delay(10);
gpio_out(GPIO_PLL_RESET_N, 1);
timer_delay(10);
/* Use unidirectional SPI mode */
ad9516_write_reg(0x000, 0x99);
/* Check the presence of the chip */
if (ad9516_read_reg(0x3) != 0xc3) {
TRACE("Error: AD9516 PLL not responding.\n");
return -1;
}
ad9516_load_regset(ad9516_base_config, ARRAY_SIZE(ad9516_base_config), 0);
ad9516_load_regset(ad9516_ref_tcxo, ARRAY_SIZE(ad9516_ref_tcxo), 1);
ad9516_wait_lock();
ad9516_sync_outputs();
ad9516_set_output_divider(9, 4, 0); /* AUX/SWCore = 187.5 MHz */
ad9516_set_output_divider(7, 12, 0); /* REF = 62.5 MHz */
ad9516_set_output_divider(4, 12, 0); /* GTX = 62.5 MHz */
ad9516_sync_outputs();
ad9516_set_vco_divider(2);
TRACE("AD9516 locked.\n");
gpio_out(GPIO_SYS_CLK_SEL, 1); /* switch the system clock to the PLL reference */
gpio_out(GPIO_PERIPH_RESET_N, 0); /* reset all peripherals which use AD9516-provided clocks */
gpio_out(GPIO_PERIPH_RESET_N, 1);
return 0;
}
int rts_debug_command(int command, int value)
{
switch(command)
{
case RTS_DEBUG_ENABLE_SERDES_CLOCKS:
if(value)
{
ad9516_write_reg(0xf4, 0x08); // OUT4 enabled
ad9516_write_reg(0x232, 0x0);
ad9516_write_reg(0x232, 0x1);
} else {
ad9516_write_reg(0xf4, 0x0a); // OUT4 power-down, no serdes clock
ad9516_write_reg(0x232, 0x0);
ad9516_write_reg(0x232, 0x1);
}
break;
}
}
/* Base configuration (global dividers, output config, reference-independent) */
const struct ad9516_reg ad9516_base_config[] = {
{0x0000, 0x99},
{0x0001, 0x00},
{0x0002, 0x10},
{0x0003, 0xC3},
{0x0004, 0x00},
{0x0010, 0x7C},
{0x0011, 0x05},
{0x0012, 0x00},
{0x0013, 0x0C},
{0x0014, 0x12},
{0x0015, 0x00},
{0x0016, 0x05},
{0x0017, 0x88},
{0x0018, 0x07},
{0x0019, 0x00},
{0x001A, 0x00},
{0x001B, 0x00},
{0x001C, 0x02},
{0x001D, 0x00},
{0x001E, 0x00},
{0x001F, 0x0E},
{0x00A0, 0x01},
{0x00A1, 0x00},
{0x00A2, 0x00},
{0x00A3, 0x01},
{0x00A4, 0x00},
{0x00A5, 0x00},
{0x00A6, 0x01},
{0x00A7, 0x00},
{0x00A8, 0x00},
{0x00A9, 0x01},
{0x00AA, 0x00},
{0x00AB, 0x00},
{0x00F0, 0x0A},
{0x00F1, 0x0A},
{0x00F2, 0x0A},
{0x00F3, 0x0A},
{0x00F4, 0x08},
{0x00F5, 0x08},
{0x0140, 0x43},
{0x0141, 0x42},
{0x0142, 0x43},
{0x0143, 0x42},
{0x0190, 0x00},
{0x0191, 0x80},
{0x0192, 0x00},
{0x0193, 0xBB},
{0x0194, 0x00},
{0x0195, 0x00},
{0x0196, 0x00},
{0x0197, 0x00},
{0x0198, 0x00},
{0x0199, 0x11},
{0x019A, 0x00},
{0x019B, 0x11},
{0x019C, 0x20},
{0x019D, 0x00},
{0x019E, 0x11},
{0x019F, 0x00},
{0x01A0, 0x11},
{0x01A1, 0x20},
{0x01A2, 0x00},
{0x01A3, 0x00},
{0x01E0, 0x04},
{0x01E1, 0x02},
{0x0230, 0x00},
{0x0231, 0x00},
};
/* Config for 25 MHz VCTCXO reference (RDiv = 5, use REF1) */
const struct ad9516_reg ad9516_ref_tcxo[] = {
{0x0011, 0x05},
{0x0012, 0x00}, /* RDiv = 5 */
{0x001C, 0x06} /* Use REF1 */
};
/* Config for 10 MHz external reference (RDiv = 2, use REF2) */
const struct ad9516_reg ad9516_ref_ext[] = {
{0x0011, 0x02},
{0x0012, 0x00}, /* RDiv = 2 */
{0x001C, 0x46} /* Use REF1 */
};
const struct {int reg; uint8_t val} ad9516_regs[] = {
{0x0000, 0x99},
{0x0001, 0x00},
{0x0002, 0x10},
{0x0003, 0xC3},
{0x0004, 0x00},
{0x0010, 0x7C},
{0x0011, 0x01},
{0x0012, 0x00},
{0x0013, 0x04},
{0x0014, 0x07},
{0x0015, 0x00},
{0x0016, 0x04},
{0x0017, 0x00},
{0x0018, 0x07},
{0x0019, 0x00},
{0x001A, 0x00},
{0x001B, 0x00},
{0x001C, 0x46},
{0x001D, 0x00},
{0x001E, 0x00},
{0x001F, 0x0E},
{0x00A0, 0x01},
{0x00A1, 0x00},
{0x00A2, 0x00},
{0x00A3, 0x01},
{0x00A4, 0x00},
{0x00A5, 0x00},
{0x00A6, 0x01},
{0x00A7, 0x00},
{0x00A8, 0x00},
{0x00A9, 0x01},
{0x00AA, 0x00},
{0x00AB, 0x00},
{0x00F0, 0x0A},
{0x00F1, 0x0A},
{0x00F2, 0x0A},
{0x00F3, 0x0A},
{0x00F4, 0x08},
{0x00F5, 0x0A},
{0x0140, 0x43},
{0x0141, 0x43},
{0x0142, 0x43},
{0x0143, 0x43},
{0x0190, 0x00},
{0x0191, 0x80},
{0x0192, 0x00},
{0x0193, 0xBB},
{0x0194, 0x00},
{0x0195, 0x00},
{0x0196, 0x00},
{0x0197, 0x00},
{0x0198, 0x00},
{0x0199, 0x22},
{0x019A, 0x00},
{0x019B, 0x11},
{0x019C, 0x00},
{0x019D, 0x00},
{0x019E, 0x22},
{0x019F, 0x00},
{0x01A0, 0x11},
{0x01A1, 0x00},
{0x01A2, 0x00},
{0x01A3, 0x00},
{0x01E0, 0x04},
{0x01E1, 0x02},
{0x0230, 0x00},
{0x0231, 0x00},
{0x0232, 0x00},
{-1, 0}};
#include "board.h"
#include "syscon.h"
uint32_t timer_get_tics()
{
return *(volatile uint32_t *) (BASE_TIMER);
}
void timer_delay(uint32_t how_long)
{
uint32_t t_start;
t_start = timer_get_tics();
if(t_start + how_long < t_start)
while(t_start + how_long < timer_get_tics());
while(t_start + how_long > timer_get_tics());
}
#ifndef __GPIO_H
#define __GPIO_H
#include <stdint.h>
#include "board.h"
struct GPIO_WB
{
uint32_t CODR; /*Clear output register*/
uint32_t SODR; /*Set output register*/
uint32_t DDR; /*Data direction register (1 means out)*/
uint32_t PSR; /*Pin state register*/
};
static volatile struct GPIO_WB *__gpio = (volatile struct GPIO_WB *) BASE_GPIO;
static inline void gpio_out(int pin, int val)
{
if(val)
__gpio->SODR = (1<<pin);
else
__gpio->CODR = (1<<pin);
}
static inline void gpio_dir(int pin, int val)
{
if(val)
__gpio->DDR |= (1<<pin);
else
__gpio->DDR &= ~(1<<pin);
}
static inline int gpio_in(int bank, int pin)
{
return __gpio->PSR & (1<<pin) ? 1: 0;
}
#endif
/*
* Private definition for mini-ipc
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Based on ideas by Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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.
*/
#ifndef __MINIPC_INT_H__
#define __MINIPC_INT_H__
#include <sys/types.h>
#if __STDC_HOSTED__ /* freestanding servers have less material */
#include <sys/un.h>
#include <sys/select.h>
#endif
#include "minipc.h"
/* be safe, in case some other header had them slightly differntly */
#undef container_of
#undef offsetof
#undef ARRAY_SIZE
/* We are strongly based on container_of internally */
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/*
* While public symbols are minipc_* internal ones are mpc_* to be shorter.
* The connection includes an fd, which is the only thing returned back
*/
/* The list of functions attached to a service */
struct mpc_flist {
const struct minipc_pd *pd;
struct mpc_flist *next;
};
/*
* The main server or client structure. Server links have client sockets
* hooking on it.
*/
struct mpc_link {
struct minipc_ch ch;
int magic;
int pid;
int flags;
struct mpc_link *nextl;
struct mpc_flist *flist;
void *memaddr;
int memsize;
#if __STDC_HOSTED__ /* these fields are not used in freestanding uC */
FILE *logf;
struct sockaddr_un addr;
int fd[MINIPC_MAX_CLIENTS];
fd_set fdset;
#endif
char name[MINIPC_MAX_NAME];
};
#define MPC_MAGIC 0xc0ffee99
#define MPC_FLAG_SERVER 0x00010000
#define MPC_FLAG_CLIENT 0x00020000
#define MPC_FLAG_SHMEM 0x00040000
#define MPC_FLAG_DEVMEM 0x00080000
#define MPC_USER_FLAGS(x) ((x) & 0xffff)
/* The request packet being transferred */
struct mpc_req_packet {
char name[MINIPC_MAX_NAME];
uint32_t args[MINIPC_MAX_ARGUMENTS];
};
/* The reply packet being transferred */
struct mpc_rep_packet {
uint32_t type;
uint8_t val[MINIPC_MAX_REPLY];
};
/* A structure for shared memory (takes more than 2kB) */
struct mpc_shmem {
uint32_t nrequest; /* incremented at each request */
uint32_t nreply; /* incremented at each reply */
struct mpc_req_packet request;
struct mpc_rep_packet reply;
};
#define MPC_TIMEOUT 1000 /* msec, hardwired */
static inline struct mpc_link *mpc_get_link(struct minipc_ch *ch)
{
return container_of(ch, struct mpc_link, ch);
}
#define CHECK_LINK(link) /* Horrible shortcut, don't tell around... */ \
if ((link)->magic != MPC_MAGIC) { \
errno = EINVAL; \
return -1; \
}
extern struct mpc_link *__mpc_base;
extern void mpc_free_flist(struct mpc_link *link, struct mpc_flist *flist);
extern struct minipc_ch *__minipc_link_create(const char *name, int flags);
/* Used for lists and structures -- sizeof(uint32_t) is 4, is it? */
#define MINIPC_GET_ANUM(len) (((len) + 3) >> 2)
#endif /* __MINIPC_INT_H__ */
/*
* Mini-ipc: Exported functions for freestanding server
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This replicates some code of minipc-core and minipc-server.
* It implements the functions needed to make a freestanding server
* (for example, an lm32 running on an FPGA -- the case I actually need).
*/
#include "minipc-int.h"
#include <string.h>
#include <sys/errno.h>
/* HACK: use a static link, one only */
static struct mpc_link __static_link;
/* The create function just picks an hex address from the name "mem:AABBCC" */
struct minipc_ch *minipc_server_create(const char *name, int flags)
{
struct mpc_link *link = &__static_link;
int i, c, addr = 0;
int memsize = sizeof(struct mpc_shmem);
if (link->magic) {
errno = EBUSY;
return NULL;
}
/* Most code from __minipc_link_create and __minipc_memlink_create */
flags |= MPC_FLAG_SERVER;
if (strncmp(name, "mem:", 4)) {
errno = EINVAL;
return NULL;
}
/* Ok, valid name. Hopefully */
link->magic = MPC_MAGIC;
link->flags = flags;
strncpy(link->name, name, sizeof(link->name) -1);
/* Parse the hex address */
for (i = 4; (c = name[i]); i++) {
addr *= 0x10;
if (c >= '0' && c <= '9') addr += c - '0';
if (c >= 'a' && c <= 'f') addr += c - 'a' + 10;
if (c >= 'A' && c <= 'F') addr += c - 'A' + 10;
}
link->flags |= MPC_FLAG_SHMEM; /* needed? */
link->memaddr = (void *)addr;
link->memsize = memsize;
link->pid = 0; /* hack: nrequest */
if (link->flags & MPC_FLAG_SERVER)
memset(link->memaddr, 0, memsize);
return &link->ch;
}
/* Close only marks the link as available */
int minipc_close(struct minipc_ch *ch)
{
struct mpc_link *link = mpc_get_link(ch);
CHECK_LINK(link);
link->magic = 0; /* available */
return 0;
}
/* HACK: use a static array of flist, to avoid malloc and free */
static struct mpc_flist __static_flist[MINIPC_MAX_EXPORT];
static void *calloc(size_t unused, size_t unused2)
{
int i;
struct mpc_flist *p;
for (p = __static_flist, i = 0; i < MINIPC_MAX_EXPORT; p++, i++)
if (!p->pd)
break;
if (i == MINIPC_MAX_EXPORT) {
errno = ENOMEM;
return NULL;
}
return p;
}
static void free(void *ptr)
{
struct mpc_flist *p = ptr;
p->pd = NULL;
}
/* From: minipc-core.c, but relying on fake free above */
void mpc_free_flist(struct mpc_link *link, struct mpc_flist *flist)
{
struct mpc_flist **nextp;
/* Look for flist and release it*/
for (nextp = &link->flist; (*nextp); nextp = &(*nextp)->next)
if (*nextp == flist)
break;
if (!*nextp) {
return;
}
*nextp = flist->next;
free(flist);
}
/* From: minipc-server.c -- but no log and relies on fake calloc above */
int minipc_export(struct minipc_ch *ch, const struct minipc_pd *pd)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_flist *flist;
CHECK_LINK(link);
flist = calloc(1, sizeof(*flist));
if (!flist)
return -1;
flist->pd = pd;
flist->next = link->flist;
link->flist = flist;
return 0;
}
/* From: minipc-server.c -- but no log file */
int minipc_unexport(struct minipc_ch *ch, const struct minipc_pd *pd)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_flist *flist;
CHECK_LINK(link);
/* We must find the flist that points to pd */
for (flist = link->flist; flist; flist = flist->next)
if (flist->pd == pd)
break;
if (!flist) {
errno = EINVAL;
return -1;
}
flist = container_of(&pd, struct mpc_flist, pd);
mpc_free_flist(link, flist);
return 0;
}
/* From: minipc-server.c */
uint32_t *minipc_get_next_arg(uint32_t arg[], uint32_t atype)
{
int asize;
char *s = (void *)arg;
if (MINIPC_GET_ATYPE(atype) != MINIPC_ATYPE_STRING)
asize = MINIPC_GET_ANUM(MINIPC_GET_ASIZE(atype));
else
asize = MINIPC_GET_ANUM(strlen(s) + 1);
return arg + asize;
}
/* From: minipc-server.c (mostly: mpc_handle_client) */
int minipc_server_action(struct minipc_ch *ch, int timeoutms)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_req_packet *p_in;
struct mpc_rep_packet *p_out;
struct mpc_shmem *shm = link->memaddr;
const struct minipc_pd *pd;
struct mpc_flist *flist;
int i;
CHECK_LINK(link);
/* keep track of the request in an otherwise unused field */
if (shm->nrequest == link->pid)
return 0;
link->pid = shm->nrequest;
p_in = &shm->request;
p_out = &shm->reply;
/* use p_in->name to look for the function */
for (flist = link->flist; flist; flist = flist->next)
if (!(strcmp(p_in->name, flist->pd->name)))
break;
if (!flist) {
p_out->type = MINIPC_ARG_ENCODE(MINIPC_ATYPE_ERROR, int);
*(int *)(&p_out->val) = EOPNOTSUPP;
goto send_reply;
}
pd = flist->pd;
/* call the function and send back stuff */
i = pd->f(pd, p_in->args, p_out->val);
if (i < 0) {
p_out->type = MINIPC_ARG_ENCODE(MINIPC_ATYPE_ERROR, int);
*(int *)(&p_out->val) = errno;
} else {
/* Use retval, but fix the length for strings */
if (MINIPC_GET_ATYPE(pd->retval) == MINIPC_ATYPE_STRING) {
int size = strlen((char *)p_out->val) + 1;
size = (size + 3) & ~3; /* align */
p_out->type =
__MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, size);
} else {
p_out->type = pd->retval;
}
}
send_reply:
shm->nreply++; /* message already in place */
return 0;
}
/*
* Public definition for mini-ipc
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Based on ideas by Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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.
*/
#ifndef __MINIPC_H__
#define __MINIPC_H__
#include <stdint.h>
#if __STDC_HOSTED__ /* freestanding servers have less material */
#include <stdio.h>
#include <sys/select.h>
#endif
/* Hard limits */
#define MINIPC_MAX_NAME 20 /* includes trailing 0 */
#define MINIPC_MAX_CLIENTS 64
#define MINIPC_MAX_ARGUMENTS 256 /* Also, max size of packet words -- 1k */
#define MINIPC_MAX_REPLY 1024 /* bytes */
#if !__STDC_HOSTED__
#define MINIPC_MAX_EXPORT 12 /* freestanding: static allocation */
#endif
/* The base pathname, mkdir is performed as needed */
#define MINIPC_BASE_PATH "/tmp/.minipc"
/* Default polling interval for memory-based channels */
#define MINIPC_DEFAULT_POLL (10*1000)
/* Argument type (and retval type). The size is encoded in the same word */
enum minipc_at {
MINIPC_ATYPE_ERROR = 0xffff,
MINIPC_ATYPE_NONE = 0, /* used as terminator */
MINIPC_ATYPE_INT = 1,
MINIPC_ATYPE_INT64,
MINIPC_ATYPE_DOUBLE, /* float is promoted to double */
MINIPC_ATYPE_STRING, /* size of strings is strlen() each time */
MINIPC_ATYPE_STRUCT
};
/* Encoding of argument type and size in one word */
#define __MINIPC_ARG_ENCODE(atype, asize) (((atype) << 16) | (asize))
#define MINIPC_ARG_ENCODE(atype, type) __MINIPC_ARG_ENCODE(atype, sizeof(type))
#define MINIPC_GET_ATYPE(word) ((word) >> 16)
#define MINIPC_GET_ASIZE(word) ((word) & 0xffff)
#define MINIPC_ARG_END __MINIPC_ARG_ENCODE(MINIPC_ATYPE_NONE, 0) /* zero */
/* The exported procedure looks like this */
struct minipc_pd;
typedef int (minipc_f)(const struct minipc_pd *, uint32_t *args, void *retval);
/* This is the "procedure definition" */
struct minipc_pd {
minipc_f *f; /* pointer to the function */
char name[MINIPC_MAX_NAME]; /* name of the function */
uint32_t flags;
uint32_t retval; /* type of return value */
uint32_t args[]; /* zero-terminated */
};
/* Flags: verbosity is about argument and retval marshall/unmarshall */
#define MINIPC_FLAG_VERBOSE 1
/* This is the channel definition */
struct minipc_ch {
int fd;
};
static inline int minipc_fileno(struct minipc_ch *ch) {return ch->fd;}
/* These return NULL with errno on error, name is the socket pathname */
struct minipc_ch *minipc_server_create(const char *name, int flags);
struct minipc_ch *minipc_client_create(const char *name, int flags);
int minipc_close(struct minipc_ch *ch);
/* Generic: set the default polling interval for mem-based channels */
int minipc_set_poll(int usec);
/* Server: register exported functions */
int minipc_export(struct minipc_ch *ch, const struct minipc_pd *pd);
int minipc_unexport(struct minipc_ch *ch, const struct minipc_pd *pd);
/* Server: helpers to unmarshall a string or struct from a request */
uint32_t *minipc_get_next_arg(uint32_t arg[], uint32_t atype);
/* Handle a request if pending, otherwise -1 and EAGAIN */
int minipc_server_action(struct minipc_ch *ch, int timeoutms);
#if __STDC_HOSTED__
/* Generic: attach diagnostics to a log file */
int minipc_set_logfile(struct minipc_ch *ch, FILE *logf);
/* Return an fdset for the user to select() on the service */
int minipc_server_get_fdset(struct minipc_ch *ch, fd_set *setptr);
/* Client: make requests */
int minipc_call(struct minipc_ch *ch, int millisec_timeout,
const struct minipc_pd *pd, void *ret, ...);
#endif /* __STDC_HOSTED__ */
#endif /* __MINIPC_H__ */
/*
* Mini-ipc: an example freestanding server, based in memory
*
* Copyright (C) 2011,2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This code is copied from trivial-server, and made even more trivial
*/
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include "minipc.h"
#define RTIPC_EXPORT_STRUCTURES
#include "rt_ipc.h"
#include <softpll_ng.h>
static struct rts_pll_state pstate;
static void clear_state()
{
int i;
for(i=0;i<RTS_PLL_CHANNELS;i++)
{
pstate.channels[i].priority = 0;
pstate.channels[i].phase_setpoint = 0;
pstate.channels[i].phase_loopback = 0;
pstate.channels[i].flags = CHAN_REF_VALID;
}
pstate.flags = 0;
pstate.current_ref = 0;
pstate.mode = RTS_MODE_DISABLED;
pstate.ipc_count = 0;
}
/* Sets the phase setpoint on a given channel */
int rts_adjust_phase(int channel, int32_t phase_setpoint)
{
// TRACE("Adjusting phase: ref channel %d, setpoint=%d ps.\n", channel, phase_setpoint);
spll_set_phase_shift(0, phase_setpoint);
pstate.channels[channel].phase_setpoint = phase_setpoint;
return 0;
}
/* Sets the RT subsystem mode (Boundary Clock or Grandmaster) */
int rts_set_mode(int mode)
{
int i;
const struct {
int mode_rt;
int mode_spll;
int do_init;
char *desc;
} options[] = {
{ RTS_MODE_GM_EXTERNAL, SPLL_MODE_GRAND_MASTER, 1, "Grand Master (external clock)" },
{ RTS_MODE_GM_FREERUNNING, SPLL_MODE_FREE_RUNNING_MASTER, 1, "Grand Master (free-running clock)" },
{ RTS_MODE_BC, SPLL_MODE_SLAVE, 0, "Boundary Clock (slave)" },
{ RTS_MODE_DISABLED, SPLL_MODE_DISABLED, 1, "PLL disabled" },
{ 0,0,0, NULL }
};
pstate.mode = mode;
for(i=0;options[i].desc != NULL;i++)
if(mode == options[i].mode_rt)
{
TRACE("RT: Setting mode to %s.\n", options[i].desc);
if(options[i].do_init)
spll_init(options[i].mode_spll, 0, 1);
else
spll_init(SPLL_MODE_DISABLED, 0, 0);
}
return 0;
}
/* Reference channel configuration (BC mode only) */
int rts_lock_channel(int channel, int priority)
{
if(pstate.mode != RTS_MODE_BC)
{
TRACE("trying to lock while not in slave mode,..\n");
return -1;
}
TRACE("RT [slave]: Locking to: %d (prio %d)\n", channel, priority);
spll_init(SPLL_MODE_SLAVE, channel, 0);
pstate.current_ref = channel;
return 0;
}
int rts_init()
{
clear_state();
}
void rts_update()
{
int i;
int n_ref;
int enabled;
spll_get_num_channels(&n_ref, NULL);
pstate.flags = (spll_check_lock(0) ? RTS_DMTD_LOCKED | RTS_REF_LOCKED : 0);
for(i=0;i<RTS_PLL_CHANNELS;i++)
{
#define CH pstate.channels[i]
CH.flags = 0;
CH.phase_loopback = 0;
CH.phase_current = 0;
// CH.phase_setpoint = 0;
CH.phase_loopback = 0;
if(i >= n_ref)
CH.flags = CHAN_DISABLED;
else {
if(i==pstate.current_ref)
{
spll_get_phase_shift(0, &CH.phase_current, NULL);
if(spll_shifter_busy(0))
CH.flags |= CHAN_SHIFTING;
}
if(spll_read_ptracker(i, &CH.phase_loopback, &enabled))
CH.flags |= CHAN_PMEAS_READY;
CH.flags |= (enabled ? CHAN_PTRACKER_ENABLED : 0);
}
#undef CH
}
}
/* fixme: this assumes the host is BE */
static int htonl(int i)
{
return i;
}
static int rts_get_state_func(const struct minipc_pd *pd, uint32_t *args, void *ret)
{
struct rts_pll_state *tmp = (struct rts_pll_state *)ret;
int i;
pstate.ipc_count++;
/* gaaaah, somebody should write a SWIG plugin for generating this stuff. */
tmp->current_ref = htonl(pstate.current_ref);
tmp->flags = htonl(pstate.flags);
tmp->holdover_duration = htonl(pstate.holdover_duration);
tmp->mode = htonl(pstate.mode);
tmp->delock_count = spll_get_delock_count();
tmp->ipc_count = pstate.ipc_count;
for(i=0; i<RTS_PLL_CHANNELS;i++)
{
tmp->channels[i].priority = htonl(pstate.channels[i].priority);
tmp->channels[i].phase_setpoint = htonl(pstate.channels[i].phase_setpoint);
tmp->channels[i].phase_current = htonl(pstate.channels[i].phase_current);
tmp->channels[i].phase_loopback = htonl(pstate.channels[i].phase_loopback);
tmp->channels[i].flags = htonl(pstate.channels[i].flags);
}
return 0;
}
static int rts_set_mode_func(const struct minipc_pd *pd, uint32_t *args, void *ret)
{
pstate.ipc_count++;
*(int *) ret = rts_set_mode(args[0]);
}
static int rts_lock_channel_func(const struct minipc_pd *pd, uint32_t *args, void *ret)
{
pstate.ipc_count++;
*(int *) ret = rts_lock_channel(args[0], (int)args[1]);
}
static int rts_adjust_phase_func(const struct minipc_pd *pd, uint32_t *args, void *ret)
{
pstate.ipc_count++;
*(int *) ret = rts_adjust_phase((int)args[0], (int)args[1]);
}
static int rts_enable_ptracker_func(const struct minipc_pd *pd, uint32_t *args, void *ret)
{
pstate.ipc_count++;
spll_enable_ptracker((int)args[0], (int)args[1]);
*(int *) ret = 0;
}
static int rts_debug_command_func(const struct minipc_pd *pd, uint32_t *args, void *ret)
{
pstate.ipc_count++;
*(int *) ret = rts_debug_command((int)args[0], (int)args[1]);
}
/* The mailbox is mapped at 0x7000 in the linker script */
static __attribute__((section(".mbox"))) _mailbox[1024];
static struct minipc_ch *server;
int rtipc_init()
{
server = minipc_server_create("mem:7000", 0);
if (!server)
return 1;
rtipc_rts_set_mode_struct.f = rts_set_mode_func;
rtipc_rts_get_state_struct.f = rts_get_state_func;
rtipc_rts_lock_channel_struct.f = rts_lock_channel_func;
rtipc_rts_adjust_phase_struct.f = rts_adjust_phase_func;
rtipc_rts_enable_ptracker_struct.f = rts_enable_ptracker_func;
rtipc_rts_debug_command_struct.f = rts_debug_command_func;
minipc_export(server, &rtipc_rts_set_mode_struct);
minipc_export(server, &rtipc_rts_get_state_struct);
minipc_export(server, &rtipc_rts_lock_channel_struct);
minipc_export(server, &rtipc_rts_adjust_phase_struct);
minipc_export(server, &rtipc_rts_enable_ptracker_struct);
minipc_export(server, &rtipc_rts_debug_command_struct);
return 0;
}
void rtipc_action()
{
minipc_server_action(server, 1000);
}
#ifndef __RT_IPC_H
#define __RT_IPC_H
#include <stdint.h>
#define RTS_PLL_CHANNELS 18
/* Individual channel flags */
/* Reference input frequency valid */
#define CHAN_REF_VALID (1<<0)
/* Frequency out of range */
#define CHAN_FREQ_OUT_OF_RANGE (1<<1)
/* Phase is drifting too fast */
#define CHAN_DRIFTING (1<<2)
/* Channel phase measurement is ready */
#define CHAN_PMEAS_READY (1<<3)
/* Channel not available/disabled */
#define CHAN_DISABLED (1<<4)
/* Channel is busy adjusting phase */
#define CHAN_SHIFTING (1<<5)
/* Channel is busy adjusting phase */
#define CHAN_PTRACKER_ENABLED (1<<6)
/* DMTD clock is present */
#define RTS_DMTD_LOCKED (1<<0)
/* 125 MHz reference locked */
#define RTS_REF_LOCKED (1<<1)
/* External 10 MHz reference present */
#define RTS_EXT_10M_VALID (1<<2)
/* External 1-PPS present */
#define RTS_EXT_PPS_VALID (1<<3)
/* External 10 MHz frequency out-of-range */
#define RTS_EXT_10M_OUT_OF_RANGE (1<<4)
/* External 1-PPS frequency out-of-range */
#define RTS_EXT_PPS_OUT_OF_RANGE (1<<5)
/* Holdover mode active */
#define RTS_HOLDOVER_ACTIVE (1<<6)
/* Grandmaster mode active (uses 10 MHz / 1-PPS reference) */
#define RTS_MODE_GM_EXTERNAL 1
/* Free-running grandmaster (uses local TCXO) */
#define RTS_MODE_GM_FREERUNNING 2
/* Boundary clock mode active (uses network reference) */
#define RTS_MODE_BC 3
/* PLL disabled */
#define RTS_MODE_DISABLED 4
/* null reference input */
#define REF_NONE 255
/* RT Subsystem debug commands, handled via rts_debug_command() */
/* Serdes reference clock enable/disable */
#define RTS_DEBUG_ENABLE_SERDES_CLOCKS 1
struct rts_pll_state {
/* State of an individual input channel (i.e. switch port) */
struct channel {
/* Switchover priority: 0 = highest, 1 - 254 = high..low, 255 = channel disabled (a master port) */
uint32_t priority;
/* channel phase setpoint in picoseconds. Used only when channel is a slave. */
int32_t phase_setpoint;
/* current phase shift in picoseconds. Used only when channel is a slave. */
int32_t phase_current;
/* TX-RX Loopback phase measurement in picoseconds. */
int32_t phase_loopback;
/* flags (per channel - see CHAN_xxx defines) */
uint32_t flags;
} channels[RTS_PLL_CHANNELS];
/* flags (global - RTS_xxx defines) */
uint32_t flags;
/* duration of current holdover period in 10us units */
int32_t holdover_duration;
/* current reference source - or REF_NONE if free-running or grandmaster */
uint32_t current_ref;
/* mode of operation (RTS_MODE_xxx) */
uint32_t mode;
uint32_t delock_count;
uint32_t ipc_count;
uint32_t debug_data[8];
};
/* API */
/* Connects to the RT CPU */
int rts_connect();
/* Queries the RT CPU PLL state */
int rts_get_state(struct rts_pll_state *state);
/* Sets the phase setpoint on a given channel */
int rts_adjust_phase(int channel, int32_t phase_setpoint);
/* Sets the RT subsystem mode (Boundary Clock or Grandmaster) */
int rts_set_mode(int mode);
/* Reference channel configuration (BC mode only) */
int rts_lock_channel(int channel, int priority);
/* Enabled/disables phase tracking on a particular port */
int rts_enable_ptracker(int channel, int enable);
/* Enabled/disables phase tracking on a particular port */
int rts_debug_command(int param, int value);
#ifdef RTIPC_EXPORT_STRUCTURES
static struct minipc_pd rtipc_rts_get_state_struct = {
.name = "aaaa",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct rts_pll_state),
.args = {
MINIPC_ARG_END
},
};
static struct minipc_pd rtipc_rts_set_mode_struct = {
.name = "bbbb",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int ),
MINIPC_ARG_END
},
};
static struct minipc_pd rtipc_rts_lock_channel_struct = {
.name = "cccc",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int ),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int ),
MINIPC_ARG_END
},
};
static struct minipc_pd rtipc_rts_adjust_phase_struct = {
.name = "dddd",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int ),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int ),
MINIPC_ARG_END
},
};
static struct minipc_pd rtipc_rts_enable_ptracker_struct = {
.name = "eeee",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int ),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int ),
MINIPC_ARG_END
},
};
static struct minipc_pd rtipc_rts_debug_command_struct = {
.name = "ffff",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int ),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int ),
MINIPC_ARG_END
},
};
#endif
#endif
#include "uart.h"
#include "syscon.h"
#include "softpll_ng.h"
#include "minipc.h"
const char *build_revision;
const char *build_date;
main()
{
uint32_t start_tics = 0;
uart_init_hw();
TRACE("WR Switch Real Time Subsystem (c) CERN 2011 - 2013\n");
TRACE("Revision: %s, built %s.\n", build_revision, build_date);
TRACE("--");
ad9516_init();
rts_init();
rtipc_init();
for(;;)
{
uint32_t tics = timer_get_tics();
if(tics - start_tics > TICS_PER_SECOND/5)
{
// TRACE("tick!\n");
spll_show_stats();
start_tics = tics;
}
rts_update();
rtipc_action();
}
return 0;
}
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