1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License version
7 * 2 as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 *
15 */
16#include <linux/i2c.h>
17#include <linux/firmware.h>
18#include <linux/device.h>
19#include <linux/export.h>
20#include "../include/linux/libmsrlisthelper.h"
21#include <linux/module.h>
22#include <linux/slab.h>
23
24/* Tagged binary data container structure definitions. */
25struct tbd_header {
26	u32 tag;          /*!< Tag identifier, also checks endianness */
27	u32 size;         /*!< Container size including this header */
28	u32 version;      /*!< Version, format 0xYYMMDDVV */
29	u32 revision;     /*!< Revision, format 0xYYMMDDVV */
30	u32 config_bits;  /*!< Configuration flag bits set */
31	u32 checksum;     /*!< Global checksum, header included */
32} __packed;
33
34struct tbd_record_header {
35	u32 size;        /*!< Size of record including header */
36	u8 format_id;    /*!< tbd_format_t enumeration values used */
37	u8 packing_key;  /*!< Packing method; 0 = no packing */
38	u16 class_id;    /*!< tbd_class_t enumeration values used */
39} __packed;
40
41struct tbd_data_record_header {
42	u16 next_offset;
43	u16 flags;
44	u16 data_offset;
45	u16 data_size;
46} __packed;
47
48#define TBD_CLASS_DRV_ID 2
49
50static int set_msr_configuration(struct i2c_client *client, uint8_t *bufptr,
51				 unsigned int size)
52{
53	/*
54	 * The configuration data contains any number of sequences where
55	 * the first byte (that is, uint8_t) that marks the number of bytes
56	 * in the sequence to follow, is indeed followed by the indicated
57	 * number of bytes of actual data to be written to sensor.
58	 * By convention, the first two bytes of actual data should be
59	 * understood as an address in the sensor address space (hibyte
60	 * followed by lobyte) where the remaining data in the sequence
61	 * will be written.
62	 */
63
64	u8 *ptr = bufptr;
65
66	while (ptr < bufptr + size) {
67		struct i2c_msg msg = {
68			.addr = client->addr,
69			.flags = 0,
70		};
71		int ret;
72
73		/* How many bytes */
74		msg.len = *ptr++;
75		/* Where the bytes are located */
76		msg.buf = ptr;
77		ptr += msg.len;
78
79		if (ptr > bufptr + size)
80			/* Accessing data beyond bounds is not tolerated */
81			return -EINVAL;
82
83		ret = i2c_transfer(client->adapter, &msg, 1);
84		if (ret < 0) {
85			dev_err(&client->dev, "i2c write error: %d", ret);
86			return ret;
87		}
88	}
89	return 0;
90}
91
92static int parse_and_apply(struct i2c_client *client, uint8_t *buffer,
93			   unsigned int size)
94{
95	u8 *endptr8 = buffer + size;
96	struct tbd_data_record_header *header =
97	    (struct tbd_data_record_header *)buffer;
98
99	/* There may be any number of datasets present */
100	unsigned int dataset = 0;
101
102	do {
103		/* In below, four variables are read from buffer */
104		if ((uint8_t *)header + sizeof(*header) > endptr8)
105			return -EINVAL;
106
107		/* All data should be located within given buffer */
108		if ((uint8_t *)header + header->data_offset +
109		    header->data_size > endptr8)
110			return -EINVAL;
111
112		/* We have a new valid dataset */
113		dataset++;
114		/* See whether there is MSR data */
115		/* If yes, update the reg info */
116		if (header->data_size && (header->flags & 1)) {
117			int ret;
118
119			dev_info(&client->dev,
120				 "New MSR data for sensor driver (dataset %02d) size:%d\n",
121				 dataset, header->data_size);
122			ret = set_msr_configuration(client,
123						    buffer + header->data_offset,
124						    header->data_size);
125			if (ret)
126				return ret;
127		}
128		header = (struct tbd_data_record_header *)(buffer +
129			 header->next_offset);
130	} while (header->next_offset);
131
132	return 0;
133}
134
135int apply_msr_data(struct i2c_client *client, const struct firmware *fw)
136{
137	struct tbd_header *header;
138	struct tbd_record_header *record;
139
140	if (!fw) {
141		dev_warn(&client->dev, "Drv data is not loaded.\n");
142		return -EINVAL;
143	}
144
145	if (sizeof(*header) > fw->size)
146		return -EINVAL;
147
148	header = (struct tbd_header *)fw->data;
149	/* Check that we have drvb block. */
150	if (memcmp(&header->tag, "DRVB", 4))
151		return -EINVAL;
152
153	/* Check the size */
154	if (header->size != fw->size)
155		return -EINVAL;
156
157	if (sizeof(*header) + sizeof(*record) > fw->size)
158		return -EINVAL;
159
160	record = (struct tbd_record_header *)(header + 1);
161	/* Check that class id mathes tbd's drv id. */
162	if (record->class_id != TBD_CLASS_DRV_ID)
163		return -EINVAL;
164
165	/* Size 0 shall not be treated as an error */
166	if (!record->size)
167		return 0;
168
169	return parse_and_apply(client, (uint8_t *)(record + 1), record->size);
170}
171EXPORT_SYMBOL_GPL(apply_msr_data);
172
173int load_msr_list(struct i2c_client *client, char *name,
174		  const struct firmware **fw)
175{
176	int ret = request_firmware(fw, name, &client->dev);
177
178	if (ret) {
179		dev_err(&client->dev,
180			"Error %d while requesting firmware %s\n",
181			ret, name);
182		return ret;
183	}
184	dev_info(&client->dev, "Received %lu bytes drv data\n",
185		 (unsigned long)(*fw)->size);
186
187	return 0;
188}
189EXPORT_SYMBOL_GPL(load_msr_list);
190
191void release_msr_list(struct i2c_client *client, const struct firmware *fw)
192{
193	release_firmware(fw);
194}
195EXPORT_SYMBOL_GPL(release_msr_list);
196
197static int init_msrlisthelper(void)
198{
199	return 0;
200}
201
202static void exit_msrlisthelper(void)
203{
204}
205
206module_init(init_msrlisthelper);
207module_exit(exit_msrlisthelper);
208
209MODULE_AUTHOR("Jukka Kaartinen <jukka.o.kaartinen@intel.com>");
210MODULE_LICENSE("GPL");
211