linux/arch/powerpc/sysdev/scom.c
<<
>>
Prefs
   1/*
   2 * Copyright 2010 Benjamin Herrenschmidt, IBM Corp
   3 *                <benh@kernel.crashing.org>
   4 *     and        David Gibson, IBM Corporation.
   5 *
   6 *   This program is free software;  you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
  14 *   the GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program;  if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19 */
  20
  21#include <linux/kernel.h>
  22#include <linux/debugfs.h>
  23#include <linux/slab.h>
  24#include <linux/export.h>
  25#include <asm/debug.h>
  26#include <asm/prom.h>
  27#include <asm/scom.h>
  28
  29const struct scom_controller *scom_controller;
  30EXPORT_SYMBOL_GPL(scom_controller);
  31
  32struct device_node *scom_find_parent(struct device_node *node)
  33{
  34        struct device_node *par, *tmp;
  35        const u32 *p;
  36
  37        for (par = of_node_get(node); par;) {
  38                if (of_get_property(par, "scom-controller", NULL))
  39                        break;
  40                p = of_get_property(par, "scom-parent", NULL);
  41                tmp = par;
  42                if (p == NULL)
  43                        par = of_get_parent(par);
  44                else
  45                        par = of_find_node_by_phandle(*p);
  46                of_node_put(tmp);
  47        }
  48        return par;
  49}
  50EXPORT_SYMBOL_GPL(scom_find_parent);
  51
  52scom_map_t scom_map_device(struct device_node *dev, int index)
  53{
  54        struct device_node *parent;
  55        unsigned int cells, size;
  56        const u32 *prop;
  57        u64 reg, cnt;
  58        scom_map_t ret;
  59
  60        parent = scom_find_parent(dev);
  61
  62        if (parent == NULL)
  63                return 0;
  64
  65        prop = of_get_property(parent, "#scom-cells", NULL);
  66        cells = prop ? *prop : 1;
  67
  68        prop = of_get_property(dev, "scom-reg", &size);
  69        if (!prop)
  70                return 0;
  71        size >>= 2;
  72
  73        if (index >= (size / (2*cells)))
  74                return 0;
  75
  76        reg = of_read_number(&prop[index * cells * 2], cells);
  77        cnt = of_read_number(&prop[index * cells * 2 + cells], cells);
  78
  79        ret = scom_map(parent, reg, cnt);
  80        of_node_put(parent);
  81
  82        return ret;
  83}
  84EXPORT_SYMBOL_GPL(scom_map_device);
  85
  86#ifdef CONFIG_SCOM_DEBUGFS
  87struct scom_debug_entry {
  88        struct device_node *dn;
  89        unsigned long addr;
  90        scom_map_t map;
  91        spinlock_t lock;
  92        char name[8];
  93        struct debugfs_blob_wrapper blob;
  94};
  95
  96static int scom_addr_set(void *data, u64 val)
  97{
  98        struct scom_debug_entry *ent = data;
  99
 100        ent->addr = 0;
 101        scom_unmap(ent->map);
 102
 103        ent->map = scom_map(ent->dn, val, 1);
 104        if (scom_map_ok(ent->map))
 105                ent->addr = val;
 106        else
 107                return -EFAULT;
 108
 109        return 0;
 110}
 111
 112static int scom_addr_get(void *data, u64 *val)
 113{
 114        struct scom_debug_entry *ent = data;
 115        *val = ent->addr;
 116        return 0;
 117}
 118DEFINE_SIMPLE_ATTRIBUTE(scom_addr_fops, scom_addr_get, scom_addr_set,
 119                        "0x%llx\n");
 120
 121static int scom_val_set(void *data, u64 val)
 122{
 123        struct scom_debug_entry *ent = data;
 124
 125        if (!scom_map_ok(ent->map))
 126                return -EFAULT;
 127
 128        scom_write(ent->map, 0, val);
 129
 130        return 0;
 131}
 132
 133static int scom_val_get(void *data, u64 *val)
 134{
 135        struct scom_debug_entry *ent = data;
 136
 137        if (!scom_map_ok(ent->map))
 138                return -EFAULT;
 139
 140        *val = scom_read(ent->map, 0);
 141        return 0;
 142}
 143DEFINE_SIMPLE_ATTRIBUTE(scom_val_fops, scom_val_get, scom_val_set,
 144                        "0x%llx\n");
 145
 146static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
 147                               int i)
 148{
 149        struct scom_debug_entry *ent;
 150        struct dentry *dir;
 151
 152        ent = kzalloc(sizeof(*ent), GFP_KERNEL);
 153        if (!ent)
 154                return -ENOMEM;
 155
 156        ent->dn = of_node_get(dn);
 157        ent->map = SCOM_MAP_INVALID;
 158        spin_lock_init(&ent->lock);
 159        snprintf(ent->name, 8, "scom%d", i);
 160        ent->blob.data = (void*) dn->full_name;
 161        ent->blob.size = strlen(dn->full_name);
 162
 163        dir = debugfs_create_dir(ent->name, root);
 164        if (!dir) {
 165                of_node_put(dn);
 166                kfree(ent);
 167                return -1;
 168        }
 169
 170        debugfs_create_file("addr", 0600, dir, ent, &scom_addr_fops);
 171        debugfs_create_file("value", 0600, dir, ent, &scom_val_fops);
 172        debugfs_create_blob("path", 0400, dir, &ent->blob);
 173
 174        return 0;
 175}
 176
 177static int scom_debug_init(void)
 178{
 179        struct device_node *dn;
 180        struct dentry *root;
 181        int i, rc;
 182
 183        root = debugfs_create_dir("scom", powerpc_debugfs_root);
 184        if (!root)
 185                return -1;
 186
 187        i = rc = 0;
 188        for_each_node_with_property(dn, "scom-controller")
 189                rc |= scom_debug_init_one(root, dn, i++);
 190
 191        return rc;
 192}
 193device_initcall(scom_debug_init);
 194#endif /* CONFIG_SCOM_DEBUGFS */
 195