10c0b7ea2SNick Crews // SPDX-License-Identifier: GPL-2.0 20c0b7ea2SNick Crews /* 30c0b7ea2SNick Crews * Copyright 2019 Google LLC 40c0b7ea2SNick Crews */ 50c0b7ea2SNick Crews 6*8673e944SAndy Shevchenko #include <linux/errno.h> 7*8673e944SAndy Shevchenko #include <linux/export.h> 80c0b7ea2SNick Crews #include <linux/platform_data/wilco-ec.h> 90c0b7ea2SNick Crews #include <linux/string.h> 10*8673e944SAndy Shevchenko #include <linux/types.h> 110cbb4f9cSStephen Boyd #include <asm/unaligned.h> 120c0b7ea2SNick Crews 130c0b7ea2SNick Crews /* Operation code; what the EC should do with the property */ 140c0b7ea2SNick Crews enum ec_property_op { 150c0b7ea2SNick Crews EC_OP_GET = 0, 160c0b7ea2SNick Crews EC_OP_SET = 1, 170c0b7ea2SNick Crews }; 180c0b7ea2SNick Crews 190c0b7ea2SNick Crews struct ec_property_request { 200c0b7ea2SNick Crews u8 op; /* One of enum ec_property_op */ 210c0b7ea2SNick Crews u8 property_id[4]; /* The 32 bit PID is stored Little Endian */ 220c0b7ea2SNick Crews u8 length; 230c0b7ea2SNick Crews u8 data[WILCO_EC_PROPERTY_MAX_SIZE]; 240c0b7ea2SNick Crews } __packed; 250c0b7ea2SNick Crews 260c0b7ea2SNick Crews struct ec_property_response { 270c0b7ea2SNick Crews u8 reserved[2]; 280c0b7ea2SNick Crews u8 op; /* One of enum ec_property_op */ 290c0b7ea2SNick Crews u8 property_id[4]; /* The 32 bit PID is stored Little Endian */ 300c0b7ea2SNick Crews u8 length; 310c0b7ea2SNick Crews u8 data[WILCO_EC_PROPERTY_MAX_SIZE]; 320c0b7ea2SNick Crews } __packed; 330c0b7ea2SNick Crews 340c0b7ea2SNick Crews static int send_property_msg(struct wilco_ec_device *ec, 350c0b7ea2SNick Crews struct ec_property_request *rq, 360c0b7ea2SNick Crews struct ec_property_response *rs) 370c0b7ea2SNick Crews { 380c0b7ea2SNick Crews struct wilco_ec_message ec_msg; 390c0b7ea2SNick Crews int ret; 400c0b7ea2SNick Crews 410c0b7ea2SNick Crews memset(&ec_msg, 0, sizeof(ec_msg)); 420c0b7ea2SNick Crews ec_msg.type = WILCO_EC_MSG_PROPERTY; 430c0b7ea2SNick Crews ec_msg.request_data = rq; 440c0b7ea2SNick Crews ec_msg.request_size = sizeof(*rq); 450c0b7ea2SNick Crews ec_msg.response_data = rs; 460c0b7ea2SNick Crews ec_msg.response_size = sizeof(*rs); 470c0b7ea2SNick Crews 480c0b7ea2SNick Crews ret = wilco_ec_mailbox(ec, &ec_msg); 490c0b7ea2SNick Crews if (ret < 0) 500c0b7ea2SNick Crews return ret; 510c0b7ea2SNick Crews if (rs->op != rq->op) 520c0b7ea2SNick Crews return -EBADMSG; 530c0b7ea2SNick Crews if (memcmp(rq->property_id, rs->property_id, sizeof(rs->property_id))) 540c0b7ea2SNick Crews return -EBADMSG; 550c0b7ea2SNick Crews 560c0b7ea2SNick Crews return 0; 570c0b7ea2SNick Crews } 580c0b7ea2SNick Crews 590c0b7ea2SNick Crews int wilco_ec_get_property(struct wilco_ec_device *ec, 600c0b7ea2SNick Crews struct wilco_ec_property_msg *prop_msg) 610c0b7ea2SNick Crews { 620c0b7ea2SNick Crews struct ec_property_request rq; 630c0b7ea2SNick Crews struct ec_property_response rs; 640c0b7ea2SNick Crews int ret; 650c0b7ea2SNick Crews 660c0b7ea2SNick Crews memset(&rq, 0, sizeof(rq)); 670c0b7ea2SNick Crews rq.op = EC_OP_GET; 680c0b7ea2SNick Crews put_unaligned_le32(prop_msg->property_id, rq.property_id); 690c0b7ea2SNick Crews 700c0b7ea2SNick Crews ret = send_property_msg(ec, &rq, &rs); 710c0b7ea2SNick Crews if (ret < 0) 720c0b7ea2SNick Crews return ret; 730c0b7ea2SNick Crews 740c0b7ea2SNick Crews prop_msg->length = rs.length; 750c0b7ea2SNick Crews memcpy(prop_msg->data, rs.data, rs.length); 760c0b7ea2SNick Crews 770c0b7ea2SNick Crews return 0; 780c0b7ea2SNick Crews } 790c0b7ea2SNick Crews EXPORT_SYMBOL_GPL(wilco_ec_get_property); 800c0b7ea2SNick Crews 810c0b7ea2SNick Crews int wilco_ec_set_property(struct wilco_ec_device *ec, 820c0b7ea2SNick Crews struct wilco_ec_property_msg *prop_msg) 830c0b7ea2SNick Crews { 840c0b7ea2SNick Crews struct ec_property_request rq; 850c0b7ea2SNick Crews struct ec_property_response rs; 860c0b7ea2SNick Crews int ret; 870c0b7ea2SNick Crews 880c0b7ea2SNick Crews memset(&rq, 0, sizeof(rq)); 890c0b7ea2SNick Crews rq.op = EC_OP_SET; 900c0b7ea2SNick Crews put_unaligned_le32(prop_msg->property_id, rq.property_id); 910c0b7ea2SNick Crews rq.length = prop_msg->length; 920c0b7ea2SNick Crews memcpy(rq.data, prop_msg->data, prop_msg->length); 930c0b7ea2SNick Crews 940c0b7ea2SNick Crews ret = send_property_msg(ec, &rq, &rs); 950c0b7ea2SNick Crews if (ret < 0) 960c0b7ea2SNick Crews return ret; 970c0b7ea2SNick Crews if (rs.length != prop_msg->length) 980c0b7ea2SNick Crews return -EBADMSG; 990c0b7ea2SNick Crews 1000c0b7ea2SNick Crews return 0; 1010c0b7ea2SNick Crews } 1020c0b7ea2SNick Crews EXPORT_SYMBOL_GPL(wilco_ec_set_property); 1030c0b7ea2SNick Crews 1040c0b7ea2SNick Crews int wilco_ec_get_byte_property(struct wilco_ec_device *ec, u32 property_id, 1050c0b7ea2SNick Crews u8 *val) 1060c0b7ea2SNick Crews { 1070c0b7ea2SNick Crews struct wilco_ec_property_msg msg; 1080c0b7ea2SNick Crews int ret; 1090c0b7ea2SNick Crews 1100c0b7ea2SNick Crews msg.property_id = property_id; 1110c0b7ea2SNick Crews 1120c0b7ea2SNick Crews ret = wilco_ec_get_property(ec, &msg); 1130c0b7ea2SNick Crews if (ret < 0) 1140c0b7ea2SNick Crews return ret; 1150c0b7ea2SNick Crews if (msg.length != 1) 1160c0b7ea2SNick Crews return -EBADMSG; 1170c0b7ea2SNick Crews 1180c0b7ea2SNick Crews *val = msg.data[0]; 1190c0b7ea2SNick Crews 1200c0b7ea2SNick Crews return 0; 1210c0b7ea2SNick Crews } 1220c0b7ea2SNick Crews EXPORT_SYMBOL_GPL(wilco_ec_get_byte_property); 1230c0b7ea2SNick Crews 1240c0b7ea2SNick Crews int wilco_ec_set_byte_property(struct wilco_ec_device *ec, u32 property_id, 1250c0b7ea2SNick Crews u8 val) 1260c0b7ea2SNick Crews { 1270c0b7ea2SNick Crews struct wilco_ec_property_msg msg; 1280c0b7ea2SNick Crews 1290c0b7ea2SNick Crews msg.property_id = property_id; 1300c0b7ea2SNick Crews msg.data[0] = val; 1310c0b7ea2SNick Crews msg.length = 1; 1320c0b7ea2SNick Crews 1330c0b7ea2SNick Crews return wilco_ec_set_property(ec, &msg); 1340c0b7ea2SNick Crews } 1350c0b7ea2SNick Crews EXPORT_SYMBOL_GPL(wilco_ec_set_byte_property); 136