1 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
3 * Freescale DPAA2 Platforms Console Driver
5 * Copyright 2015-2016 Freescale Semiconductor Inc.
9 #define pr_fmt(fmt) "dpaa2-console: " fmt
11 #include <linux/module.h>
12 #include <linux/of_device.h>
13 #include <linux/of_address.h>
14 #include <linux/miscdevice.h>
15 #include <linux/uaccess.h>
16 #include <linux/slab.h>
20 /* MC firmware base low/high registers indexes */
21 #define MCFBALR_OFFSET 0
22 #define MCFBAHR_OFFSET 1
24 /* Bit masks used to get the most/least significant part of the MC base addr */
25 #define MC_FW_ADDR_MASK_HIGH 0x1FFFF
26 #define MC_FW_ADDR_MASK_LOW 0xE0000000
28 #define MC_BUFFER_OFFSET 0x01000000
29 #define MC_BUFFER_SIZE (1024 * 1024 * 16)
30 #define MC_OFFSET_DELTA MC_BUFFER_OFFSET
32 #define AIOP_BUFFER_OFFSET 0x06000000
33 #define AIOP_BUFFER_SIZE (1024 * 1024 * 16)
34 #define AIOP_OFFSET_DELTA 0
36 #define LOG_HEADER_FLAG_BUFFER_WRAPAROUND 0x80000000
37 #define LAST_BYTE(a) ((a) & ~(LOG_HEADER_FLAG_BUFFER_WRAPAROUND))
39 /* MC and AIOP Magic words */
40 #define MAGIC_MC 0x4d430100
41 #define MAGIC_AIOP 0x41494F50
52 void __iomem
*map_addr
;
53 struct log_header __iomem
*hdr
;
54 void __iomem
*start_addr
;
55 void __iomem
*end_addr
;
56 void __iomem
*end_of_data
;
57 void __iomem
*cur_ptr
;
60 static struct resource mc_base_addr
;
62 static inline void adjust_end(struct console_data
*cd
)
64 u32 last_byte
= readl(&cd
->hdr
->last_byte
);
66 cd
->end_of_data
= cd
->start_addr
+ LAST_BYTE(last_byte
);
69 static u64
get_mc_fw_base_address(void)
72 u32 __iomem
*mcfbaregs
;
74 mcfbaregs
= ioremap(mc_base_addr
.start
, resource_size(&mc_base_addr
));
76 pr_err("could not map MC Firmware Base registers\n");
80 mcfwbase
= readl(mcfbaregs
+ MCFBAHR_OFFSET
) &
83 mcfwbase
|= readl(mcfbaregs
+ MCFBALR_OFFSET
) & MC_FW_ADDR_MASK_LOW
;
86 pr_debug("MC base address at 0x%016llx\n", mcfwbase
);
90 static ssize_t
dpaa2_console_size(struct console_data
*cd
)
94 if (cd
->cur_ptr
<= cd
->end_of_data
)
95 size
= cd
->end_of_data
- cd
->cur_ptr
;
97 size
= (cd
->end_addr
- cd
->cur_ptr
) +
98 (cd
->end_of_data
- cd
->start_addr
);
103 static int dpaa2_generic_console_open(struct inode
*node
, struct file
*fp
,
104 u64 offset
, u64 size
,
108 u32 read_magic
, wrapped
, last_byte
, buf_start
, buf_length
;
109 struct console_data
*cd
;
113 cd
= kmalloc(sizeof(*cd
), GFP_KERNEL
);
117 base_addr
= get_mc_fw_base_address();
123 cd
->map_addr
= ioremap(base_addr
+ offset
, size
);
125 pr_err("cannot map console log memory\n");
130 cd
->hdr
= (struct log_header __iomem
*)cd
->map_addr
;
131 read_magic
= readl(&cd
->hdr
->magic_word
);
132 last_byte
= readl(&cd
->hdr
->last_byte
);
133 buf_start
= readl(&cd
->hdr
->buf_start
);
134 buf_length
= readl(&cd
->hdr
->buf_length
);
136 if (read_magic
!= expected_magic
) {
137 pr_warn("expected = %08x, read = %08x\n",
138 expected_magic
, read_magic
);
143 cd
->start_addr
= cd
->map_addr
+ buf_start
- offset_delta
;
144 cd
->end_addr
= cd
->start_addr
+ buf_length
;
146 wrapped
= last_byte
& LOG_HEADER_FLAG_BUFFER_WRAPAROUND
;
149 if (wrapped
&& cd
->end_of_data
!= cd
->end_addr
)
150 cd
->cur_ptr
= cd
->end_of_data
+ 1;
152 cd
->cur_ptr
= cd
->start_addr
;
154 fp
->private_data
= cd
;
159 iounmap(cd
->map_addr
);
168 static int dpaa2_mc_console_open(struct inode
*node
, struct file
*fp
)
170 return dpaa2_generic_console_open(node
, fp
,
171 MC_BUFFER_OFFSET
, MC_BUFFER_SIZE
,
172 MAGIC_MC
, MC_OFFSET_DELTA
);
175 static int dpaa2_aiop_console_open(struct inode
*node
, struct file
*fp
)
177 return dpaa2_generic_console_open(node
, fp
,
178 AIOP_BUFFER_OFFSET
, AIOP_BUFFER_SIZE
,
179 MAGIC_AIOP
, AIOP_OFFSET_DELTA
);
182 static int dpaa2_console_close(struct inode
*node
, struct file
*fp
)
184 struct console_data
*cd
= fp
->private_data
;
186 iounmap(cd
->map_addr
);
191 static ssize_t
dpaa2_console_read(struct file
*fp
, char __user
*buf
,
192 size_t count
, loff_t
*f_pos
)
194 struct console_data
*cd
= fp
->private_data
;
195 size_t bytes
= dpaa2_console_size(cd
);
196 size_t bytes_end
= cd
->end_addr
- cd
->cur_ptr
;
201 /* Check if we need to adjust the end of data addr */
204 if (cd
->end_of_data
== cd
->cur_ptr
)
210 kbuf
= kmalloc(bytes
, GFP_KERNEL
);
214 if (bytes
> bytes_end
) {
215 memcpy_fromio(kbuf
, cd
->cur_ptr
, bytes_end
);
216 if (copy_to_user(buf
, kbuf
, bytes_end
)) {
221 cd
->cur_ptr
= cd
->start_addr
;
223 written
+= bytes_end
;
226 memcpy_fromio(kbuf
, cd
->cur_ptr
, bytes
);
227 if (copy_to_user(buf
, kbuf
, bytes
)) {
231 cd
->cur_ptr
+= bytes
;
242 static const struct file_operations dpaa2_mc_console_fops
= {
243 .owner
= THIS_MODULE
,
244 .open
= dpaa2_mc_console_open
,
245 .release
= dpaa2_console_close
,
246 .read
= dpaa2_console_read
,
249 static struct miscdevice dpaa2_mc_console_dev
= {
250 .minor
= MISC_DYNAMIC_MINOR
,
251 .name
= "dpaa2_mc_console",
252 .fops
= &dpaa2_mc_console_fops
255 static const struct file_operations dpaa2_aiop_console_fops
= {
256 .owner
= THIS_MODULE
,
257 .open
= dpaa2_aiop_console_open
,
258 .release
= dpaa2_console_close
,
259 .read
= dpaa2_console_read
,
262 static struct miscdevice dpaa2_aiop_console_dev
= {
263 .minor
= MISC_DYNAMIC_MINOR
,
264 .name
= "dpaa2_aiop_console",
265 .fops
= &dpaa2_aiop_console_fops
268 static int dpaa2_console_probe(struct platform_device
*pdev
)
272 error
= of_address_to_resource(pdev
->dev
.of_node
, 0, &mc_base_addr
);
274 pr_err("of_address_to_resource() failed for %pOF with %d\n",
275 pdev
->dev
.of_node
, error
);
279 error
= misc_register(&dpaa2_mc_console_dev
);
281 pr_err("cannot register device %s\n",
282 dpaa2_mc_console_dev
.name
);
283 goto err_register_mc
;
286 error
= misc_register(&dpaa2_aiop_console_dev
);
288 pr_err("cannot register device %s\n",
289 dpaa2_aiop_console_dev
.name
);
290 goto err_register_aiop
;
296 misc_deregister(&dpaa2_mc_console_dev
);
301 static int dpaa2_console_remove(struct platform_device
*pdev
)
303 misc_deregister(&dpaa2_mc_console_dev
);
304 misc_deregister(&dpaa2_aiop_console_dev
);
309 static const struct of_device_id dpaa2_console_match_table
[] = {
310 { .compatible
= "fsl,dpaa2-console",},
314 MODULE_DEVICE_TABLE(of
, dpaa2_console_match_table
);
316 static struct platform_driver dpaa2_console_driver
= {
318 .name
= "dpaa2-console",
320 .of_match_table
= dpaa2_console_match_table
,
322 .probe
= dpaa2_console_probe
,
323 .remove
= dpaa2_console_remove
,
325 module_platform_driver(dpaa2_console_driver
);
327 MODULE_LICENSE("Dual BSD/GPL");
328 MODULE_AUTHOR("Roy Pledge <roy.pledge@nxp.com>");
329 MODULE_DESCRIPTION("DPAA2 console driver");