📄 prom.c
字号:
{ struct device_node *np; read_lock(&devtree_lock); np = from ? from->allnext : allnodes; for (; np != 0; np = np->allnext) { if (type != NULL && !(np->type != 0 && strcasecmp(np->type, type) == 0)) continue; if (device_is_compatible(np, compatible) && of_node_get(np)) break; } if (from) of_node_put(from); read_unlock(&devtree_lock); return np;}EXPORT_SYMBOL(of_find_compatible_node);/** * of_find_node_by_path - Find a node matching a full OF path * @path: The full path to match * * Returns a node pointer with refcount incremented, use * of_node_put() on it when done. */struct device_node *of_find_node_by_path(const char *path){ struct device_node *np = allnodes; read_lock(&devtree_lock); for (; np != 0; np = np->allnext) { if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0 && of_node_get(np)) break; } read_unlock(&devtree_lock); return np;}EXPORT_SYMBOL(of_find_node_by_path);/** * of_find_node_by_phandle - Find a node given a phandle * @handle: phandle of the node to find * * Returns a node pointer with refcount incremented, use * of_node_put() on it when done. */struct device_node *of_find_node_by_phandle(phandle handle){ struct device_node *np; read_lock(&devtree_lock); for (np = allnodes; np != 0; np = np->allnext) if (np->linux_phandle == handle) break; if (np) of_node_get(np); read_unlock(&devtree_lock); return np;}EXPORT_SYMBOL(of_find_node_by_phandle);/** * of_find_all_nodes - Get next node in global list * @prev: Previous node or NULL to start iteration * of_node_put() will be called on it * * Returns a node pointer with refcount incremented, use * of_node_put() on it when done. */struct device_node *of_find_all_nodes(struct device_node *prev){ struct device_node *np; read_lock(&devtree_lock); np = prev ? prev->allnext : allnodes; for (; np != 0; np = np->allnext) if (of_node_get(np)) break; if (prev) of_node_put(prev); read_unlock(&devtree_lock); return np;}EXPORT_SYMBOL(of_find_all_nodes);/** * of_get_parent - Get a node's parent if any * @node: Node to get parent * * Returns a node pointer with refcount incremented, use * of_node_put() on it when done. */struct device_node *of_get_parent(const struct device_node *node){ struct device_node *np; if (!node) return NULL; read_lock(&devtree_lock); np = of_node_get(node->parent); read_unlock(&devtree_lock); return np;}EXPORT_SYMBOL(of_get_parent);/** * of_get_next_child - Iterate a node childs * @node: parent node * @prev: previous child of the parent node, or NULL to get first * * Returns a node pointer with refcount incremented, use * of_node_put() on it when done. */struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev){ struct device_node *next; read_lock(&devtree_lock); next = prev ? prev->sibling : node->child; for (; next != 0; next = next->sibling) if (of_node_get(next)) break; if (prev) of_node_put(prev); read_unlock(&devtree_lock); return next;}EXPORT_SYMBOL(of_get_next_child);/** * of_node_get - Increment refcount of a node * @node: Node to inc refcount, NULL is supported to * simplify writing of callers * * Returns node. */struct device_node *of_node_get(struct device_node *node){ if (node) kref_get(&node->kref); return node;}EXPORT_SYMBOL(of_node_get);static inline struct device_node * kref_to_device_node(struct kref *kref){ return container_of(kref, struct device_node, kref);}/** * of_node_release - release a dynamically allocated node * @kref: kref element of the node to be released * * In of_node_put() this function is passed to kref_put() * as the destructor. */static void of_node_release(struct kref *kref){ struct device_node *node = kref_to_device_node(kref); struct property *prop = node->properties; if (!OF_IS_DYNAMIC(node)) return; while (prop) { struct property *next = prop->next; kfree(prop->name); kfree(prop->value); kfree(prop); prop = next; } kfree(node->intrs); kfree(node->addrs); kfree(node->full_name); kfree(node->data); kfree(node);}/** * of_node_put - Decrement refcount of a node * @node: Node to dec refcount, NULL is supported to * simplify writing of callers * */void of_node_put(struct device_node *node){ if (node) kref_put(&node->kref, of_node_release);}EXPORT_SYMBOL(of_node_put);/* * Plug a device node into the tree and global list. */void of_attach_node(struct device_node *np){ write_lock(&devtree_lock); np->sibling = np->parent->child; np->allnext = allnodes; np->parent->child = np; allnodes = np; write_unlock(&devtree_lock);}/* * "Unplug" a node from the device tree. The caller must hold * a reference to the node. The memory associated with the node * is not freed until its refcount goes to zero. */void of_detach_node(const struct device_node *np){ struct device_node *parent; write_lock(&devtree_lock); parent = np->parent; if (allnodes == np) allnodes = np->allnext; else { struct device_node *prev; for (prev = allnodes; prev->allnext != np; prev = prev->allnext) ; prev->allnext = np->allnext; } if (parent->child == np) parent->child = np->sibling; else { struct device_node *prevsib; for (prevsib = np->parent->child; prevsib->sibling != np; prevsib = prevsib->sibling) ; prevsib->sibling = np->sibling; } write_unlock(&devtree_lock);}#ifdef CONFIG_PPC_PSERIES/* * Fix up the uninitialized fields in a new device node: * name, type, n_addrs, addrs, n_intrs, intrs, and pci-specific fields * * A lot of boot-time code is duplicated here, because functions such * as finish_node_interrupts, interpret_pci_props, etc. cannot use the * slab allocator. * * This should probably be split up into smaller chunks. */static int of_finish_dynamic_node(struct device_node *node, unsigned long *unused1, int unused2, int unused3, int unused4){ struct device_node *parent = of_get_parent(node); int err = 0; phandle *ibm_phandle; node->name = get_property(node, "name", NULL); node->type = get_property(node, "device_type", NULL); if (!parent) { err = -ENODEV; goto out; } /* We don't support that function on PowerMac, at least * not yet */ if (_machine == PLATFORM_POWERMAC) return -ENODEV; /* fix up new node's linux_phandle field */ if ((ibm_phandle = (unsigned int *)get_property(node, "ibm,phandle", NULL))) node->linux_phandle = *ibm_phandle;out: of_node_put(parent); return err;}static int prom_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node){ int err; switch (action) { case PSERIES_RECONFIG_ADD: err = finish_node(node, NULL, of_finish_dynamic_node, 0, 0, 0); if (err < 0) { printk(KERN_ERR "finish_node returned %d\n", err); err = NOTIFY_BAD; } break; default: err = NOTIFY_DONE; break; } return err;}static struct notifier_block prom_reconfig_nb = { .notifier_call = prom_reconfig_notifier, .priority = 10, /* This one needs to run first */};static int __init prom_reconfig_setup(void){ return pSeries_reconfig_notifier_register(&prom_reconfig_nb);}__initcall(prom_reconfig_setup);#endif/* * Find a property with a given name for a given node * and return the value. */unsigned char *get_property(struct device_node *np, const char *name, int *lenp){ struct property *pp; for (pp = np->properties; pp != 0; pp = pp->next) if (strcmp(pp->name, name) == 0) { if (lenp != 0) *lenp = pp->length; return pp->value; } return NULL;}EXPORT_SYMBOL(get_property);/* * Add a property to a node */int prom_add_property(struct device_node* np, struct property* prop){ struct property **next; prop->next = NULL; write_lock(&devtree_lock); next = &np->properties; while (*next) { if (strcmp(prop->name, (*next)->name) == 0) { /* duplicate ! don't insert it */ write_unlock(&devtree_lock); return -1; } next = &(*next)->next; } *next = prop; write_unlock(&devtree_lock);#ifdef CONFIG_PROC_DEVICETREE /* try to add to proc as well if it was initialized */ if (np->pde) proc_device_tree_add_prop(np->pde, prop);#endif /* CONFIG_PROC_DEVICETREE */ return 0;}/* I quickly hacked that one, check against spec ! */static inline unsigned longbus_space_to_resource_flags(unsigned int bus_space){ u8 space = (bus_space >> 24) & 0xf; if (space == 0) space = 0x02; if (space == 0x02) return IORESOURCE_MEM; else if (space == 0x01) return IORESOURCE_IO; else { printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n", bus_space); return 0; }}#ifdef CONFIG_PCIstatic struct resource *find_parent_pci_resource(struct pci_dev* pdev, struct address_range *range){ unsigned long mask; int i; /* Check this one */ mask = bus_space_to_resource_flags(range->space); for (i=0; i<DEVICE_COUNT_RESOURCE; i++) { if ((pdev->resource[i].flags & mask) == mask && pdev->resource[i].start <= range->address && pdev->resource[i].end > range->address) { if ((range->address + range->size - 1) > pdev->resource[i].end) { /* Add better message */ printk(KERN_WARNING "PCI/OF resource overlap !\n"); return NULL; } break; } } if (i == DEVICE_COUNT_RESOURCE) return NULL; return &pdev->resource[i];}/* * Request an OF device resource. Currently handles child of PCI devices, * or other nodes attached to the root node. Ultimately, put some * link to resources in the OF node. */struct resource *request_OF_resource(struct device_node* node, int index, const char* name_postfix){ struct pci_dev* pcidev; u8 pci_bus, pci_devfn; unsigned long iomask; struct device_node* nd; struct resource* parent; struct resource *res = NULL; int nlen, plen; if (index >= node->n_addrs) goto fail; /* Sanity check on bus space */ iomask = bus_space_to_resource_flags(node->addrs[index].space); if (iomask & IORESOURCE_MEM) parent = &iomem_resource; else if (iomask & IORESOURCE_IO) parent = &ioport_resource; else goto fail; /* Find a PCI parent if any */ nd = node; pcidev = NULL; while (nd) { if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) pcidev = pci_find_slot(pci_bus, pci_devfn); if (pcidev) break; nd = nd->parent; } if (pcidev) parent = find_parent_pci_resource(pcidev, &node->addrs[index]); if (!parent) { printk(KERN_WARNING "request_OF_resource(%s), parent not found\n", node->name); goto fail; } res = __request_region(parent, node->addrs[index].address, node->addrs[index].size, NULL); if (!res) goto fail; nlen = strlen(node->name); plen = name_postfix ? strlen(name_postfix) : 0; res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL); if (res->name) { strcpy((char *)res->name, node->name); if (plen) strcpy((char *)res->name+nlen, name_postfix); } return res;fail: return NULL;}EXPORT_SYMBOL(request_OF_resource);int release_OF_resource(struct device_node *node, int index){ struct pci_dev* pcidev; u8 pci_bus, pci_devfn; unsigned long iomask, start, end; struct device_node* nd; struct resource* parent; struct resource *res = NULL; if (index >= node->n_addrs) return -EINVAL; /* Sanity check on bus space */ iomask = bus_space_to_resource_flags(node->addrs[index].space); if (iomask & IORESOURCE_MEM) parent = &iomem_resource; else if (iomask & IORESOURCE_IO) parent = &ioport_resource; else return -EINVAL; /* Find a PCI parent if any */ nd = node; pcidev = NULL; while(nd) { if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) pcidev = pci_find_slot(pci_bus, pci_devfn); if (pcidev) break; nd = nd->parent; } if (pcidev) parent = find_parent_pci_resource(pcidev, &node->addrs[index]); if (!parent) { printk(KERN_WARNING "release_OF_resource(%s), parent not found\n", node->name); return -ENODEV; } /* Find us in the parent and its childs */ res = parent->child; start = node->addrs[index].address; end = start + node->addrs[index].size - 1; while (res) { if (res->start == start && res->end == end && (res->flags & IORESOURCE_BUSY)) break; if (res->start <= start && res->end >= end) res = res->child; else res = res->sibling; } if (!res) return -ENODEV; if (res->name) { kfree(res->name); res->name = NULL; } release_resource(res); kfree(res); return 0;}EXPORT_SYMBOL(release_OF_resource);#endif /* CONFIG_PCI */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -