properties.c 2.9 KB
Newer Older
1 2 3 4 5 6 7
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2019 Google LLC
 */

#include <linux/platform_data/wilco-ec.h>
#include <linux/string.h>
8
#include <asm/unaligned.h>
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

/* 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);