174 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include <linux/string.h>
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/of.h>
 | 
						|
#include <linux/of_device.h>
 | 
						|
#include <linux/init.h>
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/mod_devicetable.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
 | 
						|
#include <asm/errno.h>
 | 
						|
 | 
						|
/**
 | 
						|
 * of_match_device - Tell if an of_device structure has a matching
 | 
						|
 * of_match structure
 | 
						|
 * @ids: array of of device match structures to search in
 | 
						|
 * @dev: the of device structure to match against
 | 
						|
 *
 | 
						|
 * Used by a driver to check whether an of_device present in the
 | 
						|
 * system is in its list of supported devices.
 | 
						|
 */
 | 
						|
const struct of_device_id *of_match_device(const struct of_device_id *matches,
 | 
						|
					const struct of_device *dev)
 | 
						|
{
 | 
						|
	if (!dev->node)
 | 
						|
		return NULL;
 | 
						|
	return of_match_node(matches, dev->node);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(of_match_device);
 | 
						|
 | 
						|
struct of_device *of_dev_get(struct of_device *dev)
 | 
						|
{
 | 
						|
	struct device *tmp;
 | 
						|
 | 
						|
	if (!dev)
 | 
						|
		return NULL;
 | 
						|
	tmp = get_device(&dev->dev);
 | 
						|
	if (tmp)
 | 
						|
		return to_of_device(tmp);
 | 
						|
	else
 | 
						|
		return NULL;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(of_dev_get);
 | 
						|
 | 
						|
void of_dev_put(struct of_device *dev)
 | 
						|
{
 | 
						|
	if (dev)
 | 
						|
		put_device(&dev->dev);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(of_dev_put);
 | 
						|
 | 
						|
static ssize_t devspec_show(struct device *dev,
 | 
						|
				struct device_attribute *attr, char *buf)
 | 
						|
{
 | 
						|
	struct of_device *ofdev;
 | 
						|
 | 
						|
	ofdev = to_of_device(dev);
 | 
						|
	return sprintf(buf, "%s\n", ofdev->node->full_name);
 | 
						|
}
 | 
						|
 | 
						|
static ssize_t name_show(struct device *dev,
 | 
						|
				struct device_attribute *attr, char *buf)
 | 
						|
{
 | 
						|
	struct of_device *ofdev;
 | 
						|
 | 
						|
	ofdev = to_of_device(dev);
 | 
						|
	return sprintf(buf, "%s\n", ofdev->node->name);
 | 
						|
}
 | 
						|
 | 
						|
static ssize_t modalias_show(struct device *dev,
 | 
						|
				struct device_attribute *attr, char *buf)
 | 
						|
{
 | 
						|
	struct of_device *ofdev = to_of_device(dev);
 | 
						|
	ssize_t len = 0;
 | 
						|
 | 
						|
	len = of_device_get_modalias(ofdev, buf, PAGE_SIZE - 2);
 | 
						|
	buf[len] = '\n';
 | 
						|
	buf[len+1] = 0;
 | 
						|
	return len+1;
 | 
						|
}
 | 
						|
 | 
						|
struct device_attribute of_platform_device_attrs[] = {
 | 
						|
	__ATTR_RO(devspec),
 | 
						|
	__ATTR_RO(name),
 | 
						|
	__ATTR_RO(modalias),
 | 
						|
	__ATTR_NULL
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * of_release_dev - free an of device structure when all users of it are finished.
 | 
						|
 * @dev: device that's been disconnected
 | 
						|
 *
 | 
						|
 * Will be called only by the device core when all users of this of device are
 | 
						|
 * done.
 | 
						|
 */
 | 
						|
void of_release_dev(struct device *dev)
 | 
						|
{
 | 
						|
	struct of_device *ofdev;
 | 
						|
 | 
						|
	ofdev = to_of_device(dev);
 | 
						|
	of_node_put(ofdev->node);
 | 
						|
	kfree(ofdev);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(of_release_dev);
 | 
						|
 | 
						|
int of_device_register(struct of_device *ofdev)
 | 
						|
{
 | 
						|
	BUG_ON(ofdev->node == NULL);
 | 
						|
 | 
						|
	device_initialize(&ofdev->dev);
 | 
						|
 | 
						|
	/* device_add will assume that this device is on the same node as
 | 
						|
	 * the parent. If there is no parent defined, set the node
 | 
						|
	 * explicitly */
 | 
						|
	if (!ofdev->dev.parent)
 | 
						|
		set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->node));
 | 
						|
 | 
						|
	return device_add(&ofdev->dev);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(of_device_register);
 | 
						|
 | 
						|
void of_device_unregister(struct of_device *ofdev)
 | 
						|
{
 | 
						|
	device_unregister(&ofdev->dev);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(of_device_unregister);
 | 
						|
 | 
						|
ssize_t of_device_get_modalias(struct of_device *ofdev,
 | 
						|
				char *str, ssize_t len)
 | 
						|
{
 | 
						|
	const char *compat;
 | 
						|
	int cplen, i;
 | 
						|
	ssize_t tsize, csize, repend;
 | 
						|
 | 
						|
	/* Name & Type */
 | 
						|
	csize = snprintf(str, len, "of:N%sT%s",
 | 
						|
				ofdev->node->name, ofdev->node->type);
 | 
						|
 | 
						|
	/* Get compatible property if any */
 | 
						|
	compat = of_get_property(ofdev->node, "compatible", &cplen);
 | 
						|
	if (!compat)
 | 
						|
		return csize;
 | 
						|
 | 
						|
	/* Find true end (we tolerate multiple \0 at the end */
 | 
						|
	for (i = (cplen - 1); i >= 0 && !compat[i]; i--)
 | 
						|
		cplen--;
 | 
						|
	if (!cplen)
 | 
						|
		return csize;
 | 
						|
	cplen++;
 | 
						|
 | 
						|
	/* Check space (need cplen+1 chars including final \0) */
 | 
						|
	tsize = csize + cplen;
 | 
						|
	repend = tsize;
 | 
						|
 | 
						|
	if (csize >= len)		/* @ the limit, all is already filled */
 | 
						|
		return tsize;
 | 
						|
 | 
						|
	if (tsize >= len) {		/* limit compat list */
 | 
						|
		cplen = len - csize - 1;
 | 
						|
		repend = len;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Copy and do char replacement */
 | 
						|
	memcpy(&str[csize + 1], compat, cplen);
 | 
						|
	for (i = csize; i < repend; i++) {
 | 
						|
		char c = str[i];
 | 
						|
		if (c == '\0')
 | 
						|
			str[i] = 'C';
 | 
						|
		else if (c == ' ')
 | 
						|
			str[i] = '_';
 | 
						|
	}
 | 
						|
 | 
						|
	return tsize;
 | 
						|
}
 |