blob: 7536a30e5b90aa4c5153fb95436b10d47e4ba767 [file] [log] [blame]
Alex Elder4ccb6b72014-10-28 19:36:00 -05001/*
2 * Greybus protocol handling
3 *
4 * Copyright 2014 Google Inc.
Alex Eldera46e9672014-12-12 12:08:42 -06005 * Copyright 2014 Linaro Ltd.
Alex Elder4ccb6b72014-10-28 19:36:00 -05006 *
7 * Released under the GPLv2 only.
8 */
9
10#include "greybus.h"
11
12/* Global list of registered protocols */
13static DEFINE_SPINLOCK(gb_protocols_lock);
14static LIST_HEAD(gb_protocols);
15
16/* Caller must hold gb_protocols_lock */
Alex Elder6ae7fa42014-11-05 16:12:50 -060017static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor)
Alex Elder4ccb6b72014-10-28 19:36:00 -050018{
19 struct gb_protocol *protocol;
20
Alex Elderdbb88942014-11-05 16:12:52 -060021 list_for_each_entry(protocol, &gb_protocols, links) {
22 if (protocol->id < id)
23 continue;
24 if (protocol->id > id)
25 break;
26
27 if (protocol->major > major)
28 continue;
29 if (protocol->major < major)
30 break;
31
32 if (protocol->minor > minor)
33 continue;
34 if (protocol->minor < minor)
35 break;
36
37 return protocol;
38 }
Alex Elder4ccb6b72014-10-28 19:36:00 -050039 return NULL;
40}
41
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -080042int gb_protocol_register(struct gb_protocol *protocol)
Alex Elder4ccb6b72014-10-28 19:36:00 -050043{
Alex Elder4ccb6b72014-10-28 19:36:00 -050044 struct gb_protocol *existing;
Alex Elder19d03de2014-11-05 16:12:53 -060045 u8 id = protocol->id;
46 u8 major = protocol->major;
47 u8 minor = protocol->minor;
Alex Elder4ccb6b72014-10-28 19:36:00 -050048
Alex Elderdbb88942014-11-05 16:12:52 -060049 /*
50 * The protocols list is sorted first by protocol id (low to
51 * high), then by major version (high to low), and finally
52 * by minor version (high to low). Searching only by
53 * protocol id will produce the newest implemented version
54 * of the protocol.
55 */
Alex Elder4ccb6b72014-10-28 19:36:00 -050056 spin_lock_irq(&gb_protocols_lock);
Alex Elder4ccb6b72014-10-28 19:36:00 -050057
Alex Elderdbb88942014-11-05 16:12:52 -060058 list_for_each_entry(existing, &gb_protocols, links) {
59 if (existing->id < id)
60 continue;
61 if (existing->id > id)
62 break;
63
64 if (existing->major > major)
65 continue;
66 if (existing->major < major)
67 break;
68
69 if (existing->minor > minor)
70 continue;
71 if (existing->minor < minor)
72 break;
73
74 /* A matching protocol has already been registered */
75 spin_unlock_irq(&gb_protocols_lock);
Alex Elderdbb88942014-11-05 16:12:52 -060076
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -080077 return -EEXIST;
Alex Elder4ccb6b72014-10-28 19:36:00 -050078 }
79
Alex Elderdbb88942014-11-05 16:12:52 -060080 /*
81 * We need to insert the protocol here, before the existing one
82 * (or before the head if we searched the whole list)
83 */
84 list_add_tail(&protocol->links, &existing->links);
85 spin_unlock_irq(&gb_protocols_lock);
86
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -080087 return 0;
Alex Elder4ccb6b72014-10-28 19:36:00 -050088}
Greg Kroah-Hartmandf469a92014-12-23 15:16:52 -080089EXPORT_SYMBOL_GPL(gb_protocol_register);
Alex Elder4ccb6b72014-10-28 19:36:00 -050090
Alex Elder19d03de2014-11-05 16:12:53 -060091/*
92 * De-register a previously registered protocol.
93 *
94 * XXX Currently this fails (and reports an error to the caller) if
95 * XXX the protocol is currently in use. We may want to forcefully
96 * XXX kill off a protocol and all its active users at some point.
Viresh Kumar696e0cc2014-11-21 11:26:30 +053097 * XXX But I think that's better handled by quiescing modules that
Alex Elder19d03de2014-11-05 16:12:53 -060098 * XXX have users and having those users drop their reference.
99 *
100 * Returns true if successful, false otherwise.
101 */
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800102int gb_protocol_deregister(struct gb_protocol *protocol)
Alex Elder4ccb6b72014-10-28 19:36:00 -0500103{
Alex Elderdbb88942014-11-05 16:12:52 -0600104 u8 protocol_count = 0;
Alex Elder0e447652014-11-05 16:12:51 -0600105
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800106 if (!protocol)
107 return 0;
108
Alex Elder4ccb6b72014-10-28 19:36:00 -0500109 spin_lock_irq(&gb_protocols_lock);
Alex Elder0e447652014-11-05 16:12:51 -0600110 protocol = _gb_protocol_find(protocol->id, protocol->major,
111 protocol->minor);
112 if (protocol) {
113 protocol_count = protocol->count;
114 if (!protocol_count)
115 list_del(&protocol->links);
116 }
Alex Elder4ccb6b72014-10-28 19:36:00 -0500117 spin_unlock_irq(&gb_protocols_lock);
Alex Elder4ccb6b72014-10-28 19:36:00 -0500118
Alex Elder0e447652014-11-05 16:12:51 -0600119 return protocol && !protocol_count;
Alex Elder4ccb6b72014-10-28 19:36:00 -0500120}
Greg Kroah-Hartmandf469a92014-12-23 15:16:52 -0800121EXPORT_SYMBOL_GPL(gb_protocol_deregister);
Alex Elder4ccb6b72014-10-28 19:36:00 -0500122
Alex Elder0e447652014-11-05 16:12:51 -0600123/* Returns the requested protocol if available, or a null pointer */
124struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor)
Alex Elder4ccb6b72014-10-28 19:36:00 -0500125{
126 struct gb_protocol *protocol;
Alex Elder0e447652014-11-05 16:12:51 -0600127 u8 protocol_count;
Alex Elder4ccb6b72014-10-28 19:36:00 -0500128
129 spin_lock_irq(&gb_protocols_lock);
Alex Elder6ae7fa42014-11-05 16:12:50 -0600130 protocol = _gb_protocol_find(id, major, minor);
Alex Elder0e447652014-11-05 16:12:51 -0600131 if (protocol) {
132 protocol_count = protocol->count;
133 if (protocol_count != U8_MAX)
134 protocol->count++;
135 }
Alex Elder4ccb6b72014-10-28 19:36:00 -0500136 spin_unlock_irq(&gb_protocols_lock);
Alex Elder4ccb6b72014-10-28 19:36:00 -0500137
Alex Elder0e447652014-11-05 16:12:51 -0600138 if (protocol)
139 WARN_ON(protocol_count == U8_MAX);
140 else
141 pr_err("protocol id %hhu version %hhu.%hhu not found\n",
142 id, major, minor);
143
144 return protocol;
Alex Elder4ccb6b72014-10-28 19:36:00 -0500145}
146
Alex Elder0e447652014-11-05 16:12:51 -0600147void gb_protocol_put(struct gb_protocol *protocol)
Alex Elder4ccb6b72014-10-28 19:36:00 -0500148{
Alex Elder6ae7fa42014-11-05 16:12:50 -0600149 u8 major = protocol->major;
150 u8 minor = protocol->minor;
Alex Elder0e447652014-11-05 16:12:51 -0600151 u8 protocol_count;
Alex Elder4ccb6b72014-10-28 19:36:00 -0500152
153 spin_lock_irq(&gb_protocols_lock);
Alex Elder0e447652014-11-05 16:12:51 -0600154 protocol = _gb_protocol_find(protocol->id, protocol->major,
155 protocol->minor);
156 if (protocol) {
157 protocol_count = protocol->count;
158 if (protocol_count)
159 protocol->count--;
160 }
Alex Elder4ccb6b72014-10-28 19:36:00 -0500161 spin_unlock_irq(&gb_protocols_lock);
Alex Elder0e447652014-11-05 16:12:51 -0600162 if (protocol)
163 WARN_ON(!protocol_count);
164 else
165 pr_err("protocol id %hhu version %hhu.%hhu not found\n",
166 protocol->id, major, minor);
Alex Elder4ccb6b72014-10-28 19:36:00 -0500167}
Alex Elder19d03de2014-11-05 16:12:53 -0600168
169bool gb_protocol_init(void)
170{
171 bool ret = true;
172
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800173 if (gb_battery_protocol_init()) {
Alex Elder19d03de2014-11-05 16:12:53 -0600174 pr_err("error initializing battery protocol\n");
175 ret = false;
176 }
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800177 if (gb_gpio_protocol_init()) {
Alex Elder19d03de2014-11-05 16:12:53 -0600178 pr_err("error initializing gpio protocol\n");
179 ret = false;
180 }
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800181 if (gb_i2c_protocol_init()) {
Alex Elder19d03de2014-11-05 16:12:53 -0600182 pr_err("error initializing i2c protocol\n");
183 ret = false;
184 }
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800185 if (gb_pwm_protocol_init()) {
Matt Porter34c65072014-11-13 09:14:13 -0500186 pr_err("error initializing pwm protocol\n");
187 ret = false;
188 }
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800189 if (gb_uart_protocol_init()) {
Alex Elder19d03de2014-11-05 16:12:53 -0600190 pr_err("error initializing uart protocol\n");
191 ret = false;
192 }
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800193 if (gb_sdio_protocol_init()) {
Alex Elder19d03de2014-11-05 16:12:53 -0600194 pr_err("error initializing sdio protocol\n");
195 ret = false;
196 }
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800197 if (gb_vibrator_protocol_init()) {
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800198 pr_err("error initializing vibrator protocol\n");
199 ret = false;
200 }
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800201 if (gb_usb_protocol_init()) {
Greg Kroah-Hartman615772a2014-11-25 16:59:21 -0800202 pr_err("error initializing usb protocol\n");
203 ret = false;
204 }
Alex Elder19d03de2014-11-05 16:12:53 -0600205 return ret;
206}
207
208void gb_protocol_exit(void)
209{
Greg Kroah-Hartman615772a2014-11-25 16:59:21 -0800210 gb_usb_protocol_exit();
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800211 gb_vibrator_protocol_exit();
Alex Elder19d03de2014-11-05 16:12:53 -0600212 gb_sdio_protocol_exit();
213 gb_uart_protocol_exit();
214 gb_i2c_protocol_exit();
215 gb_gpio_protocol_exit();
216 gb_battery_protocol_exit();
217}