rave-sp-eeprom.c (9605B)
1// SPDX-License-Identifier: GPL-2.0+ 2 3/* 4 * EEPROM driver for RAVE SP 5 * 6 * Copyright (C) 2018 Zodiac Inflight Innovations 7 * 8 */ 9#include <linux/kernel.h> 10#include <linux/mfd/rave-sp.h> 11#include <linux/module.h> 12#include <linux/nvmem-provider.h> 13#include <linux/of_device.h> 14#include <linux/platform_device.h> 15#include <linux/sizes.h> 16 17/** 18 * enum rave_sp_eeprom_access_type - Supported types of EEPROM access 19 * 20 * @RAVE_SP_EEPROM_WRITE: EEPROM write 21 * @RAVE_SP_EEPROM_READ: EEPROM read 22 */ 23enum rave_sp_eeprom_access_type { 24 RAVE_SP_EEPROM_WRITE = 0, 25 RAVE_SP_EEPROM_READ = 1, 26}; 27 28/** 29 * enum rave_sp_eeprom_header_size - EEPROM command header sizes 30 * 31 * @RAVE_SP_EEPROM_HEADER_SMALL: EEPROM header size for "small" devices (< 8K) 32 * @RAVE_SP_EEPROM_HEADER_BIG: EEPROM header size for "big" devices (> 8K) 33 */ 34enum rave_sp_eeprom_header_size { 35 RAVE_SP_EEPROM_HEADER_SMALL = 4U, 36 RAVE_SP_EEPROM_HEADER_BIG = 5U, 37}; 38#define RAVE_SP_EEPROM_HEADER_MAX RAVE_SP_EEPROM_HEADER_BIG 39 40#define RAVE_SP_EEPROM_PAGE_SIZE 32U 41 42/** 43 * struct rave_sp_eeprom_page - RAVE SP EEPROM page 44 * 45 * @type: Access type (see enum rave_sp_eeprom_access_type) 46 * @success: Success flag (Success = 1, Failure = 0) 47 * @data: Read data 48 49 * Note this structure corresponds to RSP_*_EEPROM payload from RAVE 50 * SP ICD 51 */ 52struct rave_sp_eeprom_page { 53 u8 type; 54 u8 success; 55 u8 data[RAVE_SP_EEPROM_PAGE_SIZE]; 56} __packed; 57 58/** 59 * struct rave_sp_eeprom - RAVE SP EEPROM device 60 * 61 * @sp: Pointer to parent RAVE SP device 62 * @mutex: Lock protecting access to EEPROM 63 * @address: EEPROM device address 64 * @header_size: Size of EEPROM command header for this device 65 * @dev: Pointer to corresponding struct device used for logging 66 */ 67struct rave_sp_eeprom { 68 struct rave_sp *sp; 69 struct mutex mutex; 70 u8 address; 71 unsigned int header_size; 72 struct device *dev; 73}; 74 75/** 76 * rave_sp_eeprom_io - Low-level part of EEPROM page access 77 * 78 * @eeprom: EEPROM device to write to 79 * @type: EEPROM access type (read or write) 80 * @idx: number of the EEPROM page 81 * @page: Data to write or buffer to store result (via page->data) 82 * 83 * This function does all of the low-level work required to perform a 84 * EEPROM access. This includes formatting correct command payload, 85 * sending it and checking received results. 86 * 87 * Returns zero in case of success or negative error code in 88 * case of failure. 89 */ 90static int rave_sp_eeprom_io(struct rave_sp_eeprom *eeprom, 91 enum rave_sp_eeprom_access_type type, 92 u16 idx, 93 struct rave_sp_eeprom_page *page) 94{ 95 const bool is_write = type == RAVE_SP_EEPROM_WRITE; 96 const unsigned int data_size = is_write ? sizeof(page->data) : 0; 97 const unsigned int cmd_size = eeprom->header_size + data_size; 98 const unsigned int rsp_size = 99 is_write ? sizeof(*page) - sizeof(page->data) : sizeof(*page); 100 unsigned int offset = 0; 101 u8 cmd[RAVE_SP_EEPROM_HEADER_MAX + sizeof(page->data)]; 102 int ret; 103 104 if (WARN_ON(cmd_size > sizeof(cmd))) 105 return -EINVAL; 106 107 cmd[offset++] = eeprom->address; 108 cmd[offset++] = 0; 109 cmd[offset++] = type; 110 cmd[offset++] = idx; 111 112 /* 113 * If there's still room in this command's header it means we 114 * are talkin to EEPROM that uses 16-bit page numbers and we 115 * have to specify index's MSB in payload as well. 116 */ 117 if (offset < eeprom->header_size) 118 cmd[offset++] = idx >> 8; 119 /* 120 * Copy our data to write to command buffer first. In case of 121 * a read data_size should be zero and memcpy would become a 122 * no-op 123 */ 124 memcpy(&cmd[offset], page->data, data_size); 125 126 ret = rave_sp_exec(eeprom->sp, cmd, cmd_size, page, rsp_size); 127 if (ret) 128 return ret; 129 130 if (page->type != type) 131 return -EPROTO; 132 133 if (!page->success) 134 return -EIO; 135 136 return 0; 137} 138 139/** 140 * rave_sp_eeprom_page_access - Access single EEPROM page 141 * 142 * @eeprom: EEPROM device to access 143 * @type: Access type to perform (read or write) 144 * @offset: Offset within EEPROM to access 145 * @data: Data buffer 146 * @data_len: Size of the data buffer 147 * 148 * This function performs a generic access to a single page or a 149 * portion thereof. Requested access MUST NOT cross the EEPROM page 150 * boundary. 151 * 152 * Returns zero in case of success or negative error code in 153 * case of failure. 154 */ 155static int 156rave_sp_eeprom_page_access(struct rave_sp_eeprom *eeprom, 157 enum rave_sp_eeprom_access_type type, 158 unsigned int offset, u8 *data, 159 size_t data_len) 160{ 161 const unsigned int page_offset = offset % RAVE_SP_EEPROM_PAGE_SIZE; 162 const unsigned int page_nr = offset / RAVE_SP_EEPROM_PAGE_SIZE; 163 struct rave_sp_eeprom_page page; 164 int ret; 165 166 /* 167 * This function will not work if data access we've been asked 168 * to do is crossing EEPROM page boundary. Normally this 169 * should never happen and getting here would indicate a bug 170 * in the code. 171 */ 172 if (WARN_ON(data_len > sizeof(page.data) - page_offset)) 173 return -EINVAL; 174 175 if (type == RAVE_SP_EEPROM_WRITE) { 176 /* 177 * If doing a partial write we need to do a read first 178 * to fill the rest of the page with correct data. 179 */ 180 if (data_len < RAVE_SP_EEPROM_PAGE_SIZE) { 181 ret = rave_sp_eeprom_io(eeprom, RAVE_SP_EEPROM_READ, 182 page_nr, &page); 183 if (ret) 184 return ret; 185 } 186 187 memcpy(&page.data[page_offset], data, data_len); 188 } 189 190 ret = rave_sp_eeprom_io(eeprom, type, page_nr, &page); 191 if (ret) 192 return ret; 193 194 /* 195 * Since we receive the result of the read via 'page.data' 196 * buffer we need to copy that to 'data' 197 */ 198 if (type == RAVE_SP_EEPROM_READ) 199 memcpy(data, &page.data[page_offset], data_len); 200 201 return 0; 202} 203 204/** 205 * rave_sp_eeprom_access - Access EEPROM data 206 * 207 * @eeprom: EEPROM device to access 208 * @type: Access type to perform (read or write) 209 * @offset: Offset within EEPROM to access 210 * @data: Data buffer 211 * @data_len: Size of the data buffer 212 * 213 * This function performs a generic access (either read or write) at 214 * arbitrary offset (not necessary page aligned) of arbitrary length 215 * (is not constrained by EEPROM page size). 216 * 217 * Returns zero in case of success or negative error code in case of 218 * failure. 219 */ 220static int rave_sp_eeprom_access(struct rave_sp_eeprom *eeprom, 221 enum rave_sp_eeprom_access_type type, 222 unsigned int offset, u8 *data, 223 unsigned int data_len) 224{ 225 unsigned int residue; 226 unsigned int chunk; 227 unsigned int head; 228 int ret; 229 230 mutex_lock(&eeprom->mutex); 231 232 head = offset % RAVE_SP_EEPROM_PAGE_SIZE; 233 residue = data_len; 234 235 do { 236 /* 237 * First iteration, if we are doing an access that is 238 * not 32-byte aligned, we need to access only data up 239 * to a page boundary to avoid corssing it in 240 * rave_sp_eeprom_page_access() 241 */ 242 if (unlikely(head)) { 243 chunk = RAVE_SP_EEPROM_PAGE_SIZE - head; 244 /* 245 * This can only happen once per 246 * rave_sp_eeprom_access() call, so we set 247 * head to zero to process all the other 248 * iterations normally. 249 */ 250 head = 0; 251 } else { 252 chunk = RAVE_SP_EEPROM_PAGE_SIZE; 253 } 254 255 /* 256 * We should never read more that 'residue' bytes 257 */ 258 chunk = min(chunk, residue); 259 ret = rave_sp_eeprom_page_access(eeprom, type, offset, 260 data, chunk); 261 if (ret) 262 goto out; 263 264 residue -= chunk; 265 offset += chunk; 266 data += chunk; 267 } while (residue); 268out: 269 mutex_unlock(&eeprom->mutex); 270 return ret; 271} 272 273static int rave_sp_eeprom_reg_read(void *eeprom, unsigned int offset, 274 void *val, size_t bytes) 275{ 276 return rave_sp_eeprom_access(eeprom, RAVE_SP_EEPROM_READ, 277 offset, val, bytes); 278} 279 280static int rave_sp_eeprom_reg_write(void *eeprom, unsigned int offset, 281 void *val, size_t bytes) 282{ 283 return rave_sp_eeprom_access(eeprom, RAVE_SP_EEPROM_WRITE, 284 offset, val, bytes); 285} 286 287static int rave_sp_eeprom_probe(struct platform_device *pdev) 288{ 289 struct device *dev = &pdev->dev; 290 struct rave_sp *sp = dev_get_drvdata(dev->parent); 291 struct device_node *np = dev->of_node; 292 struct nvmem_config config = { 0 }; 293 struct rave_sp_eeprom *eeprom; 294 struct nvmem_device *nvmem; 295 u32 reg[2], size; 296 297 if (of_property_read_u32_array(np, "reg", reg, ARRAY_SIZE(reg))) { 298 dev_err(dev, "Failed to parse \"reg\" property\n"); 299 return -EINVAL; 300 } 301 302 size = reg[1]; 303 /* 304 * Per ICD, we have no more than 2 bytes to specify EEPROM 305 * page. 306 */ 307 if (size > U16_MAX * RAVE_SP_EEPROM_PAGE_SIZE) { 308 dev_err(dev, "Specified size is too big\n"); 309 return -EINVAL; 310 } 311 312 eeprom = devm_kzalloc(dev, sizeof(*eeprom), GFP_KERNEL); 313 if (!eeprom) 314 return -ENOMEM; 315 316 eeprom->address = reg[0]; 317 eeprom->sp = sp; 318 eeprom->dev = dev; 319 320 if (size > SZ_8K) 321 eeprom->header_size = RAVE_SP_EEPROM_HEADER_BIG; 322 else 323 eeprom->header_size = RAVE_SP_EEPROM_HEADER_SMALL; 324 325 mutex_init(&eeprom->mutex); 326 327 config.id = -1; 328 of_property_read_string(np, "zii,eeprom-name", &config.name); 329 config.priv = eeprom; 330 config.dev = dev; 331 config.size = size; 332 config.reg_read = rave_sp_eeprom_reg_read; 333 config.reg_write = rave_sp_eeprom_reg_write; 334 config.word_size = 1; 335 config.stride = 1; 336 337 nvmem = devm_nvmem_register(dev, &config); 338 339 return PTR_ERR_OR_ZERO(nvmem); 340} 341 342static const struct of_device_id rave_sp_eeprom_of_match[] = { 343 { .compatible = "zii,rave-sp-eeprom" }, 344 {} 345}; 346MODULE_DEVICE_TABLE(of, rave_sp_eeprom_of_match); 347 348static struct platform_driver rave_sp_eeprom_driver = { 349 .probe = rave_sp_eeprom_probe, 350 .driver = { 351 .name = KBUILD_MODNAME, 352 .of_match_table = rave_sp_eeprom_of_match, 353 }, 354}; 355module_platform_driver(rave_sp_eeprom_driver); 356 357MODULE_LICENSE("GPL"); 358MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>"); 359MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>"); 360MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); 361MODULE_DESCRIPTION("RAVE SP EEPROM driver");