brintos

brintos / linux-shallow public Read only

0
0
Text · 7.5 KiB · 6dbc77d Raw
330 lines · c
1// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)2/*3 * Freescale DPAA2 Platforms Console Driver4 *5 * Copyright 2015-2016 Freescale Semiconductor Inc.6 * Copyright 2018 NXP7 */8 9#define pr_fmt(fmt) "dpaa2-console: " fmt10 11#include <linux/module.h>12#include <linux/of.h>13#include <linux/of_address.h>14#include <linux/miscdevice.h>15#include <linux/platform_device.h>16#include <linux/uaccess.h>17#include <linux/slab.h>18#include <linux/fs.h>19#include <linux/io.h>20 21/* MC firmware base low/high registers indexes */22#define MCFBALR_OFFSET 023#define MCFBAHR_OFFSET 124 25/* Bit masks used to get the most/least significant part of the MC base addr */26#define MC_FW_ADDR_MASK_HIGH 0x1FFFF27#define MC_FW_ADDR_MASK_LOW  0xE000000028 29#define MC_BUFFER_OFFSET 0x0100000030#define MC_BUFFER_SIZE   (1024 * 1024 * 16)31#define MC_OFFSET_DELTA  MC_BUFFER_OFFSET32 33#define AIOP_BUFFER_OFFSET 0x0600000034#define AIOP_BUFFER_SIZE   (1024 * 1024 * 16)35#define AIOP_OFFSET_DELTA  036 37#define LOG_HEADER_FLAG_BUFFER_WRAPAROUND 0x8000000038#define LAST_BYTE(a) ((a) & ~(LOG_HEADER_FLAG_BUFFER_WRAPAROUND))39 40/* MC and AIOP Magic words */41#define MAGIC_MC   0x4d43010042#define MAGIC_AIOP 0x41494F5043 44struct log_header {45	__le32 magic_word;46	char reserved[4];47	__le32 buf_start;48	__le32 buf_length;49	__le32 last_byte;50};51 52struct console_data {53	void __iomem *map_addr;54	struct log_header __iomem *hdr;55	void __iomem *start_addr;56	void __iomem *end_addr;57	void __iomem *end_of_data;58	void __iomem *cur_ptr;59};60 61static struct resource mc_base_addr;62 63static inline void adjust_end(struct console_data *cd)64{65	u32 last_byte = readl(&cd->hdr->last_byte);66 67	cd->end_of_data = cd->start_addr + LAST_BYTE(last_byte);68}69 70static u64 get_mc_fw_base_address(void)71{72	u64 mcfwbase = 0ULL;73	u32 __iomem *mcfbaregs;74 75	mcfbaregs = ioremap(mc_base_addr.start, resource_size(&mc_base_addr));76	if (!mcfbaregs) {77		pr_err("could not map MC Firmware Base registers\n");78		return 0;79	}80 81	mcfwbase  = readl(mcfbaregs + MCFBAHR_OFFSET) &82			  MC_FW_ADDR_MASK_HIGH;83	mcfwbase <<= 32;84	mcfwbase |= readl(mcfbaregs + MCFBALR_OFFSET) & MC_FW_ADDR_MASK_LOW;85	iounmap(mcfbaregs);86 87	pr_debug("MC base address at 0x%016llx\n", mcfwbase);88	return mcfwbase;89}90 91static ssize_t dpaa2_console_size(struct console_data *cd)92{93	ssize_t size;94 95	if (cd->cur_ptr <= cd->end_of_data)96		size = cd->end_of_data - cd->cur_ptr;97	else98		size = (cd->end_addr - cd->cur_ptr) +99			(cd->end_of_data - cd->start_addr);100 101	return size;102}103 104static int dpaa2_generic_console_open(struct inode *node, struct file *fp,105				      u64 offset, u64 size,106				      u32 expected_magic,107				      u32 offset_delta)108{109	u32 read_magic, wrapped, last_byte, buf_start, buf_length;110	struct console_data *cd;111	u64 base_addr;112	int err;113 114	cd = kmalloc(sizeof(*cd), GFP_KERNEL);115	if (!cd)116		return -ENOMEM;117 118	base_addr = get_mc_fw_base_address();119	if (!base_addr) {120		err = -EIO;121		goto err_fwba;122	}123 124	cd->map_addr = ioremap(base_addr + offset, size);125	if (!cd->map_addr) {126		pr_err("cannot map console log memory\n");127		err = -EIO;128		goto err_ioremap;129	}130 131	cd->hdr = (struct log_header __iomem *)cd->map_addr;132	read_magic = readl(&cd->hdr->magic_word);133	last_byte =  readl(&cd->hdr->last_byte);134	buf_start =  readl(&cd->hdr->buf_start);135	buf_length = readl(&cd->hdr->buf_length);136 137	if (read_magic != expected_magic) {138		pr_warn("expected = %08x, read = %08x\n",139			expected_magic, read_magic);140		err = -EIO;141		goto err_magic;142	}143 144	cd->start_addr = cd->map_addr + buf_start - offset_delta;145	cd->end_addr = cd->start_addr + buf_length;146 147	wrapped = last_byte & LOG_HEADER_FLAG_BUFFER_WRAPAROUND;148 149	adjust_end(cd);150	if (wrapped && cd->end_of_data != cd->end_addr)151		cd->cur_ptr = cd->end_of_data + 1;152	else153		cd->cur_ptr = cd->start_addr;154 155	fp->private_data = cd;156 157	return 0;158 159err_magic:160	iounmap(cd->map_addr);161 162err_ioremap:163err_fwba:164	kfree(cd);165 166	return err;167}168 169static int dpaa2_mc_console_open(struct inode *node, struct file *fp)170{171	return dpaa2_generic_console_open(node, fp,172					  MC_BUFFER_OFFSET, MC_BUFFER_SIZE,173					  MAGIC_MC, MC_OFFSET_DELTA);174}175 176static int dpaa2_aiop_console_open(struct inode *node, struct file *fp)177{178	return dpaa2_generic_console_open(node, fp,179					  AIOP_BUFFER_OFFSET, AIOP_BUFFER_SIZE,180					  MAGIC_AIOP, AIOP_OFFSET_DELTA);181}182 183static int dpaa2_console_close(struct inode *node, struct file *fp)184{185	struct console_data *cd = fp->private_data;186 187	iounmap(cd->map_addr);188	kfree(cd);189	return 0;190}191 192static ssize_t dpaa2_console_read(struct file *fp, char __user *buf,193				  size_t count, loff_t *f_pos)194{195	struct console_data *cd = fp->private_data;196	size_t bytes = dpaa2_console_size(cd);197	size_t bytes_end = cd->end_addr - cd->cur_ptr;198	size_t written = 0;199	void *kbuf;200	int err;201 202	/* Check if we need to adjust the end of data addr */203	adjust_end(cd);204 205	if (cd->end_of_data == cd->cur_ptr)206		return 0;207 208	if (count < bytes)209		bytes = count;210 211	kbuf = kmalloc(bytes, GFP_KERNEL);212	if (!kbuf)213		return -ENOMEM;214 215	if (bytes > bytes_end) {216		memcpy_fromio(kbuf, cd->cur_ptr, bytes_end);217		if (copy_to_user(buf, kbuf, bytes_end)) {218			err = -EFAULT;219			goto err_free_buf;220		}221		buf += bytes_end;222		cd->cur_ptr = cd->start_addr;223		bytes -= bytes_end;224		written += bytes_end;225	}226 227	memcpy_fromio(kbuf, cd->cur_ptr, bytes);228	if (copy_to_user(buf, kbuf, bytes)) {229		err = -EFAULT;230		goto err_free_buf;231	}232	cd->cur_ptr += bytes;233	written += bytes;234 235	kfree(kbuf);236	return written;237 238err_free_buf:239	kfree(kbuf);240 241	return err;242}243 244static const struct file_operations dpaa2_mc_console_fops = {245	.owner          = THIS_MODULE,246	.open           = dpaa2_mc_console_open,247	.release        = dpaa2_console_close,248	.read           = dpaa2_console_read,249};250 251static struct miscdevice dpaa2_mc_console_dev = {252	.minor = MISC_DYNAMIC_MINOR,253	.name = "dpaa2_mc_console",254	.fops = &dpaa2_mc_console_fops255};256 257static const struct file_operations dpaa2_aiop_console_fops = {258	.owner          = THIS_MODULE,259	.open           = dpaa2_aiop_console_open,260	.release        = dpaa2_console_close,261	.read           = dpaa2_console_read,262};263 264static struct miscdevice dpaa2_aiop_console_dev = {265	.minor = MISC_DYNAMIC_MINOR,266	.name = "dpaa2_aiop_console",267	.fops = &dpaa2_aiop_console_fops268};269 270static int dpaa2_console_probe(struct platform_device *pdev)271{272	int error;273 274	error = of_address_to_resource(pdev->dev.of_node, 0, &mc_base_addr);275	if (error < 0) {276		pr_err("of_address_to_resource() failed for %pOF with %d\n",277		       pdev->dev.of_node, error);278		return error;279	}280 281	error = misc_register(&dpaa2_mc_console_dev);282	if (error) {283		pr_err("cannot register device %s\n",284		       dpaa2_mc_console_dev.name);285		goto err_register_mc;286	}287 288	error = misc_register(&dpaa2_aiop_console_dev);289	if (error) {290		pr_err("cannot register device %s\n",291		       dpaa2_aiop_console_dev.name);292		goto err_register_aiop;293	}294 295	return 0;296 297err_register_aiop:298	misc_deregister(&dpaa2_mc_console_dev);299err_register_mc:300	return error;301}302 303static void dpaa2_console_remove(struct platform_device *pdev)304{305	misc_deregister(&dpaa2_mc_console_dev);306	misc_deregister(&dpaa2_aiop_console_dev);307}308 309static const struct of_device_id dpaa2_console_match_table[] = {310	{ .compatible = "fsl,dpaa2-console",},311	{},312};313 314MODULE_DEVICE_TABLE(of, dpaa2_console_match_table);315 316static struct platform_driver dpaa2_console_driver = {317	.driver = {318		   .name = "dpaa2-console",319		   .pm = NULL,320		   .of_match_table = dpaa2_console_match_table,321		   },322	.probe = dpaa2_console_probe,323	.remove_new = dpaa2_console_remove,324};325module_platform_driver(dpaa2_console_driver);326 327MODULE_LICENSE("Dual BSD/GPL");328MODULE_AUTHOR("Roy Pledge <roy.pledge@nxp.com>");329MODULE_DESCRIPTION("DPAA2 console driver");330