summaryrefslogtreecommitdiff
path: root/drivers/staging/greybus/module.c
blob: e8c1c07cff60f3a0bc59db132e4f3739631897a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * Greybus module code
 *
 * Copyright 2014 Google Inc.
 * Copyright 2014 Linaro Ltd.
 *
 * Released under the GPLv2 only.
 */

#include "greybus.h"


/* module sysfs attributes */
#define gb_module_attr(field, type)					\
static ssize_t field##_show(struct device *dev,				\
			    struct device_attribute *attr,		\
			    char *buf)					\
{									\
	struct gb_module *module = to_gb_module(dev);			\
	return sprintf(buf, "%"#type"\n", module->field);		\
}									\
static DEVICE_ATTR_RO(field)

// FIXME, do we really need this attribute?
gb_module_attr(module_id, x);

static ssize_t epm_show(struct device *dev, struct device_attribute *attr,
			char *buf)
{
	// FIXME, implement something here
	return sprintf(buf, "1\n");
}

static ssize_t epm_store(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t size)
{
	// FIXME, implement something here.
	return 0;
}
static DEVICE_ATTR_RW(epm);

static struct attribute *module_attrs[] = {
	&dev_attr_module_id.attr,
	&dev_attr_epm.attr,
	NULL,
};
ATTRIBUTE_GROUPS(module);

static void greybus_module_release(struct device *dev)
{
	struct gb_module *module = to_gb_module(dev);

	kfree(module);
}

struct device_type greybus_module_type = {
	.name =		"greybus_module",
	.release =	greybus_module_release,
};

u8 get_module_id(u8 interface_id)
{
	/*
	 * FIXME:
	 *
	 * We should be able to find it from Endo ID passed during greybus
	 * control operation, while setting up AP.
	 */
	return interface_id;
}

static int module_find(struct device *dev, void *data)
{
	struct gb_module *module;
	u8 *module_id = data;

	if (!is_gb_module(dev))
		return 0;

	module = to_gb_module(dev);
	if (module->module_id == *module_id)
		return 1;

	return 0;
}

/*
 * Search the list of modules in the system.  If one is found, return it, with
 * the reference count incremented.
 */
static struct gb_module *gb_module_find(u8 module_id)
{
	struct device *dev;
	struct gb_module *module = NULL;

	dev = bus_find_device(&greybus_bus_type, NULL,
			      &module_id, module_find);
	if (dev)
		module = to_gb_module(dev);

	return module;
}

static struct gb_module *gb_module_create(struct greybus_host_device *hd,
					  u8 module_id)
{
	struct gb_module *module;
	int retval;

	module = kzalloc(sizeof(*module), GFP_KERNEL);
	if (!module)
		return NULL;

	module->module_id = module_id;
	module->refcount = 1;
	module->dev.parent = hd->parent;
	module->dev.bus = &greybus_bus_type;
	module->dev.type = &greybus_module_type;
	module->dev.groups = module_groups;
	module->dev.dma_mask = hd->parent->dma_mask;
	device_initialize(&module->dev);
	dev_set_name(&module->dev, "%d", module_id);

	retval = device_add(&module->dev);
	if (retval) {
		pr_err("failed to add module device for id 0x%02hhx\n",
			module_id);
		put_device(&module->dev);
		kfree(module);
		return NULL;
	}

	return module;
}

struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd,
					   u8 module_id)
{
	struct gb_module *module;

	module = gb_module_find(module_id);
	if (module) {
		module->refcount++;
		return module;
	}

	return gb_module_create(hd, module_id);
}

void gb_module_remove(struct gb_module *module)
{
	if (!module)
		return;

	if (!--module->refcount)
		device_unregister(&module->dev);
}