1*cf09b7a0SBartosz Golaszewski // SPDX-License-Identifier: GPL-2.0 2*cf09b7a0SBartosz Golaszewski /* 3*cf09b7a0SBartosz Golaszewski * m24lr.c - Sysfs control interface for ST M24LR series RFID/NFC chips 4*cf09b7a0SBartosz Golaszewski * 5*cf09b7a0SBartosz Golaszewski * Copyright (c) 2025 Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com> 6*cf09b7a0SBartosz Golaszewski * 7*cf09b7a0SBartosz Golaszewski * This driver implements both the sysfs-based control interface and EEPROM 8*cf09b7a0SBartosz Golaszewski * access for STMicroelectronics M24LR series chips (e.g., M24LR04E-R). 9*cf09b7a0SBartosz Golaszewski * It provides access to control registers for features such as password 10*cf09b7a0SBartosz Golaszewski * authentication, memory protection, and device configuration. In addition, 11*cf09b7a0SBartosz Golaszewski * it manages read and write operations to the EEPROM region of the chip. 12*cf09b7a0SBartosz Golaszewski */ 13*cf09b7a0SBartosz Golaszewski 14*cf09b7a0SBartosz Golaszewski #include <linux/device.h> 15*cf09b7a0SBartosz Golaszewski #include <linux/i2c.h> 16*cf09b7a0SBartosz Golaszewski #include <linux/module.h> 17*cf09b7a0SBartosz Golaszewski #include <linux/nvmem-provider.h> 18*cf09b7a0SBartosz Golaszewski #include <linux/of.h> 19*cf09b7a0SBartosz Golaszewski #include <linux/of_device.h> 20*cf09b7a0SBartosz Golaszewski #include <linux/regmap.h> 21*cf09b7a0SBartosz Golaszewski 22*cf09b7a0SBartosz Golaszewski #define M24LR_WRITE_TIMEOUT 25u 23*cf09b7a0SBartosz Golaszewski #define M24LR_READ_TIMEOUT (M24LR_WRITE_TIMEOUT) 24*cf09b7a0SBartosz Golaszewski 25*cf09b7a0SBartosz Golaszewski /** 26*cf09b7a0SBartosz Golaszewski * struct m24lr_chip - describes chip-specific sysfs layout 27*cf09b7a0SBartosz Golaszewski * @sss_len: the length of the sss region 28*cf09b7a0SBartosz Golaszewski * @page_size: chip-specific limit on the maximum number of bytes allowed 29*cf09b7a0SBartosz Golaszewski * in a single write operation. 30*cf09b7a0SBartosz Golaszewski * @eeprom_size: size of the EEPROM in byte 31*cf09b7a0SBartosz Golaszewski * 32*cf09b7a0SBartosz Golaszewski * Supports multiple M24LR chip variants (e.g., M24LRxx) by allowing each 33*cf09b7a0SBartosz Golaszewski * to define its own set of sysfs attributes, depending on its available 34*cf09b7a0SBartosz Golaszewski * registers and features. 35*cf09b7a0SBartosz Golaszewski */ 36*cf09b7a0SBartosz Golaszewski struct m24lr_chip { 37*cf09b7a0SBartosz Golaszewski unsigned int sss_len; 38*cf09b7a0SBartosz Golaszewski unsigned int page_size; 39*cf09b7a0SBartosz Golaszewski unsigned int eeprom_size; 40*cf09b7a0SBartosz Golaszewski }; 41*cf09b7a0SBartosz Golaszewski 42*cf09b7a0SBartosz Golaszewski /** 43*cf09b7a0SBartosz Golaszewski * struct m24lr - core driver data for M24LR chip control 44*cf09b7a0SBartosz Golaszewski * @uid: 64 bits unique identifier stored in the device 45*cf09b7a0SBartosz Golaszewski * @sss_len: the length of the sss region 46*cf09b7a0SBartosz Golaszewski * @page_size: chip-specific limit on the maximum number of bytes allowed 47*cf09b7a0SBartosz Golaszewski * in a single write operation. 48*cf09b7a0SBartosz Golaszewski * @eeprom_size: size of the EEPROM in byte 49*cf09b7a0SBartosz Golaszewski * @ctl_regmap: regmap interface for accessing the system parameter sector 50*cf09b7a0SBartosz Golaszewski * @eeprom_regmap: regmap interface for accessing the EEPROM 51*cf09b7a0SBartosz Golaszewski * @lock: mutex to synchronize operations to the device 52*cf09b7a0SBartosz Golaszewski * 53*cf09b7a0SBartosz Golaszewski * Central data structure holding the state and resources used by the 54*cf09b7a0SBartosz Golaszewski * M24LR device driver. 55*cf09b7a0SBartosz Golaszewski */ 56*cf09b7a0SBartosz Golaszewski struct m24lr { 57*cf09b7a0SBartosz Golaszewski u64 uid; 58*cf09b7a0SBartosz Golaszewski unsigned int sss_len; 59*cf09b7a0SBartosz Golaszewski unsigned int page_size; 60*cf09b7a0SBartosz Golaszewski unsigned int eeprom_size; 61*cf09b7a0SBartosz Golaszewski struct regmap *ctl_regmap; 62*cf09b7a0SBartosz Golaszewski struct regmap *eeprom_regmap; 63*cf09b7a0SBartosz Golaszewski struct mutex lock; /* synchronize operations to the device */ 64*cf09b7a0SBartosz Golaszewski }; 65*cf09b7a0SBartosz Golaszewski 66*cf09b7a0SBartosz Golaszewski static const struct regmap_range m24lr_ctl_vo_ranges[] = { 67*cf09b7a0SBartosz Golaszewski regmap_reg_range(0, 63), 68*cf09b7a0SBartosz Golaszewski }; 69*cf09b7a0SBartosz Golaszewski 70*cf09b7a0SBartosz Golaszewski static const struct regmap_access_table m24lr_ctl_vo_table = { 71*cf09b7a0SBartosz Golaszewski .yes_ranges = m24lr_ctl_vo_ranges, 72*cf09b7a0SBartosz Golaszewski .n_yes_ranges = ARRAY_SIZE(m24lr_ctl_vo_ranges), 73*cf09b7a0SBartosz Golaszewski }; 74*cf09b7a0SBartosz Golaszewski 75*cf09b7a0SBartosz Golaszewski static const struct regmap_config m24lr_ctl_regmap_conf = { 76*cf09b7a0SBartosz Golaszewski .name = "m24lr_ctl", 77*cf09b7a0SBartosz Golaszewski .reg_stride = 1, 78*cf09b7a0SBartosz Golaszewski .reg_bits = 16, 79*cf09b7a0SBartosz Golaszewski .val_bits = 8, 80*cf09b7a0SBartosz Golaszewski .disable_locking = false, 81*cf09b7a0SBartosz Golaszewski .cache_type = REGCACHE_RBTREE,/* Flat can't be used, there's huge gap */ 82*cf09b7a0SBartosz Golaszewski .volatile_table = &m24lr_ctl_vo_table, 83*cf09b7a0SBartosz Golaszewski }; 84*cf09b7a0SBartosz Golaszewski 85*cf09b7a0SBartosz Golaszewski /* Chip descriptor for M24LR04E-R variant */ 86*cf09b7a0SBartosz Golaszewski static const struct m24lr_chip m24lr04e_r_chip = { 87*cf09b7a0SBartosz Golaszewski .page_size = 4, 88*cf09b7a0SBartosz Golaszewski .eeprom_size = 512, 89*cf09b7a0SBartosz Golaszewski .sss_len = 4, 90*cf09b7a0SBartosz Golaszewski }; 91*cf09b7a0SBartosz Golaszewski 92*cf09b7a0SBartosz Golaszewski /* Chip descriptor for M24LR16E-R variant */ 93*cf09b7a0SBartosz Golaszewski static const struct m24lr_chip m24lr16e_r_chip = { 94*cf09b7a0SBartosz Golaszewski .page_size = 4, 95*cf09b7a0SBartosz Golaszewski .eeprom_size = 2048, 96*cf09b7a0SBartosz Golaszewski .sss_len = 16, 97*cf09b7a0SBartosz Golaszewski }; 98*cf09b7a0SBartosz Golaszewski 99*cf09b7a0SBartosz Golaszewski /* Chip descriptor for M24LR64E-R variant */ 100*cf09b7a0SBartosz Golaszewski static const struct m24lr_chip m24lr64e_r_chip = { 101*cf09b7a0SBartosz Golaszewski .page_size = 4, 102*cf09b7a0SBartosz Golaszewski .eeprom_size = 8192, 103*cf09b7a0SBartosz Golaszewski .sss_len = 64, 104*cf09b7a0SBartosz Golaszewski }; 105*cf09b7a0SBartosz Golaszewski 106*cf09b7a0SBartosz Golaszewski static const struct i2c_device_id m24lr_ids[] = { 107*cf09b7a0SBartosz Golaszewski { "m24lr04e-r", (kernel_ulong_t)&m24lr04e_r_chip}, 108*cf09b7a0SBartosz Golaszewski { "m24lr16e-r", (kernel_ulong_t)&m24lr16e_r_chip}, 109*cf09b7a0SBartosz Golaszewski { "m24lr64e-r", (kernel_ulong_t)&m24lr64e_r_chip}, 110*cf09b7a0SBartosz Golaszewski { } 111*cf09b7a0SBartosz Golaszewski }; 112*cf09b7a0SBartosz Golaszewski MODULE_DEVICE_TABLE(i2c, m24lr_ids); 113*cf09b7a0SBartosz Golaszewski 114*cf09b7a0SBartosz Golaszewski static const struct of_device_id m24lr_of_match[] = { 115*cf09b7a0SBartosz Golaszewski { .compatible = "st,m24lr04e-r", .data = &m24lr04e_r_chip}, 116*cf09b7a0SBartosz Golaszewski { .compatible = "st,m24lr16e-r", .data = &m24lr16e_r_chip}, 117*cf09b7a0SBartosz Golaszewski { .compatible = "st,m24lr64e-r", .data = &m24lr64e_r_chip}, 118*cf09b7a0SBartosz Golaszewski { } 119*cf09b7a0SBartosz Golaszewski }; 120*cf09b7a0SBartosz Golaszewski MODULE_DEVICE_TABLE(of, m24lr_of_match); 121*cf09b7a0SBartosz Golaszewski 122*cf09b7a0SBartosz Golaszewski /** 123*cf09b7a0SBartosz Golaszewski * m24lr_regmap_read - read data using regmap with retry on failure 124*cf09b7a0SBartosz Golaszewski * @regmap: regmap instance for the device 125*cf09b7a0SBartosz Golaszewski * @buf: buffer to store the read data 126*cf09b7a0SBartosz Golaszewski * @size: number of bytes to read 127*cf09b7a0SBartosz Golaszewski * @offset: starting register address 128*cf09b7a0SBartosz Golaszewski * 129*cf09b7a0SBartosz Golaszewski * Attempts to read a block of data from the device with retries and timeout. 130*cf09b7a0SBartosz Golaszewski * Some M24LR chips may transiently NACK reads (e.g., during internal write 131*cf09b7a0SBartosz Golaszewski * cycles), so this function retries with a short sleep until the timeout 132*cf09b7a0SBartosz Golaszewski * expires. 133*cf09b7a0SBartosz Golaszewski * 134*cf09b7a0SBartosz Golaszewski * Returns: 135*cf09b7a0SBartosz Golaszewski * Number of bytes read on success, 136*cf09b7a0SBartosz Golaszewski * -ETIMEDOUT if the read fails within the timeout window. 137*cf09b7a0SBartosz Golaszewski */ 138*cf09b7a0SBartosz Golaszewski static ssize_t m24lr_regmap_read(struct regmap *regmap, u8 *buf, 139*cf09b7a0SBartosz Golaszewski size_t size, unsigned int offset) 140*cf09b7a0SBartosz Golaszewski { 141*cf09b7a0SBartosz Golaszewski int err; 142*cf09b7a0SBartosz Golaszewski unsigned long timeout, read_time; 143*cf09b7a0SBartosz Golaszewski ssize_t ret = -ETIMEDOUT; 144*cf09b7a0SBartosz Golaszewski 145*cf09b7a0SBartosz Golaszewski timeout = jiffies + msecs_to_jiffies(M24LR_READ_TIMEOUT); 146*cf09b7a0SBartosz Golaszewski do { 147*cf09b7a0SBartosz Golaszewski read_time = jiffies; 148*cf09b7a0SBartosz Golaszewski 149*cf09b7a0SBartosz Golaszewski err = regmap_bulk_read(regmap, offset, buf, size); 150*cf09b7a0SBartosz Golaszewski if (!err) { 151*cf09b7a0SBartosz Golaszewski ret = size; 152*cf09b7a0SBartosz Golaszewski break; 153*cf09b7a0SBartosz Golaszewski } 154*cf09b7a0SBartosz Golaszewski 155*cf09b7a0SBartosz Golaszewski usleep_range(1000, 2000); 156*cf09b7a0SBartosz Golaszewski } while (time_before(read_time, timeout)); 157*cf09b7a0SBartosz Golaszewski 158*cf09b7a0SBartosz Golaszewski return ret; 159*cf09b7a0SBartosz Golaszewski } 160*cf09b7a0SBartosz Golaszewski 161*cf09b7a0SBartosz Golaszewski /** 162*cf09b7a0SBartosz Golaszewski * m24lr_regmap_write - write data using regmap with retry on failure 163*cf09b7a0SBartosz Golaszewski * @regmap: regmap instance for the device 164*cf09b7a0SBartosz Golaszewski * @buf: buffer containing the data to write 165*cf09b7a0SBartosz Golaszewski * @size: number of bytes to write 166*cf09b7a0SBartosz Golaszewski * @offset: starting register address 167*cf09b7a0SBartosz Golaszewski * 168*cf09b7a0SBartosz Golaszewski * Attempts to write a block of data to the device with retries and a timeout. 169*cf09b7a0SBartosz Golaszewski * Some M24LR devices may NACK I2C writes while an internal write operation 170*cf09b7a0SBartosz Golaszewski * is in progress. This function retries the write operation with a short delay 171*cf09b7a0SBartosz Golaszewski * until it succeeds or the timeout is reached. 172*cf09b7a0SBartosz Golaszewski * 173*cf09b7a0SBartosz Golaszewski * Returns: 174*cf09b7a0SBartosz Golaszewski * Number of bytes written on success, 175*cf09b7a0SBartosz Golaszewski * -ETIMEDOUT if the write fails within the timeout window. 176*cf09b7a0SBartosz Golaszewski */ 177*cf09b7a0SBartosz Golaszewski static ssize_t m24lr_regmap_write(struct regmap *regmap, const u8 *buf, 178*cf09b7a0SBartosz Golaszewski size_t size, unsigned int offset) 179*cf09b7a0SBartosz Golaszewski { 180*cf09b7a0SBartosz Golaszewski int err; 181*cf09b7a0SBartosz Golaszewski unsigned long timeout, write_time; 182*cf09b7a0SBartosz Golaszewski ssize_t ret = -ETIMEDOUT; 183*cf09b7a0SBartosz Golaszewski 184*cf09b7a0SBartosz Golaszewski timeout = jiffies + msecs_to_jiffies(M24LR_WRITE_TIMEOUT); 185*cf09b7a0SBartosz Golaszewski 186*cf09b7a0SBartosz Golaszewski do { 187*cf09b7a0SBartosz Golaszewski write_time = jiffies; 188*cf09b7a0SBartosz Golaszewski 189*cf09b7a0SBartosz Golaszewski err = regmap_bulk_write(regmap, offset, buf, size); 190*cf09b7a0SBartosz Golaszewski if (!err) { 191*cf09b7a0SBartosz Golaszewski ret = size; 192*cf09b7a0SBartosz Golaszewski break; 193*cf09b7a0SBartosz Golaszewski } 194*cf09b7a0SBartosz Golaszewski 195*cf09b7a0SBartosz Golaszewski usleep_range(1000, 2000); 196*cf09b7a0SBartosz Golaszewski } while (time_before(write_time, timeout)); 197*cf09b7a0SBartosz Golaszewski 198*cf09b7a0SBartosz Golaszewski return ret; 199*cf09b7a0SBartosz Golaszewski } 200*cf09b7a0SBartosz Golaszewski 201*cf09b7a0SBartosz Golaszewski static ssize_t m24lr_read(struct m24lr *m24lr, u8 *buf, size_t size, 202*cf09b7a0SBartosz Golaszewski unsigned int offset, bool is_eeprom) 203*cf09b7a0SBartosz Golaszewski { 204*cf09b7a0SBartosz Golaszewski struct regmap *regmap; 205*cf09b7a0SBartosz Golaszewski ssize_t ret; 206*cf09b7a0SBartosz Golaszewski 207*cf09b7a0SBartosz Golaszewski if (is_eeprom) 208*cf09b7a0SBartosz Golaszewski regmap = m24lr->eeprom_regmap; 209*cf09b7a0SBartosz Golaszewski else 210*cf09b7a0SBartosz Golaszewski regmap = m24lr->ctl_regmap; 211*cf09b7a0SBartosz Golaszewski 212*cf09b7a0SBartosz Golaszewski mutex_lock(&m24lr->lock); 213*cf09b7a0SBartosz Golaszewski ret = m24lr_regmap_read(regmap, buf, size, offset); 214*cf09b7a0SBartosz Golaszewski mutex_unlock(&m24lr->lock); 215*cf09b7a0SBartosz Golaszewski 216*cf09b7a0SBartosz Golaszewski return ret; 217*cf09b7a0SBartosz Golaszewski } 218*cf09b7a0SBartosz Golaszewski 219*cf09b7a0SBartosz Golaszewski /** 220*cf09b7a0SBartosz Golaszewski * m24lr_write - write buffer to M24LR device with page alignment handling 221*cf09b7a0SBartosz Golaszewski * @m24lr: pointer to driver context 222*cf09b7a0SBartosz Golaszewski * @buf: data buffer to write 223*cf09b7a0SBartosz Golaszewski * @size: number of bytes to write 224*cf09b7a0SBartosz Golaszewski * @offset: target register address in the device 225*cf09b7a0SBartosz Golaszewski * @is_eeprom: true if the write should target the EEPROM, 226*cf09b7a0SBartosz Golaszewski * false if it should target the system parameters sector. 227*cf09b7a0SBartosz Golaszewski * 228*cf09b7a0SBartosz Golaszewski * Writes data to the M24LR device using regmap, split into chunks no larger 229*cf09b7a0SBartosz Golaszewski * than page_size to respect device-specific write limitations (e.g., page 230*cf09b7a0SBartosz Golaszewski * size or I2C hold-time concerns). Each chunk is aligned to the page boundary 231*cf09b7a0SBartosz Golaszewski * defined by page_size. 232*cf09b7a0SBartosz Golaszewski * 233*cf09b7a0SBartosz Golaszewski * Returns: 234*cf09b7a0SBartosz Golaszewski * Total number of bytes written on success, 235*cf09b7a0SBartosz Golaszewski * A negative error code if any write fails. 236*cf09b7a0SBartosz Golaszewski */ 237*cf09b7a0SBartosz Golaszewski static ssize_t m24lr_write(struct m24lr *m24lr, const u8 *buf, size_t size, 238*cf09b7a0SBartosz Golaszewski unsigned int offset, bool is_eeprom) 239*cf09b7a0SBartosz Golaszewski { 240*cf09b7a0SBartosz Golaszewski unsigned int n, next_sector; 241*cf09b7a0SBartosz Golaszewski struct regmap *regmap; 242*cf09b7a0SBartosz Golaszewski ssize_t ret = 0; 243*cf09b7a0SBartosz Golaszewski ssize_t err; 244*cf09b7a0SBartosz Golaszewski 245*cf09b7a0SBartosz Golaszewski if (is_eeprom) 246*cf09b7a0SBartosz Golaszewski regmap = m24lr->eeprom_regmap; 247*cf09b7a0SBartosz Golaszewski else 248*cf09b7a0SBartosz Golaszewski regmap = m24lr->ctl_regmap; 249*cf09b7a0SBartosz Golaszewski 250*cf09b7a0SBartosz Golaszewski n = min_t(unsigned int, size, m24lr->page_size); 251*cf09b7a0SBartosz Golaszewski next_sector = roundup(offset + 1, m24lr->page_size); 252*cf09b7a0SBartosz Golaszewski if (offset + n > next_sector) 253*cf09b7a0SBartosz Golaszewski n = next_sector - offset; 254*cf09b7a0SBartosz Golaszewski 255*cf09b7a0SBartosz Golaszewski mutex_lock(&m24lr->lock); 256*cf09b7a0SBartosz Golaszewski while (n) { 257*cf09b7a0SBartosz Golaszewski err = m24lr_regmap_write(regmap, buf + offset, n, offset); 258*cf09b7a0SBartosz Golaszewski if (IS_ERR_VALUE(err)) { 259*cf09b7a0SBartosz Golaszewski if (!ret) 260*cf09b7a0SBartosz Golaszewski ret = err; 261*cf09b7a0SBartosz Golaszewski 262*cf09b7a0SBartosz Golaszewski break; 263*cf09b7a0SBartosz Golaszewski } 264*cf09b7a0SBartosz Golaszewski 265*cf09b7a0SBartosz Golaszewski offset += n; 266*cf09b7a0SBartosz Golaszewski size -= n; 267*cf09b7a0SBartosz Golaszewski ret += n; 268*cf09b7a0SBartosz Golaszewski n = min_t(unsigned int, size, m24lr->page_size); 269*cf09b7a0SBartosz Golaszewski } 270*cf09b7a0SBartosz Golaszewski mutex_unlock(&m24lr->lock); 271*cf09b7a0SBartosz Golaszewski 272*cf09b7a0SBartosz Golaszewski return ret; 273*cf09b7a0SBartosz Golaszewski } 274*cf09b7a0SBartosz Golaszewski 275*cf09b7a0SBartosz Golaszewski /** 276*cf09b7a0SBartosz Golaszewski * m24lr_write_pass - Write password to M24LR043-R using secure format 277*cf09b7a0SBartosz Golaszewski * @m24lr: Pointer to device control structure 278*cf09b7a0SBartosz Golaszewski * @buf: Input buffer containing hex-encoded password 279*cf09b7a0SBartosz Golaszewski * @count: Number of bytes in @buf 280*cf09b7a0SBartosz Golaszewski * @code: Operation code to embed between password copies 281*cf09b7a0SBartosz Golaszewski * 282*cf09b7a0SBartosz Golaszewski * This function parses a 4-byte password, encodes it in big-endian format, 283*cf09b7a0SBartosz Golaszewski * and constructs a 9-byte sequence of the form: 284*cf09b7a0SBartosz Golaszewski * 285*cf09b7a0SBartosz Golaszewski * [BE(password), code, BE(password)] 286*cf09b7a0SBartosz Golaszewski * 287*cf09b7a0SBartosz Golaszewski * The result is written to register 0x0900 (2304), which is the password 288*cf09b7a0SBartosz Golaszewski * register in M24LR04E-R chip. 289*cf09b7a0SBartosz Golaszewski * 290*cf09b7a0SBartosz Golaszewski * Return: Number of bytes written on success, or negative error code on failure 291*cf09b7a0SBartosz Golaszewski */ 292*cf09b7a0SBartosz Golaszewski static ssize_t m24lr_write_pass(struct m24lr *m24lr, const char *buf, 293*cf09b7a0SBartosz Golaszewski size_t count, u8 code) 294*cf09b7a0SBartosz Golaszewski { 295*cf09b7a0SBartosz Golaszewski __be32 be_pass; 296*cf09b7a0SBartosz Golaszewski u8 output[9]; 297*cf09b7a0SBartosz Golaszewski ssize_t ret; 298*cf09b7a0SBartosz Golaszewski u32 pass; 299*cf09b7a0SBartosz Golaszewski int err; 300*cf09b7a0SBartosz Golaszewski 301*cf09b7a0SBartosz Golaszewski if (!count) 302*cf09b7a0SBartosz Golaszewski return -EINVAL; 303*cf09b7a0SBartosz Golaszewski 304*cf09b7a0SBartosz Golaszewski if (count > 8) 305*cf09b7a0SBartosz Golaszewski return -EINVAL; 306*cf09b7a0SBartosz Golaszewski 307*cf09b7a0SBartosz Golaszewski err = kstrtou32(buf, 16, &pass); 308*cf09b7a0SBartosz Golaszewski if (err) 309*cf09b7a0SBartosz Golaszewski return err; 310*cf09b7a0SBartosz Golaszewski 311*cf09b7a0SBartosz Golaszewski be_pass = cpu_to_be32(pass); 312*cf09b7a0SBartosz Golaszewski 313*cf09b7a0SBartosz Golaszewski memcpy(output, &be_pass, sizeof(be_pass)); 314*cf09b7a0SBartosz Golaszewski output[4] = code; 315*cf09b7a0SBartosz Golaszewski memcpy(output + 5, &be_pass, sizeof(be_pass)); 316*cf09b7a0SBartosz Golaszewski 317*cf09b7a0SBartosz Golaszewski mutex_lock(&m24lr->lock); 318*cf09b7a0SBartosz Golaszewski ret = m24lr_regmap_write(m24lr->ctl_regmap, output, 9, 2304); 319*cf09b7a0SBartosz Golaszewski mutex_unlock(&m24lr->lock); 320*cf09b7a0SBartosz Golaszewski 321*cf09b7a0SBartosz Golaszewski return ret; 322*cf09b7a0SBartosz Golaszewski } 323*cf09b7a0SBartosz Golaszewski 324*cf09b7a0SBartosz Golaszewski static ssize_t m24lr_read_reg_le(struct m24lr *m24lr, u64 *val, 325*cf09b7a0SBartosz Golaszewski unsigned int reg_addr, 326*cf09b7a0SBartosz Golaszewski unsigned int reg_size) 327*cf09b7a0SBartosz Golaszewski { 328*cf09b7a0SBartosz Golaszewski ssize_t ret; 329*cf09b7a0SBartosz Golaszewski __le64 input = 0; 330*cf09b7a0SBartosz Golaszewski 331*cf09b7a0SBartosz Golaszewski ret = m24lr_read(m24lr, (u8 *)&input, reg_size, reg_addr, false); 332*cf09b7a0SBartosz Golaszewski if (IS_ERR_VALUE(ret)) 333*cf09b7a0SBartosz Golaszewski return ret; 334*cf09b7a0SBartosz Golaszewski 335*cf09b7a0SBartosz Golaszewski if (ret != reg_size) 336*cf09b7a0SBartosz Golaszewski return -EINVAL; 337*cf09b7a0SBartosz Golaszewski 338*cf09b7a0SBartosz Golaszewski switch (reg_size) { 339*cf09b7a0SBartosz Golaszewski case 1: 340*cf09b7a0SBartosz Golaszewski *val = *(u8 *)&input; 341*cf09b7a0SBartosz Golaszewski break; 342*cf09b7a0SBartosz Golaszewski case 2: 343*cf09b7a0SBartosz Golaszewski *val = le16_to_cpu((__le16)input); 344*cf09b7a0SBartosz Golaszewski break; 345*cf09b7a0SBartosz Golaszewski case 4: 346*cf09b7a0SBartosz Golaszewski *val = le32_to_cpu((__le32)input); 347*cf09b7a0SBartosz Golaszewski break; 348*cf09b7a0SBartosz Golaszewski case 8: 349*cf09b7a0SBartosz Golaszewski *val = le64_to_cpu((__le64)input); 350*cf09b7a0SBartosz Golaszewski break; 351*cf09b7a0SBartosz Golaszewski default: 352*cf09b7a0SBartosz Golaszewski return -EINVAL; 353*cf09b7a0SBartosz Golaszewski } 354*cf09b7a0SBartosz Golaszewski 355*cf09b7a0SBartosz Golaszewski return 0; 356*cf09b7a0SBartosz Golaszewski } 357*cf09b7a0SBartosz Golaszewski 358*cf09b7a0SBartosz Golaszewski static int m24lr_nvmem_read(void *priv, unsigned int offset, void *val, 359*cf09b7a0SBartosz Golaszewski size_t bytes) 360*cf09b7a0SBartosz Golaszewski { 361*cf09b7a0SBartosz Golaszewski ssize_t err; 362*cf09b7a0SBartosz Golaszewski struct m24lr *m24lr = priv; 363*cf09b7a0SBartosz Golaszewski 364*cf09b7a0SBartosz Golaszewski if (!bytes) 365*cf09b7a0SBartosz Golaszewski return bytes; 366*cf09b7a0SBartosz Golaszewski 367*cf09b7a0SBartosz Golaszewski if (offset + bytes > m24lr->eeprom_size) 368*cf09b7a0SBartosz Golaszewski return -EINVAL; 369*cf09b7a0SBartosz Golaszewski 370*cf09b7a0SBartosz Golaszewski err = m24lr_read(m24lr, val, bytes, offset, true); 371*cf09b7a0SBartosz Golaszewski if (IS_ERR_VALUE(err)) 372*cf09b7a0SBartosz Golaszewski return err; 373*cf09b7a0SBartosz Golaszewski 374*cf09b7a0SBartosz Golaszewski return 0; 375*cf09b7a0SBartosz Golaszewski } 376*cf09b7a0SBartosz Golaszewski 377*cf09b7a0SBartosz Golaszewski static int m24lr_nvmem_write(void *priv, unsigned int offset, void *val, 378*cf09b7a0SBartosz Golaszewski size_t bytes) 379*cf09b7a0SBartosz Golaszewski { 380*cf09b7a0SBartosz Golaszewski ssize_t err; 381*cf09b7a0SBartosz Golaszewski struct m24lr *m24lr = priv; 382*cf09b7a0SBartosz Golaszewski 383*cf09b7a0SBartosz Golaszewski if (!bytes) 384*cf09b7a0SBartosz Golaszewski return -EINVAL; 385*cf09b7a0SBartosz Golaszewski 386*cf09b7a0SBartosz Golaszewski if (offset + bytes > m24lr->eeprom_size) 387*cf09b7a0SBartosz Golaszewski return -EINVAL; 388*cf09b7a0SBartosz Golaszewski 389*cf09b7a0SBartosz Golaszewski err = m24lr_write(m24lr, val, bytes, offset, true); 390*cf09b7a0SBartosz Golaszewski if (IS_ERR_VALUE(err)) 391*cf09b7a0SBartosz Golaszewski return err; 392*cf09b7a0SBartosz Golaszewski 393*cf09b7a0SBartosz Golaszewski return 0; 394*cf09b7a0SBartosz Golaszewski } 395*cf09b7a0SBartosz Golaszewski 396*cf09b7a0SBartosz Golaszewski static ssize_t m24lr_ctl_sss_read(struct file *filep, struct kobject *kobj, 397*cf09b7a0SBartosz Golaszewski const struct bin_attribute *attr, char *buf, 398*cf09b7a0SBartosz Golaszewski loff_t offset, size_t count) 399*cf09b7a0SBartosz Golaszewski { 400*cf09b7a0SBartosz Golaszewski struct m24lr *m24lr = attr->private; 401*cf09b7a0SBartosz Golaszewski 402*cf09b7a0SBartosz Golaszewski if (!count) 403*cf09b7a0SBartosz Golaszewski return count; 404*cf09b7a0SBartosz Golaszewski 405*cf09b7a0SBartosz Golaszewski if (size_add(offset, count) > m24lr->sss_len) 406*cf09b7a0SBartosz Golaszewski return -EINVAL; 407*cf09b7a0SBartosz Golaszewski 408*cf09b7a0SBartosz Golaszewski return m24lr_read(m24lr, buf, count, offset, false); 409*cf09b7a0SBartosz Golaszewski } 410*cf09b7a0SBartosz Golaszewski 411*cf09b7a0SBartosz Golaszewski static ssize_t m24lr_ctl_sss_write(struct file *filep, struct kobject *kobj, 412*cf09b7a0SBartosz Golaszewski const struct bin_attribute *attr, char *buf, 413*cf09b7a0SBartosz Golaszewski loff_t offset, size_t count) 414*cf09b7a0SBartosz Golaszewski { 415*cf09b7a0SBartosz Golaszewski struct m24lr *m24lr = attr->private; 416*cf09b7a0SBartosz Golaszewski 417*cf09b7a0SBartosz Golaszewski if (!count) 418*cf09b7a0SBartosz Golaszewski return -EINVAL; 419*cf09b7a0SBartosz Golaszewski 420*cf09b7a0SBartosz Golaszewski if (size_add(offset, count) > m24lr->sss_len) 421*cf09b7a0SBartosz Golaszewski return -EINVAL; 422*cf09b7a0SBartosz Golaszewski 423*cf09b7a0SBartosz Golaszewski return m24lr_write(m24lr, buf, count, offset, false); 424*cf09b7a0SBartosz Golaszewski } 425*cf09b7a0SBartosz Golaszewski static BIN_ATTR(sss, 0600, m24lr_ctl_sss_read, m24lr_ctl_sss_write, 0); 426*cf09b7a0SBartosz Golaszewski 427*cf09b7a0SBartosz Golaszewski static ssize_t new_pass_store(struct device *dev, struct device_attribute *attr, 428*cf09b7a0SBartosz Golaszewski const char *buf, size_t count) 429*cf09b7a0SBartosz Golaszewski { 430*cf09b7a0SBartosz Golaszewski struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev)); 431*cf09b7a0SBartosz Golaszewski 432*cf09b7a0SBartosz Golaszewski return m24lr_write_pass(m24lr, buf, count, 7); 433*cf09b7a0SBartosz Golaszewski } 434*cf09b7a0SBartosz Golaszewski static DEVICE_ATTR_WO(new_pass); 435*cf09b7a0SBartosz Golaszewski 436*cf09b7a0SBartosz Golaszewski static ssize_t unlock_store(struct device *dev, struct device_attribute *attr, 437*cf09b7a0SBartosz Golaszewski const char *buf, size_t count) 438*cf09b7a0SBartosz Golaszewski { 439*cf09b7a0SBartosz Golaszewski struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev)); 440*cf09b7a0SBartosz Golaszewski 441*cf09b7a0SBartosz Golaszewski return m24lr_write_pass(m24lr, buf, count, 9); 442*cf09b7a0SBartosz Golaszewski } 443*cf09b7a0SBartosz Golaszewski static DEVICE_ATTR_WO(unlock); 444*cf09b7a0SBartosz Golaszewski 445*cf09b7a0SBartosz Golaszewski static ssize_t uid_show(struct device *dev, struct device_attribute *attr, 446*cf09b7a0SBartosz Golaszewski char *buf) 447*cf09b7a0SBartosz Golaszewski { 448*cf09b7a0SBartosz Golaszewski struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev)); 449*cf09b7a0SBartosz Golaszewski 450*cf09b7a0SBartosz Golaszewski return sysfs_emit(buf, "%llx\n", m24lr->uid); 451*cf09b7a0SBartosz Golaszewski } 452*cf09b7a0SBartosz Golaszewski static DEVICE_ATTR_RO(uid); 453*cf09b7a0SBartosz Golaszewski 454*cf09b7a0SBartosz Golaszewski static ssize_t total_sectors_show(struct device *dev, 455*cf09b7a0SBartosz Golaszewski struct device_attribute *attr, char *buf) 456*cf09b7a0SBartosz Golaszewski { 457*cf09b7a0SBartosz Golaszewski struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev)); 458*cf09b7a0SBartosz Golaszewski 459*cf09b7a0SBartosz Golaszewski return sysfs_emit(buf, "%x\n", m24lr->sss_len); 460*cf09b7a0SBartosz Golaszewski } 461*cf09b7a0SBartosz Golaszewski static DEVICE_ATTR_RO(total_sectors); 462*cf09b7a0SBartosz Golaszewski 463*cf09b7a0SBartosz Golaszewski static struct attribute *m24lr_ctl_dev_attrs[] = { 464*cf09b7a0SBartosz Golaszewski &dev_attr_unlock.attr, 465*cf09b7a0SBartosz Golaszewski &dev_attr_new_pass.attr, 466*cf09b7a0SBartosz Golaszewski &dev_attr_uid.attr, 467*cf09b7a0SBartosz Golaszewski &dev_attr_total_sectors.attr, 468*cf09b7a0SBartosz Golaszewski NULL, 469*cf09b7a0SBartosz Golaszewski }; 470*cf09b7a0SBartosz Golaszewski 471*cf09b7a0SBartosz Golaszewski static const struct m24lr_chip *m24lr_get_chip(struct device *dev) 472*cf09b7a0SBartosz Golaszewski { 473*cf09b7a0SBartosz Golaszewski const struct m24lr_chip *ret; 474*cf09b7a0SBartosz Golaszewski const struct i2c_device_id *id; 475*cf09b7a0SBartosz Golaszewski 476*cf09b7a0SBartosz Golaszewski id = i2c_match_id(m24lr_ids, to_i2c_client(dev)); 477*cf09b7a0SBartosz Golaszewski 478*cf09b7a0SBartosz Golaszewski if (dev->of_node && of_match_device(m24lr_of_match, dev)) 479*cf09b7a0SBartosz Golaszewski ret = of_device_get_match_data(dev); 480*cf09b7a0SBartosz Golaszewski else if (id) 481*cf09b7a0SBartosz Golaszewski ret = (void *)id->driver_data; 482*cf09b7a0SBartosz Golaszewski else 483*cf09b7a0SBartosz Golaszewski ret = acpi_device_get_match_data(dev); 484*cf09b7a0SBartosz Golaszewski 485*cf09b7a0SBartosz Golaszewski return ret; 486*cf09b7a0SBartosz Golaszewski } 487*cf09b7a0SBartosz Golaszewski 488*cf09b7a0SBartosz Golaszewski static int m24lr_probe(struct i2c_client *client) 489*cf09b7a0SBartosz Golaszewski { 490*cf09b7a0SBartosz Golaszewski struct regmap_config eeprom_regmap_conf = {0}; 491*cf09b7a0SBartosz Golaszewski struct nvmem_config nvmem_conf = {0}; 492*cf09b7a0SBartosz Golaszewski struct device *dev = &client->dev; 493*cf09b7a0SBartosz Golaszewski struct i2c_client *eeprom_client; 494*cf09b7a0SBartosz Golaszewski const struct m24lr_chip *chip; 495*cf09b7a0SBartosz Golaszewski struct regmap *eeprom_regmap; 496*cf09b7a0SBartosz Golaszewski struct nvmem_device *nvmem; 497*cf09b7a0SBartosz Golaszewski struct regmap *ctl_regmap; 498*cf09b7a0SBartosz Golaszewski struct m24lr *m24lr; 499*cf09b7a0SBartosz Golaszewski u32 regs[2]; 500*cf09b7a0SBartosz Golaszewski long err; 501*cf09b7a0SBartosz Golaszewski 502*cf09b7a0SBartosz Golaszewski if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 503*cf09b7a0SBartosz Golaszewski return -EOPNOTSUPP; 504*cf09b7a0SBartosz Golaszewski 505*cf09b7a0SBartosz Golaszewski chip = m24lr_get_chip(dev); 506*cf09b7a0SBartosz Golaszewski if (!chip) 507*cf09b7a0SBartosz Golaszewski return -ENODEV; 508*cf09b7a0SBartosz Golaszewski 509*cf09b7a0SBartosz Golaszewski m24lr = devm_kzalloc(dev, sizeof(struct m24lr), GFP_KERNEL); 510*cf09b7a0SBartosz Golaszewski if (!m24lr) 511*cf09b7a0SBartosz Golaszewski return -ENOMEM; 512*cf09b7a0SBartosz Golaszewski 513*cf09b7a0SBartosz Golaszewski err = device_property_read_u32_array(dev, "reg", regs, ARRAY_SIZE(regs)); 514*cf09b7a0SBartosz Golaszewski if (err) 515*cf09b7a0SBartosz Golaszewski return dev_err_probe(dev, err, "Failed to read 'reg' property\n"); 516*cf09b7a0SBartosz Golaszewski 517*cf09b7a0SBartosz Golaszewski /* Create a second I2C client for the eeprom interface */ 518*cf09b7a0SBartosz Golaszewski eeprom_client = devm_i2c_new_dummy_device(dev, client->adapter, regs[1]); 519*cf09b7a0SBartosz Golaszewski if (IS_ERR(eeprom_client)) 520*cf09b7a0SBartosz Golaszewski return dev_err_probe(dev, PTR_ERR(eeprom_client), 521*cf09b7a0SBartosz Golaszewski "Failed to create dummy I2C client for the EEPROM\n"); 522*cf09b7a0SBartosz Golaszewski 523*cf09b7a0SBartosz Golaszewski ctl_regmap = devm_regmap_init_i2c(client, &m24lr_ctl_regmap_conf); 524*cf09b7a0SBartosz Golaszewski if (IS_ERR(ctl_regmap)) 525*cf09b7a0SBartosz Golaszewski return dev_err_probe(dev, PTR_ERR(ctl_regmap), 526*cf09b7a0SBartosz Golaszewski "Failed to init regmap\n"); 527*cf09b7a0SBartosz Golaszewski 528*cf09b7a0SBartosz Golaszewski eeprom_regmap_conf.name = "m24lr_eeprom"; 529*cf09b7a0SBartosz Golaszewski eeprom_regmap_conf.reg_bits = 16; 530*cf09b7a0SBartosz Golaszewski eeprom_regmap_conf.val_bits = 8; 531*cf09b7a0SBartosz Golaszewski eeprom_regmap_conf.disable_locking = true; 532*cf09b7a0SBartosz Golaszewski eeprom_regmap_conf.max_register = chip->eeprom_size - 1; 533*cf09b7a0SBartosz Golaszewski 534*cf09b7a0SBartosz Golaszewski eeprom_regmap = devm_regmap_init_i2c(eeprom_client, 535*cf09b7a0SBartosz Golaszewski &eeprom_regmap_conf); 536*cf09b7a0SBartosz Golaszewski if (IS_ERR(eeprom_regmap)) 537*cf09b7a0SBartosz Golaszewski return dev_err_probe(dev, PTR_ERR(eeprom_regmap), 538*cf09b7a0SBartosz Golaszewski "Failed to init regmap\n"); 539*cf09b7a0SBartosz Golaszewski 540*cf09b7a0SBartosz Golaszewski mutex_init(&m24lr->lock); 541*cf09b7a0SBartosz Golaszewski m24lr->sss_len = chip->sss_len; 542*cf09b7a0SBartosz Golaszewski m24lr->page_size = chip->page_size; 543*cf09b7a0SBartosz Golaszewski m24lr->eeprom_size = chip->eeprom_size; 544*cf09b7a0SBartosz Golaszewski m24lr->eeprom_regmap = eeprom_regmap; 545*cf09b7a0SBartosz Golaszewski m24lr->ctl_regmap = ctl_regmap; 546*cf09b7a0SBartosz Golaszewski 547*cf09b7a0SBartosz Golaszewski nvmem_conf.dev = &eeprom_client->dev; 548*cf09b7a0SBartosz Golaszewski nvmem_conf.owner = THIS_MODULE; 549*cf09b7a0SBartosz Golaszewski nvmem_conf.type = NVMEM_TYPE_EEPROM; 550*cf09b7a0SBartosz Golaszewski nvmem_conf.reg_read = m24lr_nvmem_read; 551*cf09b7a0SBartosz Golaszewski nvmem_conf.reg_write = m24lr_nvmem_write; 552*cf09b7a0SBartosz Golaszewski nvmem_conf.size = chip->eeprom_size; 553*cf09b7a0SBartosz Golaszewski nvmem_conf.word_size = 1; 554*cf09b7a0SBartosz Golaszewski nvmem_conf.stride = 1; 555*cf09b7a0SBartosz Golaszewski nvmem_conf.priv = m24lr; 556*cf09b7a0SBartosz Golaszewski 557*cf09b7a0SBartosz Golaszewski nvmem = devm_nvmem_register(dev, &nvmem_conf); 558*cf09b7a0SBartosz Golaszewski if (IS_ERR(nvmem)) 559*cf09b7a0SBartosz Golaszewski return dev_err_probe(dev, PTR_ERR(nvmem), 560*cf09b7a0SBartosz Golaszewski "Failed to register nvmem\n"); 561*cf09b7a0SBartosz Golaszewski 562*cf09b7a0SBartosz Golaszewski i2c_set_clientdata(client, m24lr); 563*cf09b7a0SBartosz Golaszewski i2c_set_clientdata(eeprom_client, m24lr); 564*cf09b7a0SBartosz Golaszewski 565*cf09b7a0SBartosz Golaszewski bin_attr_sss.size = chip->sss_len; 566*cf09b7a0SBartosz Golaszewski bin_attr_sss.private = m24lr; 567*cf09b7a0SBartosz Golaszewski err = sysfs_create_bin_file(&dev->kobj, &bin_attr_sss); 568*cf09b7a0SBartosz Golaszewski if (err) 569*cf09b7a0SBartosz Golaszewski return dev_err_probe(dev, err, 570*cf09b7a0SBartosz Golaszewski "Failed to create sss bin file\n"); 571*cf09b7a0SBartosz Golaszewski 572*cf09b7a0SBartosz Golaszewski /* test by reading the uid, if success store it */ 573*cf09b7a0SBartosz Golaszewski err = m24lr_read_reg_le(m24lr, &m24lr->uid, 2324, sizeof(m24lr->uid)); 574*cf09b7a0SBartosz Golaszewski if (IS_ERR_VALUE(err)) 575*cf09b7a0SBartosz Golaszewski goto remove_bin_file; 576*cf09b7a0SBartosz Golaszewski 577*cf09b7a0SBartosz Golaszewski return 0; 578*cf09b7a0SBartosz Golaszewski 579*cf09b7a0SBartosz Golaszewski remove_bin_file: 580*cf09b7a0SBartosz Golaszewski sysfs_remove_bin_file(&dev->kobj, &bin_attr_sss); 581*cf09b7a0SBartosz Golaszewski 582*cf09b7a0SBartosz Golaszewski return err; 583*cf09b7a0SBartosz Golaszewski } 584*cf09b7a0SBartosz Golaszewski 585*cf09b7a0SBartosz Golaszewski static void m24lr_remove(struct i2c_client *client) 586*cf09b7a0SBartosz Golaszewski { 587*cf09b7a0SBartosz Golaszewski sysfs_remove_bin_file(&client->dev.kobj, &bin_attr_sss); 588*cf09b7a0SBartosz Golaszewski } 589*cf09b7a0SBartosz Golaszewski 590*cf09b7a0SBartosz Golaszewski ATTRIBUTE_GROUPS(m24lr_ctl_dev); 591*cf09b7a0SBartosz Golaszewski 592*cf09b7a0SBartosz Golaszewski static struct i2c_driver m24lr_driver = { 593*cf09b7a0SBartosz Golaszewski .driver = { 594*cf09b7a0SBartosz Golaszewski .name = "m24lr", 595*cf09b7a0SBartosz Golaszewski .of_match_table = m24lr_of_match, 596*cf09b7a0SBartosz Golaszewski .dev_groups = m24lr_ctl_dev_groups, 597*cf09b7a0SBartosz Golaszewski }, 598*cf09b7a0SBartosz Golaszewski .probe = m24lr_probe, 599*cf09b7a0SBartosz Golaszewski .remove = m24lr_remove, 600*cf09b7a0SBartosz Golaszewski .id_table = m24lr_ids, 601*cf09b7a0SBartosz Golaszewski }; 602*cf09b7a0SBartosz Golaszewski module_i2c_driver(m24lr_driver); 603*cf09b7a0SBartosz Golaszewski 604*cf09b7a0SBartosz Golaszewski MODULE_AUTHOR("Abd-Alrhman Masalkhi"); 605*cf09b7a0SBartosz Golaszewski MODULE_DESCRIPTION("st m24lr control driver"); 606*cf09b7a0SBartosz Golaszewski MODULE_LICENSE("GPL"); 607