forked from Imagelibrary/rtems
372 lines
8.3 KiB
C
372 lines
8.3 KiB
C
/* SPWCUC - SpaceWire - CCSDS unsegmented Code Transfer Protocol GRLIB core
|
|
* register driver interface.
|
|
*
|
|
* COPYRIGHT (c) 2009.
|
|
* Cobham Gaisler AB.
|
|
*
|
|
* The license and distribution terms for this file may be
|
|
* found in the file LICENSE in this distribution or at
|
|
* http://www.rtems.org/license/LICENSE.
|
|
*/
|
|
|
|
#include <drvmgr/drvmgr.h>
|
|
#include <grlib/ambapp_bus.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <grlib/spwcuc.h>
|
|
|
|
#include <grlib/grlib_impl.h>
|
|
|
|
/* Private structure of SPWCUC driver. */
|
|
struct spwcuc_priv {
|
|
struct drvmgr_dev *dev;
|
|
struct spwcuc_regs *regs;
|
|
int open;
|
|
|
|
spwcuc_isr_t user_isr;
|
|
void *user_isr_arg;
|
|
|
|
struct spwcuc_stats stats;
|
|
};
|
|
|
|
void spwcuc_isr(void *data);
|
|
|
|
struct amba_drv_info spwcuc_drv_info;
|
|
|
|
/* Hardware Reset of SPWCUC */
|
|
static int spwcuc_hw_reset(struct spwcuc_priv *priv)
|
|
{
|
|
struct spwcuc_regs *r = priv->regs;
|
|
int i = 1000;
|
|
|
|
r->control = 1;
|
|
|
|
while ((r->control & 1) && i > 0) {
|
|
i--;
|
|
}
|
|
|
|
spwcuc_clear_irqs(priv, -1);
|
|
|
|
return i ? 0 : -1;
|
|
}
|
|
|
|
int spwcuc_reset(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
return spwcuc_hw_reset(priv);
|
|
}
|
|
|
|
void *spwcuc_open(int minor)
|
|
{
|
|
struct spwcuc_priv *priv;
|
|
struct drvmgr_dev *dev;
|
|
|
|
/* Get Device from Minor */
|
|
if ( drvmgr_get_dev(&spwcuc_drv_info.general, minor, &dev) ) {
|
|
return NULL;
|
|
}
|
|
|
|
priv = dev->priv;
|
|
if ( (priv == NULL) || priv->open )
|
|
return NULL;
|
|
|
|
/* Set initial state of software */
|
|
priv->open = 1;
|
|
|
|
/* Clear Statistics */
|
|
spwcuc_clr_stats(priv);
|
|
priv->user_isr = NULL;
|
|
priv->user_isr_arg = NULL;
|
|
|
|
return priv;
|
|
}
|
|
|
|
void spwcuc_close(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
if ( priv->open == 0 )
|
|
return;
|
|
|
|
/* Reset Hardware */
|
|
spwcuc_hw_reset(priv);
|
|
|
|
priv->open = 0;
|
|
}
|
|
|
|
void spwcuc_int_enable(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
/* Register and Enable Interrupt at Interrupt controller */
|
|
drvmgr_interrupt_register(priv->dev, 0, "spwcuc", spwcuc_isr, priv);
|
|
}
|
|
|
|
void spwcuc_int_disable(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
/* Enable Interrupt at Interrupt controller */
|
|
drvmgr_interrupt_unregister(priv->dev, 0, spwcuc_isr, priv);
|
|
}
|
|
|
|
void spwcuc_clr_stats(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
memset(&priv->stats, 0, sizeof(priv->stats));
|
|
}
|
|
|
|
void spwcuc_get_stats(void *spwcuc, struct spwcuc_stats *stats)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
memcpy(stats, &priv->stats, sizeof(priv->stats));
|
|
}
|
|
|
|
/* Configure the spwcuc core */
|
|
void spwcuc_config(void *spwcuc, struct spwcuc_cfg *cfg)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
struct spwcuc_regs *r = priv->regs;
|
|
|
|
r->config = (cfg->sel_out & 0x1f) << 28 |
|
|
(cfg->sel_in & 0x1f) << 24 |
|
|
(cfg->mapping & 0x1f) << 16 |
|
|
(cfg->tolerance & 0x1f) << 8 |
|
|
(cfg->tid & 0x7) << 4 |
|
|
(cfg->ctf & 1) << 1 |
|
|
(cfg->cp & 1);
|
|
|
|
r->control = (cfg->txen & 1) << 1 |
|
|
(cfg->rxen & 1) << 2 |
|
|
(cfg->pktsyncen & 1) << 3 |
|
|
(cfg->pktiniten & 1) << 4 |
|
|
(cfg->pktrxen & 1) << 5;
|
|
|
|
r->dla = (cfg->dla_mask & 0xff)<<8 | (cfg->dla & 0xff);
|
|
|
|
r->pid = cfg->pid;
|
|
|
|
r->offset = cfg->offset;
|
|
}
|
|
|
|
/* Return elapsed coarse time */
|
|
unsigned int spwcuc_get_et_coarse(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
return priv->regs->etct;
|
|
}
|
|
|
|
/* Return elapsed fine time */
|
|
unsigned int spwcuc_get_et_fine(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
return (priv->regs->etft & 0xffffff) >> 8;
|
|
}
|
|
|
|
/* Return elapsed time (coarse and fine) */
|
|
unsigned long long spwcuc_get_et(void *spwcuc)
|
|
{
|
|
return (((unsigned long long)spwcuc_get_et_coarse(spwcuc)) << 24) | spwcuc_get_et_fine(spwcuc);
|
|
}
|
|
|
|
/* Return next elapsed coarse time (for use when sending SpW time packet) */
|
|
unsigned int spwcuc_get_next_et_coarse(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
return priv->regs->etct_next;
|
|
}
|
|
|
|
/* Return next elapsed fine time (for use when sending SpW time packet) */
|
|
unsigned int spwcuc_get_next_et_fine(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
return (priv->regs->etft_next & 0xffffff) >> 8;
|
|
}
|
|
|
|
/* Return next elapsed time (for use when sending SpW time packet) */
|
|
unsigned long long spwcuc_get_next_et(void *spwcuc)
|
|
{
|
|
return (((unsigned long long)spwcuc_get_next_et_coarse(spwcuc)) << 24) | spwcuc_get_next_et_fine(spwcuc);
|
|
}
|
|
|
|
/* Force/Set the elapsed time (coarse 32-bit and fine 24-bit) by writing the
|
|
* T-Field Time Packet Registers then the FORCE, NEW and INIT bits.
|
|
* The latter three are needed for the ET to be set with the new value.
|
|
*/
|
|
void spwcuc_force_et(void *spwcuc, unsigned long long time)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
struct spwcuc_regs *regs = priv->regs;
|
|
|
|
regs->etft_next = (time & 0xffffff) << 8;
|
|
regs->etct_next = (time >> 24) & 0xffffffff;
|
|
regs->pkt_pf_crc = (1 << 29) | (1 << 30) | (1 << 31);
|
|
}
|
|
|
|
/* Return received (from time packet) elapsed coarse time */
|
|
unsigned int spwcuc_get_tp_et_coarse(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
return priv->regs->pkt_ct;
|
|
}
|
|
|
|
/* Return received (from time packet) elapsed fine time */
|
|
unsigned int spwcuc_get_tp_et_fine(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
return (priv->regs->pkt_ft & 0xffffff) >> 8;
|
|
}
|
|
|
|
/* Return received (from time packet) elapsed time (coarse and fine) */
|
|
unsigned long long spwcuc_get_tp_et(void *spwcuc)
|
|
{
|
|
return (((unsigned long long)spwcuc_get_tp_et_coarse(spwcuc)) << 24) | spwcuc_get_tp_et_fine(spwcuc);
|
|
}
|
|
|
|
/* Clear interrupts */
|
|
void spwcuc_clear_irqs(void *spwcuc, int irqs)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
priv->regs->picr = irqs;
|
|
}
|
|
|
|
/* Enable interrupts */
|
|
void spwcuc_enable_irqs(void *spwcuc, int irqs)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
priv->regs->imr = irqs;
|
|
}
|
|
|
|
struct spwcuc_regs *spwcuc_get_regs(void *spwcuc)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
return priv->regs;
|
|
}
|
|
|
|
void spwcuc_int_register(void *spwcuc, spwcuc_isr_t func, void *data)
|
|
{
|
|
struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
|
|
|
|
priv->user_isr = func;
|
|
priv->user_isr_arg = data;
|
|
}
|
|
|
|
void spwcuc_isr(void *data)
|
|
{
|
|
struct spwcuc_priv *priv = data;
|
|
struct spwcuc_stats *stats = &priv->stats;
|
|
unsigned int pimr = priv->regs->pimr;
|
|
|
|
stats->nirqs++;
|
|
|
|
if (pimr & PKT_INIT_IRQ)
|
|
stats->pkt_init++;
|
|
if (pimr & PKT_ERR_IRQ)
|
|
stats->pkt_err++;
|
|
if (pimr & PKT_RX_IRQ)
|
|
stats->pkt_rx++;
|
|
if (pimr & WRAP_ERR_IRQ)
|
|
stats->wraperr++;
|
|
if (pimr & WRAP_IRQ)
|
|
stats->wrap++;
|
|
if (pimr & SYNC_ERR_IRQ)
|
|
stats->syncerr++;
|
|
if (pimr & SYNC_IRQ)
|
|
stats->sync++;
|
|
if (pimr & TOL_ERR_IRQ)
|
|
stats->tolerr++;
|
|
if (pimr & TICK_RX_ERR_IRQ)
|
|
stats->tick_rx_error++;
|
|
if (pimr & TICK_RX_WRAP_IRQ)
|
|
stats->tick_rx_wrap++;
|
|
if (pimr & TICK_RX_IRQ)
|
|
stats->tick_rx++;
|
|
if (pimr & TICK_TX_WRAP_IRQ)
|
|
stats->tick_tx_wrap++;
|
|
if (pimr & TICK_TX_IRQ)
|
|
stats->tick_tx++;
|
|
|
|
/* Let user Handle Interrupt */
|
|
if ( priv->user_isr )
|
|
priv->user_isr(pimr, priv->user_isr_arg);
|
|
}
|
|
|
|
/*** INTERFACE TO DRIVER MANAGER ***/
|
|
|
|
static int spwcuc_init2(struct drvmgr_dev *dev)
|
|
{
|
|
struct amba_dev_info *ambadev;
|
|
struct ambapp_core *pnpinfo;
|
|
struct spwcuc_priv *priv;
|
|
struct spwcuc_regs *regs;
|
|
|
|
priv = grlib_calloc(1, sizeof(*priv));
|
|
if ( priv == NULL )
|
|
return -1;
|
|
priv->dev = dev;
|
|
dev->priv = priv;
|
|
|
|
/* Get device information from AMBA PnP information */
|
|
ambadev = (struct amba_dev_info *)dev->businfo;
|
|
if ( ambadev == NULL ) {
|
|
return -1;
|
|
}
|
|
pnpinfo = &ambadev->info;
|
|
regs = (struct spwcuc_regs *)pnpinfo->apb_slv->start;
|
|
|
|
priv->regs = regs;
|
|
|
|
spwcuc_hw_reset(priv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct drvmgr_drv_ops spwcuc_ops =
|
|
{
|
|
{NULL, spwcuc_init2, NULL, NULL},
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
struct amba_dev_id spwcuc_ids[] =
|
|
{
|
|
{VENDOR_GAISLER, GAISLER_SPWCUC},
|
|
{0, 0} /* Mark end of table */
|
|
};
|
|
|
|
struct amba_drv_info spwcuc_drv_info =
|
|
{
|
|
{
|
|
DRVMGR_OBJ_DRV, /* Driver */
|
|
NULL, /* Next driver */
|
|
NULL, /* Device list */
|
|
DRIVER_AMBAPP_GAISLER_SPWCUC_ID,/* Driver ID */
|
|
"SPWCUC_DRV", /* Driver Name */
|
|
DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */
|
|
&spwcuc_ops,
|
|
NULL, /* Funcs */
|
|
0, /* No devices yet */
|
|
0,
|
|
},
|
|
&spwcuc_ids[0]
|
|
};
|
|
|
|
/* Register the SPWCUC Driver */
|
|
void spwcuc_register(void)
|
|
{
|
|
drvmgr_drv_register(&spwcuc_drv_info.general);
|
|
}
|