Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/bus/fsl-mc/dprc-driver.c
50856 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_thread - Handler thread function 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_thread(int irq_num, void *arg)
390
{
391
int error;
392
u32 status;
393
struct device *dev = arg;
394
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
395
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
396
struct fsl_mc_io *mc_io = mc_dev->mc_io;
397
int irq = mc_dev->irqs[0]->virq;
398
399
dev_dbg(dev, "DPRC IRQ %d triggered on CPU %u\n",
400
irq_num, smp_processor_id());
401
402
if (!(mc_dev->flags & FSL_MC_IS_DPRC))
403
return IRQ_HANDLED;
404
405
mutex_lock(&mc_bus->scan_mutex);
406
if (irq != (u32)irq_num)
407
goto out;
408
409
status = 0;
410
error = dprc_get_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
411
&status);
412
if (error < 0) {
413
dev_err(dev,
414
"dprc_get_irq_status() failed: %d\n", error);
415
goto out;
416
}
417
418
error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
419
status);
420
if (error < 0) {
421
dev_err(dev,
422
"dprc_clear_irq_status() failed: %d\n", error);
423
goto out;
424
}
425
426
if (status & (DPRC_IRQ_EVENT_OBJ_ADDED |
427
DPRC_IRQ_EVENT_OBJ_REMOVED |
428
DPRC_IRQ_EVENT_CONTAINER_DESTROYED |
429
DPRC_IRQ_EVENT_OBJ_DESTROYED |
430
DPRC_IRQ_EVENT_OBJ_CREATED)) {
431
432
error = dprc_scan_objects(mc_dev, true);
433
if (error < 0) {
434
/*
435
* If the error is -ENXIO, we ignore it, as it indicates
436
* that the object scan was aborted, as we detected that
437
* an object was removed from the DPRC in the MC, while
438
* we were scanning the DPRC.
439
*/
440
if (error != -ENXIO) {
441
dev_err(dev, "dprc_scan_objects() failed: %d\n",
442
error);
443
}
444
445
goto out;
446
}
447
}
448
449
out:
450
mutex_unlock(&mc_bus->scan_mutex);
451
return IRQ_HANDLED;
452
}
453
454
/*
455
* Disable and clear interrupt for a given DPRC object
456
*/
457
int disable_dprc_irq(struct fsl_mc_device *mc_dev)
458
{
459
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
460
int error;
461
struct fsl_mc_io *mc_io = mc_dev->mc_io;
462
463
/*
464
* Disable generation of interrupt, while we configure it:
465
*/
466
error = dprc_set_irq_enable(mc_io, 0, mc_dev->mc_handle, 0, 0);
467
if (error < 0) {
468
dev_err(&mc_dev->dev,
469
"Disabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
470
error);
471
return error;
472
}
473
474
/*
475
* Disable all interrupt causes for the interrupt:
476
*/
477
error = dprc_set_irq_mask(mc_io, 0, mc_dev->mc_handle, 0, 0x0);
478
if (error < 0) {
479
dev_err(&mc_dev->dev,
480
"Disabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
481
error);
482
return error;
483
}
484
485
/*
486
* Clear any leftover interrupts:
487
*/
488
error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, ~0x0U);
489
if (error < 0) {
490
dev_err(&mc_dev->dev,
491
"Disabling DPRC IRQ failed: dprc_clear_irq_status() failed: %d\n",
492
error);
493
return error;
494
}
495
496
mc_bus->irq_enabled = 0;
497
498
return 0;
499
}
500
501
int get_dprc_irq_state(struct fsl_mc_device *mc_dev)
502
{
503
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
504
505
return mc_bus->irq_enabled;
506
}
507
508
static int register_dprc_irq_handler(struct fsl_mc_device *mc_dev)
509
{
510
int error;
511
struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
512
513
/*
514
* NOTE: devm_request_threaded_irq() invokes the device-specific
515
* function that programs the MSI physically in the device
516
*/
517
error = devm_request_threaded_irq(&mc_dev->dev,
518
irq->virq,
519
NULL,
520
dprc_irq0_handler_thread,
521
IRQF_NO_SUSPEND | IRQF_ONESHOT,
522
dev_name(&mc_dev->dev),
523
&mc_dev->dev);
524
if (error < 0) {
525
dev_err(&mc_dev->dev,
526
"devm_request_threaded_irq() failed: %d\n",
527
error);
528
return error;
529
}
530
531
return 0;
532
}
533
534
int enable_dprc_irq(struct fsl_mc_device *mc_dev)
535
{
536
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
537
int error;
538
539
/*
540
* Enable all interrupt causes for the interrupt:
541
*/
542
error = dprc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 0,
543
~0x0u);
544
if (error < 0) {
545
dev_err(&mc_dev->dev,
546
"Enabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
547
error);
548
549
return error;
550
}
551
552
/*
553
* Enable generation of the interrupt:
554
*/
555
error = dprc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, 1);
556
if (error < 0) {
557
dev_err(&mc_dev->dev,
558
"Enabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
559
error);
560
561
return error;
562
}
563
564
mc_bus->irq_enabled = 1;
565
566
return 0;
567
}
568
569
/*
570
* Setup interrupt for a given DPRC device
571
*/
572
static int dprc_setup_irq(struct fsl_mc_device *mc_dev)
573
{
574
int error;
575
576
error = fsl_mc_allocate_irqs(mc_dev);
577
if (error < 0)
578
return error;
579
580
error = disable_dprc_irq(mc_dev);
581
if (error < 0)
582
goto error_free_irqs;
583
584
error = register_dprc_irq_handler(mc_dev);
585
if (error < 0)
586
goto error_free_irqs;
587
588
error = enable_dprc_irq(mc_dev);
589
if (error < 0)
590
goto error_free_irqs;
591
592
return 0;
593
594
error_free_irqs:
595
fsl_mc_free_irqs(mc_dev);
596
return error;
597
}
598
599
/**
600
* dprc_setup - opens and creates a mc_io for DPRC
601
*
602
* @mc_dev: Pointer to fsl-mc device representing a DPRC
603
*
604
* It opens the physical DPRC in the MC.
605
* It configures the DPRC portal used to communicate with MC
606
*/
607
608
int dprc_setup(struct fsl_mc_device *mc_dev)
609
{
610
struct device *parent_dev = mc_dev->dev.parent;
611
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
612
struct irq_domain *mc_msi_domain;
613
bool mc_io_created = false;
614
bool msi_domain_set = false;
615
bool uapi_created = false;
616
u16 major_ver, minor_ver;
617
size_t region_size;
618
int error;
619
620
if (!is_fsl_mc_bus_dprc(mc_dev))
621
return -EINVAL;
622
623
if (dev_get_msi_domain(&mc_dev->dev))
624
return -EINVAL;
625
626
if (!mc_dev->mc_io) {
627
/*
628
* This is a child DPRC:
629
*/
630
if (!dev_is_fsl_mc(parent_dev))
631
return -EINVAL;
632
633
if (mc_dev->obj_desc.region_count == 0)
634
return -EINVAL;
635
636
region_size = resource_size(mc_dev->regions);
637
638
error = fsl_create_mc_io(&mc_dev->dev,
639
mc_dev->regions[0].start,
640
region_size,
641
NULL,
642
FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
643
&mc_dev->mc_io);
644
if (error < 0)
645
return error;
646
647
mc_io_created = true;
648
} else {
649
error = fsl_mc_uapi_create_device_file(mc_bus);
650
if (error < 0)
651
return -EPROBE_DEFER;
652
uapi_created = true;
653
}
654
655
mc_msi_domain = fsl_mc_find_msi_domain(&mc_dev->dev);
656
if (!mc_msi_domain) {
657
dev_warn(&mc_dev->dev,
658
"WARNING: MC bus without interrupt support\n");
659
} else {
660
dev_set_msi_domain(&mc_dev->dev, mc_msi_domain);
661
msi_domain_set = true;
662
}
663
664
error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
665
&mc_dev->mc_handle);
666
if (error < 0) {
667
dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error);
668
goto error_cleanup_msi_domain;
669
}
670
671
error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle,
672
&mc_bus->dprc_attr);
673
if (error < 0) {
674
dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n",
675
error);
676
goto error_cleanup_open;
677
}
678
679
error = dprc_get_api_version(mc_dev->mc_io, 0,
680
&major_ver,
681
&minor_ver);
682
if (error < 0) {
683
dev_err(&mc_dev->dev, "dprc_get_api_version() failed: %d\n",
684
error);
685
goto error_cleanup_open;
686
}
687
688
if (major_ver < DPRC_MIN_VER_MAJOR) {
689
dev_err(&mc_dev->dev,
690
"ERROR: DPRC version %d.%d not supported\n",
691
major_ver, minor_ver);
692
error = -ENOTSUPP;
693
goto error_cleanup_open;
694
}
695
696
return 0;
697
698
error_cleanup_open:
699
(void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
700
701
error_cleanup_msi_domain:
702
if (msi_domain_set)
703
dev_set_msi_domain(&mc_dev->dev, NULL);
704
705
if (mc_io_created) {
706
fsl_destroy_mc_io(mc_dev->mc_io);
707
mc_dev->mc_io = NULL;
708
}
709
710
if (uapi_created)
711
fsl_mc_uapi_remove_device_file(mc_bus);
712
713
return error;
714
}
715
EXPORT_SYMBOL_GPL(dprc_setup);
716
717
/**
718
* dprc_probe - callback invoked when a DPRC is being bound to this driver
719
*
720
* @mc_dev: Pointer to fsl-mc device representing a DPRC
721
*
722
* It opens the physical DPRC in the MC.
723
* It scans the DPRC to discover the MC objects contained in it.
724
* It creates the interrupt pool for the MC bus associated with the DPRC.
725
* It configures the interrupts for the DPRC device itself.
726
*/
727
static int dprc_probe(struct fsl_mc_device *mc_dev)
728
{
729
int error;
730
731
error = dprc_setup(mc_dev);
732
if (error < 0)
733
return error;
734
735
/*
736
* Discover MC objects in DPRC object:
737
*/
738
error = dprc_scan_container(mc_dev, true);
739
if (error < 0)
740
goto dprc_cleanup;
741
742
/*
743
* Configure interrupt for the DPRC object associated with this MC bus:
744
*/
745
error = dprc_setup_irq(mc_dev);
746
if (error < 0)
747
goto scan_cleanup;
748
749
dev_info(&mc_dev->dev, "DPRC device bound to driver");
750
return 0;
751
752
scan_cleanup:
753
device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
754
dprc_cleanup:
755
dprc_cleanup(mc_dev);
756
return error;
757
}
758
759
/*
760
* Tear down interrupt for a given DPRC object
761
*/
762
static void dprc_teardown_irq(struct fsl_mc_device *mc_dev)
763
{
764
struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
765
766
(void)disable_dprc_irq(mc_dev);
767
768
devm_free_irq(&mc_dev->dev, irq->virq, &mc_dev->dev);
769
770
fsl_mc_free_irqs(mc_dev);
771
}
772
773
/**
774
* dprc_cleanup - function that cleanups a DPRC
775
*
776
* @mc_dev: Pointer to fsl-mc device representing the DPRC
777
*
778
* It closes the DPRC device in the MC.
779
* It destroys the interrupt pool associated with this MC bus.
780
*/
781
782
int dprc_cleanup(struct fsl_mc_device *mc_dev)
783
{
784
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
785
int error;
786
787
/* this function should be called only for DPRCs, it
788
* is an error to call it for regular objects
789
*/
790
if (!is_fsl_mc_bus_dprc(mc_dev))
791
return -EINVAL;
792
793
if (dev_get_msi_domain(&mc_dev->dev)) {
794
fsl_mc_cleanup_irq_pool(mc_dev);
795
dev_set_msi_domain(&mc_dev->dev, NULL);
796
}
797
798
/* if this step fails we cannot go further with cleanup as there is no way of
799
* communicating with the firmware
800
*/
801
if (!mc_dev->mc_io) {
802
dev_err(&mc_dev->dev, "mc_io is NULL, tear down cannot be performed in firmware\n");
803
return -EINVAL;
804
}
805
806
error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
807
if (error < 0)
808
dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
809
810
if (!fsl_mc_is_root_dprc(&mc_dev->dev)) {
811
fsl_destroy_mc_io(mc_dev->mc_io);
812
mc_dev->mc_io = NULL;
813
} else {
814
fsl_mc_uapi_remove_device_file(mc_bus);
815
}
816
817
return 0;
818
}
819
EXPORT_SYMBOL_GPL(dprc_cleanup);
820
821
/**
822
* dprc_remove - callback invoked when a DPRC is being unbound from this driver
823
*
824
* @mc_dev: Pointer to fsl-mc device representing the DPRC
825
*
826
* It removes the DPRC's child objects from Linux (not from the MC) and
827
* closes the DPRC device in the MC.
828
* It tears down the interrupts that were configured for the DPRC device.
829
* It destroys the interrupt pool associated with this MC bus.
830
*/
831
static void dprc_remove(struct fsl_mc_device *mc_dev)
832
{
833
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
834
835
if (!mc_bus->irq_resources) {
836
dev_err(&mc_dev->dev, "No irq resources, so unbinding the device failed\n");
837
return;
838
}
839
840
if (dev_get_msi_domain(&mc_dev->dev))
841
dprc_teardown_irq(mc_dev);
842
843
device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
844
845
dprc_cleanup(mc_dev);
846
847
dev_info(&mc_dev->dev, "DPRC device unbound from driver");
848
}
849
850
static const struct fsl_mc_device_id match_id_table[] = {
851
{
852
.vendor = FSL_MC_VENDOR_FREESCALE,
853
.obj_type = "dprc"},
854
{.vendor = 0x0},
855
};
856
857
static struct fsl_mc_driver dprc_driver = {
858
.driver = {
859
.name = FSL_MC_DPRC_DRIVER_NAME,
860
.owner = THIS_MODULE,
861
.pm = NULL,
862
},
863
.match_id_table = match_id_table,
864
.probe = dprc_probe,
865
.remove = dprc_remove,
866
};
867
868
int __init dprc_driver_init(void)
869
{
870
return fsl_mc_driver_register(&dprc_driver);
871
}
872
873
void dprc_driver_exit(void)
874
{
875
fsl_mc_driver_unregister(&dprc_driver);
876
}
877
878