Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/bhnd/siba/siba_subr.c
39566 views
1
/*-
2
* Copyright (c) 2015-2016 Landon Fuller <[email protected]>
3
* Copyright (c) 2017 The FreeBSD Foundation
4
* All rights reserved.
5
*
6
* Portions of this software were developed by Landon Fuller
7
* under sponsorship from the FreeBSD Foundation.
8
*
9
* Redistribution and use in source and binary forms, with or without
10
* modification, are permitted provided that the following conditions
11
* are met:
12
* 1. Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer,
14
* without modification.
15
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
16
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
17
* redistribution must be conditioned upon including a substantially
18
* similar Disclaimer requirement for further binary redistribution.
19
*
20
* NO WARRANTY
21
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
24
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
25
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
26
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31
* THE POSSIBILITY OF SUCH DAMAGES.
32
*/
33
34
#include <sys/param.h>
35
#include <sys/bus.h>
36
#include <sys/kernel.h>
37
#include <sys/limits.h>
38
#include <sys/systm.h>
39
40
#include <machine/bus.h>
41
#include <machine/resource.h>
42
43
#include <dev/bhnd/bhndvar.h>
44
45
#include "sibareg.h"
46
#include "sibavar.h"
47
48
static int siba_register_interrupts(device_t dev, device_t child,
49
struct siba_devinfo *dinfo);
50
static int siba_append_dinfo_region(struct siba_devinfo *dinfo,
51
uint8_t addridx, uint32_t base, uint32_t size,
52
uint32_t bus_reserved);
53
54
/**
55
* Map a siba(4) OCP vendor code to its corresponding JEDEC JEP-106 vendor
56
* code.
57
*
58
* @param ocp_vendor An OCP vendor code.
59
* @return The BHND_MFGID constant corresponding to @p ocp_vendor, or
60
* BHND_MFGID_INVALID if the OCP vendor is unknown.
61
*/
62
uint16_t
63
siba_get_bhnd_mfgid(uint16_t ocp_vendor)
64
{
65
switch (ocp_vendor) {
66
case OCP_VENDOR_BCM:
67
return (BHND_MFGID_BCM);
68
default:
69
return (BHND_MFGID_INVALID);
70
}
71
}
72
73
/**
74
* Allocate and return a new empty device info structure.
75
*
76
* @param bus The requesting bus device.
77
*
78
* @retval NULL if allocation failed.
79
*/
80
struct siba_devinfo *
81
siba_alloc_dinfo(device_t bus)
82
{
83
struct siba_devinfo *dinfo;
84
85
dinfo = malloc(sizeof(struct siba_devinfo), M_BHND, M_NOWAIT|M_ZERO);
86
if (dinfo == NULL)
87
return NULL;
88
89
for (u_int i = 0; i < nitems(dinfo->cfg); i++) {
90
dinfo->cfg[i] = ((struct siba_cfg_block){
91
.cb_base = 0,
92
.cb_size = 0,
93
.cb_rid = -1,
94
});
95
dinfo->cfg_res[i] = NULL;
96
dinfo->cfg_rid[i] = -1;
97
}
98
99
resource_list_init(&dinfo->resources);
100
101
dinfo->pmu_state = SIBA_PMU_NONE;
102
103
dinfo->intr = (struct siba_intr) {
104
.mapped = false,
105
.rid = -1
106
};
107
108
return dinfo;
109
}
110
111
/**
112
* Initialize a device info structure previously allocated via
113
* siba_alloc_dinfo, copying the provided core id.
114
*
115
* @param dev The requesting bus device.
116
* @param child The siba child device.
117
* @param dinfo The device info instance.
118
* @param core Device core info.
119
*
120
* @retval 0 success
121
* @retval non-zero initialization failed.
122
*/
123
int
124
siba_init_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo,
125
const struct siba_core_id *core_id)
126
{
127
int error;
128
129
dinfo->core_id = *core_id;
130
131
/* Register all address space mappings */
132
for (uint8_t i = 0; i < core_id->num_admatch; i++) {
133
uint32_t bus_reserved;
134
135
/* If this is the device's core/enumeration addrespace,
136
* reserve the Sonics configuration register blocks for the
137
* use of our bus. */
138
bus_reserved = 0;
139
if (i == SIBA_CORE_ADDRSPACE)
140
bus_reserved = core_id->num_cfg_blocks * SIBA_CFG_SIZE;
141
142
/* Append the region info */
143
error = siba_append_dinfo_region(dinfo, i,
144
core_id->admatch[i].am_base, core_id->admatch[i].am_size,
145
bus_reserved);
146
if (error)
147
return (error);
148
}
149
150
/* Register all interrupt(s) */
151
if ((error = siba_register_interrupts(dev, child, dinfo)))
152
return (error);
153
154
return (0);
155
}
156
157
/**
158
* Register and map all interrupts for @p dinfo.
159
*
160
* @param dev The siba bus device.
161
* @param child The siba child device.
162
* @param dinfo The device info instance on which to register all interrupt
163
* entries.
164
*/
165
static int
166
siba_register_interrupts(device_t dev, device_t child,
167
struct siba_devinfo *dinfo)
168
{
169
int error;
170
171
/* Is backplane interrupt distribution enabled for this core? */
172
if (!dinfo->core_id.intr_en)
173
return (0);
174
175
/* Have one interrupt */
176
dinfo->intr.mapped = false;
177
dinfo->intr.irq = 0;
178
dinfo->intr.rid = -1;
179
180
/* Map the interrupt */
181
error = BHND_BUS_MAP_INTR(dev, child, 0 /* single intr is always 0 */,
182
&dinfo->intr.irq);
183
if (error) {
184
device_printf(dev, "failed mapping interrupt line for core %u: "
185
"%d\n", dinfo->core_id.core_info.core_idx, error);
186
return (error);
187
}
188
dinfo->intr.mapped = true;
189
190
/* Update the resource list */
191
dinfo->intr.rid = resource_list_add_next(&dinfo->resources, SYS_RES_IRQ,
192
dinfo->intr.irq, dinfo->intr.irq, 1);
193
194
return (0);
195
}
196
197
/**
198
* Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port
199
* number.
200
*
201
* @param addrspace Address space index.
202
*/
203
u_int
204
siba_addrspace_device_port(u_int addrspace)
205
{
206
/* The first addrspace is always mapped to device0; the remainder
207
* are mapped to device1 */
208
if (addrspace == 0)
209
return (0);
210
else
211
return (1);
212
}
213
214
/**
215
* Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port
216
* region number.
217
*
218
* @param addrspace Address space index.
219
*/
220
u_int
221
siba_addrspace_device_region(u_int addrspace)
222
{
223
/* The first addrspace is always mapped to device0.0; the remainder
224
* are mapped to device1.0 + (n - 1) */
225
if (addrspace == 0)
226
return (0);
227
else
228
return (addrspace - 1);
229
}
230
231
/**
232
* Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port
233
* number.
234
*
235
* @param cfg Config block index.
236
*/
237
u_int
238
siba_cfg_agent_port(u_int cfg)
239
{
240
/* Always agent0 */
241
return (0);
242
}
243
244
/**
245
* Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port
246
* region number.
247
*
248
* @param cfg Config block index.
249
*/
250
u_int
251
siba_cfg_agent_region(u_int cfg)
252
{
253
/* Always agent0.<idx> */
254
return (cfg);
255
}
256
257
/**
258
* Return the number of bhnd(4) ports to advertise for the given
259
* @p core_id and @p port_type.
260
*
261
* Refer to the siba_addrspace_index() and siba_cfg_index() functions for
262
* information on siba's mapping of bhnd(4) port and region identifiers.
263
*
264
* @param core_id The siba core info.
265
* @param port_type The bhnd(4) port type.
266
*/
267
u_int
268
siba_port_count(struct siba_core_id *core_id, bhnd_port_type port_type)
269
{
270
switch (port_type) {
271
case BHND_PORT_DEVICE:
272
/* 0, 1, or 2 ports */
273
return (min(core_id->num_admatch, 2));
274
275
case BHND_PORT_AGENT:
276
/* One agent port maps all configuration blocks */
277
if (core_id->num_cfg_blocks > 0)
278
return (1);
279
280
/* Do not advertise an agent port if there are no configuration
281
* register blocks */
282
return (0);
283
284
default:
285
return (0);
286
}
287
}
288
289
/**
290
* Return true if @p port of @p port_type is defined by @p core_id, false
291
* otherwise.
292
*
293
* @param core_id The siba core info.
294
* @param port_type The bhnd(4) port type.
295
* @param port The bhnd(4) port number.
296
*/
297
bool
298
siba_is_port_valid(struct siba_core_id *core_id, bhnd_port_type port_type,
299
u_int port)
300
{
301
/* Verify the index against the port count */
302
if (siba_port_count(core_id, port_type) <= port)
303
return (false);
304
305
return (true);
306
}
307
308
/**
309
* Return the number of bhnd(4) regions to advertise for @p core_id on the
310
* @p port of @p port_type.
311
*
312
* @param core_id The siba core info.
313
* @param port_type The bhnd(4) port type.
314
*/
315
u_int
316
siba_port_region_count(struct siba_core_id *core_id, bhnd_port_type port_type,
317
u_int port)
318
{
319
/* The port must exist */
320
if (!siba_is_port_valid(core_id, port_type, port))
321
return (0);
322
323
switch (port_type) {
324
case BHND_PORT_DEVICE:
325
/* The first address space, if any, is mapped to device0.0 */
326
if (port == 0)
327
return (min(core_id->num_admatch, 1));
328
329
/* All remaining address spaces are mapped to device0.(n - 1) */
330
if (port == 1 && core_id->num_admatch >= 2)
331
return (core_id->num_admatch - 1);
332
333
break;
334
335
case BHND_PORT_AGENT:
336
/* All config blocks are mapped to a single port */
337
if (port == 0)
338
return (core_id->num_cfg_blocks);
339
340
break;
341
342
default:
343
break;
344
}
345
346
/* Validated above */
347
panic("siba_is_port_valid() returned true for unknown %s.%u port",
348
bhnd_port_type_name(port_type), port);
349
350
}
351
352
/**
353
* Map a bhnd(4) type/port/region triplet to its associated config block index,
354
* if any.
355
*
356
* We map config registers to port/region identifiers as follows:
357
*
358
* [port].[region] [cfg register block]
359
* agent0.0 0
360
* agent0.1 1
361
*
362
* @param port_type The bhnd(4) port type.
363
* @param port The bhnd(4) port number.
364
* @param region The bhnd(4) port region.
365
* @param addridx On success, the corresponding addrspace index.
366
*
367
* @retval 0 success
368
* @retval ENOENT if the given type/port/region cannot be mapped to a
369
* siba config register block.
370
*/
371
int
372
siba_cfg_index(struct siba_core_id *core_id, bhnd_port_type port_type,
373
u_int port, u_int region, u_int *cfgidx)
374
{
375
/* Config blocks are mapped to agent ports */
376
if (port_type != BHND_PORT_AGENT)
377
return (ENOENT);
378
379
/* Port must be valid */
380
if (!siba_is_port_valid(core_id, port_type, port))
381
return (ENOENT);
382
383
if (region >= core_id->num_cfg_blocks)
384
return (ENOENT);
385
386
if (region >= SIBA_MAX_CFG)
387
return (ENOENT);
388
389
/* Found */
390
*cfgidx = region;
391
return (0);
392
}
393
394
/**
395
* Map an bhnd(4) type/port/region triplet to its associated config block
396
* entry, if any.
397
*
398
* The only supported port type is BHND_PORT_DEVICE.
399
*
400
* @param dinfo The device info to search for a matching address space.
401
* @param type The bhnd(4) port type.
402
* @param port The bhnd(4) port number.
403
* @param region The bhnd(4) port region.
404
*/
405
struct siba_cfg_block *
406
siba_find_cfg_block(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
407
u_int region)
408
{
409
u_int cfgidx;
410
int error;
411
412
/* Map to addrspace index */
413
error = siba_cfg_index(&dinfo->core_id, type, port, region, &cfgidx);
414
if (error)
415
return (NULL);
416
417
/* Found */
418
return (&dinfo->cfg[cfgidx]);
419
}
420
421
/**
422
* Map a bhnd(4) type/port/region triplet to its associated address space
423
* index, if any.
424
*
425
* For compatibility with bcma(4), we map address spaces to port/region
426
* identifiers as follows:
427
*
428
* [port.region] [admatch index]
429
* device0.0 0
430
* device1.0 1
431
* device1.1 2
432
* device1.2 3
433
*
434
* @param core_id The siba core info.
435
* @param port_type The bhnd(4) port type.
436
* @param port The bhnd(4) port number.
437
* @param region The bhnd(4) port region.
438
* @param addridx On success, the corresponding addrspace index.
439
*
440
* @retval 0 success
441
* @retval ENOENT if the given type/port/region cannot be mapped to a
442
* siba address space.
443
*/
444
int
445
siba_addrspace_index(struct siba_core_id *core_id, bhnd_port_type port_type,
446
u_int port, u_int region, u_int *addridx)
447
{
448
u_int idx;
449
450
/* Address spaces are always device ports */
451
if (port_type != BHND_PORT_DEVICE)
452
return (ENOENT);
453
454
/* Port must be valid */
455
if (!siba_is_port_valid(core_id, port_type, port))
456
return (ENOENT);
457
458
if (port == 0)
459
idx = region;
460
else if (port == 1)
461
idx = region + 1;
462
else
463
return (ENOENT);
464
465
if (idx >= core_id->num_admatch)
466
return (ENOENT);
467
468
/* Found */
469
*addridx = idx;
470
return (0);
471
}
472
473
/**
474
* Map an bhnd(4) type/port/region triplet to its associated address space
475
* entry, if any.
476
*
477
* The only supported port type is BHND_PORT_DEVICE.
478
*
479
* @param dinfo The device info to search for a matching address space.
480
* @param type The bhnd(4) port type.
481
* @param port The bhnd(4) port number.
482
* @param region The bhnd(4) port region.
483
*/
484
struct siba_addrspace *
485
siba_find_addrspace(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
486
u_int region)
487
{
488
u_int addridx;
489
int error;
490
491
/* Map to addrspace index */
492
error = siba_addrspace_index(&dinfo->core_id, type, port, region,
493
&addridx);
494
if (error)
495
return (NULL);
496
497
/* Found */
498
if (addridx >= SIBA_MAX_ADDRSPACE)
499
return (NULL);
500
501
return (&dinfo->addrspace[addridx]);
502
}
503
504
/**
505
* Append an address space entry to @p dinfo.
506
*
507
* @param dinfo The device info entry to update.
508
* @param addridx The address space index.
509
* @param base The mapping's base address.
510
* @param size The mapping size.
511
* @param bus_reserved Number of bytes to reserve in @p size for bus use
512
* when registering the resource list entry. This is used to reserve bus
513
* access to the core's SIBA_CFG* register blocks.
514
*
515
* @retval 0 success
516
* @retval non-zero An error occurred appending the entry.
517
*/
518
static int
519
siba_append_dinfo_region(struct siba_devinfo *dinfo, uint8_t addridx,
520
uint32_t base, uint32_t size, uint32_t bus_reserved)
521
{
522
struct siba_addrspace *sa;
523
rman_res_t r_size;
524
525
/* Verify that base + size will not overflow */
526
if (size > 0 && UINT32_MAX - (size - 1) < base)
527
return (ERANGE);
528
529
/* Verify that size - bus_reserved will not underflow */
530
if (size < bus_reserved)
531
return (ERANGE);
532
533
/* Must not be 0-length */
534
if (size == 0)
535
return (EINVAL);
536
537
/* Must not exceed addrspace array size */
538
if (addridx >= nitems(dinfo->addrspace))
539
return (EINVAL);
540
541
/* Initialize new addrspace entry */
542
sa = &dinfo->addrspace[addridx];
543
sa->sa_base = base;
544
sa->sa_size = size;
545
sa->sa_bus_reserved = bus_reserved;
546
547
/* Populate the resource list */
548
r_size = size - bus_reserved;
549
sa->sa_rid = resource_list_add_next(&dinfo->resources, SYS_RES_MEMORY,
550
base, base + (r_size - 1), r_size);
551
552
return (0);
553
}
554
555
/**
556
* Deallocate the given device info structure and any associated resources.
557
*
558
* @param dev The requesting bus device.
559
* @param child The siba child device.
560
* @param dinfo Device info associated with @p child to be deallocated.
561
*/
562
void
563
siba_free_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo)
564
{
565
resource_list_free(&dinfo->resources);
566
567
/* Free all mapped configuration blocks */
568
for (u_int i = 0; i < nitems(dinfo->cfg); i++) {
569
if (dinfo->cfg_res[i] == NULL)
570
continue;
571
572
bhnd_release_resource(dev, SYS_RES_MEMORY, dinfo->cfg_rid[i],
573
dinfo->cfg_res[i]);
574
575
dinfo->cfg_res[i] = NULL;
576
dinfo->cfg_rid[i] = -1;
577
}
578
579
/* Unmap the core's interrupt */
580
if (dinfo->core_id.intr_en && dinfo->intr.mapped) {
581
BHND_BUS_UNMAP_INTR(dev, child, dinfo->intr.irq);
582
dinfo->intr.mapped = false;
583
}
584
585
free(dinfo, M_BHND);
586
}
587
588
/**
589
* Return the core-enumeration-relative offset for the @p addrspace
590
* SIBA_R0_ADMATCH* register.
591
*
592
* @param addrspace The address space index.
593
*
594
* @retval non-zero success
595
* @retval 0 the given @p addrspace index is not supported.
596
*/
597
u_int
598
siba_admatch_offset(uint8_t addrspace)
599
{
600
switch (addrspace) {
601
case 0:
602
return SB0_REG_ABS(SIBA_CFG0_ADMATCH0);
603
case 1:
604
return SB0_REG_ABS(SIBA_CFG0_ADMATCH1);
605
case 2:
606
return SB0_REG_ABS(SIBA_CFG0_ADMATCH2);
607
case 3:
608
return SB0_REG_ABS(SIBA_CFG0_ADMATCH3);
609
default:
610
return (0);
611
}
612
}
613
614
/**
615
* Parse a SIBA_R0_ADMATCH* register.
616
*
617
* @param addrspace The address space index.
618
* @param am The address match register value to be parsed.
619
* @param[out] admatch The parsed address match descriptor
620
*
621
* @retval 0 success
622
* @retval non-zero a parse error occurred.
623
*/
624
int
625
siba_parse_admatch(uint32_t am, struct siba_admatch *admatch)
626
{
627
u_int am_type;
628
629
/* Extract the base address and size */
630
am_type = SIBA_REG_GET(am, AM_TYPE);
631
switch (am_type) {
632
case 0:
633
/* Type 0 entries are always enabled, and do not support
634
* negative matching */
635
admatch->am_base = am & SIBA_AM_BASE0_MASK;
636
admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT0) + 1);
637
admatch->am_enabled = true;
638
admatch->am_negative = false;
639
break;
640
case 1:
641
admatch->am_base = am & SIBA_AM_BASE1_MASK;
642
admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT1) + 1);
643
admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);
644
admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);
645
break;
646
case 2:
647
admatch->am_base = am & SIBA_AM_BASE2_MASK;
648
admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT2) + 1);
649
admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);
650
admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);
651
break;
652
default:
653
return (EINVAL);
654
}
655
656
return (0);
657
}
658
659
/**
660
* Write @p value to @p dev's CFG0 target/initiator state register, performing
661
* required read-back and waiting for completion.
662
*
663
* @param dev The siba(4) child device.
664
* @param reg The CFG0 state register to write (e.g. SIBA_CFG0_TMSTATELOW,
665
* SIBA_CFG0_IMSTATE)
666
* @param value The value to write to @p reg.
667
* @param mask The mask of bits to be included from @p value.
668
*/
669
void
670
siba_write_target_state(device_t dev, struct siba_devinfo *dinfo,
671
bus_size_t reg, uint32_t value, uint32_t mask)
672
{
673
struct bhnd_resource *r;
674
uint32_t rval;
675
676
r = dinfo->cfg_res[0];
677
678
KASSERT(r != NULL, ("%s missing CFG0 mapping",
679
device_get_nameunit(dev)));
680
KASSERT(reg <= SIBA_CFG_SIZE-4, ("%s invalid CFG0 register offset %#jx",
681
device_get_nameunit(dev), (uintmax_t)reg));
682
683
rval = bhnd_bus_read_4(r, reg);
684
rval &= ~mask;
685
rval |= (value & mask);
686
687
bhnd_bus_write_4(r, reg, rval);
688
bhnd_bus_read_4(r, reg); /* read-back */
689
DELAY(1);
690
}
691
692
/**
693
* Spin for up to @p usec waiting for @p dev's CFG0 target/initiator state
694
* register value to be equal to @p value after applying @p mask bits to both
695
* values.
696
*
697
* @param dev The siba(4) child device to wait on.
698
* @param dinfo The @p dev's device info
699
* @param reg The state register to read (e.g. SIBA_CFG0_TMSTATEHIGH,
700
* SIBA_CFG0_IMSTATE)
701
* @param value The value against which @p reg will be compared.
702
* @param mask The mask to be applied when comparing @p value with @p reg.
703
* @param usec The maximum number of microseconds to wait for completion.
704
*
705
* @retval 0 if SIBA_TMH_BUSY is cleared prior to the @p usec timeout.
706
* @retval ENODEV if SIBA_CFG0 is not mapped by @p dinfo.
707
* @retval ETIMEDOUT if a timeout occurs.
708
*/
709
int
710
siba_wait_target_state(device_t dev, struct siba_devinfo *dinfo, bus_size_t reg,
711
uint32_t value, uint32_t mask, u_int usec)
712
{
713
struct bhnd_resource *r;
714
uint32_t rval;
715
716
if ((r = dinfo->cfg_res[0]) == NULL)
717
return (ENODEV);
718
719
value &= mask;
720
for (int i = 0; i < usec; i += 10) {
721
rval = bhnd_bus_read_4(r, reg);
722
if ((rval & mask) == value)
723
return (0);
724
725
DELAY(10);
726
}
727
728
return (ETIMEDOUT);
729
}
730
731