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/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 17 static DEFINE_IDA(soc_ida); 18 19 static ssize_t soc_info_get(struct device *dev, 20 struct device_attribute *attr, 21 char *buf); 22 23 struct soc_device { 24 struct device dev; 25 struct soc_device_attribute *attr; 26 int soc_dev_num; 27 }; 28 29 static struct bus_type soc_bus_type = { 30 .name = "soc", 31 }; 32 33 static DEVICE_ATTR(machine, S_IRUGO, soc_info_get, NULL); 34 static DEVICE_ATTR(family, S_IRUGO, soc_info_get, NULL); 35 static DEVICE_ATTR(soc_id, S_IRUGO, soc_info_get, NULL); 36 static DEVICE_ATTR(revision, S_IRUGO, soc_info_get, NULL); 37 38 struct device *soc_device_to_device(struct soc_device *soc_dev) 39 { 40 return &soc_dev->dev; 41 } 42 43 static umode_t soc_attribute_mode(struct kobject *kobj, 44 struct attribute *attr, 45 int index) 46 { 47 struct device *dev = container_of(kobj, struct device, kobj); 48 struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); 49 50 if ((attr == &dev_attr_machine.attr) 51 && (soc_dev->attr->machine != NULL)) 52 return attr->mode; 53 if ((attr == &dev_attr_family.attr) 54 && (soc_dev->attr->family != NULL)) 55 return attr->mode; 56 if ((attr == &dev_attr_revision.attr) 57 && (soc_dev->attr->revision != NULL)) 58 return attr->mode; 59 if ((attr == &dev_attr_soc_id.attr) 60 && (soc_dev->attr->soc_id != NULL)) 61 return attr->mode; 62 63 /* Unknown or unfilled attribute. */ 64 return 0; 65 } 66 67 static ssize_t soc_info_get(struct device *dev, 68 struct device_attribute *attr, 69 char *buf) 70 { 71 struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); 72 73 if (attr == &dev_attr_machine) 74 return sprintf(buf, "%s\n", soc_dev->attr->machine); 75 if (attr == &dev_attr_family) 76 return sprintf(buf, "%s\n", soc_dev->attr->family); 77 if (attr == &dev_attr_revision) 78 return sprintf(buf, "%s\n", soc_dev->attr->revision); 79 if (attr == &dev_attr_soc_id) 80 return sprintf(buf, "%s\n", soc_dev->attr->soc_id); 81 82 return -EINVAL; 83 84 } 85 86 static struct attribute *soc_attr[] = { 87 &dev_attr_machine.attr, 88 &dev_attr_family.attr, 89 &dev_attr_soc_id.attr, 90 &dev_attr_revision.attr, 91 NULL, 92 }; 93 94 static const struct attribute_group soc_attr_group = { 95 .attrs = soc_attr, 96 .is_visible = soc_attribute_mode, 97 }; 98 99 static const struct attribute_group *soc_attr_groups[] = { 100 &soc_attr_group, 101 NULL, 102 }; 103 104 static void soc_release(struct device *dev) 105 { 106 struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); 107 108 kfree(soc_dev); 109 } 110 111 struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr) 112 { 113 struct soc_device *soc_dev; 114 int ret; 115 116 soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL); 117 if (!soc_dev) { 118 ret = -ENOMEM; 119 goto out1; 120 } 121 122 /* Fetch a unique (reclaimable) SOC ID. */ 123 ret = ida_simple_get(&soc_ida, 0, 0, GFP_KERNEL); 124 if (ret < 0) 125 goto out2; 126 soc_dev->soc_dev_num = ret; 127 128 soc_dev->attr = soc_dev_attr; 129 soc_dev->dev.bus = &soc_bus_type; 130 soc_dev->dev.groups = soc_attr_groups; 131 soc_dev->dev.release = soc_release; 132 133 dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num); 134 135 ret = device_register(&soc_dev->dev); 136 if (ret) 137 goto out3; 138 139 return soc_dev; 140 141 out3: 142 ida_simple_remove(&soc_ida, soc_dev->soc_dev_num); 143 out2: 144 kfree(soc_dev); 145 out1: 146 return ERR_PTR(ret); 147 } 148 149 /* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */ 150 void soc_device_unregister(struct soc_device *soc_dev) 151 { 152 ida_simple_remove(&soc_ida, soc_dev->soc_dev_num); 153 154 device_unregister(&soc_dev->dev); 155 } 156 157 static int __init soc_bus_register(void) 158 { 159 return bus_register(&soc_bus_type); 160 } 161 core_initcall(soc_bus_register); 162