18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * EEPROM driver for RAVE SP 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2018 Zodiac Inflight Innovations 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/mfd/rave-sp.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/nvmem-provider.h> 138c2ecf20Sopenharmony_ci#include <linux/of_device.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/sizes.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/** 188c2ecf20Sopenharmony_ci * enum rave_sp_eeprom_access_type - Supported types of EEPROM access 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * @RAVE_SP_EEPROM_WRITE: EEPROM write 218c2ecf20Sopenharmony_ci * @RAVE_SP_EEPROM_READ: EEPROM read 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_cienum rave_sp_eeprom_access_type { 248c2ecf20Sopenharmony_ci RAVE_SP_EEPROM_WRITE = 0, 258c2ecf20Sopenharmony_ci RAVE_SP_EEPROM_READ = 1, 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/** 298c2ecf20Sopenharmony_ci * enum rave_sp_eeprom_header_size - EEPROM command header sizes 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * @RAVE_SP_EEPROM_HEADER_SMALL: EEPROM header size for "small" devices (< 8K) 328c2ecf20Sopenharmony_ci * @RAVE_SP_EEPROM_HEADER_BIG: EEPROM header size for "big" devices (> 8K) 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_cienum rave_sp_eeprom_header_size { 358c2ecf20Sopenharmony_ci RAVE_SP_EEPROM_HEADER_SMALL = 4U, 368c2ecf20Sopenharmony_ci RAVE_SP_EEPROM_HEADER_BIG = 5U, 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci#define RAVE_SP_EEPROM_HEADER_MAX RAVE_SP_EEPROM_HEADER_BIG 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define RAVE_SP_EEPROM_PAGE_SIZE 32U 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/** 438c2ecf20Sopenharmony_ci * struct rave_sp_eeprom_page - RAVE SP EEPROM page 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * @type: Access type (see enum rave_sp_eeprom_access_type) 468c2ecf20Sopenharmony_ci * @success: Success flag (Success = 1, Failure = 0) 478c2ecf20Sopenharmony_ci * @data: Read data 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci * Note this structure corresponds to RSP_*_EEPROM payload from RAVE 508c2ecf20Sopenharmony_ci * SP ICD 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistruct rave_sp_eeprom_page { 538c2ecf20Sopenharmony_ci u8 type; 548c2ecf20Sopenharmony_ci u8 success; 558c2ecf20Sopenharmony_ci u8 data[RAVE_SP_EEPROM_PAGE_SIZE]; 568c2ecf20Sopenharmony_ci} __packed; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/** 598c2ecf20Sopenharmony_ci * struct rave_sp_eeprom - RAVE SP EEPROM device 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * @sp: Pointer to parent RAVE SP device 628c2ecf20Sopenharmony_ci * @mutex: Lock protecting access to EEPROM 638c2ecf20Sopenharmony_ci * @address: EEPROM device address 648c2ecf20Sopenharmony_ci * @header_size: Size of EEPROM command header for this device 658c2ecf20Sopenharmony_ci * @dev: Pointer to corresponding struct device used for logging 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistruct rave_sp_eeprom { 688c2ecf20Sopenharmony_ci struct rave_sp *sp; 698c2ecf20Sopenharmony_ci struct mutex mutex; 708c2ecf20Sopenharmony_ci u8 address; 718c2ecf20Sopenharmony_ci unsigned int header_size; 728c2ecf20Sopenharmony_ci struct device *dev; 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/** 768c2ecf20Sopenharmony_ci * rave_sp_eeprom_io - Low-level part of EEPROM page access 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * @eeprom: EEPROM device to write to 798c2ecf20Sopenharmony_ci * @type: EEPROM access type (read or write) 808c2ecf20Sopenharmony_ci * @idx: number of the EEPROM page 818c2ecf20Sopenharmony_ci * @page: Data to write or buffer to store result (via page->data) 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * This function does all of the low-level work required to perform a 848c2ecf20Sopenharmony_ci * EEPROM access. This includes formatting correct command payload, 858c2ecf20Sopenharmony_ci * sending it and checking received results. 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * Returns zero in case of success or negative error code in 888c2ecf20Sopenharmony_ci * case of failure. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_cistatic int rave_sp_eeprom_io(struct rave_sp_eeprom *eeprom, 918c2ecf20Sopenharmony_ci enum rave_sp_eeprom_access_type type, 928c2ecf20Sopenharmony_ci u16 idx, 938c2ecf20Sopenharmony_ci struct rave_sp_eeprom_page *page) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci const bool is_write = type == RAVE_SP_EEPROM_WRITE; 968c2ecf20Sopenharmony_ci const unsigned int data_size = is_write ? sizeof(page->data) : 0; 978c2ecf20Sopenharmony_ci const unsigned int cmd_size = eeprom->header_size + data_size; 988c2ecf20Sopenharmony_ci const unsigned int rsp_size = 998c2ecf20Sopenharmony_ci is_write ? sizeof(*page) - sizeof(page->data) : sizeof(*page); 1008c2ecf20Sopenharmony_ci unsigned int offset = 0; 1018c2ecf20Sopenharmony_ci u8 cmd[RAVE_SP_EEPROM_HEADER_MAX + sizeof(page->data)]; 1028c2ecf20Sopenharmony_ci int ret; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (WARN_ON(cmd_size > sizeof(cmd))) 1058c2ecf20Sopenharmony_ci return -EINVAL; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci cmd[offset++] = eeprom->address; 1088c2ecf20Sopenharmony_ci cmd[offset++] = 0; 1098c2ecf20Sopenharmony_ci cmd[offset++] = type; 1108c2ecf20Sopenharmony_ci cmd[offset++] = idx; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* 1138c2ecf20Sopenharmony_ci * If there's still room in this command's header it means we 1148c2ecf20Sopenharmony_ci * are talkin to EEPROM that uses 16-bit page numbers and we 1158c2ecf20Sopenharmony_ci * have to specify index's MSB in payload as well. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci if (offset < eeprom->header_size) 1188c2ecf20Sopenharmony_ci cmd[offset++] = idx >> 8; 1198c2ecf20Sopenharmony_ci /* 1208c2ecf20Sopenharmony_ci * Copy our data to write to command buffer first. In case of 1218c2ecf20Sopenharmony_ci * a read data_size should be zero and memcpy would become a 1228c2ecf20Sopenharmony_ci * no-op 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci memcpy(&cmd[offset], page->data, data_size); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ret = rave_sp_exec(eeprom->sp, cmd, cmd_size, page, rsp_size); 1278c2ecf20Sopenharmony_ci if (ret) 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (page->type != type) 1318c2ecf20Sopenharmony_ci return -EPROTO; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (!page->success) 1348c2ecf20Sopenharmony_ci return -EIO; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/** 1408c2ecf20Sopenharmony_ci * rave_sp_eeprom_page_access - Access single EEPROM page 1418c2ecf20Sopenharmony_ci * 1428c2ecf20Sopenharmony_ci * @eeprom: EEPROM device to access 1438c2ecf20Sopenharmony_ci * @type: Access type to perform (read or write) 1448c2ecf20Sopenharmony_ci * @offset: Offset within EEPROM to access 1458c2ecf20Sopenharmony_ci * @data: Data buffer 1468c2ecf20Sopenharmony_ci * @data_len: Size of the data buffer 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * This function performs a generic access to a single page or a 1498c2ecf20Sopenharmony_ci * portion thereof. Requested access MUST NOT cross the EEPROM page 1508c2ecf20Sopenharmony_ci * boundary. 1518c2ecf20Sopenharmony_ci * 1528c2ecf20Sopenharmony_ci * Returns zero in case of success or negative error code in 1538c2ecf20Sopenharmony_ci * case of failure. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_cistatic int 1568c2ecf20Sopenharmony_cirave_sp_eeprom_page_access(struct rave_sp_eeprom *eeprom, 1578c2ecf20Sopenharmony_ci enum rave_sp_eeprom_access_type type, 1588c2ecf20Sopenharmony_ci unsigned int offset, u8 *data, 1598c2ecf20Sopenharmony_ci size_t data_len) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci const unsigned int page_offset = offset % RAVE_SP_EEPROM_PAGE_SIZE; 1628c2ecf20Sopenharmony_ci const unsigned int page_nr = offset / RAVE_SP_EEPROM_PAGE_SIZE; 1638c2ecf20Sopenharmony_ci struct rave_sp_eeprom_page page; 1648c2ecf20Sopenharmony_ci int ret; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* 1678c2ecf20Sopenharmony_ci * This function will not work if data access we've been asked 1688c2ecf20Sopenharmony_ci * to do is crossing EEPROM page boundary. Normally this 1698c2ecf20Sopenharmony_ci * should never happen and getting here would indicate a bug 1708c2ecf20Sopenharmony_ci * in the code. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci if (WARN_ON(data_len > sizeof(page.data) - page_offset)) 1738c2ecf20Sopenharmony_ci return -EINVAL; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (type == RAVE_SP_EEPROM_WRITE) { 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * If doing a partial write we need to do a read first 1788c2ecf20Sopenharmony_ci * to fill the rest of the page with correct data. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci if (data_len < RAVE_SP_EEPROM_PAGE_SIZE) { 1818c2ecf20Sopenharmony_ci ret = rave_sp_eeprom_io(eeprom, RAVE_SP_EEPROM_READ, 1828c2ecf20Sopenharmony_ci page_nr, &page); 1838c2ecf20Sopenharmony_ci if (ret) 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci memcpy(&page.data[page_offset], data, data_len); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ret = rave_sp_eeprom_io(eeprom, type, page_nr, &page); 1918c2ecf20Sopenharmony_ci if (ret) 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* 1958c2ecf20Sopenharmony_ci * Since we receive the result of the read via 'page.data' 1968c2ecf20Sopenharmony_ci * buffer we need to copy that to 'data' 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci if (type == RAVE_SP_EEPROM_READ) 1998c2ecf20Sopenharmony_ci memcpy(data, &page.data[page_offset], data_len); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/** 2058c2ecf20Sopenharmony_ci * rave_sp_eeprom_access - Access EEPROM data 2068c2ecf20Sopenharmony_ci * 2078c2ecf20Sopenharmony_ci * @eeprom: EEPROM device to access 2088c2ecf20Sopenharmony_ci * @type: Access type to perform (read or write) 2098c2ecf20Sopenharmony_ci * @offset: Offset within EEPROM to access 2108c2ecf20Sopenharmony_ci * @data: Data buffer 2118c2ecf20Sopenharmony_ci * @data_len: Size of the data buffer 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * This function performs a generic access (either read or write) at 2148c2ecf20Sopenharmony_ci * arbitrary offset (not necessary page aligned) of arbitrary length 2158c2ecf20Sopenharmony_ci * (is not constrained by EEPROM page size). 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci * Returns zero in case of success or negative error code in case of 2188c2ecf20Sopenharmony_ci * failure. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_cistatic int rave_sp_eeprom_access(struct rave_sp_eeprom *eeprom, 2218c2ecf20Sopenharmony_ci enum rave_sp_eeprom_access_type type, 2228c2ecf20Sopenharmony_ci unsigned int offset, u8 *data, 2238c2ecf20Sopenharmony_ci unsigned int data_len) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci unsigned int residue; 2268c2ecf20Sopenharmony_ci unsigned int chunk; 2278c2ecf20Sopenharmony_ci unsigned int head; 2288c2ecf20Sopenharmony_ci int ret; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci mutex_lock(&eeprom->mutex); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci head = offset % RAVE_SP_EEPROM_PAGE_SIZE; 2338c2ecf20Sopenharmony_ci residue = data_len; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci do { 2368c2ecf20Sopenharmony_ci /* 2378c2ecf20Sopenharmony_ci * First iteration, if we are doing an access that is 2388c2ecf20Sopenharmony_ci * not 32-byte aligned, we need to access only data up 2398c2ecf20Sopenharmony_ci * to a page boundary to avoid corssing it in 2408c2ecf20Sopenharmony_ci * rave_sp_eeprom_page_access() 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci if (unlikely(head)) { 2438c2ecf20Sopenharmony_ci chunk = RAVE_SP_EEPROM_PAGE_SIZE - head; 2448c2ecf20Sopenharmony_ci /* 2458c2ecf20Sopenharmony_ci * This can only happen once per 2468c2ecf20Sopenharmony_ci * rave_sp_eeprom_access() call, so we set 2478c2ecf20Sopenharmony_ci * head to zero to process all the other 2488c2ecf20Sopenharmony_ci * iterations normally. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci head = 0; 2518c2ecf20Sopenharmony_ci } else { 2528c2ecf20Sopenharmony_ci chunk = RAVE_SP_EEPROM_PAGE_SIZE; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* 2568c2ecf20Sopenharmony_ci * We should never read more that 'residue' bytes 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci chunk = min(chunk, residue); 2598c2ecf20Sopenharmony_ci ret = rave_sp_eeprom_page_access(eeprom, type, offset, 2608c2ecf20Sopenharmony_ci data, chunk); 2618c2ecf20Sopenharmony_ci if (ret) 2628c2ecf20Sopenharmony_ci goto out; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci residue -= chunk; 2658c2ecf20Sopenharmony_ci offset += chunk; 2668c2ecf20Sopenharmony_ci data += chunk; 2678c2ecf20Sopenharmony_ci } while (residue); 2688c2ecf20Sopenharmony_ciout: 2698c2ecf20Sopenharmony_ci mutex_unlock(&eeprom->mutex); 2708c2ecf20Sopenharmony_ci return ret; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int rave_sp_eeprom_reg_read(void *eeprom, unsigned int offset, 2748c2ecf20Sopenharmony_ci void *val, size_t bytes) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci return rave_sp_eeprom_access(eeprom, RAVE_SP_EEPROM_READ, 2778c2ecf20Sopenharmony_ci offset, val, bytes); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int rave_sp_eeprom_reg_write(void *eeprom, unsigned int offset, 2818c2ecf20Sopenharmony_ci void *val, size_t bytes) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci return rave_sp_eeprom_access(eeprom, RAVE_SP_EEPROM_WRITE, 2848c2ecf20Sopenharmony_ci offset, val, bytes); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int rave_sp_eeprom_probe(struct platform_device *pdev) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2908c2ecf20Sopenharmony_ci struct rave_sp *sp = dev_get_drvdata(dev->parent); 2918c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 2928c2ecf20Sopenharmony_ci struct nvmem_config config = { 0 }; 2938c2ecf20Sopenharmony_ci struct rave_sp_eeprom *eeprom; 2948c2ecf20Sopenharmony_ci struct nvmem_device *nvmem; 2958c2ecf20Sopenharmony_ci u32 reg[2], size; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (of_property_read_u32_array(np, "reg", reg, ARRAY_SIZE(reg))) { 2988c2ecf20Sopenharmony_ci dev_err(dev, "Failed to parse \"reg\" property\n"); 2998c2ecf20Sopenharmony_ci return -EINVAL; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci size = reg[1]; 3038c2ecf20Sopenharmony_ci /* 3048c2ecf20Sopenharmony_ci * Per ICD, we have no more than 2 bytes to specify EEPROM 3058c2ecf20Sopenharmony_ci * page. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci if (size > U16_MAX * RAVE_SP_EEPROM_PAGE_SIZE) { 3088c2ecf20Sopenharmony_ci dev_err(dev, "Specified size is too big\n"); 3098c2ecf20Sopenharmony_ci return -EINVAL; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci eeprom = devm_kzalloc(dev, sizeof(*eeprom), GFP_KERNEL); 3138c2ecf20Sopenharmony_ci if (!eeprom) 3148c2ecf20Sopenharmony_ci return -ENOMEM; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci eeprom->address = reg[0]; 3178c2ecf20Sopenharmony_ci eeprom->sp = sp; 3188c2ecf20Sopenharmony_ci eeprom->dev = dev; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (size > SZ_8K) 3218c2ecf20Sopenharmony_ci eeprom->header_size = RAVE_SP_EEPROM_HEADER_BIG; 3228c2ecf20Sopenharmony_ci else 3238c2ecf20Sopenharmony_ci eeprom->header_size = RAVE_SP_EEPROM_HEADER_SMALL; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci mutex_init(&eeprom->mutex); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci config.id = -1; 3288c2ecf20Sopenharmony_ci of_property_read_string(np, "zii,eeprom-name", &config.name); 3298c2ecf20Sopenharmony_ci config.priv = eeprom; 3308c2ecf20Sopenharmony_ci config.dev = dev; 3318c2ecf20Sopenharmony_ci config.size = size; 3328c2ecf20Sopenharmony_ci config.reg_read = rave_sp_eeprom_reg_read; 3338c2ecf20Sopenharmony_ci config.reg_write = rave_sp_eeprom_reg_write; 3348c2ecf20Sopenharmony_ci config.word_size = 1; 3358c2ecf20Sopenharmony_ci config.stride = 1; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci nvmem = devm_nvmem_register(dev, &config); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(nvmem); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic const struct of_device_id rave_sp_eeprom_of_match[] = { 3438c2ecf20Sopenharmony_ci { .compatible = "zii,rave-sp-eeprom" }, 3448c2ecf20Sopenharmony_ci {} 3458c2ecf20Sopenharmony_ci}; 3468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rave_sp_eeprom_of_match); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic struct platform_driver rave_sp_eeprom_driver = { 3498c2ecf20Sopenharmony_ci .probe = rave_sp_eeprom_probe, 3508c2ecf20Sopenharmony_ci .driver = { 3518c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 3528c2ecf20Sopenharmony_ci .of_match_table = rave_sp_eeprom_of_match, 3538c2ecf20Sopenharmony_ci }, 3548c2ecf20Sopenharmony_ci}; 3558c2ecf20Sopenharmony_cimodule_platform_driver(rave_sp_eeprom_driver); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>"); 3598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>"); 3608c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); 3618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RAVE SP EEPROM driver"); 362