Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/bus/fsl-mc/dprc-driver.c
26282 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Freescale data path resource container (DPRC) driver
4
*
5
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
6
* Copyright 2019-2020 NXP
7
* Author: German Rivera <[email protected]>
8
*
9
*/
10
11
#include <linux/module.h>
12
#include <linux/slab.h>
13
#include <linux/interrupt.h>
14
#include <linux/fsl/mc.h>
15
16
#include "fsl-mc-private.h"
17
18
#define FSL_MC_DPRC_DRIVER_NAME "fsl_mc_dprc"
19
20
struct fsl_mc_child_objs {
21
int child_count;
22
struct fsl_mc_obj_desc *child_array;
23
};
24
25
static bool fsl_mc_device_match(const struct fsl_mc_device *mc_dev,
26
const struct fsl_mc_obj_desc *obj_desc)
27
{
28
return mc_dev->obj_desc.id == obj_desc->id &&
29
strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0;
30
}
31
32
static bool fsl_mc_obj_desc_is_allocatable(struct fsl_mc_obj_desc *obj)
33
{
34
if (strcmp(obj->type, "dpmcp") == 0 ||
35
strcmp(obj->type, "dpcon") == 0 ||
36
strcmp(obj->type, "dpbp") == 0)
37
return true;
38
else
39
return false;
40
}
41
42
static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data)
43
{
44
int i;
45
struct fsl_mc_child_objs *objs;
46
struct fsl_mc_device *mc_dev;
47
48
if (!dev_is_fsl_mc(dev))
49
return 0;
50
51
mc_dev = to_fsl_mc_device(dev);
52
objs = data;
53
54
for (i = 0; i < objs->child_count; i++) {
55
struct fsl_mc_obj_desc *obj_desc = &objs->child_array[i];
56
57
if (strlen(obj_desc->type) != 0 &&
58
fsl_mc_device_match(mc_dev, obj_desc))
59
break;
60
}
61
62
if (i == objs->child_count)
63
fsl_mc_device_remove(mc_dev);
64
65
return 0;
66
}
67
68
static int __fsl_mc_device_remove(struct device *dev, void *data)
69
{
70
if (!dev_is_fsl_mc(dev))
71
return 0;
72
73
fsl_mc_device_remove(to_fsl_mc_device(dev));
74
return 0;
75
}
76
77
/**
78
* dprc_remove_devices - Removes devices for objects removed from a DPRC
79
*
80
* @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
81
* @obj_desc_array: array of object descriptors for child objects currently
82
* present in the DPRC in the MC.
83
* @num_child_objects_in_mc: number of entries in obj_desc_array
84
*
85
* Synchronizes the state of the Linux bus driver with the actual state of
86
* the MC by removing devices that represent MC objects that have
87
* been dynamically removed in the physical DPRC.
88
*/
89
void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
90
struct fsl_mc_obj_desc *obj_desc_array,
91
int num_child_objects_in_mc)
92
{
93
if (num_child_objects_in_mc != 0) {
94
/*
95
* Remove child objects that are in the DPRC in Linux,
96
* but not in the MC:
97
*/
98
struct fsl_mc_child_objs objs;
99
100
objs.child_count = num_child_objects_in_mc;
101
objs.child_array = obj_desc_array;
102
device_for_each_child(&mc_bus_dev->dev, &objs,
103
__fsl_mc_device_remove_if_not_in_mc);
104
} else {
105
/*
106
* There are no child objects for this DPRC in the MC.
107
* So, remove all the child devices from Linux:
108
*/
109
device_for_each_child(&mc_bus_dev->dev, NULL,
110
__fsl_mc_device_remove);
111
}
112
}
113
EXPORT_SYMBOL_GPL(dprc_remove_devices);
114
115
static int __fsl_mc_device_match(struct device *dev, const void *data)
116
{
117
const struct fsl_mc_obj_desc *obj_desc = data;
118
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
119
120
return fsl_mc_device_match(mc_dev, obj_desc);
121
}
122
123
struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc *obj_desc,
124
struct fsl_mc_device *mc_bus_dev)
125
{
126
struct device *dev;
127
128
dev = device_find_child(&mc_bus_dev->dev, obj_desc,
129
__fsl_mc_device_match);
130
131
return dev ? to_fsl_mc_device(dev) : NULL;
132
}
133
134
/**
135
* check_plugged_state_change - Check change in an MC object's plugged state
136
*
137
* @mc_dev: pointer to the fsl-mc device for a given MC object
138
* @obj_desc: pointer to the MC object's descriptor in the MC
139
*
140
* If the plugged state has changed from unplugged to plugged, the fsl-mc
141
* device is bound to the corresponding device driver.
142
* If the plugged state has changed from plugged to unplugged, the fsl-mc
143
* device is unbound from the corresponding device driver.
144
*/
145
static void check_plugged_state_change(struct fsl_mc_device *mc_dev,
146
struct fsl_mc_obj_desc *obj_desc)
147
{
148
int error;
149
u32 plugged_flag_at_mc =
150
obj_desc->state & FSL_MC_OBJ_STATE_PLUGGED;
151
152
if (plugged_flag_at_mc !=
153
(mc_dev->obj_desc.state & FSL_MC_OBJ_STATE_PLUGGED)) {
154
if (plugged_flag_at_mc) {
155
mc_dev->obj_desc.state |= FSL_MC_OBJ_STATE_PLUGGED;
156
error = device_attach(&mc_dev->dev);
157
if (error < 0) {
158
dev_err(&mc_dev->dev,
159
"device_attach() failed: %d\n",
160
error);
161
}
162
} else {
163
mc_dev->obj_desc.state &= ~FSL_MC_OBJ_STATE_PLUGGED;
164
device_release_driver(&mc_dev->dev);
165
}
166
}
167
}
168
169
static void fsl_mc_obj_device_add(struct fsl_mc_device *mc_bus_dev,
170
struct fsl_mc_obj_desc *obj_desc)
171
{
172
int error;
173
struct fsl_mc_device *child_dev;
174
175
/*
176
* Check if device is already known to Linux:
177
*/
178
child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev);
179
if (child_dev) {
180
check_plugged_state_change(child_dev, obj_desc);
181
put_device(&child_dev->dev);
182
} else {
183
error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev,
184
&child_dev);
185
if (error < 0)
186
return;
187
}
188
}
189
190
/**
191
* dprc_add_new_devices - Adds devices to the logical bus for a DPRC
192
*
193
* @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
194
* @obj_desc_array: array of device descriptors for child devices currently
195
* present in the physical DPRC.
196
* @num_child_objects_in_mc: number of entries in obj_desc_array
197
*
198
* Synchronizes the state of the Linux bus driver with the actual
199
* state of the MC by adding objects that have been newly discovered
200
* in the physical DPRC.
201
*/
202
static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev,
203
struct fsl_mc_obj_desc *obj_desc_array,
204
int num_child_objects_in_mc)
205
{
206
int i;
207
208
/* probe the allocable objects first */
209
for (i = 0; i < num_child_objects_in_mc; i++) {
210
struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i];
211
212
if (strlen(obj_desc->type) > 0 &&
213
fsl_mc_obj_desc_is_allocatable(obj_desc))
214
fsl_mc_obj_device_add(mc_bus_dev, obj_desc);
215
}
216
217
for (i = 0; i < num_child_objects_in_mc; i++) {
218
struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i];
219
220
if (strlen(obj_desc->type) > 0 &&
221
!fsl_mc_obj_desc_is_allocatable(obj_desc))
222
fsl_mc_obj_device_add(mc_bus_dev, obj_desc);
223
}
224
}
225
226
/**
227
* dprc_scan_objects - Discover objects in a DPRC
228
*
229
* @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
230
* @alloc_interrupts: if true the function allocates the interrupt pool,
231
* otherwise the interrupt allocation is delayed
232
*
233
* Detects objects added and removed from a DPRC and synchronizes the
234
* state of the Linux bus driver, MC by adding and removing
235
* devices accordingly.
236
* Two types of devices can be found in a DPRC: allocatable objects (e.g.,
237
* dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni).
238
* All allocatable devices needed to be probed before all non-allocatable
239
* devices, to ensure that device drivers for non-allocatable
240
* devices can allocate any type of allocatable devices.
241
* That is, we need to ensure that the corresponding resource pools are
242
* populated before they can get allocation requests from probe callbacks
243
* of the device drivers for the non-allocatable devices.
244
*/
245
int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
246
bool alloc_interrupts)
247
{
248
int num_child_objects;
249
int dprc_get_obj_failures;
250
int error;
251
unsigned int irq_count = mc_bus_dev->obj_desc.irq_count;
252
struct fsl_mc_obj_desc *child_obj_desc_array = NULL;
253
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
254
255
error = dprc_get_obj_count(mc_bus_dev->mc_io,
256
0,
257
mc_bus_dev->mc_handle,
258
&num_child_objects);
259
if (error < 0) {
260
dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n",
261
error);
262
return error;
263
}
264
265
if (num_child_objects != 0) {
266
int i;
267
268
child_obj_desc_array =
269
devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects,
270
sizeof(*child_obj_desc_array),
271
GFP_KERNEL);
272
if (!child_obj_desc_array)
273
return -ENOMEM;
274
275
/*
276
* Discover objects currently present in the physical DPRC:
277
*/
278
dprc_get_obj_failures = 0;
279
for (i = 0; i < num_child_objects; i++) {
280
struct fsl_mc_obj_desc *obj_desc =
281
&child_obj_desc_array[i];
282
283
error = dprc_get_obj(mc_bus_dev->mc_io,
284
0,
285
mc_bus_dev->mc_handle,
286
i, obj_desc);
287
if (error < 0) {
288
dev_err(&mc_bus_dev->dev,
289
"dprc_get_obj(i=%d) failed: %d\n",
290
i, error);
291
/*
292
* Mark the obj entry as "invalid", by using the
293
* empty string as obj type:
294
*/
295
obj_desc->type[0] = '\0';
296
obj_desc->id = error;
297
dprc_get_obj_failures++;
298
continue;
299
}
300
301
/*
302
* add a quirk for all versions of dpsec < 4.0...none
303
* are coherent regardless of what the MC reports.
304
*/
305
if ((strcmp(obj_desc->type, "dpseci") == 0) &&
306
(obj_desc->ver_major < 4))
307
obj_desc->flags |=
308
FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY;
309
310
irq_count += obj_desc->irq_count;
311
dev_dbg(&mc_bus_dev->dev,
312
"Discovered object: type %s, id %d\n",
313
obj_desc->type, obj_desc->id);
314
}
315
316
if (dprc_get_obj_failures != 0) {
317
dev_err(&mc_bus_dev->dev,
318
"%d out of %d devices could not be retrieved\n",
319
dprc_get_obj_failures, num_child_objects);
320
}
321
}
322
323
/*
324
* Allocate IRQ's before binding the scanned devices with their
325
* respective drivers.
326
*/
327
if (dev_get_msi_domain(&mc_bus_dev->dev)) {
328
if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) {
329
dev_warn(&mc_bus_dev->dev,
330
"IRQs needed (%u) exceed IRQs preallocated (%u)\n",
331
irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
332
}
333
334
if (alloc_interrupts && !mc_bus->irq_resources) {
335
error = fsl_mc_populate_irq_pool(mc_bus_dev,
336
FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
337
if (error < 0)
338
return error;
339
}
340
}
341
342
dprc_remove_devices(mc_bus_dev, child_obj_desc_array,
343
num_child_objects);
344
345
dprc_add_new_devices(mc_bus_dev, child_obj_desc_array,
346
num_child_objects);
347
348
if (child_obj_desc_array)
349
devm_kfree(&mc_bus_dev->dev, child_obj_desc_array);
350
351
return 0;
352
}
353
354
/**
355
* dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state
356
*
357
* @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
358
* @alloc_interrupts: if true the function allocates the interrupt pool,
359
* otherwise the interrupt allocation is delayed
360
* Scans the physical DPRC and synchronizes the state of the Linux
361
* bus driver with the actual state of the MC by adding and removing
362
* devices as appropriate.
363
*/
364
int dprc_scan_container(struct fsl_mc_device *mc_bus_dev,
365
bool alloc_interrupts)
366
{
367
int error = 0;
368
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
369
370
fsl_mc_init_all_resource_pools(mc_bus_dev);
371
372
/*
373
* Discover objects in the DPRC:
374
*/
375
mutex_lock(&mc_bus->scan_mutex);
376
error = dprc_scan_objects(mc_bus_dev, alloc_interrupts);
377
mutex_unlock(&mc_bus->scan_mutex);
378
379
return error;
380
}
381
EXPORT_SYMBOL_GPL(dprc_scan_container);
382
383
/**
384
* dprc_irq0_handler - Regular ISR for DPRC interrupt 0
385
*
386
* @irq_num: IRQ number of the interrupt being handled
387
* @arg: Pointer to device structure
388
*/
389
static irqreturn_t dprc_irq0_handler(int irq_num, void *arg)
390
{
391
return IRQ_WAKE_THREAD;
392
}
393
394
/**
395
* dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0
396
*
397
* @irq_num: IRQ number of the interrupt being handled
398
* @arg: Pointer to device structure
399
*/
400
static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg)
401
{
402
int error;
403
u32 status;
404
struct device *dev = arg;
405
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
406
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
407
struct fsl_mc_io *mc_io = mc_dev->mc_io;
408
int irq = mc_dev->irqs[0]->virq;
409
410
dev_dbg(dev, "DPRC IRQ %d triggered on CPU %u\n",
411
irq_num, smp_processor_id());
412
413
if (!(mc_dev->flags & FSL_MC_IS_DPRC))
414
return IRQ_HANDLED;
415
416
mutex_lock(&mc_bus->scan_mutex);
417
if (irq != (u32)irq_num)
418
goto out;
419
420
status = 0;
421
error = dprc_get_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
422
&status);
423
if (error < 0) {
424
dev_err(dev,
425
"dprc_get_irq_status() failed: %d\n", error);
426
goto out;
427
}
428
429
error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
430
status);
431
if (error < 0) {
432
dev_err(dev,
433
"dprc_clear_irq_status() failed: %d\n", error);
434
goto out;
435
}
436
437
if (status & (DPRC_IRQ_EVENT_OBJ_ADDED |
438
DPRC_IRQ_EVENT_OBJ_REMOVED |
439
DPRC_IRQ_EVENT_CONTAINER_DESTROYED |
440
DPRC_IRQ_EVENT_OBJ_DESTROYED |
441
DPRC_IRQ_EVENT_OBJ_CREATED)) {
442
443
error = dprc_scan_objects(mc_dev, true);
444
if (error < 0) {
445
/*
446
* If the error is -ENXIO, we ignore it, as it indicates
447
* that the object scan was aborted, as we detected that
448
* an object was removed from the DPRC in the MC, while
449
* we were scanning the DPRC.
450
*/
451
if (error != -ENXIO) {
452
dev_err(dev, "dprc_scan_objects() failed: %d\n",
453
error);
454
}
455
456
goto out;
457
}
458
}
459
460
out:
461
mutex_unlock(&mc_bus->scan_mutex);
462
return IRQ_HANDLED;
463
}
464
465
/*
466
* Disable and clear interrupt for a given DPRC object
467
*/
468
int disable_dprc_irq(struct fsl_mc_device *mc_dev)
469
{
470
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
471
int error;
472
struct fsl_mc_io *mc_io = mc_dev->mc_io;
473
474
/*
475
* Disable generation of interrupt, while we configure it:
476
*/
477
error = dprc_set_irq_enable(mc_io, 0, mc_dev->mc_handle, 0, 0);
478
if (error < 0) {
479
dev_err(&mc_dev->dev,
480
"Disabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
481
error);
482
return error;
483
}
484
485
/*
486
* Disable all interrupt causes for the interrupt:
487
*/
488
error = dprc_set_irq_mask(mc_io, 0, mc_dev->mc_handle, 0, 0x0);
489
if (error < 0) {
490
dev_err(&mc_dev->dev,
491
"Disabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
492
error);
493
return error;
494
}
495
496
/*
497
* Clear any leftover interrupts:
498
*/
499
error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, ~0x0U);
500
if (error < 0) {
501
dev_err(&mc_dev->dev,
502
"Disabling DPRC IRQ failed: dprc_clear_irq_status() failed: %d\n",
503
error);
504
return error;
505
}
506
507
mc_bus->irq_enabled = 0;
508
509
return 0;
510
}
511
512
int get_dprc_irq_state(struct fsl_mc_device *mc_dev)
513
{
514
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
515
516
return mc_bus->irq_enabled;
517
}
518
519
static int register_dprc_irq_handler(struct fsl_mc_device *mc_dev)
520
{
521
int error;
522
struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
523
524
/*
525
* NOTE: devm_request_threaded_irq() invokes the device-specific
526
* function that programs the MSI physically in the device
527
*/
528
error = devm_request_threaded_irq(&mc_dev->dev,
529
irq->virq,
530
dprc_irq0_handler,
531
dprc_irq0_handler_thread,
532
IRQF_NO_SUSPEND | IRQF_ONESHOT,
533
dev_name(&mc_dev->dev),
534
&mc_dev->dev);
535
if (error < 0) {
536
dev_err(&mc_dev->dev,
537
"devm_request_threaded_irq() failed: %d\n",
538
error);
539
return error;
540
}
541
542
return 0;
543
}
544
545
int enable_dprc_irq(struct fsl_mc_device *mc_dev)
546
{
547
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
548
int error;
549
550
/*
551
* Enable all interrupt causes for the interrupt:
552
*/
553
error = dprc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 0,
554
~0x0u);
555
if (error < 0) {
556
dev_err(&mc_dev->dev,
557
"Enabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
558
error);
559
560
return error;
561
}
562
563
/*
564
* Enable generation of the interrupt:
565
*/
566
error = dprc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, 1);
567
if (error < 0) {
568
dev_err(&mc_dev->dev,
569
"Enabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
570
error);
571
572
return error;
573
}
574
575
mc_bus->irq_enabled = 1;
576
577
return 0;
578
}
579
580
/*
581
* Setup interrupt for a given DPRC device
582
*/
583
static int dprc_setup_irq(struct fsl_mc_device *mc_dev)
584
{
585
int error;
586
587
error = fsl_mc_allocate_irqs(mc_dev);
588
if (error < 0)
589
return error;
590
591
error = disable_dprc_irq(mc_dev);
592
if (error < 0)
593
goto error_free_irqs;
594
595
error = register_dprc_irq_handler(mc_dev);
596
if (error < 0)
597
goto error_free_irqs;
598
599
error = enable_dprc_irq(mc_dev);
600
if (error < 0)
601
goto error_free_irqs;
602
603
return 0;
604
605
error_free_irqs:
606
fsl_mc_free_irqs(mc_dev);
607
return error;
608
}
609
610
/**
611
* dprc_setup - opens and creates a mc_io for DPRC
612
*
613
* @mc_dev: Pointer to fsl-mc device representing a DPRC
614
*
615
* It opens the physical DPRC in the MC.
616
* It configures the DPRC portal used to communicate with MC
617
*/
618
619
int dprc_setup(struct fsl_mc_device *mc_dev)
620
{
621
struct device *parent_dev = mc_dev->dev.parent;
622
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
623
struct irq_domain *mc_msi_domain;
624
bool mc_io_created = false;
625
bool msi_domain_set = false;
626
bool uapi_created = false;
627
u16 major_ver, minor_ver;
628
size_t region_size;
629
int error;
630
631
if (!is_fsl_mc_bus_dprc(mc_dev))
632
return -EINVAL;
633
634
if (dev_get_msi_domain(&mc_dev->dev))
635
return -EINVAL;
636
637
if (!mc_dev->mc_io) {
638
/*
639
* This is a child DPRC:
640
*/
641
if (!dev_is_fsl_mc(parent_dev))
642
return -EINVAL;
643
644
if (mc_dev->obj_desc.region_count == 0)
645
return -EINVAL;
646
647
region_size = resource_size(mc_dev->regions);
648
649
error = fsl_create_mc_io(&mc_dev->dev,
650
mc_dev->regions[0].start,
651
region_size,
652
NULL,
653
FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
654
&mc_dev->mc_io);
655
if (error < 0)
656
return error;
657
658
mc_io_created = true;
659
} else {
660
error = fsl_mc_uapi_create_device_file(mc_bus);
661
if (error < 0)
662
return -EPROBE_DEFER;
663
uapi_created = true;
664
}
665
666
mc_msi_domain = fsl_mc_find_msi_domain(&mc_dev->dev);
667
if (!mc_msi_domain) {
668
dev_warn(&mc_dev->dev,
669
"WARNING: MC bus without interrupt support\n");
670
} else {
671
dev_set_msi_domain(&mc_dev->dev, mc_msi_domain);
672
msi_domain_set = true;
673
}
674
675
error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
676
&mc_dev->mc_handle);
677
if (error < 0) {
678
dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error);
679
goto error_cleanup_msi_domain;
680
}
681
682
error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle,
683
&mc_bus->dprc_attr);
684
if (error < 0) {
685
dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n",
686
error);
687
goto error_cleanup_open;
688
}
689
690
error = dprc_get_api_version(mc_dev->mc_io, 0,
691
&major_ver,
692
&minor_ver);
693
if (error < 0) {
694
dev_err(&mc_dev->dev, "dprc_get_api_version() failed: %d\n",
695
error);
696
goto error_cleanup_open;
697
}
698
699
if (major_ver < DPRC_MIN_VER_MAJOR) {
700
dev_err(&mc_dev->dev,
701
"ERROR: DPRC version %d.%d not supported\n",
702
major_ver, minor_ver);
703
error = -ENOTSUPP;
704
goto error_cleanup_open;
705
}
706
707
return 0;
708
709
error_cleanup_open:
710
(void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
711
712
error_cleanup_msi_domain:
713
if (msi_domain_set)
714
dev_set_msi_domain(&mc_dev->dev, NULL);
715
716
if (mc_io_created) {
717
fsl_destroy_mc_io(mc_dev->mc_io);
718
mc_dev->mc_io = NULL;
719
}
720
721
if (uapi_created)
722
fsl_mc_uapi_remove_device_file(mc_bus);
723
724
return error;
725
}
726
EXPORT_SYMBOL_GPL(dprc_setup);
727
728
/**
729
* dprc_probe - callback invoked when a DPRC is being bound to this driver
730
*
731
* @mc_dev: Pointer to fsl-mc device representing a DPRC
732
*
733
* It opens the physical DPRC in the MC.
734
* It scans the DPRC to discover the MC objects contained in it.
735
* It creates the interrupt pool for the MC bus associated with the DPRC.
736
* It configures the interrupts for the DPRC device itself.
737
*/
738
static int dprc_probe(struct fsl_mc_device *mc_dev)
739
{
740
int error;
741
742
error = dprc_setup(mc_dev);
743
if (error < 0)
744
return error;
745
746
/*
747
* Discover MC objects in DPRC object:
748
*/
749
error = dprc_scan_container(mc_dev, true);
750
if (error < 0)
751
goto dprc_cleanup;
752
753
/*
754
* Configure interrupt for the DPRC object associated with this MC bus:
755
*/
756
error = dprc_setup_irq(mc_dev);
757
if (error < 0)
758
goto scan_cleanup;
759
760
dev_info(&mc_dev->dev, "DPRC device bound to driver");
761
return 0;
762
763
scan_cleanup:
764
device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
765
dprc_cleanup:
766
dprc_cleanup(mc_dev);
767
return error;
768
}
769
770
/*
771
* Tear down interrupt for a given DPRC object
772
*/
773
static void dprc_teardown_irq(struct fsl_mc_device *mc_dev)
774
{
775
struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
776
777
(void)disable_dprc_irq(mc_dev);
778
779
devm_free_irq(&mc_dev->dev, irq->virq, &mc_dev->dev);
780
781
fsl_mc_free_irqs(mc_dev);
782
}
783
784
/**
785
* dprc_cleanup - function that cleanups a DPRC
786
*
787
* @mc_dev: Pointer to fsl-mc device representing the DPRC
788
*
789
* It closes the DPRC device in the MC.
790
* It destroys the interrupt pool associated with this MC bus.
791
*/
792
793
int dprc_cleanup(struct fsl_mc_device *mc_dev)
794
{
795
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
796
int error;
797
798
/* this function should be called only for DPRCs, it
799
* is an error to call it for regular objects
800
*/
801
if (!is_fsl_mc_bus_dprc(mc_dev))
802
return -EINVAL;
803
804
if (dev_get_msi_domain(&mc_dev->dev)) {
805
fsl_mc_cleanup_irq_pool(mc_dev);
806
dev_set_msi_domain(&mc_dev->dev, NULL);
807
}
808
809
/* if this step fails we cannot go further with cleanup as there is no way of
810
* communicating with the firmware
811
*/
812
if (!mc_dev->mc_io) {
813
dev_err(&mc_dev->dev, "mc_io is NULL, tear down cannot be performed in firmware\n");
814
return -EINVAL;
815
}
816
817
error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
818
if (error < 0)
819
dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
820
821
if (!fsl_mc_is_root_dprc(&mc_dev->dev)) {
822
fsl_destroy_mc_io(mc_dev->mc_io);
823
mc_dev->mc_io = NULL;
824
} else {
825
fsl_mc_uapi_remove_device_file(mc_bus);
826
}
827
828
return 0;
829
}
830
EXPORT_SYMBOL_GPL(dprc_cleanup);
831
832
/**
833
* dprc_remove - callback invoked when a DPRC is being unbound from this driver
834
*
835
* @mc_dev: Pointer to fsl-mc device representing the DPRC
836
*
837
* It removes the DPRC's child objects from Linux (not from the MC) and
838
* closes the DPRC device in the MC.
839
* It tears down the interrupts that were configured for the DPRC device.
840
* It destroys the interrupt pool associated with this MC bus.
841
*/
842
static void dprc_remove(struct fsl_mc_device *mc_dev)
843
{
844
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
845
846
if (!mc_bus->irq_resources) {
847
dev_err(&mc_dev->dev, "No irq resources, so unbinding the device failed\n");
848
return;
849
}
850
851
if (dev_get_msi_domain(&mc_dev->dev))
852
dprc_teardown_irq(mc_dev);
853
854
device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
855
856
dprc_cleanup(mc_dev);
857
858
dev_info(&mc_dev->dev, "DPRC device unbound from driver");
859
}
860
861
static const struct fsl_mc_device_id match_id_table[] = {
862
{
863
.vendor = FSL_MC_VENDOR_FREESCALE,
864
.obj_type = "dprc"},
865
{.vendor = 0x0},
866
};
867
868
static struct fsl_mc_driver dprc_driver = {
869
.driver = {
870
.name = FSL_MC_DPRC_DRIVER_NAME,
871
.owner = THIS_MODULE,
872
.pm = NULL,
873
},
874
.match_id_table = match_id_table,
875
.probe = dprc_probe,
876
.remove = dprc_remove,
877
};
878
879
int __init dprc_driver_init(void)
880
{
881
return fsl_mc_driver_register(&dprc_driver);
882
}
883
884
void dprc_driver_exit(void)
885
{
886
fsl_mc_driver_unregister(&dprc_driver);
887
}
888
889