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. */
25 struct 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 
34 struct 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 
41 struct 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 
set_msr_configuration(struct i2c_client * client,uint8_t * bufptr,unsigned int size)50 static int set_msr_configuration(struct i2c_client *client, uint8_t *bufptr,
51 				 unsigned int size)
52 {
53 	/* The configuration data contains any number of sequences where
54 	 * the first byte (that is, uint8_t) that marks the number of bytes
55 	 * in the sequence to follow, is indeed followed by the indicated
56 	 * number of bytes of actual data to be written to sensor.
57 	 * By convention, the first two bytes of actual data should be
58 	 * understood as an address in the sensor address space (hibyte
59 	 * followed by lobyte) where the remaining data in the sequence
60 	 * will be written. */
61 
62 	u8 *ptr = bufptr;
63 
64 	while (ptr < bufptr + size) {
65 		struct i2c_msg msg = {
66 			.addr = client->addr,
67 			.flags = 0,
68 		};
69 		int ret;
70 
71 		/* How many bytes */
72 		msg.len = *ptr++;
73 		/* Where the bytes are located */
74 		msg.buf = ptr;
75 		ptr += msg.len;
76 
77 		if (ptr > bufptr + size)
78 			/* Accessing data beyond bounds is not tolerated */
79 			return -EINVAL;
80 
81 		ret = i2c_transfer(client->adapter, &msg, 1);
82 		if (ret < 0) {
83 			dev_err(&client->dev, "i2c write error: %d", ret);
84 			return ret;
85 		}
86 	}
87 	return 0;
88 }
89 
parse_and_apply(struct i2c_client * client,uint8_t * buffer,unsigned int size)90 static int parse_and_apply(struct i2c_client *client, uint8_t *buffer,
91 			   unsigned int size)
92 {
93 	u8 *endptr8 = buffer + size;
94 	struct tbd_data_record_header *header =
95 	    (struct tbd_data_record_header *)buffer;
96 
97 	/* There may be any number of datasets present */
98 	unsigned int dataset = 0;
99 
100 	do {
101 		/* In below, four variables are read from buffer */
102 		if ((uint8_t *)header + sizeof(*header) > endptr8)
103 			return -EINVAL;
104 
105 		/* All data should be located within given buffer */
106 		if ((uint8_t *)header + header->data_offset +
107 		    header->data_size > endptr8)
108 			return -EINVAL;
109 
110 		/* We have a new valid dataset */
111 		dataset++;
112 		/* See whether there is MSR data */
113 		/* If yes, update the reg info */
114 		if (header->data_size && (header->flags & 1)) {
115 			int ret;
116 
117 			dev_info(&client->dev,
118 				 "New MSR data for sensor driver (dataset %02d) size:%d\n",
119 				 dataset, header->data_size);
120 			ret = set_msr_configuration(client,
121 						    buffer + header->data_offset,
122 						    header->data_size);
123 			if (ret)
124 				return ret;
125 		}
126 		header = (struct tbd_data_record_header *)(buffer +
127 			 header->next_offset);
128 	} while (header->next_offset);
129 
130 	return 0;
131 }
132 
apply_msr_data(struct i2c_client * client,const struct firmware * fw)133 int apply_msr_data(struct i2c_client *client, const struct firmware *fw)
134 {
135 	struct tbd_header *header;
136 	struct tbd_record_header *record;
137 
138 	if (!fw) {
139 		dev_warn(&client->dev, "Drv data is not loaded.\n");
140 		return -EINVAL;
141 	}
142 
143 	if (sizeof(*header) > fw->size)
144 		return -EINVAL;
145 
146 	header = (struct tbd_header *)fw->data;
147 	/* Check that we have drvb block. */
148 	if (memcmp(&header->tag, "DRVB", 4))
149 		return -EINVAL;
150 
151 	/* Check the size */
152 	if (header->size != fw->size)
153 		return -EINVAL;
154 
155 	if (sizeof(*header) + sizeof(*record) > fw->size)
156 		return -EINVAL;
157 
158 	record = (struct tbd_record_header *)(header + 1);
159 	/* Check that class id mathes tbd's drv id. */
160 	if (record->class_id != TBD_CLASS_DRV_ID)
161 		return -EINVAL;
162 
163 	/* Size 0 shall not be treated as an error */
164 	if (!record->size)
165 		return 0;
166 
167 	return parse_and_apply(client, (uint8_t *)(record + 1), record->size);
168 }
169 EXPORT_SYMBOL_GPL(apply_msr_data);
170 
load_msr_list(struct i2c_client * client,char * name,const struct firmware ** fw)171 int load_msr_list(struct i2c_client *client, char *name,
172 		  const struct firmware **fw)
173 {
174 	int ret = request_firmware(fw, name, &client->dev);
175 
176 	if (ret) {
177 		dev_err(&client->dev,
178 			"Error %d while requesting firmware %s\n",
179 			ret, name);
180 		return ret;
181 	}
182 	dev_info(&client->dev, "Received %lu bytes drv data\n",
183 		 (unsigned long)(*fw)->size);
184 
185 	return 0;
186 }
187 EXPORT_SYMBOL_GPL(load_msr_list);
188 
release_msr_list(struct i2c_client * client,const struct firmware * fw)189 void release_msr_list(struct i2c_client *client, const struct firmware *fw)
190 {
191 	release_firmware(fw);
192 }
193 EXPORT_SYMBOL_GPL(release_msr_list);
194 
init_msrlisthelper(void)195 static int init_msrlisthelper(void)
196 {
197 	return 0;
198 }
199 
exit_msrlisthelper(void)200 static void exit_msrlisthelper(void)
201 {
202 }
203 
204 module_init(init_msrlisthelper);
205 module_exit(exit_msrlisthelper);
206 
207 MODULE_AUTHOR("Jukka Kaartinen <jukka.o.kaartinen@intel.com>");
208 MODULE_LICENSE("GPL");
209