1
2
3
4
5
6
7
8
9
10
11
12
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
25struct tbd_header {
26 u32 tag;
27 u32 size;
28 u32 version;
29 u32 revision;
30 u32 config_bits;
31 u32 checksum;
32} __packed;
33
34struct tbd_record_header {
35 u32 size;
36 u8 format_id;
37 u8 packing_key;
38 u16 class_id;
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
55
56
57
58
59
60
61
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
74 msg.len = *ptr++;
75
76 msg.buf = ptr;
77 ptr += msg.len;
78
79 if (ptr > bufptr + size)
80
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
100 unsigned int dataset = 0;
101
102 do {
103
104 if ((uint8_t *)header + sizeof(*header) > endptr8)
105 return -EINVAL;
106
107
108 if ((uint8_t *)header + header->data_offset +
109 header->data_size > endptr8)
110 return -EINVAL;
111
112
113 dataset++;
114
115
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
150 if (memcmp(&header->tag, "DRVB", 4))
151 return -EINVAL;
152
153
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
162 if (record->class_id != TBD_CLASS_DRV_ID)
163 return -EINVAL;
164
165
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