summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/sparc/shared/time/spwcuc.c
blob: 082ced0772fb7530fe1f15f59939e9c7162b6720 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
/*  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 <drvmgr/ambapp_bus.h>
#include <stdlib.h>
#include <string.h>
 
#include <bsp/spwcuc.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 = (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);
}