292 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * w1_ds250x.c - w1 family 09/0b/89/91 (DS250x) driver4 */5 6#include <linux/kernel.h>7#include <linux/module.h>8#include <linux/moduleparam.h>9#include <linux/device.h>10#include <linux/types.h>11#include <linux/delay.h>12#include <linux/slab.h>13#include <linux/crc16.h>14 15#include <linux/w1.h>16#include <linux/nvmem-provider.h>17 18#define W1_DS2501_UNW_FAMILY 0x9119#define W1_DS2501_SIZE 6420 21#define W1_DS2502_FAMILY 0x0922#define W1_DS2502_UNW_FAMILY 0x8923#define W1_DS2502_SIZE 12824 25#define W1_DS2505_FAMILY 0x0b26#define W1_DS2505_SIZE 204827 28#define W1_PAGE_SIZE 3229 30#define W1_EXT_READ_MEMORY 0xA531#define W1_READ_DATA_CRC 0xC332 33#define OFF2PG(off) ((off) / W1_PAGE_SIZE)34 35#define CRC16_INIT 036#define CRC16_VALID 0xb00137 38struct w1_eprom_data {39 size_t size;40 int (*read)(struct w1_slave *sl, int pageno);41 u8 eprom[W1_DS2505_SIZE];42 DECLARE_BITMAP(page_present, W1_DS2505_SIZE / W1_PAGE_SIZE);43 char nvmem_name[64];44};45 46static int w1_ds2502_read_page(struct w1_slave *sl, int pageno)47{48 struct w1_eprom_data *data = sl->family_data;49 int pgoff = pageno * W1_PAGE_SIZE;50 int ret = -EIO;51 u8 buf[3];52 u8 crc8;53 54 if (test_bit(pageno, data->page_present))55 return 0; /* page already present */56 57 mutex_lock(&sl->master->bus_mutex);58 59 if (w1_reset_select_slave(sl))60 goto err;61 62 buf[0] = W1_READ_DATA_CRC;63 buf[1] = pgoff & 0xff;64 buf[2] = pgoff >> 8;65 w1_write_block(sl->master, buf, 3);66 67 crc8 = w1_read_8(sl->master);68 if (w1_calc_crc8(buf, 3) != crc8)69 goto err;70 71 w1_read_block(sl->master, &data->eprom[pgoff], W1_PAGE_SIZE);72 73 crc8 = w1_read_8(sl->master);74 if (w1_calc_crc8(&data->eprom[pgoff], W1_PAGE_SIZE) != crc8)75 goto err;76 77 set_bit(pageno, data->page_present); /* mark page present */78 ret = 0;79err:80 mutex_unlock(&sl->master->bus_mutex);81 return ret;82}83 84static int w1_ds2505_read_page(struct w1_slave *sl, int pageno)85{86 struct w1_eprom_data *data = sl->family_data;87 int redir_retries = 16;88 int pgoff, epoff;89 int ret = -EIO;90 u8 buf[6];91 u8 redir;92 u16 crc;93 94 if (test_bit(pageno, data->page_present))95 return 0; /* page already present */96 97 epoff = pgoff = pageno * W1_PAGE_SIZE;98 mutex_lock(&sl->master->bus_mutex);99 100retry:101 if (w1_reset_select_slave(sl))102 goto err;103 104 buf[0] = W1_EXT_READ_MEMORY;105 buf[1] = pgoff & 0xff;106 buf[2] = pgoff >> 8;107 w1_write_block(sl->master, buf, 3);108 w1_read_block(sl->master, buf + 3, 3); /* redir, crc16 */109 redir = buf[3];110 crc = crc16(CRC16_INIT, buf, 6);111 112 if (crc != CRC16_VALID)113 goto err;114 115 116 if (redir != 0xff) {117 redir_retries--;118 if (redir_retries < 0)119 goto err;120 121 pgoff = (redir ^ 0xff) * W1_PAGE_SIZE;122 goto retry;123 }124 125 w1_read_block(sl->master, &data->eprom[epoff], W1_PAGE_SIZE);126 w1_read_block(sl->master, buf, 2); /* crc16 */127 crc = crc16(CRC16_INIT, &data->eprom[epoff], W1_PAGE_SIZE);128 crc = crc16(crc, buf, 2);129 130 if (crc != CRC16_VALID)131 goto err;132 133 set_bit(pageno, data->page_present);134 ret = 0;135err:136 mutex_unlock(&sl->master->bus_mutex);137 return ret;138}139 140static int w1_nvmem_read(void *priv, unsigned int off, void *buf, size_t count)141{142 struct w1_slave *sl = priv;143 struct w1_eprom_data *data = sl->family_data;144 size_t eprom_size = data->size;145 int ret;146 int i;147 148 if (off > eprom_size)149 return -EINVAL;150 151 if ((off + count) > eprom_size)152 count = eprom_size - off;153 154 i = OFF2PG(off);155 do {156 ret = data->read(sl, i++);157 if (ret < 0)158 return ret;159 } while (i < OFF2PG(off + count));160 161 memcpy(buf, &data->eprom[off], count);162 return 0;163}164 165static int w1_eprom_add_slave(struct w1_slave *sl)166{167 struct w1_eprom_data *data;168 struct nvmem_device *nvmem;169 struct nvmem_config nvmem_cfg = {170 .dev = &sl->dev,171 .add_legacy_fixed_of_cells = true,172 .reg_read = w1_nvmem_read,173 .type = NVMEM_TYPE_OTP,174 .read_only = true,175 .word_size = 1,176 .priv = sl,177 .id = -1178 };179 180 data = devm_kzalloc(&sl->dev, sizeof(struct w1_eprom_data), GFP_KERNEL);181 if (!data)182 return -ENOMEM;183 184 sl->family_data = data;185 switch (sl->family->fid) {186 case W1_DS2501_UNW_FAMILY:187 data->size = W1_DS2501_SIZE;188 data->read = w1_ds2502_read_page;189 break;190 case W1_DS2502_FAMILY:191 case W1_DS2502_UNW_FAMILY:192 data->size = W1_DS2502_SIZE;193 data->read = w1_ds2502_read_page;194 break;195 case W1_DS2505_FAMILY:196 data->size = W1_DS2505_SIZE;197 data->read = w1_ds2505_read_page;198 break;199 }200 201 if (sl->master->bus_master->dev_id)202 snprintf(data->nvmem_name, sizeof(data->nvmem_name),203 "%s-%02x-%012llx",204 sl->master->bus_master->dev_id, sl->reg_num.family,205 (unsigned long long)sl->reg_num.id);206 else207 snprintf(data->nvmem_name, sizeof(data->nvmem_name),208 "%02x-%012llx",209 sl->reg_num.family,210 (unsigned long long)sl->reg_num.id);211 212 nvmem_cfg.name = data->nvmem_name;213 nvmem_cfg.size = data->size;214 215 nvmem = devm_nvmem_register(&sl->dev, &nvmem_cfg);216 return PTR_ERR_OR_ZERO(nvmem);217}218 219static const struct w1_family_ops w1_eprom_fops = {220 .add_slave = w1_eprom_add_slave,221};222 223static struct w1_family w1_family_09 = {224 .fid = W1_DS2502_FAMILY,225 .fops = &w1_eprom_fops,226};227 228static struct w1_family w1_family_0b = {229 .fid = W1_DS2505_FAMILY,230 .fops = &w1_eprom_fops,231};232 233static struct w1_family w1_family_89 = {234 .fid = W1_DS2502_UNW_FAMILY,235 .fops = &w1_eprom_fops,236};237 238static struct w1_family w1_family_91 = {239 .fid = W1_DS2501_UNW_FAMILY,240 .fops = &w1_eprom_fops,241};242 243static int __init w1_ds250x_init(void)244{245 int err;246 247 err = w1_register_family(&w1_family_09);248 if (err)249 return err;250 251 err = w1_register_family(&w1_family_0b);252 if (err)253 goto err_0b;254 255 err = w1_register_family(&w1_family_89);256 if (err)257 goto err_89;258 259 err = w1_register_family(&w1_family_91);260 if (err)261 goto err_91;262 263 return 0;264 265err_91:266 w1_unregister_family(&w1_family_89);267err_89:268 w1_unregister_family(&w1_family_0b);269err_0b:270 w1_unregister_family(&w1_family_09);271 return err;272}273 274static void __exit w1_ds250x_exit(void)275{276 w1_unregister_family(&w1_family_09);277 w1_unregister_family(&w1_family_0b);278 w1_unregister_family(&w1_family_89);279 w1_unregister_family(&w1_family_91);280}281 282module_init(w1_ds250x_init);283module_exit(w1_ds250x_exit);284 285MODULE_AUTHOR("Thomas Bogendoerfer <tbogendoerfe@suse.de>");286MODULE_DESCRIPTION("w1 family driver for DS250x Add Only Memory");287MODULE_LICENSE("GPL");288MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_FAMILY));289MODULE_ALIAS("w1-family-" __stringify(W1_DS2505_FAMILY));290MODULE_ALIAS("w1-family-" __stringify(W1_DS2501_UNW_FAMILY));291MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_UNW_FAMILY));292