/* 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.com/license/LICENSE. */ #include #include #include #include /* 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 */ 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 ***/ 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 = (struct spwcuc_priv *)malloc(sizeof(*priv)); if ( priv == NULL ) return -1; memset(priv, 0, sizeof(*priv)); 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); }