1 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
3 * Copyright 2014-2016 Freescale Semiconductor Inc.
8 #include <linux/types.h>
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/interrupt.h>
13 #include <linux/msi.h>
14 #include <linux/dma-mapping.h>
15 #include <linux/delay.h>
18 #include <linux/fsl/mc.h>
19 #include <soc/fsl/dpaa2-io.h>
21 #include "qbman-portal.h"
25 MODULE_LICENSE("Dual BSD/GPL");
26 MODULE_AUTHOR("Freescale Semiconductor, Inc");
27 MODULE_DESCRIPTION("DPIO Driver");
33 static cpumask_var_t cpus_unused_mask
;
35 static irqreturn_t
dpio_irq_handler(int irq_num
, void *arg
)
37 struct device
*dev
= (struct device
*)arg
;
38 struct dpio_priv
*priv
= dev_get_drvdata(dev
);
40 return dpaa2_io_irq(priv
->io
);
43 static void unregister_dpio_irq_handlers(struct fsl_mc_device
*dpio_dev
)
45 struct fsl_mc_device_irq
*irq
;
47 irq
= dpio_dev
->irqs
[0];
49 /* clear the affinity hint */
50 irq_set_affinity_hint(irq
->msi_desc
->irq
, NULL
);
53 static int register_dpio_irq_handlers(struct fsl_mc_device
*dpio_dev
, int cpu
)
56 struct fsl_mc_device_irq
*irq
;
59 irq
= dpio_dev
->irqs
[0];
60 error
= devm_request_irq(&dpio_dev
->dev
,
64 dev_name(&dpio_dev
->dev
),
67 dev_err(&dpio_dev
->dev
,
68 "devm_request_irq() failed: %d\n",
73 /* set the affinity hint */
75 cpumask_set_cpu(cpu
, &mask
);
76 if (irq_set_affinity_hint(irq
->msi_desc
->irq
, &mask
))
77 dev_err(&dpio_dev
->dev
,
78 "irq_set_affinity failed irq %d cpu %d\n",
79 irq
->msi_desc
->irq
, cpu
);
84 static int dpaa2_dpio_probe(struct fsl_mc_device
*dpio_dev
)
86 struct dpio_attr dpio_attrs
;
87 struct dpaa2_io_desc desc
;
88 struct dpio_priv
*priv
;
90 struct device
*dev
= &dpio_dev
->dev
;
91 int possible_next_cpu
;
93 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
97 dev_set_drvdata(dev
, priv
);
99 err
= fsl_mc_portal_allocate(dpio_dev
, 0, &dpio_dev
->mc_io
);
101 dev_dbg(dev
, "MC portal allocation failed\n");
106 err
= dpio_open(dpio_dev
->mc_io
, 0, dpio_dev
->obj_desc
.id
,
107 &dpio_dev
->mc_handle
);
109 dev_err(dev
, "dpio_open() failed\n");
113 err
= dpio_reset(dpio_dev
->mc_io
, 0, dpio_dev
->mc_handle
);
115 dev_err(dev
, "dpio_reset() failed\n");
119 err
= dpio_get_attributes(dpio_dev
->mc_io
, 0, dpio_dev
->mc_handle
,
122 dev_err(dev
, "dpio_get_attributes() failed %d\n", err
);
125 desc
.qman_version
= dpio_attrs
.qbman_version
;
127 err
= dpio_enable(dpio_dev
->mc_io
, 0, dpio_dev
->mc_handle
);
129 dev_err(dev
, "dpio_enable() failed %d\n", err
);
133 /* initialize DPIO descriptor */
134 desc
.receives_notifications
= dpio_attrs
.num_priorities
? 1 : 0;
135 desc
.has_8prio
= dpio_attrs
.num_priorities
== 8 ? 1 : 0;
136 desc
.dpio_id
= dpio_dev
->obj_desc
.id
;
138 /* get the cpu to use for the affinity hint */
139 possible_next_cpu
= cpumask_first(cpus_unused_mask
);
140 if (possible_next_cpu
>= nr_cpu_ids
) {
141 dev_err(dev
, "probe failed. Number of DPIOs exceeds NR_CPUS.\n");
143 goto err_allocate_irqs
;
145 desc
.cpu
= possible_next_cpu
;
146 cpumask_clear_cpu(possible_next_cpu
, cpus_unused_mask
);
149 * Set the CENA regs to be the cache inhibited area of the portal to
150 * avoid coherency issues if a user migrates to another core.
152 desc
.regs_cena
= devm_memremap(dev
, dpio_dev
->regions
[1].start
,
153 resource_size(&dpio_dev
->regions
[1]),
155 if (IS_ERR(desc
.regs_cena
)) {
156 dev_err(dev
, "devm_memremap failed\n");
157 err
= PTR_ERR(desc
.regs_cena
);
158 goto err_allocate_irqs
;
161 desc
.regs_cinh
= devm_ioremap(dev
, dpio_dev
->regions
[1].start
,
162 resource_size(&dpio_dev
->regions
[1]));
163 if (!desc
.regs_cinh
) {
165 dev_err(dev
, "devm_ioremap failed\n");
166 goto err_allocate_irqs
;
169 err
= fsl_mc_allocate_irqs(dpio_dev
);
171 dev_err(dev
, "fsl_mc_allocate_irqs failed. err=%d\n", err
);
172 goto err_allocate_irqs
;
175 err
= register_dpio_irq_handlers(dpio_dev
, desc
.cpu
);
177 goto err_register_dpio_irq
;
179 priv
->io
= dpaa2_io_create(&desc
, dev
);
181 dev_err(dev
, "dpaa2_io_create failed\n");
183 goto err_dpaa2_io_create
;
186 dev_info(dev
, "probed\n");
187 dev_dbg(dev
, " receives_notifications = %d\n",
188 desc
.receives_notifications
);
189 dpio_close(dpio_dev
->mc_io
, 0, dpio_dev
->mc_handle
);
194 unregister_dpio_irq_handlers(dpio_dev
);
195 err_register_dpio_irq
:
196 fsl_mc_free_irqs(dpio_dev
);
198 dpio_disable(dpio_dev
->mc_io
, 0, dpio_dev
->mc_handle
);
201 dpio_close(dpio_dev
->mc_io
, 0, dpio_dev
->mc_handle
);
203 fsl_mc_portal_free(dpio_dev
->mc_io
);
208 /* Tear down interrupts for a given DPIO object */
209 static void dpio_teardown_irqs(struct fsl_mc_device
*dpio_dev
)
211 unregister_dpio_irq_handlers(dpio_dev
);
212 fsl_mc_free_irqs(dpio_dev
);
215 static int dpaa2_dpio_remove(struct fsl_mc_device
*dpio_dev
)
218 struct dpio_priv
*priv
;
221 dev
= &dpio_dev
->dev
;
222 priv
= dev_get_drvdata(dev
);
224 dpaa2_io_down(priv
->io
);
226 dpio_teardown_irqs(dpio_dev
);
228 cpu
= dpaa2_io_get_cpu(priv
->io
);
229 cpumask_set_cpu(cpu
, cpus_unused_mask
);
231 err
= dpio_open(dpio_dev
->mc_io
, 0, dpio_dev
->obj_desc
.id
,
232 &dpio_dev
->mc_handle
);
234 dev_err(dev
, "dpio_open() failed\n");
238 dpio_disable(dpio_dev
->mc_io
, 0, dpio_dev
->mc_handle
);
240 dpio_close(dpio_dev
->mc_io
, 0, dpio_dev
->mc_handle
);
242 fsl_mc_portal_free(dpio_dev
->mc_io
);
247 fsl_mc_portal_free(dpio_dev
->mc_io
);
252 static const struct fsl_mc_device_id dpaa2_dpio_match_id_table
[] = {
254 .vendor
= FSL_MC_VENDOR_FREESCALE
,
260 static struct fsl_mc_driver dpaa2_dpio_driver
= {
262 .name
= KBUILD_MODNAME
,
263 .owner
= THIS_MODULE
,
265 .probe
= dpaa2_dpio_probe
,
266 .remove
= dpaa2_dpio_remove
,
267 .match_id_table
= dpaa2_dpio_match_id_table
270 static int dpio_driver_init(void)
272 if (!zalloc_cpumask_var(&cpus_unused_mask
, GFP_KERNEL
))
274 cpumask_copy(cpus_unused_mask
, cpu_online_mask
);
276 return fsl_mc_driver_register(&dpaa2_dpio_driver
);
279 static void dpio_driver_exit(void)
281 free_cpumask_var(cpus_unused_mask
);
282 fsl_mc_driver_unregister(&dpaa2_dpio_driver
);
284 module_init(dpio_driver_init
);
285 module_exit(dpio_driver_exit
);