1998-05-30 18:28:12 +00:00
|
|
|
/*
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
* "THE BEER-WARE LICENSE" (Revision 42):
|
|
|
|
* <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
|
|
|
|
* can do whatever you want with this stuff. If we meet some day, and you think
|
|
|
|
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
*
|
1999-01-12 01:42:43 +00:00
|
|
|
* $Id: xrpu.c,v 1.5 1998/12/14 06:32:58 dillon Exp $
|
1998-05-30 18:28:12 +00:00
|
|
|
*
|
|
|
|
* A very simple device driver for PCI cards based on Xilinx 6200 series
|
|
|
|
* FPGA/RPU devices. Current Functionality is to allow you to open and
|
|
|
|
* mmap the entire thing into your program.
|
|
|
|
*
|
|
|
|
* Hardware currently supported:
|
|
|
|
* www.vcc.com HotWorks 1 6216 based card.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
1998-10-24 19:47:42 +00:00
|
|
|
#include "xrpu.h"
|
1998-05-30 18:28:12 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/conf.h>
|
|
|
|
#include <sys/kernel.h>
|
1998-10-24 19:47:42 +00:00
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/timepps.h>
|
1998-05-30 18:28:12 +00:00
|
|
|
#include <sys/devfsext.h>
|
1998-10-24 19:47:42 +00:00
|
|
|
#include <sys/xrpuio.h>
|
1998-05-30 18:28:12 +00:00
|
|
|
#include <pci/pcireg.h>
|
|
|
|
#include <pci/pcivar.h>
|
|
|
|
|
1998-12-14 06:37:37 +00:00
|
|
|
static const char* xrpu_probe (pcici_t tag, pcidi_t type);
|
1998-05-30 18:28:12 +00:00
|
|
|
static void xrpu_attach (pcici_t tag, int unit);
|
|
|
|
static u_long xrpu_count;
|
|
|
|
|
1998-10-24 19:47:42 +00:00
|
|
|
static void xrpu_poll_pps(struct timecounter *tc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Device driver initialization stuff
|
|
|
|
*/
|
|
|
|
|
|
|
|
static d_open_t xrpu_open;
|
|
|
|
static d_close_t xrpu_close;
|
|
|
|
static d_ioctl_t xrpu_ioctl;
|
|
|
|
static d_mmap_t xrpu_mmap;
|
|
|
|
|
|
|
|
#define CDEV_MAJOR 100
|
|
|
|
static struct cdevsw xrpudevsw = {
|
|
|
|
xrpu_open, xrpu_close, noread, nowrite,
|
|
|
|
xrpu_ioctl, nullstop, noreset, nodevtotty,
|
|
|
|
seltrue, xrpu_mmap, nostrategy, "xrpu",
|
|
|
|
NULL, -1
|
|
|
|
};
|
|
|
|
|
|
|
|
static MALLOC_DEFINE(M_XRPU, "xrpu", "XRPU related");
|
|
|
|
|
|
|
|
#define dev2unit(devt) (minor(devt) & 0xff)
|
|
|
|
#define dev2pps(devt) ((minor(devt) >> 16)-1)
|
|
|
|
|
|
|
|
static struct softc {
|
|
|
|
pcici_t tag;
|
|
|
|
enum { NORMAL, TIMECOUNTER } mode;
|
|
|
|
vm_offset_t virbase, physbase;
|
|
|
|
u_int *virbase62;
|
|
|
|
struct timecounter tc;
|
|
|
|
u_int *trigger, *latch, dummy;
|
|
|
|
struct {
|
|
|
|
pps_params_t params;
|
|
|
|
pps_info_t info;
|
|
|
|
int cap;
|
|
|
|
u_int *assert, last_assert;
|
|
|
|
u_int *clear, last_clear;
|
|
|
|
} pps[XRPU_MAX_PPS];
|
|
|
|
} *softc[NXRPU];
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
xrpu_get_timecount(struct timecounter *tc)
|
|
|
|
{
|
|
|
|
struct softc *sc = tc->tc_priv;
|
|
|
|
|
|
|
|
sc->dummy += *sc->trigger;
|
|
|
|
return (*sc->latch & tc->tc_counter_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
xrpu_poll_pps(struct timecounter *tc)
|
|
|
|
{
|
|
|
|
struct softc *sc = tc->tc_priv;
|
|
|
|
int i;
|
|
|
|
unsigned count1, ppscount;
|
|
|
|
|
|
|
|
for (i = 0; i < XRPU_MAX_PPS; i++) {
|
|
|
|
if (sc->pps[i].assert) {
|
|
|
|
ppscount = *(sc->pps[i].assert) & tc->tc_counter_mask;
|
|
|
|
do {
|
|
|
|
count1 = ppscount;
|
|
|
|
ppscount = *(sc->pps[i].assert) & tc->tc_counter_mask;
|
|
|
|
} while (ppscount != count1);
|
|
|
|
if (ppscount != sc->pps[i].last_assert) {
|
|
|
|
timecounter_timespec(ppscount, &sc->pps[i].info.assert_timestamp);
|
|
|
|
if (sc->pps[i].params.mode & PPS_OFFSETASSERT) {
|
|
|
|
timespecadd(&sc->pps[i].info.assert_timestamp,
|
|
|
|
&sc->pps[i].params.assert_offset);
|
|
|
|
if (sc->pps[i].info.assert_timestamp.tv_nsec < 0) {
|
|
|
|
sc->pps[i].info.assert_timestamp.tv_nsec += 1000000000;
|
|
|
|
sc->pps[i].info.assert_timestamp.tv_sec -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sc->pps[i].info.assert_sequence++;
|
|
|
|
sc->pps[i].last_assert = ppscount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sc->pps[i].clear) {
|
|
|
|
ppscount = *(sc->pps[i].clear) & tc->tc_counter_mask;
|
|
|
|
do {
|
|
|
|
count1 = ppscount;
|
|
|
|
ppscount = *(sc->pps[i].clear) & tc->tc_counter_mask;
|
|
|
|
} while (ppscount != count1);
|
|
|
|
if (ppscount != sc->pps[i].last_clear) {
|
|
|
|
timecounter_timespec(ppscount, &sc->pps[i].info.clear_timestamp);
|
|
|
|
if (sc->pps[i].params.mode & PPS_OFFSETASSERT) {
|
|
|
|
timespecadd(&sc->pps[i].info.clear_timestamp,
|
|
|
|
&sc->pps[i].params.clear_offset);
|
|
|
|
if (sc->pps[i].info.clear_timestamp.tv_nsec < 0) {
|
|
|
|
sc->pps[i].info.clear_timestamp.tv_nsec += 1000000000;
|
|
|
|
sc->pps[i].info.clear_timestamp.tv_sec -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sc->pps[i].info.clear_sequence++;
|
|
|
|
sc->pps[i].last_clear = ppscount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1998-05-30 18:28:12 +00:00
|
|
|
|
|
|
|
static int
|
1998-10-24 19:47:42 +00:00
|
|
|
xrpu_open(dev_t dev, int flag, int mode, struct proc *p)
|
1998-05-30 18:28:12 +00:00
|
|
|
{
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
1998-10-24 19:47:42 +00:00
|
|
|
xrpu_close(dev_t dev, int flag, int mode, struct proc *p)
|
1998-05-30 18:28:12 +00:00
|
|
|
{
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
1998-11-08 12:39:07 +00:00
|
|
|
xrpu_mmap(dev_t dev, vm_offset_t offset, int nprot)
|
1998-05-30 18:28:12 +00:00
|
|
|
{
|
1998-10-24 19:47:42 +00:00
|
|
|
struct softc *sc = softc[dev2unit(dev)];
|
1998-05-30 18:28:12 +00:00
|
|
|
if (offset >= 0x1000000)
|
|
|
|
return (-1);
|
1998-10-24 19:47:42 +00:00
|
|
|
return (i386_btop(sc->physbase + offset));
|
1998-05-30 18:28:12 +00:00
|
|
|
}
|
|
|
|
|
1998-10-24 19:47:42 +00:00
|
|
|
static int
|
|
|
|
xrpu_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *pr)
|
|
|
|
{
|
|
|
|
struct softc *sc = softc[dev2unit(dev)];
|
|
|
|
int i, error;
|
1998-05-30 18:28:12 +00:00
|
|
|
|
1998-10-24 19:47:42 +00:00
|
|
|
if (sc->mode == TIMECOUNTER) {
|
|
|
|
i = dev2pps(dev);
|
|
|
|
if (i < 0 || i >= XRPU_MAX_PPS)
|
|
|
|
return ENODEV;
|
|
|
|
if (!sc->pps[i].cap)
|
|
|
|
return ENODEV;
|
|
|
|
error = std_pps_ioctl(cmd, arg, &sc->pps[i].params,
|
|
|
|
&sc->pps[i].info, sc->pps[i].cap);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd == XRPU_IOC_TIMECOUNTING) {
|
|
|
|
struct xrpu_timecounting *xt = (struct xrpu_timecounting *)arg;
|
|
|
|
|
|
|
|
/* Name SHALL be zero terminated */
|
|
|
|
xt->xt_name[sizeof xt->xt_name - 1] = '\0';
|
|
|
|
i = strlen(xt->xt_name);
|
|
|
|
sc->tc.tc_name = (char *)malloc(i + 1, M_XRPU, M_WAITOK);
|
|
|
|
strcpy(sc->tc.tc_name, xt->xt_name);
|
|
|
|
sc->tc.tc_frequency = xt->xt_frequency;
|
|
|
|
sc->tc.tc_get_timecount = xrpu_get_timecount;
|
|
|
|
sc->tc.tc_poll_pps = xrpu_poll_pps;
|
|
|
|
sc->tc.tc_priv = sc;
|
|
|
|
sc->tc.tc_counter_mask = xt->xt_mask;
|
|
|
|
sc->trigger = sc->virbase62 + xt->xt_addr_trigger;
|
|
|
|
sc->latch = sc->virbase62 + xt->xt_addr_latch;
|
|
|
|
|
|
|
|
for (i = 0; i < XRPU_MAX_PPS; i++) {
|
|
|
|
if (xt->xt_pps[i].xt_addr_assert == 0
|
|
|
|
&& xt->xt_pps[i].xt_addr_clear == 0)
|
|
|
|
continue;
|
|
|
|
devfs_add_devswf(&xrpudevsw, (i+1)<<16, DV_CHR, UID_ROOT, GID_WHEEL, 0600,
|
|
|
|
"xpps%d", i);
|
|
|
|
/* DEVFS */
|
|
|
|
if (xt->xt_pps[i].xt_addr_assert) {
|
|
|
|
sc->pps[i].assert = sc->virbase62 + xt->xt_pps[i].xt_addr_assert;
|
|
|
|
sc->pps[i].cap |= PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
|
|
|
|
}
|
|
|
|
if (xt->xt_pps[i].xt_addr_clear) {
|
|
|
|
sc->pps[i].clear = sc->virbase62 + xt->xt_pps[i].xt_addr_clear;
|
|
|
|
sc->pps[i].cap |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sc->mode = TIMECOUNTER;
|
|
|
|
init_timecounter(&sc->tc);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
error = ENOTTY;
|
|
|
|
return (error);
|
|
|
|
}
|
1998-05-30 18:28:12 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* PCI initialization stuff
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct pci_device xrpu_device = {
|
|
|
|
"xrpu",
|
|
|
|
xrpu_probe,
|
|
|
|
xrpu_attach,
|
|
|
|
&xrpu_count,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
DATA_SET (pcidevice_set, xrpu_device);
|
|
|
|
|
1998-12-14 06:37:37 +00:00
|
|
|
static const char*
|
1998-05-30 18:28:12 +00:00
|
|
|
xrpu_probe (pcici_t tag, pcidi_t typea)
|
|
|
|
{
|
1999-01-12 01:42:43 +00:00
|
|
|
u_int id;
|
1998-05-30 18:28:12 +00:00
|
|
|
const char *vendor, *chip, *type;
|
|
|
|
|
1999-01-12 01:42:43 +00:00
|
|
|
(void)pci_conf_read(tag, PCI_CLASS_REG);
|
|
|
|
id = pci_conf_read(tag, PCI_ID_REG);
|
|
|
|
|
1998-05-30 18:28:12 +00:00
|
|
|
vendor = chip = type = 0;
|
|
|
|
|
|
|
|
if (id == 0x6216133e) {
|
|
|
|
return "VCC Hotworks-I xc6216";
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xrpu_attach (pcici_t tag, int unit)
|
|
|
|
{
|
1998-10-24 19:47:42 +00:00
|
|
|
struct softc *sc;
|
|
|
|
dev_t cdev = makedev(CDEV_MAJOR, unit);
|
|
|
|
|
|
|
|
sc = (struct softc *)malloc(sizeof *sc, M_XRPU, M_WAITOK);
|
|
|
|
softc[unit] = sc;
|
|
|
|
bzero(sc, sizeof *sc);
|
|
|
|
|
|
|
|
sc->tag = tag;
|
|
|
|
sc->mode = NORMAL;
|
|
|
|
|
|
|
|
pci_map_mem(tag, PCI_MAP_REG_START, &sc->virbase, &sc->physbase);
|
1998-05-30 18:28:12 +00:00
|
|
|
|
1998-10-24 19:47:42 +00:00
|
|
|
sc->virbase62 = (u_int *)(sc->virbase + 0x800000);
|
1998-05-30 18:28:12 +00:00
|
|
|
|
1998-10-24 19:47:42 +00:00
|
|
|
if (bootverbose)
|
|
|
|
printf("Mapped physbase %#lx to virbase %#lx\n",
|
|
|
|
(u_long)sc->physbase, (u_long)sc->virbase);
|
1998-05-30 18:28:12 +00:00
|
|
|
|
1998-10-24 19:47:42 +00:00
|
|
|
if (!unit)
|
|
|
|
cdevsw_add(&cdev, &xrpudevsw, NULL);
|
1998-05-30 18:28:12 +00:00
|
|
|
|
|
|
|
devfs_add_devswf(&xrpudevsw, 0, DV_CHR, UID_ROOT, GID_WHEEL, 0600,
|
1998-10-24 19:47:42 +00:00
|
|
|
"xrpu%d", unit);
|
1998-05-30 18:28:12 +00:00
|
|
|
}
|