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#include <asm/uaccess.h>
  29
  30const struct scom_controller *scom_controller;
  31EXPORT_SYMBOL_GPL(scom_controller);
  32
  33struct device_node *scom_find_parent(struct device_node *node)
  34{
  35        struct device_node *par, *tmp;
  36        const u32 *p;
  37
  38        for (par = of_node_get(node); par;) {
  39                if (of_get_property(par, "scom-controller", NULL))
  40                        break;
  41                p = of_get_property(par, "scom-parent", NULL);
  42                tmp = par;
  43                if (p == NULL)
  44                        par = of_get_parent(par);
  45                else
  46                        par = of_find_node_by_phandle(*p);
  47                of_node_put(tmp);
  48        }
  49        return par;
  50}
  51EXPORT_SYMBOL_GPL(scom_find_parent);
  52
  53scom_map_t scom_map_device(struct device_node *dev, int index)
  54{
  55        struct device_node *parent;
  56        unsigned int cells, size;
  57        const __be32 *prop, *sprop;
  58        u64 reg, cnt;
  59        scom_map_t ret;
  60
  61        parent = scom_find_parent(dev);
  62
  63        if (parent == NULL)
  64                return 0;
  65
  66        /*
  67         * We support "scom-reg" properties for adding scom registers
  68         * to a random device-tree node with an explicit scom-parent
  69         *
  70         * We also support the simple "reg" property if the device is
  71         * a direct child of a scom controller.
  72         *
  73         * In case both exist, "scom-reg" takes precedence.
  74         */
  75        prop = of_get_property(dev, "scom-reg", &size);
  76        sprop = of_get_property(parent, "#scom-cells", NULL);
  77        if (!prop && parent == dev->parent) {
  78                prop = of_get_property(dev, "reg", &size);
  79                sprop = of_get_property(parent, "#address-cells", NULL);
  80        }
  81        if (!prop)
  82                return NULL;
  83        cells = sprop ? be32_to_cpup(sprop) : 1;
  84        size >>= 2;
  85
  86        if (index >= (size / (2*cells)))
  87                return 0;
  88
  89        reg = of_read_number(&prop[index * cells * 2], cells);
  90        cnt = of_read_number(&prop[index * cells * 2 + cells], cells);
  91
  92        ret = scom_map(parent, reg, cnt);
  93        of_node_put(parent);
  94
  95        return ret;
  96}
  97EXPORT_SYMBOL_GPL(scom_map_device);
  98
  99#ifdef CONFIG_SCOM_DEBUGFS
 100struct scom_debug_entry {
 101        struct device_node *dn;
 102        struct debugfs_blob_wrapper path;
 103        char name[16];
 104};
 105
 106static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,
 107                               size_t count, loff_t *ppos)
 108{
 109        struct scom_debug_entry *ent = filp->private_data;
 110        u64 __user *ubuf64 = (u64 __user *)ubuf;
 111        loff_t off = *ppos;
 112        ssize_t done = 0; 
 113        u64 reg, reg_cnt, val;
 114        scom_map_t map;
 115        int rc;
 116
 117        if (off < 0 || (off & 7) || (count & 7))
 118                return -EINVAL;
 119        reg = off >> 3;
 120        reg_cnt = count >> 3;
 121
 122        map = scom_map(ent->dn, reg, reg_cnt);
 123        if (!scom_map_ok(map))
 124                return -ENXIO;
 125
 126        for (reg = 0; reg < reg_cnt; reg++) {
 127                rc = scom_read(map, reg, &val);
 128                if (!rc)
 129                        rc = put_user(val, ubuf64);
 130                if (rc) {
 131                        if (!done)
 132                                done = rc;
 133                        break;
 134                }
 135                ubuf64++;
 136                *ppos += 8;
 137                done += 8;
 138        }
 139        scom_unmap(map);
 140        return done;
 141}
 142
 143static ssize_t scom_debug_write(struct file* filp, const char __user *ubuf,
 144                                size_t count, loff_t *ppos)
 145{
 146        struct scom_debug_entry *ent = filp->private_data;
 147        u64 __user *ubuf64 = (u64 __user *)ubuf;
 148        loff_t off = *ppos;
 149        ssize_t done = 0; 
 150        u64 reg, reg_cnt, val;
 151        scom_map_t map;
 152        int rc;
 153
 154        if (off < 0 || (off & 7) || (count & 7))
 155                return -EINVAL;
 156        reg = off >> 3;
 157        reg_cnt = count >> 3;
 158
 159        map = scom_map(ent->dn, reg, reg_cnt);
 160        if (!scom_map_ok(map))
 161                return -ENXIO;
 162
 163        for (reg = 0; reg < reg_cnt; reg++) {
 164                rc = get_user(val, ubuf64);
 165                if (!rc)
 166                        rc = scom_write(map, reg,  val);
 167                if (rc) {
 168                        if (!done)
 169                                done = rc;
 170                        break;
 171                }
 172                ubuf64++;
 173                done += 8;
 174        }
 175        scom_unmap(map);
 176        return done;
 177}
 178
 179static const struct file_operations scom_debug_fops = {
 180        .read =         scom_debug_read,
 181        .write =        scom_debug_write,
 182        .open =         simple_open,
 183        .llseek =       default_llseek,
 184};
 185
 186static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
 187                               int i)
 188{
 189        struct scom_debug_entry *ent;
 190        struct dentry *dir;
 191
 192        ent = kzalloc(sizeof(*ent), GFP_KERNEL);
 193        if (!ent)
 194                return -ENOMEM;
 195
 196        ent->dn = of_node_get(dn);
 197        snprintf(ent->name, 16, "%08x", i);
 198        ent->path.data = (void*) dn->full_name;
 199        ent->path.size = strlen(dn->full_name);
 200
 201        dir = debugfs_create_dir(ent->name, root);
 202        if (!dir) {
 203                of_node_put(dn);
 204                kfree(ent);
 205                return -1;
 206        }
 207
 208        debugfs_create_blob("devspec", 0400, dir, &ent->path);
 209        debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);
 210
 211        return 0;
 212}
 213
 214static int scom_debug_init(void)
 215{
 216        struct device_node *dn;
 217        struct dentry *root;
 218        int i, rc;
 219
 220        root = debugfs_create_dir("scom", powerpc_debugfs_root);
 221        if (!root)
 222                return -1;
 223
 224        i = rc = 0;
 225        for_each_node_with_property(dn, "scom-controller") {
 226                int id = of_get_ibm_chip_id(dn);
 227                if (id == -1)
 228                        id = i;
 229                rc |= scom_debug_init_one(root, dn, id);
 230                i++;
 231        }
 232
 233        return rc;
 234}
 235device_initcall(scom_debug_init);
 236#endif /* CONFIG_SCOM_DEBUGFS */
 237