1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2019 Google LLC
4 */
5
6#include <linux/errno.h>
7#include <linux/export.h>
8#include <linux/platform_data/wilco-ec.h>
9#include <linux/string.h>
10#include <linux/types.h>
11#include <asm/unaligned.h>
12
13/* Operation code; what the EC should do with the property */
14enum ec_property_op {
15	EC_OP_GET = 0,
16	EC_OP_SET = 1,
17};
18
19struct ec_property_request {
20	u8 op; /* One of enum ec_property_op */
21	u8 property_id[4]; /* The 32 bit PID is stored Little Endian */
22	u8 length;
23	u8 data[WILCO_EC_PROPERTY_MAX_SIZE];
24} __packed;
25
26struct ec_property_response {
27	u8 reserved[2];
28	u8 op; /* One of enum ec_property_op */
29	u8 property_id[4]; /* The 32 bit PID is stored Little Endian */
30	u8 length;
31	u8 data[WILCO_EC_PROPERTY_MAX_SIZE];
32} __packed;
33
34static int send_property_msg(struct wilco_ec_device *ec,
35			     struct ec_property_request *rq,
36			     struct ec_property_response *rs)
37{
38	struct wilco_ec_message ec_msg;
39	int ret;
40
41	memset(&ec_msg, 0, sizeof(ec_msg));
42	ec_msg.type = WILCO_EC_MSG_PROPERTY;
43	ec_msg.request_data = rq;
44	ec_msg.request_size = sizeof(*rq);
45	ec_msg.response_data = rs;
46	ec_msg.response_size = sizeof(*rs);
47
48	ret = wilco_ec_mailbox(ec, &ec_msg);
49	if (ret < 0)
50		return ret;
51	if (rs->op != rq->op)
52		return -EBADMSG;
53	if (memcmp(rq->property_id, rs->property_id, sizeof(rs->property_id)))
54		return -EBADMSG;
55
56	return 0;
57}
58
59int wilco_ec_get_property(struct wilco_ec_device *ec,
60			  struct wilco_ec_property_msg *prop_msg)
61{
62	struct ec_property_request rq;
63	struct ec_property_response rs;
64	int ret;
65
66	memset(&rq, 0, sizeof(rq));
67	rq.op = EC_OP_GET;
68	put_unaligned_le32(prop_msg->property_id, rq.property_id);
69
70	ret = send_property_msg(ec, &rq, &rs);
71	if (ret < 0)
72		return ret;
73
74	prop_msg->length = rs.length;
75	memcpy(prop_msg->data, rs.data, rs.length);
76
77	return 0;
78}
79EXPORT_SYMBOL_GPL(wilco_ec_get_property);
80
81int wilco_ec_set_property(struct wilco_ec_device *ec,
82			  struct wilco_ec_property_msg *prop_msg)
83{
84	struct ec_property_request rq;
85	struct ec_property_response rs;
86	int ret;
87
88	memset(&rq, 0, sizeof(rq));
89	rq.op = EC_OP_SET;
90	put_unaligned_le32(prop_msg->property_id, rq.property_id);
91	rq.length = prop_msg->length;
92	memcpy(rq.data, prop_msg->data, prop_msg->length);
93
94	ret = send_property_msg(ec, &rq, &rs);
95	if (ret < 0)
96		return ret;
97	if (rs.length != prop_msg->length)
98		return -EBADMSG;
99
100	return 0;
101}
102EXPORT_SYMBOL_GPL(wilco_ec_set_property);
103
104int wilco_ec_get_byte_property(struct wilco_ec_device *ec, u32 property_id,
105			       u8 *val)
106{
107	struct wilco_ec_property_msg msg;
108	int ret;
109
110	msg.property_id = property_id;
111
112	ret = wilco_ec_get_property(ec, &msg);
113	if (ret < 0)
114		return ret;
115	if (msg.length != 1)
116		return -EBADMSG;
117
118	*val = msg.data[0];
119
120	return 0;
121}
122EXPORT_SYMBOL_GPL(wilco_ec_get_byte_property);
123
124int wilco_ec_set_byte_property(struct wilco_ec_device *ec, u32 property_id,
125			       u8 val)
126{
127	struct wilco_ec_property_msg msg;
128
129	msg.property_id = property_id;
130	msg.data[0] = val;
131	msg.length = 1;
132
133	return wilco_ec_set_property(ec, &msg);
134}
135EXPORT_SYMBOL_GPL(wilco_ec_set_byte_property);
136