linux/drivers/base/soc.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) ST-Ericsson SA 2011
   3 *
   4 * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
   5 * License terms:  GNU General Public License (GPL), version 2
   6 */
   7
   8#include <linux/sysfs.h>
   9#include <linux/module.h>
  10#include <linux/init.h>
  11#include <linux/stat.h>
  12#include <linux/slab.h>
  13#include <linux/idr.h>
  14#include <linux/spinlock.h>
  15#include <linux/sys_soc.h>
  16#include <linux/err.h>
  17
  18static DEFINE_IDA(soc_ida);
  19static DEFINE_SPINLOCK(soc_lock);
  20
  21static ssize_t soc_info_get(struct device *dev,
  22                            struct device_attribute *attr,
  23                            char *buf);
  24
  25struct soc_device {
  26        struct device dev;
  27        struct soc_device_attribute *attr;
  28        int soc_dev_num;
  29};
  30
  31static struct bus_type soc_bus_type = {
  32        .name  = "soc",
  33};
  34
  35static DEVICE_ATTR(machine,  S_IRUGO, soc_info_get,  NULL);
  36static DEVICE_ATTR(family,   S_IRUGO, soc_info_get,  NULL);
  37static DEVICE_ATTR(soc_id,   S_IRUGO, soc_info_get,  NULL);
  38static DEVICE_ATTR(revision, S_IRUGO, soc_info_get,  NULL);
  39
  40struct device *soc_device_to_device(struct soc_device *soc_dev)
  41{
  42        return &soc_dev->dev;
  43}
  44
  45static umode_t soc_attribute_mode(struct kobject *kobj,
  46                                 struct attribute *attr,
  47                                 int index)
  48{
  49        struct device *dev = container_of(kobj, struct device, kobj);
  50        struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
  51
  52        if ((attr == &dev_attr_machine.attr)
  53            && (soc_dev->attr->machine != NULL))
  54                return attr->mode;
  55        if ((attr == &dev_attr_family.attr)
  56            && (soc_dev->attr->family != NULL))
  57                return attr->mode;
  58        if ((attr == &dev_attr_revision.attr)
  59            && (soc_dev->attr->revision != NULL))
  60                return attr->mode;
  61        if ((attr == &dev_attr_soc_id.attr)
  62            && (soc_dev->attr->soc_id != NULL))
  63                return attr->mode;
  64
  65        /* Unknown or unfilled attribute. */
  66        return 0;
  67}
  68
  69static ssize_t soc_info_get(struct device *dev,
  70                            struct device_attribute *attr,
  71                            char *buf)
  72{
  73        struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
  74
  75        if (attr == &dev_attr_machine)
  76                return sprintf(buf, "%s\n", soc_dev->attr->machine);
  77        if (attr == &dev_attr_family)
  78                return sprintf(buf, "%s\n", soc_dev->attr->family);
  79        if (attr == &dev_attr_revision)
  80                return sprintf(buf, "%s\n", soc_dev->attr->revision);
  81        if (attr == &dev_attr_soc_id)
  82                return sprintf(buf, "%s\n", soc_dev->attr->soc_id);
  83
  84        return -EINVAL;
  85
  86}
  87
  88static struct attribute *soc_attr[] = {
  89        &dev_attr_machine.attr,
  90        &dev_attr_family.attr,
  91        &dev_attr_soc_id.attr,
  92        &dev_attr_revision.attr,
  93        NULL,
  94};
  95
  96static const struct attribute_group soc_attr_group = {
  97        .attrs = soc_attr,
  98        .is_visible = soc_attribute_mode,
  99};
 100
 101static const struct attribute_group *soc_attr_groups[] = {
 102        &soc_attr_group,
 103        NULL,
 104};
 105
 106static void soc_release(struct device *dev)
 107{
 108        struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
 109
 110        kfree(soc_dev);
 111}
 112
 113struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
 114{
 115        struct soc_device *soc_dev;
 116        int ret;
 117
 118        soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
 119        if (!soc_dev) {
 120                ret = -ENOMEM;
 121                goto out1;
 122        }
 123
 124        /* Fetch a unique (reclaimable) SOC ID. */
 125        do {
 126                if (!ida_pre_get(&soc_ida, GFP_KERNEL)) {
 127                        ret = -ENOMEM;
 128                        goto out2;
 129                }
 130
 131                spin_lock(&soc_lock);
 132                ret = ida_get_new(&soc_ida, &soc_dev->soc_dev_num);
 133                spin_unlock(&soc_lock);
 134
 135        } while (ret == -EAGAIN);
 136
 137        if (ret)
 138                 goto out2;
 139
 140        soc_dev->attr = soc_dev_attr;
 141        soc_dev->dev.bus = &soc_bus_type;
 142        soc_dev->dev.groups = soc_attr_groups;
 143        soc_dev->dev.release = soc_release;
 144
 145        dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num);
 146
 147        ret = device_register(&soc_dev->dev);
 148        if (ret)
 149                goto out3;
 150
 151        return soc_dev;
 152
 153out3:
 154        ida_remove(&soc_ida, soc_dev->soc_dev_num);
 155out2:
 156        kfree(soc_dev);
 157out1:
 158        return ERR_PTR(ret);
 159}
 160
 161/* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */
 162void soc_device_unregister(struct soc_device *soc_dev)
 163{
 164        ida_remove(&soc_ida, soc_dev->soc_dev_num);
 165
 166        device_unregister(&soc_dev->dev);
 167}
 168
 169static int __init soc_bus_register(void)
 170{
 171        return bus_register(&soc_bus_type);
 172}
 173core_initcall(soc_bus_register);
 174
 175static void __exit soc_bus_unregister(void)
 176{
 177        ida_destroy(&soc_ida);
 178
 179        bus_unregister(&soc_bus_type);
 180}
 181module_exit(soc_bus_unregister);
 182