0001
0002
0003
0004
0005
0006
0007
0008
0009 #define pr_fmt(fmt) "dpaa2-console: " fmt
0010
0011 #include <linux/module.h>
0012 #include <linux/of_device.h>
0013 #include <linux/of_address.h>
0014 #include <linux/miscdevice.h>
0015 #include <linux/uaccess.h>
0016 #include <linux/slab.h>
0017 #include <linux/fs.h>
0018 #include <linux/io.h>
0019
0020
0021 #define MCFBALR_OFFSET 0
0022 #define MCFBAHR_OFFSET 1
0023
0024
0025 #define MC_FW_ADDR_MASK_HIGH 0x1FFFF
0026 #define MC_FW_ADDR_MASK_LOW 0xE0000000
0027
0028 #define MC_BUFFER_OFFSET 0x01000000
0029 #define MC_BUFFER_SIZE (1024 * 1024 * 16)
0030 #define MC_OFFSET_DELTA MC_BUFFER_OFFSET
0031
0032 #define AIOP_BUFFER_OFFSET 0x06000000
0033 #define AIOP_BUFFER_SIZE (1024 * 1024 * 16)
0034 #define AIOP_OFFSET_DELTA 0
0035
0036 #define LOG_HEADER_FLAG_BUFFER_WRAPAROUND 0x80000000
0037 #define LAST_BYTE(a) ((a) & ~(LOG_HEADER_FLAG_BUFFER_WRAPAROUND))
0038
0039
0040 #define MAGIC_MC 0x4d430100
0041 #define MAGIC_AIOP 0x41494F50
0042
0043 struct log_header {
0044 __le32 magic_word;
0045 char reserved[4];
0046 __le32 buf_start;
0047 __le32 buf_length;
0048 __le32 last_byte;
0049 };
0050
0051 struct console_data {
0052 void __iomem *map_addr;
0053 struct log_header __iomem *hdr;
0054 void __iomem *start_addr;
0055 void __iomem *end_addr;
0056 void __iomem *end_of_data;
0057 void __iomem *cur_ptr;
0058 };
0059
0060 static struct resource mc_base_addr;
0061
0062 static inline void adjust_end(struct console_data *cd)
0063 {
0064 u32 last_byte = readl(&cd->hdr->last_byte);
0065
0066 cd->end_of_data = cd->start_addr + LAST_BYTE(last_byte);
0067 }
0068
0069 static u64 get_mc_fw_base_address(void)
0070 {
0071 u64 mcfwbase = 0ULL;
0072 u32 __iomem *mcfbaregs;
0073
0074 mcfbaregs = ioremap(mc_base_addr.start, resource_size(&mc_base_addr));
0075 if (!mcfbaregs) {
0076 pr_err("could not map MC Firmware Base registers\n");
0077 return 0;
0078 }
0079
0080 mcfwbase = readl(mcfbaregs + MCFBAHR_OFFSET) &
0081 MC_FW_ADDR_MASK_HIGH;
0082 mcfwbase <<= 32;
0083 mcfwbase |= readl(mcfbaregs + MCFBALR_OFFSET) & MC_FW_ADDR_MASK_LOW;
0084 iounmap(mcfbaregs);
0085
0086 pr_debug("MC base address at 0x%016llx\n", mcfwbase);
0087 return mcfwbase;
0088 }
0089
0090 static ssize_t dpaa2_console_size(struct console_data *cd)
0091 {
0092 ssize_t size;
0093
0094 if (cd->cur_ptr <= cd->end_of_data)
0095 size = cd->end_of_data - cd->cur_ptr;
0096 else
0097 size = (cd->end_addr - cd->cur_ptr) +
0098 (cd->end_of_data - cd->start_addr);
0099
0100 return size;
0101 }
0102
0103 static int dpaa2_generic_console_open(struct inode *node, struct file *fp,
0104 u64 offset, u64 size,
0105 u32 expected_magic,
0106 u32 offset_delta)
0107 {
0108 u32 read_magic, wrapped, last_byte, buf_start, buf_length;
0109 struct console_data *cd;
0110 u64 base_addr;
0111 int err;
0112
0113 cd = kmalloc(sizeof(*cd), GFP_KERNEL);
0114 if (!cd)
0115 return -ENOMEM;
0116
0117 base_addr = get_mc_fw_base_address();
0118 if (!base_addr) {
0119 err = -EIO;
0120 goto err_fwba;
0121 }
0122
0123 cd->map_addr = ioremap(base_addr + offset, size);
0124 if (!cd->map_addr) {
0125 pr_err("cannot map console log memory\n");
0126 err = -EIO;
0127 goto err_ioremap;
0128 }
0129
0130 cd->hdr = (struct log_header __iomem *)cd->map_addr;
0131 read_magic = readl(&cd->hdr->magic_word);
0132 last_byte = readl(&cd->hdr->last_byte);
0133 buf_start = readl(&cd->hdr->buf_start);
0134 buf_length = readl(&cd->hdr->buf_length);
0135
0136 if (read_magic != expected_magic) {
0137 pr_warn("expected = %08x, read = %08x\n",
0138 expected_magic, read_magic);
0139 err = -EIO;
0140 goto err_magic;
0141 }
0142
0143 cd->start_addr = cd->map_addr + buf_start - offset_delta;
0144 cd->end_addr = cd->start_addr + buf_length;
0145
0146 wrapped = last_byte & LOG_HEADER_FLAG_BUFFER_WRAPAROUND;
0147
0148 adjust_end(cd);
0149 if (wrapped && cd->end_of_data != cd->end_addr)
0150 cd->cur_ptr = cd->end_of_data + 1;
0151 else
0152 cd->cur_ptr = cd->start_addr;
0153
0154 fp->private_data = cd;
0155
0156 return 0;
0157
0158 err_magic:
0159 iounmap(cd->map_addr);
0160
0161 err_ioremap:
0162 err_fwba:
0163 kfree(cd);
0164
0165 return err;
0166 }
0167
0168 static int dpaa2_mc_console_open(struct inode *node, struct file *fp)
0169 {
0170 return dpaa2_generic_console_open(node, fp,
0171 MC_BUFFER_OFFSET, MC_BUFFER_SIZE,
0172 MAGIC_MC, MC_OFFSET_DELTA);
0173 }
0174
0175 static int dpaa2_aiop_console_open(struct inode *node, struct file *fp)
0176 {
0177 return dpaa2_generic_console_open(node, fp,
0178 AIOP_BUFFER_OFFSET, AIOP_BUFFER_SIZE,
0179 MAGIC_AIOP, AIOP_OFFSET_DELTA);
0180 }
0181
0182 static int dpaa2_console_close(struct inode *node, struct file *fp)
0183 {
0184 struct console_data *cd = fp->private_data;
0185
0186 iounmap(cd->map_addr);
0187 kfree(cd);
0188 return 0;
0189 }
0190
0191 static ssize_t dpaa2_console_read(struct file *fp, char __user *buf,
0192 size_t count, loff_t *f_pos)
0193 {
0194 struct console_data *cd = fp->private_data;
0195 size_t bytes = dpaa2_console_size(cd);
0196 size_t bytes_end = cd->end_addr - cd->cur_ptr;
0197 size_t written = 0;
0198 void *kbuf;
0199 int err;
0200
0201
0202 adjust_end(cd);
0203
0204 if (cd->end_of_data == cd->cur_ptr)
0205 return 0;
0206
0207 if (count < bytes)
0208 bytes = count;
0209
0210 kbuf = kmalloc(bytes, GFP_KERNEL);
0211 if (!kbuf)
0212 return -ENOMEM;
0213
0214 if (bytes > bytes_end) {
0215 memcpy_fromio(kbuf, cd->cur_ptr, bytes_end);
0216 if (copy_to_user(buf, kbuf, bytes_end)) {
0217 err = -EFAULT;
0218 goto err_free_buf;
0219 }
0220 buf += bytes_end;
0221 cd->cur_ptr = cd->start_addr;
0222 bytes -= bytes_end;
0223 written += bytes_end;
0224 }
0225
0226 memcpy_fromio(kbuf, cd->cur_ptr, bytes);
0227 if (copy_to_user(buf, kbuf, bytes)) {
0228 err = -EFAULT;
0229 goto err_free_buf;
0230 }
0231 cd->cur_ptr += bytes;
0232 written += bytes;
0233
0234 kfree(kbuf);
0235 return written;
0236
0237 err_free_buf:
0238 kfree(kbuf);
0239
0240 return err;
0241 }
0242
0243 static const struct file_operations dpaa2_mc_console_fops = {
0244 .owner = THIS_MODULE,
0245 .open = dpaa2_mc_console_open,
0246 .release = dpaa2_console_close,
0247 .read = dpaa2_console_read,
0248 };
0249
0250 static struct miscdevice dpaa2_mc_console_dev = {
0251 .minor = MISC_DYNAMIC_MINOR,
0252 .name = "dpaa2_mc_console",
0253 .fops = &dpaa2_mc_console_fops
0254 };
0255
0256 static const struct file_operations dpaa2_aiop_console_fops = {
0257 .owner = THIS_MODULE,
0258 .open = dpaa2_aiop_console_open,
0259 .release = dpaa2_console_close,
0260 .read = dpaa2_console_read,
0261 };
0262
0263 static struct miscdevice dpaa2_aiop_console_dev = {
0264 .minor = MISC_DYNAMIC_MINOR,
0265 .name = "dpaa2_aiop_console",
0266 .fops = &dpaa2_aiop_console_fops
0267 };
0268
0269 static int dpaa2_console_probe(struct platform_device *pdev)
0270 {
0271 int error;
0272
0273 error = of_address_to_resource(pdev->dev.of_node, 0, &mc_base_addr);
0274 if (error < 0) {
0275 pr_err("of_address_to_resource() failed for %pOF with %d\n",
0276 pdev->dev.of_node, error);
0277 return error;
0278 }
0279
0280 error = misc_register(&dpaa2_mc_console_dev);
0281 if (error) {
0282 pr_err("cannot register device %s\n",
0283 dpaa2_mc_console_dev.name);
0284 goto err_register_mc;
0285 }
0286
0287 error = misc_register(&dpaa2_aiop_console_dev);
0288 if (error) {
0289 pr_err("cannot register device %s\n",
0290 dpaa2_aiop_console_dev.name);
0291 goto err_register_aiop;
0292 }
0293
0294 return 0;
0295
0296 err_register_aiop:
0297 misc_deregister(&dpaa2_mc_console_dev);
0298 err_register_mc:
0299 return error;
0300 }
0301
0302 static int dpaa2_console_remove(struct platform_device *pdev)
0303 {
0304 misc_deregister(&dpaa2_mc_console_dev);
0305 misc_deregister(&dpaa2_aiop_console_dev);
0306
0307 return 0;
0308 }
0309
0310 static const struct of_device_id dpaa2_console_match_table[] = {
0311 { .compatible = "fsl,dpaa2-console",},
0312 {},
0313 };
0314
0315 MODULE_DEVICE_TABLE(of, dpaa2_console_match_table);
0316
0317 static struct platform_driver dpaa2_console_driver = {
0318 .driver = {
0319 .name = "dpaa2-console",
0320 .pm = NULL,
0321 .of_match_table = dpaa2_console_match_table,
0322 },
0323 .probe = dpaa2_console_probe,
0324 .remove = dpaa2_console_remove,
0325 };
0326 module_platform_driver(dpaa2_console_driver);
0327
0328 MODULE_LICENSE("Dual BSD/GPL");
0329 MODULE_AUTHOR("Roy Pledge <roy.pledge@nxp.com>");
0330 MODULE_DESCRIPTION("DPAA2 console driver");