diff options
author | Grant Likely <grant.likely@linaro.org> | 2014-08-11 09:06:23 -0400 |
---|---|---|
committer | Grant Likely <grant.likely@linaro.org> | 2014-08-11 09:06:23 -0400 |
commit | 663d3f7c2e5e1b018a4c53277ccfde40329d98ca (patch) | |
tree | a3afcc2d8fe682bdcc4c2e39b47ab3987b38c69a /drivers/of | |
parent | b775e642bf958a02210ac4d4edd1a1b7067c49fa (diff) | |
parent | b6ae5dc54b0a5c542d06d46b9083ceb70bf7e083 (diff) |
Merge branch 'devicetree/next-overlay' into devicetree/next
Conflicts:
drivers/of/testcase-data/testcases.dts
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/Makefile | 1 | ||||
-rw-r--r-- | drivers/of/base.c | 423 | ||||
-rw-r--r-- | drivers/of/device.c | 4 | ||||
-rw-r--r-- | drivers/of/dynamic.c | 660 | ||||
-rw-r--r-- | drivers/of/of_private.h | 59 | ||||
-rw-r--r-- | drivers/of/platform.c | 32 | ||||
-rw-r--r-- | drivers/of/selftest.c | 79 | ||||
-rw-r--r-- | drivers/of/testcase-data/testcases.dts | 10 |
8 files changed, 925 insertions, 343 deletions
diff --git a/drivers/of/Makefile b/drivers/of/Makefile index b9e753b56964..2b6a7b129d10 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | obj-y = base.o device.o platform.o | 1 | obj-y = base.o device.o platform.o |
2 | obj-$(CONFIG_OF_DYNAMIC) += dynamic.o | ||
2 | obj-$(CONFIG_OF_FLATTREE) += fdt.o | 3 | obj-$(CONFIG_OF_FLATTREE) += fdt.o |
3 | obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o | 4 | obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o |
4 | obj-$(CONFIG_OF_PROMTREE) += pdt.o | 5 | obj-$(CONFIG_OF_PROMTREE) += pdt.o |
diff --git a/drivers/of/base.c b/drivers/of/base.c index e4f95ba0a3eb..d8574adf0d62 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c | |||
@@ -38,13 +38,15 @@ struct device_node *of_chosen; | |||
38 | struct device_node *of_aliases; | 38 | struct device_node *of_aliases; |
39 | struct device_node *of_stdout; | 39 | struct device_node *of_stdout; |
40 | 40 | ||
41 | static struct kset *of_kset; | 41 | struct kset *of_kset; |
42 | 42 | ||
43 | /* | 43 | /* |
44 | * Used to protect the of_aliases; but also overloaded to hold off addition of | 44 | * Used to protect the of_aliases, to hold off addition of nodes to sysfs. |
45 | * nodes to sysfs | 45 | * This mutex must be held whenever modifications are being made to the |
46 | * device tree. The of_{attach,detach}_node() and | ||
47 | * of_{add,remove,update}_property() helpers make sure this happens. | ||
46 | */ | 48 | */ |
47 | DEFINE_MUTEX(of_aliases_mutex); | 49 | DEFINE_MUTEX(of_mutex); |
48 | 50 | ||
49 | /* use when traversing tree through the allnext, child, sibling, | 51 | /* use when traversing tree through the allnext, child, sibling, |
50 | * or parent members of struct device_node. | 52 | * or parent members of struct device_node. |
@@ -90,79 +92,7 @@ int __weak of_node_to_nid(struct device_node *np) | |||
90 | } | 92 | } |
91 | #endif | 93 | #endif |
92 | 94 | ||
93 | #if defined(CONFIG_OF_DYNAMIC) | 95 | #ifndef CONFIG_OF_DYNAMIC |
94 | /** | ||
95 | * of_node_get - Increment refcount of a node | ||
96 | * @node: Node to inc refcount, NULL is supported to | ||
97 | * simplify writing of callers | ||
98 | * | ||
99 | * Returns node. | ||
100 | */ | ||
101 | struct device_node *of_node_get(struct device_node *node) | ||
102 | { | ||
103 | if (node) | ||
104 | kobject_get(&node->kobj); | ||
105 | return node; | ||
106 | } | ||
107 | EXPORT_SYMBOL(of_node_get); | ||
108 | |||
109 | static inline struct device_node *kobj_to_device_node(struct kobject *kobj) | ||
110 | { | ||
111 | return container_of(kobj, struct device_node, kobj); | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * of_node_release - release a dynamically allocated node | ||
116 | * @kref: kref element of the node to be released | ||
117 | * | ||
118 | * In of_node_put() this function is passed to kref_put() | ||
119 | * as the destructor. | ||
120 | */ | ||
121 | static void of_node_release(struct kobject *kobj) | ||
122 | { | ||
123 | struct device_node *node = kobj_to_device_node(kobj); | ||
124 | struct property *prop = node->properties; | ||
125 | |||
126 | /* We should never be releasing nodes that haven't been detached. */ | ||
127 | if (!of_node_check_flag(node, OF_DETACHED)) { | ||
128 | pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name); | ||
129 | dump_stack(); | ||
130 | return; | ||
131 | } | ||
132 | |||
133 | if (!of_node_check_flag(node, OF_DYNAMIC)) | ||
134 | return; | ||
135 | |||
136 | while (prop) { | ||
137 | struct property *next = prop->next; | ||
138 | kfree(prop->name); | ||
139 | kfree(prop->value); | ||
140 | kfree(prop); | ||
141 | prop = next; | ||
142 | |||
143 | if (!prop) { | ||
144 | prop = node->deadprops; | ||
145 | node->deadprops = NULL; | ||
146 | } | ||
147 | } | ||
148 | kfree(node->full_name); | ||
149 | kfree(node->data); | ||
150 | kfree(node); | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * of_node_put - Decrement refcount of a node | ||
155 | * @node: Node to dec refcount, NULL is supported to | ||
156 | * simplify writing of callers | ||
157 | * | ||
158 | */ | ||
159 | void of_node_put(struct device_node *node) | ||
160 | { | ||
161 | if (node) | ||
162 | kobject_put(&node->kobj); | ||
163 | } | ||
164 | EXPORT_SYMBOL(of_node_put); | ||
165 | #else | ||
166 | static void of_node_release(struct kobject *kobj) | 96 | static void of_node_release(struct kobject *kobj) |
167 | { | 97 | { |
168 | /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */ | 98 | /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */ |
@@ -201,13 +131,16 @@ static const char *safe_name(struct kobject *kobj, const char *orig_name) | |||
201 | return name; | 131 | return name; |
202 | } | 132 | } |
203 | 133 | ||
204 | static int __of_add_property_sysfs(struct device_node *np, struct property *pp) | 134 | int __of_add_property_sysfs(struct device_node *np, struct property *pp) |
205 | { | 135 | { |
206 | int rc; | 136 | int rc; |
207 | 137 | ||
208 | /* Important: Don't leak passwords */ | 138 | /* Important: Don't leak passwords */ |
209 | bool secure = strncmp(pp->name, "security-", 9) == 0; | 139 | bool secure = strncmp(pp->name, "security-", 9) == 0; |
210 | 140 | ||
141 | if (!of_kset || !of_node_is_attached(np)) | ||
142 | return 0; | ||
143 | |||
211 | sysfs_bin_attr_init(&pp->attr); | 144 | sysfs_bin_attr_init(&pp->attr); |
212 | pp->attr.attr.name = safe_name(&np->kobj, pp->name); | 145 | pp->attr.attr.name = safe_name(&np->kobj, pp->name); |
213 | pp->attr.attr.mode = secure ? S_IRUSR : S_IRUGO; | 146 | pp->attr.attr.mode = secure ? S_IRUSR : S_IRUGO; |
@@ -219,12 +152,15 @@ static int __of_add_property_sysfs(struct device_node *np, struct property *pp) | |||
219 | return rc; | 152 | return rc; |
220 | } | 153 | } |
221 | 154 | ||
222 | static int __of_node_add(struct device_node *np) | 155 | int __of_attach_node_sysfs(struct device_node *np) |
223 | { | 156 | { |
224 | const char *name; | 157 | const char *name; |
225 | struct property *pp; | 158 | struct property *pp; |
226 | int rc; | 159 | int rc; |
227 | 160 | ||
161 | if (!of_kset) | ||
162 | return 0; | ||
163 | |||
228 | np->kobj.kset = of_kset; | 164 | np->kobj.kset = of_kset; |
229 | if (!np->parent) { | 165 | if (!np->parent) { |
230 | /* Nodes without parents are new top level trees */ | 166 | /* Nodes without parents are new top level trees */ |
@@ -246,59 +182,20 @@ static int __of_node_add(struct device_node *np) | |||
246 | return 0; | 182 | return 0; |
247 | } | 183 | } |
248 | 184 | ||
249 | int of_node_add(struct device_node *np) | ||
250 | { | ||
251 | int rc = 0; | ||
252 | |||
253 | BUG_ON(!of_node_is_initialized(np)); | ||
254 | |||
255 | /* | ||
256 | * Grab the mutex here so that in a race condition between of_init() and | ||
257 | * of_node_add(), node addition will still be consistent. | ||
258 | */ | ||
259 | mutex_lock(&of_aliases_mutex); | ||
260 | if (of_kset) | ||
261 | rc = __of_node_add(np); | ||
262 | else | ||
263 | /* This scenario may be perfectly valid, but report it anyway */ | ||
264 | pr_info("of_node_add(%s) before of_init()\n", np->full_name); | ||
265 | mutex_unlock(&of_aliases_mutex); | ||
266 | return rc; | ||
267 | } | ||
268 | |||
269 | #if defined(CONFIG_OF_DYNAMIC) | ||
270 | static void of_node_remove(struct device_node *np) | ||
271 | { | ||
272 | struct property *pp; | ||
273 | |||
274 | BUG_ON(!of_node_is_initialized(np)); | ||
275 | |||
276 | /* only remove properties if on sysfs */ | ||
277 | if (of_node_is_attached(np)) { | ||
278 | for_each_property_of_node(np, pp) | ||
279 | sysfs_remove_bin_file(&np->kobj, &pp->attr); | ||
280 | kobject_del(&np->kobj); | ||
281 | } | ||
282 | |||
283 | /* finally remove the kobj_init ref */ | ||
284 | of_node_put(np); | ||
285 | } | ||
286 | #endif | ||
287 | |||
288 | static int __init of_init(void) | 185 | static int __init of_init(void) |
289 | { | 186 | { |
290 | struct device_node *np; | 187 | struct device_node *np; |
291 | 188 | ||
292 | /* Create the kset, and register existing nodes */ | 189 | /* Create the kset, and register existing nodes */ |
293 | mutex_lock(&of_aliases_mutex); | 190 | mutex_lock(&of_mutex); |
294 | of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj); | 191 | of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj); |
295 | if (!of_kset) { | 192 | if (!of_kset) { |
296 | mutex_unlock(&of_aliases_mutex); | 193 | mutex_unlock(&of_mutex); |
297 | return -ENOMEM; | 194 | return -ENOMEM; |
298 | } | 195 | } |
299 | for_each_of_allnodes(np) | 196 | for_each_of_allnodes(np) |
300 | __of_node_add(np); | 197 | __of_attach_node_sysfs(np); |
301 | mutex_unlock(&of_aliases_mutex); | 198 | mutex_unlock(&of_mutex); |
302 | 199 | ||
303 | /* Symlink in /proc as required by userspace ABI */ | 200 | /* Symlink in /proc as required by userspace ABI */ |
304 | if (of_allnodes) | 201 | if (of_allnodes) |
@@ -370,8 +267,8 @@ EXPORT_SYMBOL(of_find_all_nodes); | |||
370 | * Find a property with a given name for a given node | 267 | * Find a property with a given name for a given node |
371 | * and return the value. | 268 | * and return the value. |
372 | */ | 269 | */ |
373 | static const void *__of_get_property(const struct device_node *np, | 270 | const void *__of_get_property(const struct device_node *np, |
374 | const char *name, int *lenp) | 271 | const char *name, int *lenp) |
375 | { | 272 | { |
376 | struct property *pp = __of_find_property(np, name, lenp); | 273 | struct property *pp = __of_find_property(np, name, lenp); |
377 | 274 | ||
@@ -1749,32 +1646,10 @@ int of_count_phandle_with_args(const struct device_node *np, const char *list_na | |||
1749 | } | 1646 | } |
1750 | EXPORT_SYMBOL(of_count_phandle_with_args); | 1647 | EXPORT_SYMBOL(of_count_phandle_with_args); |
1751 | 1648 | ||
1752 | #if defined(CONFIG_OF_DYNAMIC) | ||
1753 | static int of_property_notify(int action, struct device_node *np, | ||
1754 | struct property *prop) | ||
1755 | { | ||
1756 | struct of_prop_reconfig pr; | ||
1757 | |||
1758 | /* only call notifiers if the node is attached */ | ||
1759 | if (!of_node_is_attached(np)) | ||
1760 | return 0; | ||
1761 | |||
1762 | pr.dn = np; | ||
1763 | pr.prop = prop; | ||
1764 | return of_reconfig_notify(action, &pr); | ||
1765 | } | ||
1766 | #else | ||
1767 | static int of_property_notify(int action, struct device_node *np, | ||
1768 | struct property *prop) | ||
1769 | { | ||
1770 | return 0; | ||
1771 | } | ||
1772 | #endif | ||
1773 | |||
1774 | /** | 1649 | /** |
1775 | * __of_add_property - Add a property to a node without lock operations | 1650 | * __of_add_property - Add a property to a node without lock operations |
1776 | */ | 1651 | */ |
1777 | static int __of_add_property(struct device_node *np, struct property *prop) | 1652 | int __of_add_property(struct device_node *np, struct property *prop) |
1778 | { | 1653 | { |
1779 | struct property **next; | 1654 | struct property **next; |
1780 | 1655 | ||
@@ -1800,22 +1675,49 @@ int of_add_property(struct device_node *np, struct property *prop) | |||
1800 | unsigned long flags; | 1675 | unsigned long flags; |
1801 | int rc; | 1676 | int rc; |
1802 | 1677 | ||
1803 | rc = of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop); | 1678 | mutex_lock(&of_mutex); |
1804 | if (rc) | ||
1805 | return rc; | ||
1806 | 1679 | ||
1807 | raw_spin_lock_irqsave(&devtree_lock, flags); | 1680 | raw_spin_lock_irqsave(&devtree_lock, flags); |
1808 | rc = __of_add_property(np, prop); | 1681 | rc = __of_add_property(np, prop); |
1809 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | 1682 | raw_spin_unlock_irqrestore(&devtree_lock, flags); |
1810 | if (rc) | ||
1811 | return rc; | ||
1812 | 1683 | ||
1813 | if (of_node_is_attached(np)) | 1684 | if (!rc) |
1814 | __of_add_property_sysfs(np, prop); | 1685 | __of_add_property_sysfs(np, prop); |
1815 | 1686 | ||
1687 | mutex_unlock(&of_mutex); | ||
1688 | |||
1689 | if (!rc) | ||
1690 | of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop, NULL); | ||
1691 | |||
1816 | return rc; | 1692 | return rc; |
1817 | } | 1693 | } |
1818 | 1694 | ||
1695 | int __of_remove_property(struct device_node *np, struct property *prop) | ||
1696 | { | ||
1697 | struct property **next; | ||
1698 | |||
1699 | for (next = &np->properties; *next; next = &(*next)->next) { | ||
1700 | if (*next == prop) | ||
1701 | break; | ||
1702 | } | ||
1703 | if (*next == NULL) | ||
1704 | return -ENODEV; | ||
1705 | |||
1706 | /* found the node */ | ||
1707 | *next = prop->next; | ||
1708 | prop->next = np->deadprops; | ||
1709 | np->deadprops = prop; | ||
1710 | |||
1711 | return 0; | ||
1712 | } | ||
1713 | |||
1714 | void __of_remove_property_sysfs(struct device_node *np, struct property *prop) | ||
1715 | { | ||
1716 | /* at early boot, bail here and defer setup to of_init() */ | ||
1717 | if (of_kset && of_node_is_attached(np)) | ||
1718 | sysfs_remove_bin_file(&np->kobj, &prop->attr); | ||
1719 | } | ||
1720 | |||
1819 | /** | 1721 | /** |
1820 | * of_remove_property - Remove a property from a node. | 1722 | * of_remove_property - Remove a property from a node. |
1821 | * | 1723 | * |
@@ -1826,211 +1728,98 @@ int of_add_property(struct device_node *np, struct property *prop) | |||
1826 | */ | 1728 | */ |
1827 | int of_remove_property(struct device_node *np, struct property *prop) | 1729 | int of_remove_property(struct device_node *np, struct property *prop) |
1828 | { | 1730 | { |
1829 | struct property **next; | ||
1830 | unsigned long flags; | 1731 | unsigned long flags; |
1831 | int found = 0; | ||
1832 | int rc; | 1732 | int rc; |
1833 | 1733 | ||
1834 | rc = of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop); | 1734 | mutex_lock(&of_mutex); |
1835 | if (rc) | ||
1836 | return rc; | ||
1837 | 1735 | ||
1838 | raw_spin_lock_irqsave(&devtree_lock, flags); | 1736 | raw_spin_lock_irqsave(&devtree_lock, flags); |
1839 | next = &np->properties; | 1737 | rc = __of_remove_property(np, prop); |
1840 | while (*next) { | ||
1841 | if (*next == prop) { | ||
1842 | /* found the node */ | ||
1843 | *next = prop->next; | ||
1844 | prop->next = np->deadprops; | ||
1845 | np->deadprops = prop; | ||
1846 | found = 1; | ||
1847 | break; | ||
1848 | } | ||
1849 | next = &(*next)->next; | ||
1850 | } | ||
1851 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | 1738 | raw_spin_unlock_irqrestore(&devtree_lock, flags); |
1852 | 1739 | ||
1853 | if (!found) | 1740 | if (!rc) |
1854 | return -ENODEV; | 1741 | __of_remove_property_sysfs(np, prop); |
1855 | 1742 | ||
1856 | /* at early boot, bail hear and defer setup to of_init() */ | 1743 | mutex_unlock(&of_mutex); |
1857 | if (!of_kset) | ||
1858 | return 0; | ||
1859 | 1744 | ||
1860 | sysfs_remove_bin_file(&np->kobj, &prop->attr); | 1745 | if (!rc) |
1746 | of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop, NULL); | ||
1861 | 1747 | ||
1862 | return 0; | 1748 | return rc; |
1863 | } | 1749 | } |
1864 | 1750 | ||
1865 | /* | 1751 | int __of_update_property(struct device_node *np, struct property *newprop, |
1866 | * of_update_property - Update a property in a node, if the property does | 1752 | struct property **oldpropp) |
1867 | * not exist, add it. | ||
1868 | * | ||
1869 | * Note that we don't actually remove it, since we have given out | ||
1870 | * who-knows-how-many pointers to the data using get-property. | ||
1871 | * Instead we just move the property to the "dead properties" list, | ||
1872 | * and add the new property to the property list | ||
1873 | */ | ||
1874 | int of_update_property(struct device_node *np, struct property *newprop) | ||
1875 | { | 1753 | { |
1876 | struct property **next, *oldprop; | 1754 | struct property **next, *oldprop; |
1877 | unsigned long flags; | ||
1878 | int rc; | ||
1879 | |||
1880 | rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop); | ||
1881 | if (rc) | ||
1882 | return rc; | ||
1883 | 1755 | ||
1884 | if (!newprop->name) | 1756 | for (next = &np->properties; *next; next = &(*next)->next) { |
1885 | return -EINVAL; | 1757 | if (of_prop_cmp((*next)->name, newprop->name) == 0) |
1758 | break; | ||
1759 | } | ||
1760 | *oldpropp = oldprop = *next; | ||
1886 | 1761 | ||
1887 | raw_spin_lock_irqsave(&devtree_lock, flags); | 1762 | if (oldprop) { |
1888 | next = &np->properties; | ||
1889 | oldprop = __of_find_property(np, newprop->name, NULL); | ||
1890 | if (!oldprop) { | ||
1891 | /* add the new node */ | ||
1892 | rc = __of_add_property(np, newprop); | ||
1893 | } else while (*next) { | ||
1894 | /* replace the node */ | 1763 | /* replace the node */ |
1895 | if (*next == oldprop) { | 1764 | newprop->next = oldprop->next; |
1896 | newprop->next = oldprop->next; | 1765 | *next = newprop; |
1897 | *next = newprop; | 1766 | oldprop->next = np->deadprops; |
1898 | oldprop->next = np->deadprops; | 1767 | np->deadprops = oldprop; |
1899 | np->deadprops = oldprop; | 1768 | } else { |
1900 | break; | 1769 | /* new node */ |
1901 | } | 1770 | newprop->next = NULL; |
1902 | next = &(*next)->next; | 1771 | *next = newprop; |
1903 | } | 1772 | } |
1904 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | ||
1905 | if (rc) | ||
1906 | return rc; | ||
1907 | 1773 | ||
1774 | return 0; | ||
1775 | } | ||
1776 | |||
1777 | void __of_update_property_sysfs(struct device_node *np, struct property *newprop, | ||
1778 | struct property *oldprop) | ||
1779 | { | ||
1908 | /* At early boot, bail out and defer setup to of_init() */ | 1780 | /* At early boot, bail out and defer setup to of_init() */ |
1909 | if (!of_kset) | 1781 | if (!of_kset) |
1910 | return 0; | 1782 | return; |
1911 | 1783 | ||
1912 | /* Update the sysfs attribute */ | ||
1913 | if (oldprop) | 1784 | if (oldprop) |
1914 | sysfs_remove_bin_file(&np->kobj, &oldprop->attr); | 1785 | sysfs_remove_bin_file(&np->kobj, &oldprop->attr); |
1915 | __of_add_property_sysfs(np, newprop); | 1786 | __of_add_property_sysfs(np, newprop); |
1916 | |||
1917 | return 0; | ||
1918 | } | 1787 | } |
1919 | 1788 | ||
1920 | #if defined(CONFIG_OF_DYNAMIC) | ||
1921 | /* | 1789 | /* |
1922 | * Support for dynamic device trees. | 1790 | * of_update_property - Update a property in a node, if the property does |
1791 | * not exist, add it. | ||
1923 | * | 1792 | * |
1924 | * On some platforms, the device tree can be manipulated at runtime. | 1793 | * Note that we don't actually remove it, since we have given out |
1925 | * The routines in this section support adding, removing and changing | 1794 | * who-knows-how-many pointers to the data using get-property. |
1926 | * device tree nodes. | 1795 | * Instead we just move the property to the "dead properties" list, |
1927 | */ | 1796 | * and add the new property to the property list |
1928 | |||
1929 | static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain); | ||
1930 | |||
1931 | int of_reconfig_notifier_register(struct notifier_block *nb) | ||
1932 | { | ||
1933 | return blocking_notifier_chain_register(&of_reconfig_chain, nb); | ||
1934 | } | ||
1935 | EXPORT_SYMBOL_GPL(of_reconfig_notifier_register); | ||
1936 | |||
1937 | int of_reconfig_notifier_unregister(struct notifier_block *nb) | ||
1938 | { | ||
1939 | return blocking_notifier_chain_unregister(&of_reconfig_chain, nb); | ||
1940 | } | ||
1941 | EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister); | ||
1942 | |||
1943 | int of_reconfig_notify(unsigned long action, void *p) | ||
1944 | { | ||
1945 | int rc; | ||
1946 | |||
1947 | rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p); | ||
1948 | return notifier_to_errno(rc); | ||
1949 | } | ||
1950 | |||
1951 | /** | ||
1952 | * of_attach_node - Plug a device node into the tree and global list. | ||
1953 | */ | 1797 | */ |
1954 | int of_attach_node(struct device_node *np) | 1798 | int of_update_property(struct device_node *np, struct property *newprop) |
1955 | { | 1799 | { |
1800 | struct property *oldprop; | ||
1956 | unsigned long flags; | 1801 | unsigned long flags; |
1957 | int rc; | 1802 | int rc; |
1958 | 1803 | ||
1959 | rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); | 1804 | if (!newprop->name) |
1960 | if (rc) | 1805 | return -EINVAL; |
1961 | return rc; | ||
1962 | |||
1963 | raw_spin_lock_irqsave(&devtree_lock, flags); | ||
1964 | np->sibling = np->parent->child; | ||
1965 | np->allnext = np->parent->allnext; | ||
1966 | np->parent->allnext = np; | ||
1967 | np->parent->child = np; | ||
1968 | of_node_clear_flag(np, OF_DETACHED); | ||
1969 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | ||
1970 | |||
1971 | of_node_add(np); | ||
1972 | return 0; | ||
1973 | } | ||
1974 | |||
1975 | /** | ||
1976 | * of_detach_node - "Unplug" a node from the device tree. | ||
1977 | * | ||
1978 | * The caller must hold a reference to the node. The memory associated with | ||
1979 | * the node is not freed until its refcount goes to zero. | ||
1980 | */ | ||
1981 | int of_detach_node(struct device_node *np) | ||
1982 | { | ||
1983 | struct device_node *parent; | ||
1984 | unsigned long flags; | ||
1985 | int rc = 0; | ||
1986 | 1806 | ||
1987 | rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); | 1807 | mutex_lock(&of_mutex); |
1988 | if (rc) | ||
1989 | return rc; | ||
1990 | 1808 | ||
1991 | raw_spin_lock_irqsave(&devtree_lock, flags); | 1809 | raw_spin_lock_irqsave(&devtree_lock, flags); |
1810 | rc = __of_update_property(np, newprop, &oldprop); | ||
1811 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | ||
1992 | 1812 | ||
1993 | if (of_node_check_flag(np, OF_DETACHED)) { | 1813 | if (!rc) |
1994 | /* someone already detached it */ | 1814 | __of_update_property_sysfs(np, newprop, oldprop); |
1995 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | ||
1996 | return rc; | ||
1997 | } | ||
1998 | |||
1999 | parent = np->parent; | ||
2000 | if (!parent) { | ||
2001 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | ||
2002 | return rc; | ||
2003 | } | ||
2004 | |||
2005 | if (of_allnodes == np) | ||
2006 | of_allnodes = np->allnext; | ||
2007 | else { | ||
2008 | struct device_node *prev; | ||
2009 | for (prev = of_allnodes; | ||
2010 | prev->allnext != np; | ||
2011 | prev = prev->allnext) | ||
2012 | ; | ||
2013 | prev->allnext = np->allnext; | ||
2014 | } | ||
2015 | 1815 | ||
2016 | if (parent->child == np) | 1816 | mutex_unlock(&of_mutex); |
2017 | parent->child = np->sibling; | ||
2018 | else { | ||
2019 | struct device_node *prevsib; | ||
2020 | for (prevsib = np->parent->child; | ||
2021 | prevsib->sibling != np; | ||
2022 | prevsib = prevsib->sibling) | ||
2023 | ; | ||
2024 | prevsib->sibling = np->sibling; | ||
2025 | } | ||
2026 | 1817 | ||
2027 | of_node_set_flag(np, OF_DETACHED); | 1818 | if (!rc) |
2028 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | 1819 | of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop, oldprop); |
2029 | 1820 | ||
2030 | of_node_remove(np); | ||
2031 | return rc; | 1821 | return rc; |
2032 | } | 1822 | } |
2033 | #endif /* defined(CONFIG_OF_DYNAMIC) */ | ||
2034 | 1823 | ||
2035 | static void of_alias_add(struct alias_prop *ap, struct device_node *np, | 1824 | static void of_alias_add(struct alias_prop *ap, struct device_node *np, |
2036 | int id, const char *stem, int stem_len) | 1825 | int id, const char *stem, int stem_len) |
@@ -2126,7 +1915,7 @@ int of_alias_get_id(struct device_node *np, const char *stem) | |||
2126 | struct alias_prop *app; | 1915 | struct alias_prop *app; |
2127 | int id = -ENODEV; | 1916 | int id = -ENODEV; |
2128 | 1917 | ||
2129 | mutex_lock(&of_aliases_mutex); | 1918 | mutex_lock(&of_mutex); |
2130 | list_for_each_entry(app, &aliases_lookup, link) { | 1919 | list_for_each_entry(app, &aliases_lookup, link) { |
2131 | if (strcmp(app->stem, stem) != 0) | 1920 | if (strcmp(app->stem, stem) != 0) |
2132 | continue; | 1921 | continue; |
@@ -2136,7 +1925,7 @@ int of_alias_get_id(struct device_node *np, const char *stem) | |||
2136 | break; | 1925 | break; |
2137 | } | 1926 | } |
2138 | } | 1927 | } |
2139 | mutex_unlock(&of_aliases_mutex); | 1928 | mutex_unlock(&of_mutex); |
2140 | 1929 | ||
2141 | return id; | 1930 | return id; |
2142 | } | 1931 | } |
diff --git a/drivers/of/device.c b/drivers/of/device.c index dafb9736ab9b..46d6c75c1404 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c | |||
@@ -160,7 +160,7 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
160 | add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen); | 160 | add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen); |
161 | 161 | ||
162 | seen = 0; | 162 | seen = 0; |
163 | mutex_lock(&of_aliases_mutex); | 163 | mutex_lock(&of_mutex); |
164 | list_for_each_entry(app, &aliases_lookup, link) { | 164 | list_for_each_entry(app, &aliases_lookup, link) { |
165 | if (dev->of_node == app->np) { | 165 | if (dev->of_node == app->np) { |
166 | add_uevent_var(env, "OF_ALIAS_%d=%s", seen, | 166 | add_uevent_var(env, "OF_ALIAS_%d=%s", seen, |
@@ -168,7 +168,7 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
168 | seen++; | 168 | seen++; |
169 | } | 169 | } |
170 | } | 170 | } |
171 | mutex_unlock(&of_aliases_mutex); | 171 | mutex_unlock(&of_mutex); |
172 | } | 172 | } |
173 | 173 | ||
174 | int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) | 174 | int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) |
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c new file mode 100644 index 000000000000..54fecc49a1fe --- /dev/null +++ b/drivers/of/dynamic.c | |||
@@ -0,0 +1,660 @@ | |||
1 | /* | ||
2 | * Support for dynamic device trees. | ||
3 | * | ||
4 | * On some platforms, the device tree can be manipulated at runtime. | ||
5 | * The routines in this section support adding, removing and changing | ||
6 | * device tree nodes. | ||
7 | */ | ||
8 | |||
9 | #include <linux/of.h> | ||
10 | #include <linux/spinlock.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/string.h> | ||
13 | #include <linux/proc_fs.h> | ||
14 | |||
15 | #include "of_private.h" | ||
16 | |||
17 | /** | ||
18 | * of_node_get() - Increment refcount of a node | ||
19 | * @node: Node to inc refcount, NULL is supported to simplify writing of | ||
20 | * callers | ||
21 | * | ||
22 | * Returns node. | ||
23 | */ | ||
24 | struct device_node *of_node_get(struct device_node *node) | ||
25 | { | ||
26 | if (node) | ||
27 | kobject_get(&node->kobj); | ||
28 | return node; | ||
29 | } | ||
30 | EXPORT_SYMBOL(of_node_get); | ||
31 | |||
32 | /** | ||
33 | * of_node_put() - Decrement refcount of a node | ||
34 | * @node: Node to dec refcount, NULL is supported to simplify writing of | ||
35 | * callers | ||
36 | */ | ||
37 | void of_node_put(struct device_node *node) | ||
38 | { | ||
39 | if (node) | ||
40 | kobject_put(&node->kobj); | ||
41 | } | ||
42 | EXPORT_SYMBOL(of_node_put); | ||
43 | |||
44 | void __of_detach_node_sysfs(struct device_node *np) | ||
45 | { | ||
46 | struct property *pp; | ||
47 | |||
48 | BUG_ON(!of_node_is_initialized(np)); | ||
49 | if (!of_kset) | ||
50 | return; | ||
51 | |||
52 | /* only remove properties if on sysfs */ | ||
53 | if (of_node_is_attached(np)) { | ||
54 | for_each_property_of_node(np, pp) | ||
55 | sysfs_remove_bin_file(&np->kobj, &pp->attr); | ||
56 | kobject_del(&np->kobj); | ||
57 | } | ||
58 | |||
59 | /* finally remove the kobj_init ref */ | ||
60 | of_node_put(np); | ||
61 | } | ||
62 | |||
63 | static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain); | ||
64 | |||
65 | int of_reconfig_notifier_register(struct notifier_block *nb) | ||
66 | { | ||
67 | return blocking_notifier_chain_register(&of_reconfig_chain, nb); | ||
68 | } | ||
69 | EXPORT_SYMBOL_GPL(of_reconfig_notifier_register); | ||
70 | |||
71 | int of_reconfig_notifier_unregister(struct notifier_block *nb) | ||
72 | { | ||
73 | return blocking_notifier_chain_unregister(&of_reconfig_chain, nb); | ||
74 | } | ||
75 | EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister); | ||
76 | |||
77 | int of_reconfig_notify(unsigned long action, void *p) | ||
78 | { | ||
79 | int rc; | ||
80 | |||
81 | rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p); | ||
82 | return notifier_to_errno(rc); | ||
83 | } | ||
84 | |||
85 | int of_property_notify(int action, struct device_node *np, | ||
86 | struct property *prop, struct property *oldprop) | ||
87 | { | ||
88 | struct of_prop_reconfig pr; | ||
89 | |||
90 | /* only call notifiers if the node is attached */ | ||
91 | if (!of_node_is_attached(np)) | ||
92 | return 0; | ||
93 | |||
94 | pr.dn = np; | ||
95 | pr.prop = prop; | ||
96 | pr.old_prop = oldprop; | ||
97 | return of_reconfig_notify(action, &pr); | ||
98 | } | ||
99 | |||
100 | void __of_attach_node(struct device_node *np) | ||
101 | { | ||
102 | const __be32 *phandle; | ||
103 | int sz; | ||
104 | |||
105 | np->name = __of_get_property(np, "name", NULL) ? : "<NULL>"; | ||
106 | np->type = __of_get_property(np, "device_type", NULL) ? : "<NULL>"; | ||
107 | |||
108 | phandle = __of_get_property(np, "phandle", &sz); | ||
109 | if (!phandle) | ||
110 | phandle = __of_get_property(np, "linux,phandle", &sz); | ||
111 | if (IS_ENABLED(PPC_PSERIES) && !phandle) | ||
112 | phandle = __of_get_property(np, "ibm,phandle", &sz); | ||
113 | np->phandle = (phandle && (sz >= 4)) ? be32_to_cpup(phandle) : 0; | ||
114 | |||
115 | np->child = NULL; | ||
116 | np->sibling = np->parent->child; | ||
117 | np->allnext = np->parent->allnext; | ||
118 | np->parent->allnext = np; | ||
119 | np->parent->child = np; | ||
120 | of_node_clear_flag(np, OF_DETACHED); | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * of_attach_node() - Plug a device node into the tree and global list. | ||
125 | */ | ||
126 | int of_attach_node(struct device_node *np) | ||
127 | { | ||
128 | unsigned long flags; | ||
129 | |||
130 | mutex_lock(&of_mutex); | ||
131 | raw_spin_lock_irqsave(&devtree_lock, flags); | ||
132 | __of_attach_node(np); | ||
133 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | ||
134 | |||
135 | __of_attach_node_sysfs(np); | ||
136 | mutex_unlock(&of_mutex); | ||
137 | |||
138 | of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | void __of_detach_node(struct device_node *np) | ||
144 | { | ||
145 | struct device_node *parent; | ||
146 | |||
147 | if (WARN_ON(of_node_check_flag(np, OF_DETACHED))) | ||
148 | return; | ||
149 | |||
150 | parent = np->parent; | ||
151 | if (WARN_ON(!parent)) | ||
152 | return; | ||
153 | |||
154 | if (of_allnodes == np) | ||
155 | of_allnodes = np->allnext; | ||
156 | else { | ||
157 | struct device_node *prev; | ||
158 | for (prev = of_allnodes; | ||
159 | prev->allnext != np; | ||
160 | prev = prev->allnext) | ||
161 | ; | ||
162 | prev->allnext = np->allnext; | ||
163 | } | ||
164 | |||
165 | if (parent->child == np) | ||
166 | parent->child = np->sibling; | ||
167 | else { | ||
168 | struct device_node *prevsib; | ||
169 | for (prevsib = np->parent->child; | ||
170 | prevsib->sibling != np; | ||
171 | prevsib = prevsib->sibling) | ||
172 | ; | ||
173 | prevsib->sibling = np->sibling; | ||
174 | } | ||
175 | |||
176 | of_node_set_flag(np, OF_DETACHED); | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * of_detach_node() - "Unplug" a node from the device tree. | ||
181 | * | ||
182 | * The caller must hold a reference to the node. The memory associated with | ||
183 | * the node is not freed until its refcount goes to zero. | ||
184 | */ | ||
185 | int of_detach_node(struct device_node *np) | ||
186 | { | ||
187 | unsigned long flags; | ||
188 | int rc = 0; | ||
189 | |||
190 | mutex_lock(&of_mutex); | ||
191 | raw_spin_lock_irqsave(&devtree_lock, flags); | ||
192 | __of_detach_node(np); | ||
193 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | ||
194 | |||
195 | __of_detach_node_sysfs(np); | ||
196 | mutex_unlock(&of_mutex); | ||
197 | |||
198 | of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); | ||
199 | |||
200 | return rc; | ||
201 | } | ||
202 | |||
203 | /** | ||
204 | * of_node_release() - release a dynamically allocated node | ||
205 | * @kref: kref element of the node to be released | ||
206 | * | ||
207 | * In of_node_put() this function is passed to kref_put() as the destructor. | ||
208 | */ | ||
209 | void of_node_release(struct kobject *kobj) | ||
210 | { | ||
211 | struct device_node *node = kobj_to_device_node(kobj); | ||
212 | struct property *prop = node->properties; | ||
213 | |||
214 | /* We should never be releasing nodes that haven't been detached. */ | ||
215 | if (!of_node_check_flag(node, OF_DETACHED)) { | ||
216 | pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name); | ||
217 | dump_stack(); | ||
218 | return; | ||
219 | } | ||
220 | |||
221 | if (!of_node_check_flag(node, OF_DYNAMIC)) | ||
222 | return; | ||
223 | |||
224 | while (prop) { | ||
225 | struct property *next = prop->next; | ||
226 | kfree(prop->name); | ||
227 | kfree(prop->value); | ||
228 | kfree(prop); | ||
229 | prop = next; | ||
230 | |||
231 | if (!prop) { | ||
232 | prop = node->deadprops; | ||
233 | node->deadprops = NULL; | ||
234 | } | ||
235 | } | ||
236 | kfree(node->full_name); | ||
237 | kfree(node->data); | ||
238 | kfree(node); | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * __of_prop_dup - Copy a property dynamically. | ||
243 | * @prop: Property to copy | ||
244 | * @allocflags: Allocation flags (typically pass GFP_KERNEL) | ||
245 | * | ||
246 | * Copy a property by dynamically allocating the memory of both the | ||
247 | * property stucture and the property name & contents. The property's | ||
248 | * flags have the OF_DYNAMIC bit set so that we can differentiate between | ||
249 | * dynamically allocated properties and not. | ||
250 | * Returns the newly allocated property or NULL on out of memory error. | ||
251 | */ | ||
252 | struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags) | ||
253 | { | ||
254 | struct property *new; | ||
255 | |||
256 | new = kzalloc(sizeof(*new), allocflags); | ||
257 | if (!new) | ||
258 | return NULL; | ||
259 | |||
260 | /* | ||
261 | * NOTE: There is no check for zero length value. | ||
262 | * In case of a boolean property, this will allocate a value | ||
263 | * of zero bytes. We do this to work around the use | ||
264 | * of of_get_property() calls on boolean values. | ||
265 | */ | ||
266 | new->name = kstrdup(prop->name, allocflags); | ||
267 | new->value = kmemdup(prop->value, prop->length, allocflags); | ||
268 | new->length = prop->length; | ||
269 | if (!new->name || !new->value) | ||
270 | goto err_free; | ||
271 | |||
272 | /* mark the property as dynamic */ | ||
273 | of_property_set_flag(new, OF_DYNAMIC); | ||
274 | |||
275 | return new; | ||
276 | |||
277 | err_free: | ||
278 | kfree(new->name); | ||
279 | kfree(new->value); | ||
280 | kfree(new); | ||
281 | return NULL; | ||
282 | } | ||
283 | |||
284 | /** | ||
285 | * __of_node_alloc() - Create an empty device node dynamically. | ||
286 | * @full_name: Full name of the new device node | ||
287 | * @allocflags: Allocation flags (typically pass GFP_KERNEL) | ||
288 | * | ||
289 | * Create an empty device tree node, suitable for further modification. | ||
290 | * The node data are dynamically allocated and all the node flags | ||
291 | * have the OF_DYNAMIC & OF_DETACHED bits set. | ||
292 | * Returns the newly allocated node or NULL on out of memory error. | ||
293 | */ | ||
294 | struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags) | ||
295 | { | ||
296 | struct device_node *node; | ||
297 | |||
298 | node = kzalloc(sizeof(*node), allocflags); | ||
299 | if (!node) | ||
300 | return NULL; | ||
301 | |||
302 | node->full_name = kstrdup(full_name, allocflags); | ||
303 | of_node_set_flag(node, OF_DYNAMIC); | ||
304 | of_node_set_flag(node, OF_DETACHED); | ||
305 | if (!node->full_name) | ||
306 | goto err_free; | ||
307 | |||
308 | of_node_init(node); | ||
309 | |||
310 | return node; | ||
311 | |||
312 | err_free: | ||
313 | kfree(node->full_name); | ||
314 | kfree(node); | ||
315 | return NULL; | ||
316 | } | ||
317 | |||
318 | static void __of_changeset_entry_destroy(struct of_changeset_entry *ce) | ||
319 | { | ||
320 | of_node_put(ce->np); | ||
321 | list_del(&ce->node); | ||
322 | kfree(ce); | ||
323 | } | ||
324 | |||
325 | #ifdef DEBUG | ||
326 | static void __of_changeset_entry_dump(struct of_changeset_entry *ce) | ||
327 | { | ||
328 | switch (ce->action) { | ||
329 | case OF_RECONFIG_ADD_PROPERTY: | ||
330 | pr_debug("%p: %s %s/%s\n", | ||
331 | ce, "ADD_PROPERTY ", ce->np->full_name, | ||
332 | ce->prop->name); | ||
333 | break; | ||
334 | case OF_RECONFIG_REMOVE_PROPERTY: | ||
335 | pr_debug("%p: %s %s/%s\n", | ||
336 | ce, "REMOVE_PROPERTY", ce->np->full_name, | ||
337 | ce->prop->name); | ||
338 | break; | ||
339 | case OF_RECONFIG_UPDATE_PROPERTY: | ||
340 | pr_debug("%p: %s %s/%s\n", | ||
341 | ce, "UPDATE_PROPERTY", ce->np->full_name, | ||
342 | ce->prop->name); | ||
343 | break; | ||
344 | case OF_RECONFIG_ATTACH_NODE: | ||
345 | pr_debug("%p: %s %s\n", | ||
346 | ce, "ATTACH_NODE ", ce->np->full_name); | ||
347 | break; | ||
348 | case OF_RECONFIG_DETACH_NODE: | ||
349 | pr_debug("%p: %s %s\n", | ||
350 | ce, "DETACH_NODE ", ce->np->full_name); | ||
351 | break; | ||
352 | } | ||
353 | } | ||
354 | #else | ||
355 | static inline void __of_changeset_entry_dump(struct of_changeset_entry *ce) | ||
356 | { | ||
357 | /* empty */ | ||
358 | } | ||
359 | #endif | ||
360 | |||
361 | static void __of_changeset_entry_invert(struct of_changeset_entry *ce, | ||
362 | struct of_changeset_entry *rce) | ||
363 | { | ||
364 | memcpy(rce, ce, sizeof(*rce)); | ||
365 | |||
366 | switch (ce->action) { | ||
367 | case OF_RECONFIG_ATTACH_NODE: | ||
368 | rce->action = OF_RECONFIG_DETACH_NODE; | ||
369 | break; | ||
370 | case OF_RECONFIG_DETACH_NODE: | ||
371 | rce->action = OF_RECONFIG_ATTACH_NODE; | ||
372 | break; | ||
373 | case OF_RECONFIG_ADD_PROPERTY: | ||
374 | rce->action = OF_RECONFIG_REMOVE_PROPERTY; | ||
375 | break; | ||
376 | case OF_RECONFIG_REMOVE_PROPERTY: | ||
377 | rce->action = OF_RECONFIG_ADD_PROPERTY; | ||
378 | break; | ||
379 | case OF_RECONFIG_UPDATE_PROPERTY: | ||
380 | rce->old_prop = ce->prop; | ||
381 | rce->prop = ce->old_prop; | ||
382 | break; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert) | ||
387 | { | ||
388 | struct of_changeset_entry ce_inverted; | ||
389 | int ret; | ||
390 | |||
391 | if (revert) { | ||
392 | __of_changeset_entry_invert(ce, &ce_inverted); | ||
393 | ce = &ce_inverted; | ||
394 | } | ||
395 | |||
396 | switch (ce->action) { | ||
397 | case OF_RECONFIG_ATTACH_NODE: | ||
398 | case OF_RECONFIG_DETACH_NODE: | ||
399 | ret = of_reconfig_notify(ce->action, ce->np); | ||
400 | break; | ||
401 | case OF_RECONFIG_ADD_PROPERTY: | ||
402 | case OF_RECONFIG_REMOVE_PROPERTY: | ||
403 | case OF_RECONFIG_UPDATE_PROPERTY: | ||
404 | ret = of_property_notify(ce->action, ce->np, ce->prop, ce->old_prop); | ||
405 | break; | ||
406 | default: | ||
407 | pr_err("%s: invalid devicetree changeset action: %i\n", __func__, | ||
408 | (int)ce->action); | ||
409 | return; | ||
410 | } | ||
411 | |||
412 | if (ret) | ||
413 | pr_err("%s: notifier error @%s\n", __func__, ce->np->full_name); | ||
414 | } | ||
415 | |||
416 | static int __of_changeset_entry_apply(struct of_changeset_entry *ce) | ||
417 | { | ||
418 | struct property *old_prop, **propp; | ||
419 | unsigned long flags; | ||
420 | int ret = 0; | ||
421 | |||
422 | __of_changeset_entry_dump(ce); | ||
423 | |||
424 | raw_spin_lock_irqsave(&devtree_lock, flags); | ||
425 | switch (ce->action) { | ||
426 | case OF_RECONFIG_ATTACH_NODE: | ||
427 | __of_attach_node(ce->np); | ||
428 | break; | ||
429 | case OF_RECONFIG_DETACH_NODE: | ||
430 | __of_detach_node(ce->np); | ||
431 | break; | ||
432 | case OF_RECONFIG_ADD_PROPERTY: | ||
433 | /* If the property is in deadprops then it must be removed */ | ||
434 | for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) { | ||
435 | if (*propp == ce->prop) { | ||
436 | *propp = ce->prop->next; | ||
437 | ce->prop->next = NULL; | ||
438 | break; | ||
439 | } | ||
440 | } | ||
441 | |||
442 | ret = __of_add_property(ce->np, ce->prop); | ||
443 | if (ret) { | ||
444 | pr_err("%s: add_property failed @%s/%s\n", | ||
445 | __func__, ce->np->full_name, | ||
446 | ce->prop->name); | ||
447 | break; | ||
448 | } | ||
449 | break; | ||
450 | case OF_RECONFIG_REMOVE_PROPERTY: | ||
451 | ret = __of_remove_property(ce->np, ce->prop); | ||
452 | if (ret) { | ||
453 | pr_err("%s: remove_property failed @%s/%s\n", | ||
454 | __func__, ce->np->full_name, | ||
455 | ce->prop->name); | ||
456 | break; | ||
457 | } | ||
458 | break; | ||
459 | |||
460 | case OF_RECONFIG_UPDATE_PROPERTY: | ||
461 | /* If the property is in deadprops then it must be removed */ | ||
462 | for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) { | ||
463 | if (*propp == ce->prop) { | ||
464 | *propp = ce->prop->next; | ||
465 | ce->prop->next = NULL; | ||
466 | break; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | ret = __of_update_property(ce->np, ce->prop, &old_prop); | ||
471 | if (ret) { | ||
472 | pr_err("%s: update_property failed @%s/%s\n", | ||
473 | __func__, ce->np->full_name, | ||
474 | ce->prop->name); | ||
475 | break; | ||
476 | } | ||
477 | break; | ||
478 | default: | ||
479 | ret = -EINVAL; | ||
480 | } | ||
481 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | ||
482 | |||
483 | if (ret) | ||
484 | return ret; | ||
485 | |||
486 | switch (ce->action) { | ||
487 | case OF_RECONFIG_ATTACH_NODE: | ||
488 | __of_attach_node_sysfs(ce->np); | ||
489 | break; | ||
490 | case OF_RECONFIG_DETACH_NODE: | ||
491 | __of_detach_node_sysfs(ce->np); | ||
492 | break; | ||
493 | case OF_RECONFIG_ADD_PROPERTY: | ||
494 | /* ignore duplicate names */ | ||
495 | __of_add_property_sysfs(ce->np, ce->prop); | ||
496 | break; | ||
497 | case OF_RECONFIG_REMOVE_PROPERTY: | ||
498 | __of_remove_property_sysfs(ce->np, ce->prop); | ||
499 | break; | ||
500 | case OF_RECONFIG_UPDATE_PROPERTY: | ||
501 | __of_update_property_sysfs(ce->np, ce->prop, ce->old_prop); | ||
502 | break; | ||
503 | } | ||
504 | |||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | static inline int __of_changeset_entry_revert(struct of_changeset_entry *ce) | ||
509 | { | ||
510 | struct of_changeset_entry ce_inverted; | ||
511 | |||
512 | __of_changeset_entry_invert(ce, &ce_inverted); | ||
513 | return __of_changeset_entry_apply(&ce_inverted); | ||
514 | } | ||
515 | |||
516 | /** | ||
517 | * of_changeset_init - Initialize a changeset for use | ||
518 | * | ||
519 | * @ocs: changeset pointer | ||
520 | * | ||
521 | * Initialize a changeset structure | ||
522 | */ | ||
523 | void of_changeset_init(struct of_changeset *ocs) | ||
524 | { | ||
525 | memset(ocs, 0, sizeof(*ocs)); | ||
526 | INIT_LIST_HEAD(&ocs->entries); | ||
527 | } | ||
528 | |||
529 | /** | ||
530 | * of_changeset_destroy - Destroy a changeset | ||
531 | * | ||
532 | * @ocs: changeset pointer | ||
533 | * | ||
534 | * Destroys a changeset. Note that if a changeset is applied, | ||
535 | * its changes to the tree cannot be reverted. | ||
536 | */ | ||
537 | void of_changeset_destroy(struct of_changeset *ocs) | ||
538 | { | ||
539 | struct of_changeset_entry *ce, *cen; | ||
540 | |||
541 | list_for_each_entry_safe_reverse(ce, cen, &ocs->entries, node) | ||
542 | __of_changeset_entry_destroy(ce); | ||
543 | } | ||
544 | |||
545 | /** | ||
546 | * of_changeset_apply - Applies a changeset | ||
547 | * | ||
548 | * @ocs: changeset pointer | ||
549 | * | ||
550 | * Applies a changeset to the live tree. | ||
551 | * Any side-effects of live tree state changes are applied here on | ||
552 | * sucess, like creation/destruction of devices and side-effects | ||
553 | * like creation of sysfs properties and directories. | ||
554 | * Returns 0 on success, a negative error value in case of an error. | ||
555 | * On error the partially applied effects are reverted. | ||
556 | */ | ||
557 | int of_changeset_apply(struct of_changeset *ocs) | ||
558 | { | ||
559 | struct of_changeset_entry *ce; | ||
560 | int ret; | ||
561 | |||
562 | /* perform the rest of the work */ | ||
563 | pr_debug("of_changeset: applying...\n"); | ||
564 | list_for_each_entry(ce, &ocs->entries, node) { | ||
565 | ret = __of_changeset_entry_apply(ce); | ||
566 | if (ret) { | ||
567 | pr_err("%s: Error applying changeset (%d)\n", __func__, ret); | ||
568 | list_for_each_entry_continue_reverse(ce, &ocs->entries, node) | ||
569 | __of_changeset_entry_revert(ce); | ||
570 | return ret; | ||
571 | } | ||
572 | } | ||
573 | pr_debug("of_changeset: applied, emitting notifiers.\n"); | ||
574 | |||
575 | /* drop the global lock while emitting notifiers */ | ||
576 | mutex_unlock(&of_mutex); | ||
577 | list_for_each_entry(ce, &ocs->entries, node) | ||
578 | __of_changeset_entry_notify(ce, 0); | ||
579 | mutex_lock(&of_mutex); | ||
580 | pr_debug("of_changeset: notifiers sent.\n"); | ||
581 | |||
582 | return 0; | ||
583 | } | ||
584 | |||
585 | /** | ||
586 | * of_changeset_revert - Reverts an applied changeset | ||
587 | * | ||
588 | * @ocs: changeset pointer | ||
589 | * | ||
590 | * Reverts a changeset returning the state of the tree to what it | ||
591 | * was before the application. | ||
592 | * Any side-effects like creation/destruction of devices and | ||
593 | * removal of sysfs properties and directories are applied. | ||
594 | * Returns 0 on success, a negative error value in case of an error. | ||
595 | */ | ||
596 | int of_changeset_revert(struct of_changeset *ocs) | ||
597 | { | ||
598 | struct of_changeset_entry *ce; | ||
599 | int ret; | ||
600 | |||
601 | pr_debug("of_changeset: reverting...\n"); | ||
602 | list_for_each_entry_reverse(ce, &ocs->entries, node) { | ||
603 | ret = __of_changeset_entry_revert(ce); | ||
604 | if (ret) { | ||
605 | pr_err("%s: Error reverting changeset (%d)\n", __func__, ret); | ||
606 | list_for_each_entry_continue(ce, &ocs->entries, node) | ||
607 | __of_changeset_entry_apply(ce); | ||
608 | return ret; | ||
609 | } | ||
610 | } | ||
611 | pr_debug("of_changeset: reverted, emitting notifiers.\n"); | ||
612 | |||
613 | /* drop the global lock while emitting notifiers */ | ||
614 | mutex_unlock(&of_mutex); | ||
615 | list_for_each_entry_reverse(ce, &ocs->entries, node) | ||
616 | __of_changeset_entry_notify(ce, 1); | ||
617 | mutex_lock(&of_mutex); | ||
618 | pr_debug("of_changeset: notifiers sent.\n"); | ||
619 | |||
620 | return 0; | ||
621 | } | ||
622 | |||
623 | /** | ||
624 | * of_changeset_action - Perform a changeset action | ||
625 | * | ||
626 | * @ocs: changeset pointer | ||
627 | * @action: action to perform | ||
628 | * @np: Pointer to device node | ||
629 | * @prop: Pointer to property | ||
630 | * | ||
631 | * On action being one of: | ||
632 | * + OF_RECONFIG_ATTACH_NODE | ||
633 | * + OF_RECONFIG_DETACH_NODE, | ||
634 | * + OF_RECONFIG_ADD_PROPERTY | ||
635 | * + OF_RECONFIG_REMOVE_PROPERTY, | ||
636 | * + OF_RECONFIG_UPDATE_PROPERTY | ||
637 | * Returns 0 on success, a negative error value in case of an error. | ||
638 | */ | ||
639 | int of_changeset_action(struct of_changeset *ocs, unsigned long action, | ||
640 | struct device_node *np, struct property *prop) | ||
641 | { | ||
642 | struct of_changeset_entry *ce; | ||
643 | |||
644 | ce = kzalloc(sizeof(*ce), GFP_KERNEL); | ||
645 | if (!ce) { | ||
646 | pr_err("%s: Failed to allocate\n", __func__); | ||
647 | return -ENOMEM; | ||
648 | } | ||
649 | /* get a reference to the node */ | ||
650 | ce->action = action; | ||
651 | ce->np = of_node_get(np); | ||
652 | ce->prop = prop; | ||
653 | |||
654 | if (action == OF_RECONFIG_UPDATE_PROPERTY && prop) | ||
655 | ce->old_prop = of_find_property(np, prop->name, NULL); | ||
656 | |||
657 | /* add it to the list */ | ||
658 | list_add_tail(&ce->node, &ocs->entries); | ||
659 | return 0; | ||
660 | } | ||
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index ff350c8fa7ac..858e0a5d9a11 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h | |||
@@ -31,6 +31,63 @@ struct alias_prop { | |||
31 | char stem[0]; | 31 | char stem[0]; |
32 | }; | 32 | }; |
33 | 33 | ||
34 | extern struct mutex of_aliases_mutex; | 34 | extern struct mutex of_mutex; |
35 | extern struct list_head aliases_lookup; | 35 | extern struct list_head aliases_lookup; |
36 | extern struct kset *of_kset; | ||
37 | |||
38 | |||
39 | static inline struct device_node *kobj_to_device_node(struct kobject *kobj) | ||
40 | { | ||
41 | return container_of(kobj, struct device_node, kobj); | ||
42 | } | ||
43 | |||
44 | #if defined(CONFIG_OF_DYNAMIC) | ||
45 | extern int of_property_notify(int action, struct device_node *np, | ||
46 | struct property *prop, struct property *old_prop); | ||
47 | extern void of_node_release(struct kobject *kobj); | ||
48 | #else /* CONFIG_OF_DYNAMIC */ | ||
49 | static inline int of_property_notify(int action, struct device_node *np, | ||
50 | struct property *prop, struct property *old_prop) | ||
51 | { | ||
52 | return 0; | ||
53 | } | ||
54 | #endif /* CONFIG_OF_DYNAMIC */ | ||
55 | |||
56 | /** | ||
57 | * General utilities for working with live trees. | ||
58 | * | ||
59 | * All functions with two leading underscores operate | ||
60 | * without taking node references, so you either have to | ||
61 | * own the devtree lock or work on detached trees only. | ||
62 | */ | ||
63 | struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags); | ||
64 | struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags); | ||
65 | |||
66 | extern const void *__of_get_property(const struct device_node *np, | ||
67 | const char *name, int *lenp); | ||
68 | extern int __of_add_property(struct device_node *np, struct property *prop); | ||
69 | extern int __of_add_property_sysfs(struct device_node *np, | ||
70 | struct property *prop); | ||
71 | extern int __of_remove_property(struct device_node *np, struct property *prop); | ||
72 | extern void __of_remove_property_sysfs(struct device_node *np, | ||
73 | struct property *prop); | ||
74 | extern int __of_update_property(struct device_node *np, | ||
75 | struct property *newprop, struct property **oldprop); | ||
76 | extern void __of_update_property_sysfs(struct device_node *np, | ||
77 | struct property *newprop, struct property *oldprop); | ||
78 | |||
79 | extern void __of_attach_node(struct device_node *np); | ||
80 | extern int __of_attach_node_sysfs(struct device_node *np); | ||
81 | extern void __of_detach_node(struct device_node *np); | ||
82 | extern void __of_detach_node_sysfs(struct device_node *np); | ||
83 | |||
84 | /* iterators for transactions, used for overlays */ | ||
85 | /* forward iterator */ | ||
86 | #define for_each_transaction_entry(_oft, _te) \ | ||
87 | list_for_each_entry(_te, &(_oft)->te_list, node) | ||
88 | |||
89 | /* reverse iterator */ | ||
90 | #define for_each_transaction_entry_reverse(_oft, _te) \ | ||
91 | list_for_each_entry_reverse(_te, &(_oft)->te_list, node) | ||
92 | |||
36 | #endif /* _LINUX_OF_PRIVATE_H */ | 93 | #endif /* _LINUX_OF_PRIVATE_H */ |
diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 500436f9be7f..0197725e033a 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c | |||
@@ -422,6 +422,7 @@ static int of_platform_bus_create(struct device_node *bus, | |||
422 | break; | 422 | break; |
423 | } | 423 | } |
424 | } | 424 | } |
425 | of_node_set_flag(bus, OF_POPULATED_BUS); | ||
425 | return rc; | 426 | return rc; |
426 | } | 427 | } |
427 | 428 | ||
@@ -508,19 +509,13 @@ EXPORT_SYMBOL_GPL(of_platform_populate); | |||
508 | 509 | ||
509 | static int of_platform_device_destroy(struct device *dev, void *data) | 510 | static int of_platform_device_destroy(struct device *dev, void *data) |
510 | { | 511 | { |
511 | bool *children_left = data; | ||
512 | |||
513 | /* Do not touch devices not populated from the device tree */ | 512 | /* Do not touch devices not populated from the device tree */ |
514 | if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) { | 513 | if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) |
515 | *children_left = true; | ||
516 | return 0; | 514 | return 0; |
517 | } | ||
518 | 515 | ||
519 | /* Recurse, but don't touch this device if it has any children left */ | 516 | /* Recurse for any nodes that were treated as busses */ |
520 | if (of_platform_depopulate(dev) != 0) { | 517 | if (of_node_check_flag(dev->of_node, OF_POPULATED_BUS)) |
521 | *children_left = true; | 518 | device_for_each_child(dev, NULL, of_platform_device_destroy); |
522 | return 0; | ||
523 | } | ||
524 | 519 | ||
525 | if (dev->bus == &platform_bus_type) | 520 | if (dev->bus == &platform_bus_type) |
526 | platform_device_unregister(to_platform_device(dev)); | 521 | platform_device_unregister(to_platform_device(dev)); |
@@ -528,19 +523,15 @@ static int of_platform_device_destroy(struct device *dev, void *data) | |||
528 | else if (dev->bus == &amba_bustype) | 523 | else if (dev->bus == &amba_bustype) |
529 | amba_device_unregister(to_amba_device(dev)); | 524 | amba_device_unregister(to_amba_device(dev)); |
530 | #endif | 525 | #endif |
531 | else { | ||
532 | *children_left = true; | ||
533 | return 0; | ||
534 | } | ||
535 | 526 | ||
536 | of_node_clear_flag(dev->of_node, OF_POPULATED); | 527 | of_node_clear_flag(dev->of_node, OF_POPULATED); |
537 | 528 | of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); | |
538 | return 0; | 529 | return 0; |
539 | } | 530 | } |
540 | 531 | ||
541 | /** | 532 | /** |
542 | * of_platform_depopulate() - Remove devices populated from device tree | 533 | * of_platform_depopulate() - Remove devices populated from device tree |
543 | * @parent: device which childred will be removed | 534 | * @parent: device which children will be removed |
544 | * | 535 | * |
545 | * Complementary to of_platform_populate(), this function removes children | 536 | * Complementary to of_platform_populate(), this function removes children |
546 | * of the given device (and, recurrently, their children) that have been | 537 | * of the given device (and, recurrently, their children) that have been |
@@ -550,14 +541,9 @@ static int of_platform_device_destroy(struct device *dev, void *data) | |||
550 | * Returns 0 when all children devices have been removed or | 541 | * Returns 0 when all children devices have been removed or |
551 | * -EBUSY when some children remained. | 542 | * -EBUSY when some children remained. |
552 | */ | 543 | */ |
553 | int of_platform_depopulate(struct device *parent) | 544 | void of_platform_depopulate(struct device *parent) |
554 | { | 545 | { |
555 | bool children_left = false; | 546 | device_for_each_child(parent, NULL, of_platform_device_destroy); |
556 | |||
557 | device_for_each_child(parent, &children_left, | ||
558 | of_platform_device_destroy); | ||
559 | |||
560 | return children_left ? -EBUSY : 0; | ||
561 | } | 547 | } |
562 | EXPORT_SYMBOL_GPL(of_platform_depopulate); | 548 | EXPORT_SYMBOL_GPL(of_platform_depopulate); |
563 | 549 | ||
diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index df599db1554c..d41002667833 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | #include <linux/device.h> | 18 | #include <linux/device.h> |
19 | 19 | ||
20 | #include "of_private.h" | ||
21 | |||
20 | static struct selftest_results { | 22 | static struct selftest_results { |
21 | int passed; | 23 | int passed; |
22 | int failed; | 24 | int failed; |
@@ -271,6 +273,81 @@ static void __init of_selftest_property_match_string(void) | |||
271 | selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc); | 273 | selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc); |
272 | } | 274 | } |
273 | 275 | ||
276 | #define propcmp(p1, p2) (((p1)->length == (p2)->length) && \ | ||
277 | (p1)->value && (p2)->value && \ | ||
278 | !memcmp((p1)->value, (p2)->value, (p1)->length) && \ | ||
279 | !strcmp((p1)->name, (p2)->name)) | ||
280 | static void __init of_selftest_property_copy(void) | ||
281 | { | ||
282 | #ifdef CONFIG_OF_DYNAMIC | ||
283 | struct property p1 = { .name = "p1", .length = 0, .value = "" }; | ||
284 | struct property p2 = { .name = "p2", .length = 5, .value = "abcd" }; | ||
285 | struct property *new; | ||
286 | |||
287 | new = __of_prop_dup(&p1, GFP_KERNEL); | ||
288 | selftest(new && propcmp(&p1, new), "empty property didn't copy correctly\n"); | ||
289 | kfree(new->value); | ||
290 | kfree(new->name); | ||
291 | kfree(new); | ||
292 | |||
293 | new = __of_prop_dup(&p2, GFP_KERNEL); | ||
294 | selftest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n"); | ||
295 | kfree(new->value); | ||
296 | kfree(new->name); | ||
297 | kfree(new); | ||
298 | #endif | ||
299 | } | ||
300 | |||
301 | static void __init of_selftest_changeset(void) | ||
302 | { | ||
303 | #ifdef CONFIG_OF_DYNAMIC | ||
304 | struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" }; | ||
305 | struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" }; | ||
306 | struct property *ppremove; | ||
307 | struct device_node *n1, *n2, *n21, *nremove, *parent; | ||
308 | struct of_changeset chgset; | ||
309 | |||
310 | of_changeset_init(&chgset); | ||
311 | n1 = __of_node_alloc("/testcase-data/changeset/n1", GFP_KERNEL); | ||
312 | selftest(n1, "testcase setup failure\n"); | ||
313 | n2 = __of_node_alloc("/testcase-data/changeset/n2", GFP_KERNEL); | ||
314 | selftest(n2, "testcase setup failure\n"); | ||
315 | n21 = __of_node_alloc("/testcase-data/changeset/n2/n21", GFP_KERNEL); | ||
316 | selftest(n21, "testcase setup failure %p\n", n21); | ||
317 | nremove = of_find_node_by_path("/testcase-data/changeset/node-remove"); | ||
318 | selftest(nremove, "testcase setup failure\n"); | ||
319 | ppadd = __of_prop_dup(&padd, GFP_KERNEL); | ||
320 | selftest(ppadd, "testcase setup failure\n"); | ||
321 | ppupdate = __of_prop_dup(&pupdate, GFP_KERNEL); | ||
322 | selftest(ppupdate, "testcase setup failure\n"); | ||
323 | parent = nremove->parent; | ||
324 | n1->parent = parent; | ||
325 | n2->parent = parent; | ||
326 | n21->parent = n2; | ||
327 | n2->child = n21; | ||
328 | ppremove = of_find_property(parent, "prop-remove", NULL); | ||
329 | selftest(ppremove, "failed to find removal prop"); | ||
330 | |||
331 | of_changeset_init(&chgset); | ||
332 | selftest(!of_changeset_attach_node(&chgset, n1), "fail attach n1\n"); | ||
333 | selftest(!of_changeset_attach_node(&chgset, n2), "fail attach n2\n"); | ||
334 | selftest(!of_changeset_detach_node(&chgset, nremove), "fail remove node\n"); | ||
335 | selftest(!of_changeset_attach_node(&chgset, n21), "fail attach n21\n"); | ||
336 | selftest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n"); | ||
337 | selftest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n"); | ||
338 | selftest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n"); | ||
339 | mutex_lock(&of_mutex); | ||
340 | selftest(!of_changeset_apply(&chgset), "apply failed\n"); | ||
341 | mutex_unlock(&of_mutex); | ||
342 | |||
343 | mutex_lock(&of_mutex); | ||
344 | selftest(!of_changeset_revert(&chgset), "revert failed\n"); | ||
345 | mutex_unlock(&of_mutex); | ||
346 | |||
347 | of_changeset_destroy(&chgset); | ||
348 | #endif | ||
349 | } | ||
350 | |||
274 | static void __init of_selftest_parse_interrupts(void) | 351 | static void __init of_selftest_parse_interrupts(void) |
275 | { | 352 | { |
276 | struct device_node *np; | 353 | struct device_node *np; |
@@ -685,6 +762,8 @@ static int __init of_selftest(void) | |||
685 | of_selftest_dynamic(); | 762 | of_selftest_dynamic(); |
686 | of_selftest_parse_phandle_with_args(); | 763 | of_selftest_parse_phandle_with_args(); |
687 | of_selftest_property_match_string(); | 764 | of_selftest_property_match_string(); |
765 | of_selftest_property_copy(); | ||
766 | of_selftest_changeset(); | ||
688 | of_selftest_parse_interrupts(); | 767 | of_selftest_parse_interrupts(); |
689 | of_selftest_parse_interrupts_extended(); | 768 | of_selftest_parse_interrupts_extended(); |
690 | of_selftest_match_node(); | 769 | of_selftest_match_node(); |
diff --git a/drivers/of/testcase-data/testcases.dts b/drivers/of/testcase-data/testcases.dts index 8e7568ee3175..219ef9324e9c 100644 --- a/drivers/of/testcase-data/testcases.dts +++ b/drivers/of/testcase-data/testcases.dts | |||
@@ -1,4 +1,14 @@ | |||
1 | /dts-v1/; | 1 | /dts-v1/; |
2 | / { | ||
3 | testcase-data { | ||
4 | changeset { | ||
5 | prop-update = "hello"; | ||
6 | prop-remove = "world"; | ||
7 | node-remove { | ||
8 | }; | ||
9 | }; | ||
10 | }; | ||
11 | }; | ||
2 | #include "tests-phandle.dtsi" | 12 | #include "tests-phandle.dtsi" |
3 | #include "tests-interrupts.dtsi" | 13 | #include "tests-interrupts.dtsi" |
4 | #include "tests-match.dtsi" | 14 | #include "tests-match.dtsi" |