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