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