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 50 static 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 92 static 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 135 int 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 } 171 EXPORT_SYMBOL_GPL(apply_msr_data); 172 173 int 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 } 189 EXPORT_SYMBOL_GPL(load_msr_list); 190 191 void release_msr_list(struct i2c_client *client, const struct firmware *fw) 192 { 193 release_firmware(fw); 194 } 195 EXPORT_SYMBOL_GPL(release_msr_list); 196 197 static int init_msrlisthelper(void) 198 { 199 return 0; 200 } 201 202 static void exit_msrlisthelper(void) 203 { 204 } 205 206 module_init(init_msrlisthelper); 207 module_exit(exit_msrlisthelper); 208 209 MODULE_AUTHOR("Jukka Kaartinen <jukka.o.kaartinen@intel.com>"); 210 MODULE_LICENSE("GPL"); 211