| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright 2019 Google LLC |
| */ |
| |
| #include <linux/errno.h> |
| #include <linux/export.h> |
| #include <linux/platform_data/wilco-ec.h> |
| #include <linux/string.h> |
| #include <linux/types.h> |
| #include <linux/unaligned.h> |
| |
| /* Operation code; what the EC should do with the property */ |
| enum ec_property_op { |
| EC_OP_GET = 0, |
| EC_OP_SET = 1, |
| }; |
| |
| struct ec_property_request { |
| u8 op; /* One of enum ec_property_op */ |
| u8 property_id[4]; /* The 32 bit PID is stored Little Endian */ |
| u8 length; |
| u8 data[WILCO_EC_PROPERTY_MAX_SIZE]; |
| } __packed; |
| |
| struct ec_property_response { |
| u8 reserved[2]; |
| u8 op; /* One of enum ec_property_op */ |
| u8 property_id[4]; /* The 32 bit PID is stored Little Endian */ |
| u8 length; |
| u8 data[WILCO_EC_PROPERTY_MAX_SIZE]; |
| } __packed; |
| |
| static int send_property_msg(struct wilco_ec_device *ec, |
| struct ec_property_request *rq, |
| struct ec_property_response *rs) |
| { |
| struct wilco_ec_message ec_msg; |
| int ret; |
| |
| memset(&ec_msg, 0, sizeof(ec_msg)); |
| ec_msg.type = WILCO_EC_MSG_PROPERTY; |
| ec_msg.request_data = rq; |
| ec_msg.request_size = sizeof(*rq); |
| ec_msg.response_data = rs; |
| ec_msg.response_size = sizeof(*rs); |
| |
| ret = wilco_ec_mailbox(ec, &ec_msg); |
| if (ret < 0) |
| return ret; |
| if (rs->op != rq->op) |
| return -EBADMSG; |
| if (memcmp(rq->property_id, rs->property_id, sizeof(rs->property_id))) |
| return -EBADMSG; |
| |
| return 0; |
| } |
| |
| int wilco_ec_get_property(struct wilco_ec_device *ec, |
| struct wilco_ec_property_msg *prop_msg) |
| { |
| struct ec_property_request rq; |
| struct ec_property_response rs; |
| int ret; |
| |
| memset(&rq, 0, sizeof(rq)); |
| rq.op = EC_OP_GET; |
| put_unaligned_le32(prop_msg->property_id, rq.property_id); |
| |
| ret = send_property_msg(ec, &rq, &rs); |
| if (ret < 0) |
| return ret; |
| |
| prop_msg->length = rs.length; |
| memcpy(prop_msg->data, rs.data, rs.length); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(wilco_ec_get_property); |
| |
| int wilco_ec_set_property(struct wilco_ec_device *ec, |
| struct wilco_ec_property_msg *prop_msg) |
| { |
| struct ec_property_request rq; |
| struct ec_property_response rs; |
| int ret; |
| |
| memset(&rq, 0, sizeof(rq)); |
| rq.op = EC_OP_SET; |
| put_unaligned_le32(prop_msg->property_id, rq.property_id); |
| rq.length = prop_msg->length; |
| memcpy(rq.data, prop_msg->data, prop_msg->length); |
| |
| ret = send_property_msg(ec, &rq, &rs); |
| if (ret < 0) |
| return ret; |
| if (rs.length != prop_msg->length) |
| return -EBADMSG; |
| |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(wilco_ec_set_property); |
| |
| int wilco_ec_get_byte_property(struct wilco_ec_device *ec, u32 property_id, |
| u8 *val) |
| { |
| struct wilco_ec_property_msg msg; |
| int ret; |
| |
| msg.property_id = property_id; |
| |
| ret = wilco_ec_get_property(ec, &msg); |
| if (ret < 0) |
| return ret; |
| if (msg.length != 1) |
| return -EBADMSG; |
| |
| *val = msg.data[0]; |
| |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(wilco_ec_get_byte_property); |
| |
| int wilco_ec_set_byte_property(struct wilco_ec_device *ec, u32 property_id, |
| u8 val) |
| { |
| struct wilco_ec_property_msg msg; |
| |
| msg.property_id = property_id; |
| msg.data[0] = val; |
| msg.length = 1; |
| |
| return wilco_ec_set_property(ec, &msg); |
| } |
| EXPORT_SYMBOL_GPL(wilco_ec_set_byte_property); |