| Stephen Rothwell | f85ff30 | 2007-05-01 16:40:36 +1000 | [diff] [blame] | 1 | #include <linux/string.h> | 
|  | 2 | #include <linux/kernel.h> | 
|  | 3 | #include <linux/of.h> | 
| Stephen Rothwell | f898f8d | 2007-05-01 16:49:51 +1000 | [diff] [blame] | 4 | #include <linux/of_device.h> | 
| Stephen Rothwell | f85ff30 | 2007-05-01 16:40:36 +1000 | [diff] [blame] | 5 | #include <linux/init.h> | 
|  | 6 | #include <linux/module.h> | 
|  | 7 | #include <linux/mod_devicetable.h> | 
|  | 8 | #include <linux/slab.h> | 
|  | 9 |  | 
|  | 10 | #include <asm/errno.h> | 
| Stephen Rothwell | f85ff30 | 2007-05-01 16:40:36 +1000 | [diff] [blame] | 11 |  | 
|  | 12 | /** | 
| Stephen Rothwell | f85ff30 | 2007-05-01 16:40:36 +1000 | [diff] [blame] | 13 | * of_match_device - Tell if an of_device structure has a matching | 
|  | 14 | * of_match structure | 
|  | 15 | * @ids: array of of device match structures to search in | 
|  | 16 | * @dev: the of device structure to match against | 
|  | 17 | * | 
|  | 18 | * Used by a driver to check whether an of_device present in the | 
|  | 19 | * system is in its list of supported devices. | 
|  | 20 | */ | 
|  | 21 | const struct of_device_id *of_match_device(const struct of_device_id *matches, | 
|  | 22 | const struct of_device *dev) | 
|  | 23 | { | 
|  | 24 | if (!dev->node) | 
|  | 25 | return NULL; | 
|  | 26 | return of_match_node(matches, dev->node); | 
|  | 27 | } | 
|  | 28 | EXPORT_SYMBOL(of_match_device); | 
|  | 29 |  | 
|  | 30 | struct of_device *of_dev_get(struct of_device *dev) | 
|  | 31 | { | 
|  | 32 | struct device *tmp; | 
|  | 33 |  | 
|  | 34 | if (!dev) | 
|  | 35 | return NULL; | 
|  | 36 | tmp = get_device(&dev->dev); | 
|  | 37 | if (tmp) | 
|  | 38 | return to_of_device(tmp); | 
|  | 39 | else | 
|  | 40 | return NULL; | 
|  | 41 | } | 
|  | 42 | EXPORT_SYMBOL(of_dev_get); | 
|  | 43 |  | 
|  | 44 | void of_dev_put(struct of_device *dev) | 
|  | 45 | { | 
|  | 46 | if (dev) | 
|  | 47 | put_device(&dev->dev); | 
|  | 48 | } | 
|  | 49 | EXPORT_SYMBOL(of_dev_put); | 
|  | 50 |  | 
| Olaf Hering | 140b932 | 2008-04-24 23:16:00 +1000 | [diff] [blame] | 51 | static ssize_t devspec_show(struct device *dev, | 
| Stephen Rothwell | f85ff30 | 2007-05-01 16:40:36 +1000 | [diff] [blame] | 52 | struct device_attribute *attr, char *buf) | 
|  | 53 | { | 
|  | 54 | struct of_device *ofdev; | 
|  | 55 |  | 
|  | 56 | ofdev = to_of_device(dev); | 
| Olaf Hering | 140b932 | 2008-04-24 23:16:00 +1000 | [diff] [blame] | 57 | return sprintf(buf, "%s\n", ofdev->node->full_name); | 
| Stephen Rothwell | f85ff30 | 2007-05-01 16:40:36 +1000 | [diff] [blame] | 58 | } | 
|  | 59 |  | 
| Joachim Fenkes | 4589f1f | 2008-08-06 00:30:18 +1000 | [diff] [blame] | 60 | static ssize_t name_show(struct device *dev, | 
|  | 61 | struct device_attribute *attr, char *buf) | 
|  | 62 | { | 
|  | 63 | struct of_device *ofdev; | 
|  | 64 |  | 
|  | 65 | ofdev = to_of_device(dev); | 
|  | 66 | return sprintf(buf, "%s\n", ofdev->node->name); | 
|  | 67 | } | 
|  | 68 |  | 
| Olaf Hering | 140b932 | 2008-04-24 23:16:00 +1000 | [diff] [blame] | 69 | static ssize_t modalias_show(struct device *dev, | 
|  | 70 | struct device_attribute *attr, char *buf) | 
|  | 71 | { | 
|  | 72 | struct of_device *ofdev = to_of_device(dev); | 
|  | 73 | ssize_t len = 0; | 
|  | 74 |  | 
|  | 75 | len = of_device_get_modalias(ofdev, buf, PAGE_SIZE - 2); | 
|  | 76 | buf[len] = '\n'; | 
|  | 77 | buf[len+1] = 0; | 
|  | 78 | return len+1; | 
|  | 79 | } | 
|  | 80 |  | 
|  | 81 | struct device_attribute of_platform_device_attrs[] = { | 
|  | 82 | __ATTR_RO(devspec), | 
| Joachim Fenkes | 4589f1f | 2008-08-06 00:30:18 +1000 | [diff] [blame] | 83 | __ATTR_RO(name), | 
| Olaf Hering | 140b932 | 2008-04-24 23:16:00 +1000 | [diff] [blame] | 84 | __ATTR_RO(modalias), | 
|  | 85 | __ATTR_NULL | 
|  | 86 | }; | 
| Stephen Rothwell | f85ff30 | 2007-05-01 16:40:36 +1000 | [diff] [blame] | 87 |  | 
|  | 88 | /** | 
|  | 89 | * of_release_dev - free an of device structure when all users of it are finished. | 
|  | 90 | * @dev: device that's been disconnected | 
|  | 91 | * | 
|  | 92 | * Will be called only by the device core when all users of this of device are | 
|  | 93 | * done. | 
|  | 94 | */ | 
|  | 95 | void of_release_dev(struct device *dev) | 
|  | 96 | { | 
|  | 97 | struct of_device *ofdev; | 
|  | 98 |  | 
|  | 99 | ofdev = to_of_device(dev); | 
|  | 100 | of_node_put(ofdev->node); | 
|  | 101 | kfree(ofdev); | 
|  | 102 | } | 
|  | 103 | EXPORT_SYMBOL(of_release_dev); | 
|  | 104 |  | 
|  | 105 | int of_device_register(struct of_device *ofdev) | 
|  | 106 | { | 
| Stephen Rothwell | f85ff30 | 2007-05-01 16:40:36 +1000 | [diff] [blame] | 107 | BUG_ON(ofdev->node == NULL); | 
| Jeremy Kerr | 6098e2e | 2008-10-26 21:51:25 +0000 | [diff] [blame] | 108 |  | 
|  | 109 | device_initialize(&ofdev->dev); | 
|  | 110 |  | 
|  | 111 | /* device_add will assume that this device is on the same node as | 
|  | 112 | * the parent. If there is no parent defined, set the node | 
|  | 113 | * explicitly */ | 
|  | 114 | if (!ofdev->dev.parent) | 
|  | 115 | set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->node)); | 
|  | 116 |  | 
|  | 117 | return device_add(&ofdev->dev); | 
| Stephen Rothwell | f85ff30 | 2007-05-01 16:40:36 +1000 | [diff] [blame] | 118 | } | 
|  | 119 | EXPORT_SYMBOL(of_device_register); | 
|  | 120 |  | 
|  | 121 | void of_device_unregister(struct of_device *ofdev) | 
|  | 122 | { | 
| Stephen Rothwell | f85ff30 | 2007-05-01 16:40:36 +1000 | [diff] [blame] | 123 | device_unregister(&ofdev->dev); | 
|  | 124 | } | 
|  | 125 | EXPORT_SYMBOL(of_device_unregister); | 
| Stephen Rothwell | 09e67ca | 2008-05-16 11:57:45 +1000 | [diff] [blame] | 126 |  | 
|  | 127 | ssize_t of_device_get_modalias(struct of_device *ofdev, | 
|  | 128 | char *str, ssize_t len) | 
|  | 129 | { | 
|  | 130 | const char *compat; | 
|  | 131 | int cplen, i; | 
|  | 132 | ssize_t tsize, csize, repend; | 
|  | 133 |  | 
|  | 134 | /* Name & Type */ | 
|  | 135 | csize = snprintf(str, len, "of:N%sT%s", | 
|  | 136 | ofdev->node->name, ofdev->node->type); | 
|  | 137 |  | 
|  | 138 | /* Get compatible property if any */ | 
|  | 139 | compat = of_get_property(ofdev->node, "compatible", &cplen); | 
|  | 140 | if (!compat) | 
|  | 141 | return csize; | 
|  | 142 |  | 
|  | 143 | /* Find true end (we tolerate multiple \0 at the end */ | 
|  | 144 | for (i = (cplen - 1); i >= 0 && !compat[i]; i--) | 
|  | 145 | cplen--; | 
|  | 146 | if (!cplen) | 
|  | 147 | return csize; | 
|  | 148 | cplen++; | 
|  | 149 |  | 
|  | 150 | /* Check space (need cplen+1 chars including final \0) */ | 
|  | 151 | tsize = csize + cplen; | 
|  | 152 | repend = tsize; | 
|  | 153 |  | 
|  | 154 | if (csize >= len)		/* @ the limit, all is already filled */ | 
|  | 155 | return tsize; | 
|  | 156 |  | 
|  | 157 | if (tsize >= len) {		/* limit compat list */ | 
|  | 158 | cplen = len - csize - 1; | 
|  | 159 | repend = len; | 
|  | 160 | } | 
|  | 161 |  | 
|  | 162 | /* Copy and do char replacement */ | 
|  | 163 | memcpy(&str[csize + 1], compat, cplen); | 
|  | 164 | for (i = csize; i < repend; i++) { | 
|  | 165 | char c = str[i]; | 
|  | 166 | if (c == '\0') | 
|  | 167 | str[i] = 'C'; | 
|  | 168 | else if (c == ' ') | 
|  | 169 | str[i] = '_'; | 
|  | 170 | } | 
|  | 171 |  | 
|  | 172 | return tsize; | 
|  | 173 | } |