aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuenter Roeck <groeck@chromium.org>2019-04-03 10:05:28 -0400
committerEnric Balletbo i Serra <enric.balletbo@collabora.com>2019-04-15 06:13:24 -0400
commita2679b64719085196a8e1762a40e90e92b1f3cf5 (patch)
tree5c752d1d076ae48125b72abe8db7a4440b4a759e
parent37a186225a0c020516bafad2727fdcdfc039a1e4 (diff)
platform/chrome: Add CrOS USB PD logging driver
The CrOS USB PD logging feature is logically separate functionality of the charge manager, hence has its own driver. The driver logs the event data for the USB PD charger available in some ChromeOS Embedded Controllers. Signed-off-by: Guenter Roeck <groeck@chromium.org> [remove macro to APPEND_STRING and minor cleanups] Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
-rw-r--r--drivers/platform/chrome/Kconfig12
-rw-r--r--drivers/platform/chrome/Makefile1
-rw-r--r--drivers/platform/chrome/cros_usbpd_logger.c262
3 files changed, 275 insertions, 0 deletions
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 9186d81a51cc..4313a8bcf0ab 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -152,6 +152,18 @@ config CROS_EC_SYSFS
152 To compile this driver as a module, choose M here: the 152 To compile this driver as a module, choose M here: the
153 module will be called cros_ec_sysfs. 153 module will be called cros_ec_sysfs.
154 154
155config CROS_USBPD_LOGGER
156 tristate "Logging driver for USB PD charger"
157 depends on CHARGER_CROS_USBPD
158 default y
159 select RTC_LIB
160 help
161 This option enables support for logging event data for the USB PD charger
162 available in the Embedded Controller on ChromeOS systems.
163
164 To compile this driver as a module, choose M here: the
165 module will be called cros_usbpd_logger.
166
155source "drivers/platform/chrome/wilco_ec/Kconfig" 167source "drivers/platform/chrome/wilco_ec/Kconfig"
156 168
157endif # CHROMEOS_PLATFORMS 169endif # CHROMEOS_PLATFORMS
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index 1e2f0029b597..0bee2e44748e 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -14,5 +14,6 @@ obj-$(CONFIG_CROS_EC_LIGHTBAR) += cros_ec_lightbar.o
14obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o 14obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o
15obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o 15obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o
16obj-$(CONFIG_CROS_EC_SYSFS) += cros_ec_sysfs.o 16obj-$(CONFIG_CROS_EC_SYSFS) += cros_ec_sysfs.o
17obj-$(CONFIG_CROS_USBPD_LOGGER) += cros_usbpd_logger.o
17 18
18obj-$(CONFIG_WILCO_EC) += wilco_ec/ 19obj-$(CONFIG_WILCO_EC) += wilco_ec/
diff --git a/drivers/platform/chrome/cros_usbpd_logger.c b/drivers/platform/chrome/cros_usbpd_logger.c
new file mode 100644
index 000000000000..7c7b267626a0
--- /dev/null
+++ b/drivers/platform/chrome/cros_usbpd_logger.c
@@ -0,0 +1,262 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Logging driver for ChromeOS EC based USBPD Charger.
4 *
5 * Copyright 2018 Google LLC.
6 */
7
8#include <linux/ktime.h>
9#include <linux/math64.h>
10#include <linux/mfd/cros_ec.h>
11#include <linux/mfd/cros_ec_commands.h>
12#include <linux/module.h>
13#include <linux/platform_device.h>
14#include <linux/rtc.h>
15
16#define DRV_NAME "cros-usbpd-logger"
17
18#define CROS_USBPD_MAX_LOG_ENTRIES 30
19#define CROS_USBPD_LOG_UPDATE_DELAY msecs_to_jiffies(60000)
20#define CROS_USBPD_DATA_SIZE 16
21#define CROS_USBPD_LOG_RESP_SIZE (sizeof(struct ec_response_pd_log) + \
22 CROS_USBPD_DATA_SIZE)
23#define CROS_USBPD_BUFFER_SIZE (sizeof(struct cros_ec_command) + \
24 CROS_USBPD_LOG_RESP_SIZE)
25/* Buffer for building the PDLOG string */
26#define BUF_SIZE 80
27
28struct logger_data {
29 struct device *dev;
30 struct cros_ec_dev *ec_dev;
31 u8 ec_buffer[CROS_USBPD_BUFFER_SIZE];
32 struct delayed_work log_work;
33 struct workqueue_struct *log_workqueue;
34};
35
36static const char * const chg_type_names[] = {
37 "None", "PD", "Type-C", "Proprietary", "DCP", "CDP", "SDP",
38 "Other", "VBUS"
39};
40
41static const char * const role_names[] = {
42 "Disconnected", "SRC", "SNK", "SNK (not charging)"
43};
44
45static const char * const fault_names[] = {
46 "---", "OCP", "fast OCP", "OVP", "Discharge"
47};
48
49static int append_str(char *buf, int pos, const char *fmt, ...)
50{
51 va_list args;
52 int i;
53
54 va_start(args, fmt);
55 i = vsnprintf(buf + pos, BUF_SIZE - pos, fmt, args);
56 va_end(args);
57
58 return i;
59}
60
61static struct ec_response_pd_log *ec_get_log_entry(struct logger_data *logger)
62{
63 struct cros_ec_dev *ec_dev = logger->ec_dev;
64 struct cros_ec_command *msg;
65 int ret;
66
67 msg = (struct cros_ec_command *)logger->ec_buffer;
68
69 msg->command = ec_dev->cmd_offset + EC_CMD_PD_GET_LOG_ENTRY;
70 msg->insize = CROS_USBPD_LOG_RESP_SIZE;
71
72 ret = cros_ec_cmd_xfer_status(ec_dev->ec_dev, msg);
73 if (ret < 0)
74 return ERR_PTR(ret);
75
76 return (struct ec_response_pd_log *)msg->data;
77}
78
79static void cros_usbpd_print_log_entry(struct ec_response_pd_log *r,
80 ktime_t tstamp)
81{
82 const char *fault, *role, *chg_type;
83 struct usb_chg_measures *meas;
84 struct mcdp_info *minfo;
85 int role_idx, type_idx;
86 char buf[BUF_SIZE + 1];
87 struct rtc_time rt;
88 int len = 0;
89 s32 rem;
90 int i;
91
92 /* The timestamp is the number of 1024th of seconds in the past */
93 tstamp = ktime_sub_us(tstamp, r->timestamp << PD_LOG_TIMESTAMP_SHIFT);
94 rt = rtc_ktime_to_tm(tstamp);
95
96 switch (r->type) {
97 case PD_EVENT_MCU_CHARGE:
98 if (r->data & CHARGE_FLAGS_OVERRIDE)
99 len += append_str(buf, len, "override ");
100
101 if (r->data & CHARGE_FLAGS_DELAYED_OVERRIDE)
102 len += append_str(buf, len, "pending_override ");
103
104 role_idx = r->data & CHARGE_FLAGS_ROLE_MASK;
105 role = role_idx < ARRAY_SIZE(role_names) ?
106 role_names[role_idx] : "Unknown";
107
108 type_idx = (r->data & CHARGE_FLAGS_TYPE_MASK)
109 >> CHARGE_FLAGS_TYPE_SHIFT;
110
111 chg_type = type_idx < ARRAY_SIZE(chg_type_names) ?
112 chg_type_names[type_idx] : "???";
113
114 if (role_idx == USB_PD_PORT_POWER_DISCONNECTED ||
115 role_idx == USB_PD_PORT_POWER_SOURCE) {
116 len += append_str(buf, len, "%s", role);
117 break;
118 }
119
120 meas = (struct usb_chg_measures *)r->payload;
121 len += append_str(buf, len, "%s %s %s %dmV max %dmV / %dmA",
122 role, r->data & CHARGE_FLAGS_DUAL_ROLE ?
123 "DRP" : "Charger",
124 chg_type, meas->voltage_now,
125 meas->voltage_max, meas->current_max);
126 break;
127 case PD_EVENT_ACC_RW_FAIL:
128 len += append_str(buf, len, "RW signature check failed");
129 break;
130 case PD_EVENT_PS_FAULT:
131 fault = r->data < ARRAY_SIZE(fault_names) ? fault_names[r->data]
132 : "???";
133 len += append_str(buf, len, "Power supply fault: %s", fault);
134 break;
135 case PD_EVENT_VIDEO_DP_MODE:
136 len += append_str(buf, len, "DP mode %sabled", r->data == 1 ?
137 "en" : "dis");
138 break;
139 case PD_EVENT_VIDEO_CODEC:
140 minfo = (struct mcdp_info *)r->payload;
141 len += append_str(buf, len, "HDMI info: family:%04x chipid:%04x ",
142 MCDP_FAMILY(minfo->family),
143 MCDP_CHIPID(minfo->chipid));
144 len += append_str(buf, len, "irom:%d.%d.%d fw:%d.%d.%d",
145 minfo->irom.major, minfo->irom.minor,
146 minfo->irom.build, minfo->fw.major,
147 minfo->fw.minor, minfo->fw.build);
148 break;
149 default:
150 len += append_str(buf, len, "Event %02x (%04x) [", r->type,
151 r->data);
152
153 for (i = 0; i < PD_LOG_SIZE(r->size_port); i++)
154 len += append_str(buf, len, "%02x ", r->payload[i]);
155
156 len += append_str(buf, len, "]");
157 break;
158 }
159
160 div_s64_rem(ktime_to_ms(tstamp), MSEC_PER_SEC, &rem);
161 pr_info("PDLOG %d/%02d/%02d %02d:%02d:%02d.%03d P%d %s\n",
162 rt.tm_year + 1900, rt.tm_mon + 1, rt.tm_mday,
163 rt.tm_hour, rt.tm_min, rt.tm_sec, rem,
164 PD_LOG_PORT(r->size_port), buf);
165}
166
167static void cros_usbpd_log_check(struct work_struct *work)
168{
169 struct logger_data *logger = container_of(to_delayed_work(work),
170 struct logger_data,
171 log_work);
172 struct device *dev = logger->dev;
173 struct ec_response_pd_log *r;
174 int entries = 0;
175 ktime_t now;
176
177 while (entries++ < CROS_USBPD_MAX_LOG_ENTRIES) {
178 r = ec_get_log_entry(logger);
179 now = ktime_get_real();
180 if (IS_ERR(r)) {
181 dev_dbg(dev, "Cannot get PD log %ld\n", PTR_ERR(r));
182 break;
183 }
184 if (r->type == PD_EVENT_NO_ENTRY)
185 break;
186
187 cros_usbpd_print_log_entry(r, now);
188 }
189
190 queue_delayed_work(logger->log_workqueue, &logger->log_work,
191 CROS_USBPD_LOG_UPDATE_DELAY);
192}
193
194static int cros_usbpd_logger_probe(struct platform_device *pd)
195{
196 struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
197 struct device *dev = &pd->dev;
198 struct logger_data *logger;
199
200 logger = devm_kzalloc(dev, sizeof(*logger), GFP_KERNEL);
201 if (!logger)
202 return -ENOMEM;
203
204 logger->dev = dev;
205 logger->ec_dev = ec_dev;
206
207 platform_set_drvdata(pd, logger);
208
209 /* Retrieve PD event logs periodically */
210 INIT_DELAYED_WORK(&logger->log_work, cros_usbpd_log_check);
211 logger->log_workqueue = create_singlethread_workqueue("cros_usbpd_log");
212 queue_delayed_work(logger->log_workqueue, &logger->log_work,
213 CROS_USBPD_LOG_UPDATE_DELAY);
214
215 return 0;
216}
217
218static int cros_usbpd_logger_remove(struct platform_device *pd)
219{
220 struct logger_data *logger = platform_get_drvdata(pd);
221
222 cancel_delayed_work_sync(&logger->log_work);
223
224 return 0;
225}
226
227static int __maybe_unused cros_usbpd_logger_resume(struct device *dev)
228{
229 struct logger_data *logger = dev_get_drvdata(dev);
230
231 queue_delayed_work(logger->log_workqueue, &logger->log_work,
232 CROS_USBPD_LOG_UPDATE_DELAY);
233
234 return 0;
235}
236
237static int __maybe_unused cros_usbpd_logger_suspend(struct device *dev)
238{
239 struct logger_data *logger = dev_get_drvdata(dev);
240
241 cancel_delayed_work_sync(&logger->log_work);
242
243 return 0;
244}
245
246static SIMPLE_DEV_PM_OPS(cros_usbpd_logger_pm_ops, cros_usbpd_logger_suspend,
247 cros_usbpd_logger_resume);
248
249static struct platform_driver cros_usbpd_logger_driver = {
250 .driver = {
251 .name = DRV_NAME,
252 .pm = &cros_usbpd_logger_pm_ops,
253 },
254 .probe = cros_usbpd_logger_probe,
255 .remove = cros_usbpd_logger_remove,
256};
257
258module_platform_driver(cros_usbpd_logger_driver);
259
260MODULE_LICENSE("GPL v2");
261MODULE_DESCRIPTION("Logging driver for ChromeOS EC USBPD Charger.");
262MODULE_ALIAS("platform:" DRV_NAME);