linux/drivers/base/soc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) ST-Ericsson SA 2011
   4 *
   5 * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
   6 */
   7
   8#include <linux/sysfs.h>
   9#include <linux/init.h>
  10#include <linux/stat.h>
  11#include <linux/slab.h>
  12#include <linux/idr.h>
  13#include <linux/spinlock.h>
  14#include <linux/sys_soc.h>
  15#include <linux/err.h>
  16#include <linux/glob.h>
  17
  18static DEFINE_IDA(soc_ida);
  19
  20/* Prototype to allow declarations of DEVICE_ATTR(<foo>) before soc_info_show */
  21static ssize_t soc_info_show(struct device *dev, struct device_attribute *attr,
  22                             char *buf);
  23
  24struct soc_device {
  25        struct device dev;
  26        struct soc_device_attribute *attr;
  27        int soc_dev_num;
  28};
  29
  30static struct bus_type soc_bus_type = {
  31        .name  = "soc",
  32};
  33
  34static DEVICE_ATTR(machine,             0444, soc_info_show,  NULL);
  35static DEVICE_ATTR(family,              0444, soc_info_show,  NULL);
  36static DEVICE_ATTR(serial_number,       0444, soc_info_show,  NULL);
  37static DEVICE_ATTR(soc_id,              0444, soc_info_show,  NULL);
  38static DEVICE_ATTR(revision,            0444, soc_info_show,  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 = kobj_to_dev(kobj);
  50        struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
  51
  52        if ((attr == &dev_attr_machine.attr) && soc_dev->attr->machine)
  53                return attr->mode;
  54        if ((attr == &dev_attr_family.attr) && soc_dev->attr->family)
  55                return attr->mode;
  56        if ((attr == &dev_attr_revision.attr) && soc_dev->attr->revision)
  57                return attr->mode;
  58        if ((attr == &dev_attr_serial_number.attr) && soc_dev->attr->serial_number)
  59                return attr->mode;
  60        if ((attr == &dev_attr_soc_id.attr) && soc_dev->attr->soc_id)
  61                return attr->mode;
  62
  63        /* Unknown or unfilled attribute */
  64        return 0;
  65}
  66
  67static ssize_t soc_info_show(struct device *dev, struct device_attribute *attr,
  68                             char *buf)
  69{
  70        struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
  71        const char *output;
  72
  73        if (attr == &dev_attr_machine)
  74                output = soc_dev->attr->machine;
  75        else if (attr == &dev_attr_family)
  76                output = soc_dev->attr->family;
  77        else if (attr == &dev_attr_revision)
  78                output = soc_dev->attr->revision;
  79        else if (attr == &dev_attr_serial_number)
  80                output = soc_dev->attr->serial_number;
  81        else if (attr == &dev_attr_soc_id)
  82                output = soc_dev->attr->soc_id;
  83        else
  84                return -EINVAL;
  85
  86        return sysfs_emit(buf, "%s\n", output);
  87}
  88
  89static struct attribute *soc_attr[] = {
  90        &dev_attr_machine.attr,
  91        &dev_attr_family.attr,
  92        &dev_attr_serial_number.attr,
  93        &dev_attr_soc_id.attr,
  94        &dev_attr_revision.attr,
  95        NULL,
  96};
  97
  98static const struct attribute_group soc_attr_group = {
  99        .attrs = soc_attr,
 100        .is_visible = soc_attribute_mode,
 101};
 102
 103static void soc_release(struct device *dev)
 104{
 105        struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
 106
 107        ida_simple_remove(&soc_ida, soc_dev->soc_dev_num);
 108        kfree(soc_dev->dev.groups);
 109        kfree(soc_dev);
 110}
 111
 112static struct soc_device_attribute *early_soc_dev_attr;
 113
 114struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
 115{
 116        struct soc_device *soc_dev;
 117        const struct attribute_group **soc_attr_groups;
 118        int ret;
 119
 120        if (!soc_bus_type.p) {
 121                if (early_soc_dev_attr)
 122                        return ERR_PTR(-EBUSY);
 123                early_soc_dev_attr = soc_dev_attr;
 124                return NULL;
 125        }
 126
 127        soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
 128        if (!soc_dev) {
 129                ret = -ENOMEM;
 130                goto out1;
 131        }
 132
 133        soc_attr_groups = kcalloc(3, sizeof(*soc_attr_groups), GFP_KERNEL);
 134        if (!soc_attr_groups) {
 135                ret = -ENOMEM;
 136                goto out2;
 137        }
 138        soc_attr_groups[0] = &soc_attr_group;
 139        soc_attr_groups[1] = soc_dev_attr->custom_attr_group;
 140
 141        /* Fetch a unique (reclaimable) SOC ID. */
 142        ret = ida_simple_get(&soc_ida, 0, 0, GFP_KERNEL);
 143        if (ret < 0)
 144                goto out3;
 145        soc_dev->soc_dev_num = ret;
 146
 147        soc_dev->attr = soc_dev_attr;
 148        soc_dev->dev.bus = &soc_bus_type;
 149        soc_dev->dev.groups = soc_attr_groups;
 150        soc_dev->dev.release = soc_release;
 151
 152        dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num);
 153
 154        ret = device_register(&soc_dev->dev);
 155        if (ret) {
 156                put_device(&soc_dev->dev);
 157                return ERR_PTR(ret);
 158        }
 159
 160        return soc_dev;
 161
 162out3:
 163        kfree(soc_attr_groups);
 164out2:
 165        kfree(soc_dev);
 166out1:
 167        return ERR_PTR(ret);
 168}
 169EXPORT_SYMBOL_GPL(soc_device_register);
 170
 171/* Ensure soc_dev->attr is freed after calling soc_device_unregister. */
 172void soc_device_unregister(struct soc_device *soc_dev)
 173{
 174        device_unregister(&soc_dev->dev);
 175        early_soc_dev_attr = NULL;
 176}
 177EXPORT_SYMBOL_GPL(soc_device_unregister);
 178
 179static int __init soc_bus_register(void)
 180{
 181        int ret;
 182
 183        ret = bus_register(&soc_bus_type);
 184        if (ret)
 185                return ret;
 186
 187        if (early_soc_dev_attr)
 188                return PTR_ERR(soc_device_register(early_soc_dev_attr));
 189
 190        return 0;
 191}
 192core_initcall(soc_bus_register);
 193
 194static int soc_device_match_attr(const struct soc_device_attribute *attr,
 195                                 const struct soc_device_attribute *match)
 196{
 197        if (match->machine &&
 198            (!attr->machine || !glob_match(match->machine, attr->machine)))
 199                return 0;
 200
 201        if (match->family &&
 202            (!attr->family || !glob_match(match->family, attr->family)))
 203                return 0;
 204
 205        if (match->revision &&
 206            (!attr->revision || !glob_match(match->revision, attr->revision)))
 207                return 0;
 208
 209        if (match->soc_id &&
 210            (!attr->soc_id || !glob_match(match->soc_id, attr->soc_id)))
 211                return 0;
 212
 213        return 1;
 214}
 215
 216static int soc_device_match_one(struct device *dev, void *arg)
 217{
 218        struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
 219
 220        return soc_device_match_attr(soc_dev->attr, arg);
 221}
 222
 223/*
 224 * soc_device_match - identify the SoC in the machine
 225 * @matches: zero-terminated array of possible matches
 226 *
 227 * returns the first matching entry of the argument array, or NULL
 228 * if none of them match.
 229 *
 230 * This function is meant as a helper in place of of_match_node()
 231 * in cases where either no device tree is available or the information
 232 * in a device node is insufficient to identify a particular variant
 233 * by its compatible strings or other properties. For new devices,
 234 * the DT binding should always provide unique compatible strings
 235 * that allow the use of of_match_node() instead.
 236 *
 237 * The calling function can use the .data entry of the
 238 * soc_device_attribute to pass a structure or function pointer for
 239 * each entry.
 240 */
 241const struct soc_device_attribute *soc_device_match(
 242        const struct soc_device_attribute *matches)
 243{
 244        int ret = 0;
 245
 246        if (!matches)
 247                return NULL;
 248
 249        while (!ret) {
 250                if (!(matches->machine || matches->family ||
 251                      matches->revision || matches->soc_id))
 252                        break;
 253                ret = bus_for_each_dev(&soc_bus_type, NULL, (void *)matches,
 254                                       soc_device_match_one);
 255                if (ret < 0 && early_soc_dev_attr)
 256                        ret = soc_device_match_attr(early_soc_dev_attr,
 257                                                    matches);
 258                if (ret < 0)
 259                        return NULL;
 260                if (!ret)
 261                        matches++;
 262                else
 263                        return matches;
 264        }
 265        return NULL;
 266}
 267EXPORT_SYMBOL_GPL(soc_device_match);
 268