123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * w1_ds250x.c - w1 family 09/0b/89/91 (DS250x) driver
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/device.h>
- #include <linux/types.h>
- #include <linux/delay.h>
- #include <linux/slab.h>
- #include <linux/crc16.h>
- #include <linux/w1.h>
- #include <linux/nvmem-provider.h>
- #define W1_DS2501_UNW_FAMILY 0x91
- #define W1_DS2501_SIZE 64
- #define W1_DS2502_FAMILY 0x09
- #define W1_DS2502_UNW_FAMILY 0x89
- #define W1_DS2502_SIZE 128
- #define W1_DS2505_FAMILY 0x0b
- #define W1_DS2505_SIZE 2048
- #define W1_PAGE_SIZE 32
- #define W1_EXT_READ_MEMORY 0xA5
- #define W1_READ_DATA_CRC 0xC3
- #define OFF2PG(off) ((off) / W1_PAGE_SIZE)
- #define CRC16_INIT 0
- #define CRC16_VALID 0xb001
- struct w1_eprom_data {
- size_t size;
- int (*read)(struct w1_slave *sl, int pageno);
- u8 eprom[W1_DS2505_SIZE];
- DECLARE_BITMAP(page_present, W1_DS2505_SIZE / W1_PAGE_SIZE);
- char nvmem_name[64];
- };
- static int w1_ds2502_read_page(struct w1_slave *sl, int pageno)
- {
- struct w1_eprom_data *data = sl->family_data;
- int pgoff = pageno * W1_PAGE_SIZE;
- int ret = -EIO;
- u8 buf[3];
- u8 crc8;
- if (test_bit(pageno, data->page_present))
- return 0; /* page already present */
- mutex_lock(&sl->master->bus_mutex);
- if (w1_reset_select_slave(sl))
- goto err;
- buf[0] = W1_READ_DATA_CRC;
- buf[1] = pgoff & 0xff;
- buf[2] = pgoff >> 8;
- w1_write_block(sl->master, buf, 3);
- crc8 = w1_read_8(sl->master);
- if (w1_calc_crc8(buf, 3) != crc8)
- goto err;
- w1_read_block(sl->master, &data->eprom[pgoff], W1_PAGE_SIZE);
- crc8 = w1_read_8(sl->master);
- if (w1_calc_crc8(&data->eprom[pgoff], W1_PAGE_SIZE) != crc8)
- goto err;
- set_bit(pageno, data->page_present); /* mark page present */
- ret = 0;
- err:
- mutex_unlock(&sl->master->bus_mutex);
- return ret;
- }
- static int w1_ds2505_read_page(struct w1_slave *sl, int pageno)
- {
- struct w1_eprom_data *data = sl->family_data;
- int redir_retries = 16;
- int pgoff, epoff;
- int ret = -EIO;
- u8 buf[6];
- u8 redir;
- u16 crc;
- if (test_bit(pageno, data->page_present))
- return 0; /* page already present */
- epoff = pgoff = pageno * W1_PAGE_SIZE;
- mutex_lock(&sl->master->bus_mutex);
- retry:
- if (w1_reset_select_slave(sl))
- goto err;
- buf[0] = W1_EXT_READ_MEMORY;
- buf[1] = pgoff & 0xff;
- buf[2] = pgoff >> 8;
- w1_write_block(sl->master, buf, 3);
- w1_read_block(sl->master, buf + 3, 3); /* redir, crc16 */
- redir = buf[3];
- crc = crc16(CRC16_INIT, buf, 6);
- if (crc != CRC16_VALID)
- goto err;
- if (redir != 0xff) {
- redir_retries--;
- if (redir_retries < 0)
- goto err;
- pgoff = (redir ^ 0xff) * W1_PAGE_SIZE;
- goto retry;
- }
- w1_read_block(sl->master, &data->eprom[epoff], W1_PAGE_SIZE);
- w1_read_block(sl->master, buf, 2); /* crc16 */
- crc = crc16(CRC16_INIT, &data->eprom[epoff], W1_PAGE_SIZE);
- crc = crc16(crc, buf, 2);
- if (crc != CRC16_VALID)
- goto err;
- set_bit(pageno, data->page_present);
- ret = 0;
- err:
- mutex_unlock(&sl->master->bus_mutex);
- return ret;
- }
- static int w1_nvmem_read(void *priv, unsigned int off, void *buf, size_t count)
- {
- struct w1_slave *sl = priv;
- struct w1_eprom_data *data = sl->family_data;
- size_t eprom_size = data->size;
- int ret;
- int i;
- if (off > eprom_size)
- return -EINVAL;
- if ((off + count) > eprom_size)
- count = eprom_size - off;
- i = OFF2PG(off);
- do {
- ret = data->read(sl, i++);
- if (ret < 0)
- return ret;
- } while (i < OFF2PG(off + count));
- memcpy(buf, &data->eprom[off], count);
- return 0;
- }
- static int w1_eprom_add_slave(struct w1_slave *sl)
- {
- struct w1_eprom_data *data;
- struct nvmem_device *nvmem;
- struct nvmem_config nvmem_cfg = {
- .dev = &sl->dev,
- .reg_read = w1_nvmem_read,
- .type = NVMEM_TYPE_OTP,
- .read_only = true,
- .word_size = 1,
- .priv = sl,
- .id = -1
- };
- data = devm_kzalloc(&sl->dev, sizeof(struct w1_eprom_data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- sl->family_data = data;
- switch (sl->family->fid) {
- case W1_DS2501_UNW_FAMILY:
- data->size = W1_DS2501_SIZE;
- data->read = w1_ds2502_read_page;
- break;
- case W1_DS2502_FAMILY:
- case W1_DS2502_UNW_FAMILY:
- data->size = W1_DS2502_SIZE;
- data->read = w1_ds2502_read_page;
- break;
- case W1_DS2505_FAMILY:
- data->size = W1_DS2505_SIZE;
- data->read = w1_ds2505_read_page;
- break;
- }
- if (sl->master->bus_master->dev_id)
- snprintf(data->nvmem_name, sizeof(data->nvmem_name),
- "%s-%02x-%012llx",
- sl->master->bus_master->dev_id, sl->reg_num.family,
- (unsigned long long)sl->reg_num.id);
- else
- snprintf(data->nvmem_name, sizeof(data->nvmem_name),
- "%02x-%012llx",
- sl->reg_num.family,
- (unsigned long long)sl->reg_num.id);
- nvmem_cfg.name = data->nvmem_name;
- nvmem_cfg.size = data->size;
- nvmem = devm_nvmem_register(&sl->dev, &nvmem_cfg);
- return PTR_ERR_OR_ZERO(nvmem);
- }
- static const struct w1_family_ops w1_eprom_fops = {
- .add_slave = w1_eprom_add_slave,
- };
- static struct w1_family w1_family_09 = {
- .fid = W1_DS2502_FAMILY,
- .fops = &w1_eprom_fops,
- };
- static struct w1_family w1_family_0b = {
- .fid = W1_DS2505_FAMILY,
- .fops = &w1_eprom_fops,
- };
- static struct w1_family w1_family_89 = {
- .fid = W1_DS2502_UNW_FAMILY,
- .fops = &w1_eprom_fops,
- };
- static struct w1_family w1_family_91 = {
- .fid = W1_DS2501_UNW_FAMILY,
- .fops = &w1_eprom_fops,
- };
- static int __init w1_ds250x_init(void)
- {
- int err;
- err = w1_register_family(&w1_family_09);
- if (err)
- return err;
- err = w1_register_family(&w1_family_0b);
- if (err)
- goto err_0b;
- err = w1_register_family(&w1_family_89);
- if (err)
- goto err_89;
- err = w1_register_family(&w1_family_91);
- if (err)
- goto err_91;
- return 0;
- err_91:
- w1_unregister_family(&w1_family_89);
- err_89:
- w1_unregister_family(&w1_family_0b);
- err_0b:
- w1_unregister_family(&w1_family_09);
- return err;
- }
- static void __exit w1_ds250x_exit(void)
- {
- w1_unregister_family(&w1_family_09);
- w1_unregister_family(&w1_family_0b);
- w1_unregister_family(&w1_family_89);
- w1_unregister_family(&w1_family_91);
- }
- module_init(w1_ds250x_init);
- module_exit(w1_ds250x_exit);
- MODULE_AUTHOR("Thomas Bogendoerfer <[email protected]>");
- MODULE_DESCRIPTION("w1 family driver for DS250x Add Only Memory");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_FAMILY));
- MODULE_ALIAS("w1-family-" __stringify(W1_DS2505_FAMILY));
- MODULE_ALIAS("w1-family-" __stringify(W1_DS2501_UNW_FAMILY));
- MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_UNW_FAMILY));
|