Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:07

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*  SPWCUC - SpaceWire - CCSDS unsegmented Code Transfer Protocol GRLIB core
0004  *  register driver interface.
0005  *
0006  *  COPYRIGHT (c) 2009.
0007  *  Cobham Gaisler AB.
0008  *
0009  * Redistribution and use in source and binary forms, with or without
0010  * modification, are permitted provided that the following conditions
0011  * are met:
0012  * 1. Redistributions of source code must retain the above copyright
0013  *    notice, this list of conditions and the following disclaimer.
0014  * 2. Redistributions in binary form must reproduce the above copyright
0015  *    notice, this list of conditions and the following disclaimer in the
0016  *    documentation and/or other materials provided with the distribution.
0017  *
0018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0022  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0023  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0024  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0025  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0026  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0027  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0028  * POSSIBILITY OF SUCH DAMAGE.
0029  */
0030 
0031 #include <drvmgr/drvmgr.h>
0032 #include <grlib/ambapp_bus.h>
0033 #include <stdlib.h>
0034 #include <string.h>
0035 
0036 #include <grlib/spwcuc.h>
0037 
0038 #include <grlib/grlib_impl.h>
0039 
0040 /* Private structure of SPWCUC driver. */
0041 struct spwcuc_priv {
0042     struct drvmgr_dev *dev;
0043     struct spwcuc_regs *regs;
0044     int open;
0045     
0046     spwcuc_isr_t user_isr;
0047     void *user_isr_arg;
0048 
0049     struct spwcuc_stats stats;
0050 };
0051 
0052 void spwcuc_isr(void *data);
0053 
0054 struct amba_drv_info spwcuc_drv_info;
0055 
0056 /* Hardware Reset of SPWCUC */
0057 static int spwcuc_hw_reset(struct spwcuc_priv *priv)
0058 {
0059     struct spwcuc_regs *r = priv->regs;
0060     int i = 1000;
0061 
0062     r->control = 1;
0063 
0064     while ((r->control & 1) && i > 0) {
0065         i--;
0066     }
0067 
0068     spwcuc_clear_irqs(priv, -1);
0069 
0070     return i ? 0 : -1;
0071 }
0072 
0073 int spwcuc_reset(void *spwcuc)
0074 {
0075     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0076 
0077     return spwcuc_hw_reset(priv);
0078 }
0079 
0080 void *spwcuc_open(int minor)
0081 {
0082     struct spwcuc_priv *priv;
0083     struct drvmgr_dev *dev;
0084 
0085     /* Get Device from Minor */
0086     if ( drvmgr_get_dev(&spwcuc_drv_info.general, minor, &dev) ) {
0087         return NULL;
0088     }
0089 
0090     priv = dev->priv;
0091     if ( (priv == NULL) || priv->open )
0092         return NULL;
0093 
0094     /* Set initial state of software */
0095     priv->open = 1;
0096 
0097     /* Clear Statistics */
0098     spwcuc_clr_stats(priv);
0099     priv->user_isr = NULL;
0100     priv->user_isr_arg = NULL;
0101 
0102     return priv;
0103 }
0104 
0105 void spwcuc_close(void *spwcuc)
0106 {
0107     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0108 
0109     if ( priv->open == 0 )
0110         return;
0111 
0112     /* Reset Hardware */
0113     spwcuc_hw_reset(priv);
0114 
0115     priv->open = 0; 
0116 }
0117 
0118 void spwcuc_int_enable(void *spwcuc)
0119 {
0120     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0121 
0122     /* Register and Enable Interrupt at Interrupt controller */
0123     drvmgr_interrupt_register(priv->dev, 0, "spwcuc", spwcuc_isr, priv);
0124 }
0125 
0126 void spwcuc_int_disable(void *spwcuc)
0127 {
0128     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0129 
0130     /* Enable Interrupt at Interrupt controller */
0131     drvmgr_interrupt_unregister(priv->dev, 0, spwcuc_isr, priv);
0132 }
0133 
0134 void spwcuc_clr_stats(void *spwcuc)
0135 {
0136     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0137 
0138     memset(&priv->stats, 0, sizeof(priv->stats));
0139 }
0140 
0141 void spwcuc_get_stats(void *spwcuc, struct spwcuc_stats *stats)
0142 {
0143     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0144 
0145     memcpy(stats, &priv->stats, sizeof(priv->stats));
0146 }
0147 
0148 /* Configure the spwcuc core */
0149 void spwcuc_config(void *spwcuc, struct spwcuc_cfg *cfg)
0150 {
0151     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0152     struct spwcuc_regs *r = priv->regs;
0153 
0154     r->config = (cfg->sel_out & 0x1f)   << 28 |
0155                 (cfg->sel_in & 0x1f)    << 24 |
0156             (cfg->mapping & 0x1f)   << 16 |
0157             (cfg->tolerance & 0x1f) << 8  |
0158             (cfg->tid & 0x7)        << 4  |
0159             (cfg->ctf & 1)          << 1  |
0160             (cfg->cp & 1);
0161 
0162     r->control = (cfg->txen & 1)      << 1 |
0163              (cfg->rxen & 1)      << 2 |
0164              (cfg->pktsyncen & 1) << 3 |
0165              (cfg->pktiniten & 1) << 4 |
0166              (cfg->pktrxen & 1)   << 5;
0167 
0168     r->dla = (cfg->dla_mask & 0xff)<<8 | (cfg->dla & 0xff);
0169 
0170     r->pid = cfg->pid;
0171 
0172     r->offset = cfg->offset;
0173 }
0174 
0175 /* Return elapsed coarse time */
0176 unsigned int spwcuc_get_et_coarse(void *spwcuc)
0177 {
0178     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0179 
0180     return priv->regs->etct;
0181 }
0182 
0183 /* Return elapsed fine time */
0184 unsigned int spwcuc_get_et_fine(void *spwcuc)
0185 {
0186     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0187 
0188     return (priv->regs->etft & 0xffffff) >> 8;
0189 }
0190 
0191 /* Return elapsed time (coarse and fine) */
0192 unsigned long long spwcuc_get_et(void *spwcuc)
0193 {
0194     return (((unsigned long long)spwcuc_get_et_coarse(spwcuc)) << 24) | spwcuc_get_et_fine(spwcuc);
0195 }
0196 
0197 /* Return next elapsed coarse time (for use when sending SpW time packet) */
0198 unsigned int spwcuc_get_next_et_coarse(void *spwcuc)
0199 {
0200     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0201 
0202     return priv->regs->etct_next;
0203 }
0204 
0205 /* Return next elapsed fine time (for use when sending SpW time packet) */
0206 unsigned int spwcuc_get_next_et_fine(void *spwcuc)
0207 {
0208     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0209 
0210     return (priv->regs->etft_next & 0xffffff) >> 8;
0211 }
0212 
0213 /* Return next elapsed time (for use when sending SpW time packet) */
0214 unsigned long long spwcuc_get_next_et(void *spwcuc)
0215 {
0216     return (((unsigned long long)spwcuc_get_next_et_coarse(spwcuc)) << 24) | spwcuc_get_next_et_fine(spwcuc);
0217 }
0218 
0219 /* Force/Set the elapsed time (coarse 32-bit and fine 24-bit) by writing the
0220  * T-Field Time Packet Registers then the FORCE, NEW and INIT bits. 
0221  * The latter three are needed for the ET to be set with the new value.
0222  */
0223 void spwcuc_force_et(void *spwcuc, unsigned long long time)
0224 {
0225     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0226     struct spwcuc_regs *regs = priv->regs;
0227 
0228     regs->etft_next = (time & 0xffffff) << 8;
0229     regs->etct_next = (time >> 24) & 0xffffffff;
0230     regs->pkt_pf_crc = (1 << 29) | (1 << 30) | (1 << 31);
0231 }
0232 
0233 /* Return received (from time packet) elapsed coarse time */
0234 unsigned int spwcuc_get_tp_et_coarse(void *spwcuc)
0235 {
0236     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0237 
0238     return priv->regs->pkt_ct;
0239 }
0240 
0241 /* Return received (from time packet) elapsed fine time */
0242 unsigned int spwcuc_get_tp_et_fine(void *spwcuc)
0243 {
0244     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0245 
0246     return (priv->regs->pkt_ft & 0xffffff) >> 8;
0247 }
0248 
0249 /* Return received (from time packet) elapsed time (coarse and fine) */
0250 unsigned long long spwcuc_get_tp_et(void *spwcuc)
0251 {
0252     return (((unsigned long long)spwcuc_get_tp_et_coarse(spwcuc)) << 24) | spwcuc_get_tp_et_fine(spwcuc);
0253 }
0254 
0255 /* Clear interrupts */
0256 void spwcuc_clear_irqs(void *spwcuc, int irqs)
0257 {
0258     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0259 
0260     priv->regs->picr = irqs;
0261 }
0262 
0263 /* Enable interrupts */
0264 void spwcuc_enable_irqs(void *spwcuc, int irqs)
0265 {
0266     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0267 
0268     priv->regs->imr  = irqs;
0269 }
0270 
0271 struct spwcuc_regs *spwcuc_get_regs(void *spwcuc)
0272 {
0273     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0274 
0275     return priv->regs;
0276 }
0277 
0278 void spwcuc_int_register(void *spwcuc, spwcuc_isr_t func, void *data)
0279 {
0280     struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc;
0281 
0282     priv->user_isr = func;
0283     priv->user_isr_arg = data;
0284 }
0285 
0286 void spwcuc_isr(void *data)
0287 {
0288     struct spwcuc_priv *priv = data;
0289     struct spwcuc_stats *stats = &priv->stats;
0290     unsigned int pimr = priv->regs->pimr;
0291 
0292     stats->nirqs++;
0293 
0294     if (pimr & PKT_INIT_IRQ)
0295         stats->pkt_init++;
0296     if (pimr & PKT_ERR_IRQ)
0297         stats->pkt_err++;
0298     if (pimr & PKT_RX_IRQ)
0299         stats->pkt_rx++;
0300     if (pimr & WRAP_ERR_IRQ)
0301         stats->wraperr++;
0302     if (pimr & WRAP_IRQ)
0303         stats->wrap++;
0304     if (pimr & SYNC_ERR_IRQ)
0305         stats->syncerr++;
0306     if (pimr & SYNC_IRQ)
0307         stats->sync++;
0308     if (pimr & TOL_ERR_IRQ)
0309         stats->tolerr++;
0310     if (pimr & TICK_RX_ERR_IRQ)
0311         stats->tick_rx_error++;
0312     if (pimr & TICK_RX_WRAP_IRQ)
0313         stats->tick_rx_wrap++;
0314     if (pimr & TICK_RX_IRQ)
0315         stats->tick_rx++;
0316     if (pimr & TICK_TX_WRAP_IRQ)
0317         stats->tick_tx_wrap++;
0318     if (pimr & TICK_TX_IRQ)
0319         stats->tick_tx++;
0320 
0321     /* Let user Handle Interrupt */
0322     if ( priv->user_isr )
0323         priv->user_isr(pimr, priv->user_isr_arg);
0324 }
0325 
0326 /*** INTERFACE TO DRIVER MANAGER ***/
0327 
0328 static int spwcuc_init2(struct drvmgr_dev *dev)
0329 {
0330     struct amba_dev_info *ambadev;
0331     struct ambapp_core *pnpinfo;
0332     struct spwcuc_priv *priv;
0333     struct spwcuc_regs *regs;
0334 
0335     priv = grlib_calloc(1, sizeof(*priv));
0336     if ( priv == NULL )
0337         return -1;
0338     priv->dev = dev;
0339     dev->priv = priv;
0340 
0341     /* Get device information from AMBA PnP information */
0342     ambadev = (struct amba_dev_info *)dev->businfo;
0343     if ( ambadev == NULL ) {
0344         return -1;
0345     }
0346     pnpinfo = &ambadev->info;
0347     regs = (struct spwcuc_regs *)pnpinfo->apb_slv->start;
0348 
0349     priv->regs = regs;
0350 
0351     spwcuc_hw_reset(priv);
0352 
0353     return 0;
0354 }
0355 
0356 struct drvmgr_drv_ops spwcuc_ops =
0357 {
0358     {NULL, spwcuc_init2, NULL, NULL},
0359     NULL,
0360     NULL
0361 };
0362 
0363 struct amba_dev_id spwcuc_ids[] =
0364 {
0365     {VENDOR_GAISLER, GAISLER_SPWCUC},
0366     {0, 0}  /* Mark end of table */
0367 };
0368 
0369 struct amba_drv_info spwcuc_drv_info =
0370 {
0371     {
0372         DRVMGR_OBJ_DRV,         /* Driver */
0373         NULL,               /* Next driver */
0374         NULL,               /* Device list */
0375         DRIVER_AMBAPP_GAISLER_SPWCUC_ID,/* Driver ID */
0376         "SPWCUC_DRV",           /* Driver Name */
0377         DRVMGR_BUS_TYPE_AMBAPP,     /* Bus Type */
0378         &spwcuc_ops,
0379         NULL,               /* Funcs */
0380         0,              /* No devices yet */
0381         0,
0382     },
0383     &spwcuc_ids[0]
0384 };
0385 
0386 /* Register the SPWCUC Driver */
0387 void spwcuc_register(void)
0388 {
0389     drvmgr_drv_register(&spwcuc_drv_info.general);
0390 }