xref: /openbmc/linux/drivers/w1/slaves/w1_ds250x.c (revision 26e2fe4c)
125ec8710SThomas Bogendoerfer // SPDX-License-Identifier: GPL-2.0
225ec8710SThomas Bogendoerfer /*
325ec8710SThomas Bogendoerfer  * w1_ds250x.c - w1 family 09/0b/89/91 (DS250x) driver
425ec8710SThomas Bogendoerfer  */
525ec8710SThomas Bogendoerfer 
625ec8710SThomas Bogendoerfer #include <linux/kernel.h>
725ec8710SThomas Bogendoerfer #include <linux/module.h>
825ec8710SThomas Bogendoerfer #include <linux/moduleparam.h>
925ec8710SThomas Bogendoerfer #include <linux/device.h>
1025ec8710SThomas Bogendoerfer #include <linux/types.h>
1125ec8710SThomas Bogendoerfer #include <linux/delay.h>
1225ec8710SThomas Bogendoerfer #include <linux/slab.h>
1325ec8710SThomas Bogendoerfer #include <linux/crc16.h>
1425ec8710SThomas Bogendoerfer 
1525ec8710SThomas Bogendoerfer #include <linux/w1.h>
1625ec8710SThomas Bogendoerfer #include <linux/nvmem-provider.h>
1725ec8710SThomas Bogendoerfer 
1825ec8710SThomas Bogendoerfer #define W1_DS2501_UNW_FAMILY    0x91
1925ec8710SThomas Bogendoerfer #define W1_DS2501_SIZE          64
2025ec8710SThomas Bogendoerfer 
2125ec8710SThomas Bogendoerfer #define W1_DS2502_FAMILY        0x09
2225ec8710SThomas Bogendoerfer #define W1_DS2502_UNW_FAMILY    0x89
2325ec8710SThomas Bogendoerfer #define W1_DS2502_SIZE          128
2425ec8710SThomas Bogendoerfer 
2525ec8710SThomas Bogendoerfer #define W1_DS2505_FAMILY	0x0b
2625ec8710SThomas Bogendoerfer #define W1_DS2505_SIZE		2048
2725ec8710SThomas Bogendoerfer 
2825ec8710SThomas Bogendoerfer #define W1_PAGE_SIZE		32
2925ec8710SThomas Bogendoerfer 
3025ec8710SThomas Bogendoerfer #define W1_EXT_READ_MEMORY	0xA5
3125ec8710SThomas Bogendoerfer #define W1_READ_DATA_CRC        0xC3
3225ec8710SThomas Bogendoerfer 
3325ec8710SThomas Bogendoerfer #define OFF2PG(off)	((off) / W1_PAGE_SIZE)
3425ec8710SThomas Bogendoerfer 
3525ec8710SThomas Bogendoerfer #define CRC16_INIT		0
3625ec8710SThomas Bogendoerfer #define CRC16_VALID		0xb001
3725ec8710SThomas Bogendoerfer 
3825ec8710SThomas Bogendoerfer struct w1_eprom_data {
3925ec8710SThomas Bogendoerfer 	size_t size;
4025ec8710SThomas Bogendoerfer 	int (*read)(struct w1_slave *sl, int pageno);
4125ec8710SThomas Bogendoerfer 	u8 eprom[W1_DS2505_SIZE];
4225ec8710SThomas Bogendoerfer 	DECLARE_BITMAP(page_present, W1_DS2505_SIZE / W1_PAGE_SIZE);
4325ec8710SThomas Bogendoerfer 	char nvmem_name[64];
4425ec8710SThomas Bogendoerfer };
4525ec8710SThomas Bogendoerfer 
w1_ds2502_read_page(struct w1_slave * sl,int pageno)4625ec8710SThomas Bogendoerfer static int w1_ds2502_read_page(struct w1_slave *sl, int pageno)
4725ec8710SThomas Bogendoerfer {
4825ec8710SThomas Bogendoerfer 	struct w1_eprom_data *data = sl->family_data;
4925ec8710SThomas Bogendoerfer 	int pgoff = pageno * W1_PAGE_SIZE;
5025ec8710SThomas Bogendoerfer 	int ret = -EIO;
5125ec8710SThomas Bogendoerfer 	u8 buf[3];
5225ec8710SThomas Bogendoerfer 	u8 crc8;
5325ec8710SThomas Bogendoerfer 
5425ec8710SThomas Bogendoerfer 	if (test_bit(pageno, data->page_present))
5525ec8710SThomas Bogendoerfer 		return 0; /* page already present */
5625ec8710SThomas Bogendoerfer 
5725ec8710SThomas Bogendoerfer 	mutex_lock(&sl->master->bus_mutex);
5825ec8710SThomas Bogendoerfer 
5925ec8710SThomas Bogendoerfer 	if (w1_reset_select_slave(sl))
6025ec8710SThomas Bogendoerfer 		goto err;
6125ec8710SThomas Bogendoerfer 
6225ec8710SThomas Bogendoerfer 	buf[0] = W1_READ_DATA_CRC;
6325ec8710SThomas Bogendoerfer 	buf[1] = pgoff & 0xff;
6425ec8710SThomas Bogendoerfer 	buf[2] = pgoff >> 8;
6525ec8710SThomas Bogendoerfer 	w1_write_block(sl->master, buf, 3);
6625ec8710SThomas Bogendoerfer 
6725ec8710SThomas Bogendoerfer 	crc8 = w1_read_8(sl->master);
6825ec8710SThomas Bogendoerfer 	if (w1_calc_crc8(buf, 3) != crc8)
6925ec8710SThomas Bogendoerfer 		goto err;
7025ec8710SThomas Bogendoerfer 
7125ec8710SThomas Bogendoerfer 	w1_read_block(sl->master, &data->eprom[pgoff], W1_PAGE_SIZE);
7225ec8710SThomas Bogendoerfer 
7325ec8710SThomas Bogendoerfer 	crc8 = w1_read_8(sl->master);
7425ec8710SThomas Bogendoerfer 	if (w1_calc_crc8(&data->eprom[pgoff], W1_PAGE_SIZE) != crc8)
7525ec8710SThomas Bogendoerfer 		goto err;
7625ec8710SThomas Bogendoerfer 
7725ec8710SThomas Bogendoerfer 	set_bit(pageno, data->page_present); /* mark page present */
7825ec8710SThomas Bogendoerfer 	ret = 0;
7925ec8710SThomas Bogendoerfer err:
8025ec8710SThomas Bogendoerfer 	mutex_unlock(&sl->master->bus_mutex);
8125ec8710SThomas Bogendoerfer 	return ret;
8225ec8710SThomas Bogendoerfer }
8325ec8710SThomas Bogendoerfer 
w1_ds2505_read_page(struct w1_slave * sl,int pageno)8425ec8710SThomas Bogendoerfer static int w1_ds2505_read_page(struct w1_slave *sl, int pageno)
8525ec8710SThomas Bogendoerfer {
8625ec8710SThomas Bogendoerfer 	struct w1_eprom_data *data = sl->family_data;
8725ec8710SThomas Bogendoerfer 	int redir_retries = 16;
8825ec8710SThomas Bogendoerfer 	int pgoff, epoff;
8925ec8710SThomas Bogendoerfer 	int ret = -EIO;
9025ec8710SThomas Bogendoerfer 	u8 buf[6];
9125ec8710SThomas Bogendoerfer 	u8 redir;
9225ec8710SThomas Bogendoerfer 	u16 crc;
9325ec8710SThomas Bogendoerfer 
9425ec8710SThomas Bogendoerfer 	if (test_bit(pageno, data->page_present))
9525ec8710SThomas Bogendoerfer 		return 0; /* page already present */
9625ec8710SThomas Bogendoerfer 
9725ec8710SThomas Bogendoerfer 	epoff = pgoff = pageno * W1_PAGE_SIZE;
9825ec8710SThomas Bogendoerfer 	mutex_lock(&sl->master->bus_mutex);
9925ec8710SThomas Bogendoerfer 
10025ec8710SThomas Bogendoerfer retry:
10125ec8710SThomas Bogendoerfer 	if (w1_reset_select_slave(sl))
10225ec8710SThomas Bogendoerfer 		goto err;
10325ec8710SThomas Bogendoerfer 
10425ec8710SThomas Bogendoerfer 	buf[0] = W1_EXT_READ_MEMORY;
10525ec8710SThomas Bogendoerfer 	buf[1] = pgoff & 0xff;
10625ec8710SThomas Bogendoerfer 	buf[2] = pgoff >> 8;
10725ec8710SThomas Bogendoerfer 	w1_write_block(sl->master, buf, 3);
10825ec8710SThomas Bogendoerfer 	w1_read_block(sl->master, buf + 3, 3); /* redir, crc16 */
10925ec8710SThomas Bogendoerfer 	redir = buf[3];
11025ec8710SThomas Bogendoerfer 	crc = crc16(CRC16_INIT, buf, 6);
11125ec8710SThomas Bogendoerfer 
11225ec8710SThomas Bogendoerfer 	if (crc != CRC16_VALID)
11325ec8710SThomas Bogendoerfer 		goto err;
11425ec8710SThomas Bogendoerfer 
11525ec8710SThomas Bogendoerfer 
11625ec8710SThomas Bogendoerfer 	if (redir != 0xff) {
11725ec8710SThomas Bogendoerfer 		redir_retries--;
11825ec8710SThomas Bogendoerfer 		if (redir_retries < 0)
11925ec8710SThomas Bogendoerfer 			goto err;
12025ec8710SThomas Bogendoerfer 
12125ec8710SThomas Bogendoerfer 		pgoff = (redir ^ 0xff) * W1_PAGE_SIZE;
12225ec8710SThomas Bogendoerfer 		goto retry;
12325ec8710SThomas Bogendoerfer 	}
12425ec8710SThomas Bogendoerfer 
12525ec8710SThomas Bogendoerfer 	w1_read_block(sl->master, &data->eprom[epoff], W1_PAGE_SIZE);
12625ec8710SThomas Bogendoerfer 	w1_read_block(sl->master, buf, 2); /* crc16 */
12725ec8710SThomas Bogendoerfer 	crc = crc16(CRC16_INIT, &data->eprom[epoff], W1_PAGE_SIZE);
12825ec8710SThomas Bogendoerfer 	crc = crc16(crc, buf, 2);
12925ec8710SThomas Bogendoerfer 
13025ec8710SThomas Bogendoerfer 	if (crc != CRC16_VALID)
13125ec8710SThomas Bogendoerfer 		goto err;
13225ec8710SThomas Bogendoerfer 
13325ec8710SThomas Bogendoerfer 	set_bit(pageno, data->page_present);
13425ec8710SThomas Bogendoerfer 	ret = 0;
13525ec8710SThomas Bogendoerfer err:
13625ec8710SThomas Bogendoerfer 	mutex_unlock(&sl->master->bus_mutex);
13725ec8710SThomas Bogendoerfer 	return ret;
13825ec8710SThomas Bogendoerfer }
13925ec8710SThomas Bogendoerfer 
w1_nvmem_read(void * priv,unsigned int off,void * buf,size_t count)14025ec8710SThomas Bogendoerfer static int w1_nvmem_read(void *priv, unsigned int off, void *buf, size_t count)
14125ec8710SThomas Bogendoerfer {
14225ec8710SThomas Bogendoerfer 	struct w1_slave *sl = priv;
14325ec8710SThomas Bogendoerfer 	struct w1_eprom_data *data = sl->family_data;
14425ec8710SThomas Bogendoerfer 	size_t eprom_size = data->size;
14525ec8710SThomas Bogendoerfer 	int ret;
14625ec8710SThomas Bogendoerfer 	int i;
14725ec8710SThomas Bogendoerfer 
14825ec8710SThomas Bogendoerfer 	if (off > eprom_size)
14925ec8710SThomas Bogendoerfer 		return -EINVAL;
15025ec8710SThomas Bogendoerfer 
15125ec8710SThomas Bogendoerfer 	if ((off + count) > eprom_size)
15225ec8710SThomas Bogendoerfer 		count = eprom_size - off;
15325ec8710SThomas Bogendoerfer 
15425ec8710SThomas Bogendoerfer 	i = OFF2PG(off);
15525ec8710SThomas Bogendoerfer 	do {
15625ec8710SThomas Bogendoerfer 		ret = data->read(sl, i++);
15725ec8710SThomas Bogendoerfer 		if (ret < 0)
15825ec8710SThomas Bogendoerfer 			return ret;
15925ec8710SThomas Bogendoerfer 	} while (i < OFF2PG(off + count));
16025ec8710SThomas Bogendoerfer 
16125ec8710SThomas Bogendoerfer 	memcpy(buf, &data->eprom[off], count);
16225ec8710SThomas Bogendoerfer 	return 0;
16325ec8710SThomas Bogendoerfer }
16425ec8710SThomas Bogendoerfer 
w1_eprom_add_slave(struct w1_slave * sl)16525ec8710SThomas Bogendoerfer static int w1_eprom_add_slave(struct w1_slave *sl)
16625ec8710SThomas Bogendoerfer {
16725ec8710SThomas Bogendoerfer 	struct w1_eprom_data *data;
16825ec8710SThomas Bogendoerfer 	struct nvmem_device *nvmem;
16925ec8710SThomas Bogendoerfer 	struct nvmem_config nvmem_cfg = {
17025ec8710SThomas Bogendoerfer 		.dev = &sl->dev,
17126e2fe4cSRafał Miłecki 		.add_legacy_fixed_of_cells = true,
17225ec8710SThomas Bogendoerfer 		.reg_read = w1_nvmem_read,
17325ec8710SThomas Bogendoerfer 		.type = NVMEM_TYPE_OTP,
17425ec8710SThomas Bogendoerfer 		.read_only = true,
17525ec8710SThomas Bogendoerfer 		.word_size = 1,
17625ec8710SThomas Bogendoerfer 		.priv = sl,
17725ec8710SThomas Bogendoerfer 		.id = -1
17825ec8710SThomas Bogendoerfer 	};
17925ec8710SThomas Bogendoerfer 
18025ec8710SThomas Bogendoerfer 	data = devm_kzalloc(&sl->dev, sizeof(struct w1_eprom_data), GFP_KERNEL);
18125ec8710SThomas Bogendoerfer 	if (!data)
18225ec8710SThomas Bogendoerfer 		return -ENOMEM;
18325ec8710SThomas Bogendoerfer 
18425ec8710SThomas Bogendoerfer 	sl->family_data = data;
18525ec8710SThomas Bogendoerfer 	switch (sl->family->fid) {
18625ec8710SThomas Bogendoerfer 	case W1_DS2501_UNW_FAMILY:
18725ec8710SThomas Bogendoerfer 		data->size = W1_DS2501_SIZE;
18825ec8710SThomas Bogendoerfer 		data->read = w1_ds2502_read_page;
18925ec8710SThomas Bogendoerfer 		break;
19025ec8710SThomas Bogendoerfer 	case W1_DS2502_FAMILY:
19125ec8710SThomas Bogendoerfer 	case W1_DS2502_UNW_FAMILY:
19225ec8710SThomas Bogendoerfer 		data->size = W1_DS2502_SIZE;
19325ec8710SThomas Bogendoerfer 		data->read = w1_ds2502_read_page;
19425ec8710SThomas Bogendoerfer 		break;
19525ec8710SThomas Bogendoerfer 	case W1_DS2505_FAMILY:
19625ec8710SThomas Bogendoerfer 		data->size = W1_DS2505_SIZE;
19725ec8710SThomas Bogendoerfer 		data->read = w1_ds2505_read_page;
19825ec8710SThomas Bogendoerfer 		break;
19925ec8710SThomas Bogendoerfer 	}
20025ec8710SThomas Bogendoerfer 
20125ec8710SThomas Bogendoerfer 	if (sl->master->bus_master->dev_id)
20225ec8710SThomas Bogendoerfer 		snprintf(data->nvmem_name, sizeof(data->nvmem_name),
20325ec8710SThomas Bogendoerfer 			 "%s-%02x-%012llx",
20425ec8710SThomas Bogendoerfer 			 sl->master->bus_master->dev_id, sl->reg_num.family,
20525ec8710SThomas Bogendoerfer 			 (unsigned long long)sl->reg_num.id);
20625ec8710SThomas Bogendoerfer 	else
20725ec8710SThomas Bogendoerfer 		snprintf(data->nvmem_name, sizeof(data->nvmem_name),
20825ec8710SThomas Bogendoerfer 			 "%02x-%012llx",
20925ec8710SThomas Bogendoerfer 			 sl->reg_num.family,
21025ec8710SThomas Bogendoerfer 			 (unsigned long long)sl->reg_num.id);
21125ec8710SThomas Bogendoerfer 
21225ec8710SThomas Bogendoerfer 	nvmem_cfg.name = data->nvmem_name;
21325ec8710SThomas Bogendoerfer 	nvmem_cfg.size = data->size;
21425ec8710SThomas Bogendoerfer 
21525ec8710SThomas Bogendoerfer 	nvmem = devm_nvmem_register(&sl->dev, &nvmem_cfg);
21625ec8710SThomas Bogendoerfer 	return PTR_ERR_OR_ZERO(nvmem);
21725ec8710SThomas Bogendoerfer }
21825ec8710SThomas Bogendoerfer 
21957de2dfcSRikard Falkeborn static const struct w1_family_ops w1_eprom_fops = {
22025ec8710SThomas Bogendoerfer 	.add_slave	= w1_eprom_add_slave,
22125ec8710SThomas Bogendoerfer };
22225ec8710SThomas Bogendoerfer 
22325ec8710SThomas Bogendoerfer static struct w1_family w1_family_09 = {
22425ec8710SThomas Bogendoerfer 	.fid = W1_DS2502_FAMILY,
22525ec8710SThomas Bogendoerfer 	.fops = &w1_eprom_fops,
22625ec8710SThomas Bogendoerfer };
22725ec8710SThomas Bogendoerfer 
22825ec8710SThomas Bogendoerfer static struct w1_family w1_family_0b = {
22925ec8710SThomas Bogendoerfer 	.fid = W1_DS2505_FAMILY,
23025ec8710SThomas Bogendoerfer 	.fops = &w1_eprom_fops,
23125ec8710SThomas Bogendoerfer };
23225ec8710SThomas Bogendoerfer 
23325ec8710SThomas Bogendoerfer static struct w1_family w1_family_89 = {
23425ec8710SThomas Bogendoerfer 	.fid = W1_DS2502_UNW_FAMILY,
23525ec8710SThomas Bogendoerfer 	.fops = &w1_eprom_fops,
23625ec8710SThomas Bogendoerfer };
23725ec8710SThomas Bogendoerfer 
23825ec8710SThomas Bogendoerfer static struct w1_family w1_family_91 = {
23925ec8710SThomas Bogendoerfer 	.fid = W1_DS2501_UNW_FAMILY,
24025ec8710SThomas Bogendoerfer 	.fops = &w1_eprom_fops,
24125ec8710SThomas Bogendoerfer };
24225ec8710SThomas Bogendoerfer 
w1_ds250x_init(void)24325ec8710SThomas Bogendoerfer static int __init w1_ds250x_init(void)
24425ec8710SThomas Bogendoerfer {
24525ec8710SThomas Bogendoerfer 	int err;
24625ec8710SThomas Bogendoerfer 
24725ec8710SThomas Bogendoerfer 	err = w1_register_family(&w1_family_09);
24825ec8710SThomas Bogendoerfer 	if (err)
24925ec8710SThomas Bogendoerfer 		return err;
25025ec8710SThomas Bogendoerfer 
25125ec8710SThomas Bogendoerfer 	err = w1_register_family(&w1_family_0b);
25225ec8710SThomas Bogendoerfer 	if (err)
25325ec8710SThomas Bogendoerfer 		goto err_0b;
25425ec8710SThomas Bogendoerfer 
25525ec8710SThomas Bogendoerfer 	err = w1_register_family(&w1_family_89);
25625ec8710SThomas Bogendoerfer 	if (err)
25725ec8710SThomas Bogendoerfer 		goto err_89;
25825ec8710SThomas Bogendoerfer 
25925ec8710SThomas Bogendoerfer 	err = w1_register_family(&w1_family_91);
26025ec8710SThomas Bogendoerfer 	if (err)
26125ec8710SThomas Bogendoerfer 		goto err_91;
26225ec8710SThomas Bogendoerfer 
26325ec8710SThomas Bogendoerfer 	return 0;
26425ec8710SThomas Bogendoerfer 
26525ec8710SThomas Bogendoerfer err_91:
26625ec8710SThomas Bogendoerfer 	w1_unregister_family(&w1_family_89);
26725ec8710SThomas Bogendoerfer err_89:
26825ec8710SThomas Bogendoerfer 	w1_unregister_family(&w1_family_0b);
26925ec8710SThomas Bogendoerfer err_0b:
27025ec8710SThomas Bogendoerfer 	w1_unregister_family(&w1_family_09);
27125ec8710SThomas Bogendoerfer 	return err;
27225ec8710SThomas Bogendoerfer }
27325ec8710SThomas Bogendoerfer 
w1_ds250x_exit(void)27425ec8710SThomas Bogendoerfer static void __exit w1_ds250x_exit(void)
27525ec8710SThomas Bogendoerfer {
27625ec8710SThomas Bogendoerfer 	w1_unregister_family(&w1_family_09);
27725ec8710SThomas Bogendoerfer 	w1_unregister_family(&w1_family_0b);
27825ec8710SThomas Bogendoerfer 	w1_unregister_family(&w1_family_89);
27925ec8710SThomas Bogendoerfer 	w1_unregister_family(&w1_family_91);
28025ec8710SThomas Bogendoerfer }
28125ec8710SThomas Bogendoerfer 
28225ec8710SThomas Bogendoerfer module_init(w1_ds250x_init);
28325ec8710SThomas Bogendoerfer module_exit(w1_ds250x_exit);
28425ec8710SThomas Bogendoerfer 
28525ec8710SThomas Bogendoerfer MODULE_AUTHOR("Thomas Bogendoerfer <tbogendoerfe@suse.de>");
28625ec8710SThomas Bogendoerfer MODULE_DESCRIPTION("w1 family driver for DS250x Add Only Memory");
28725ec8710SThomas Bogendoerfer MODULE_LICENSE("GPL");
28825ec8710SThomas Bogendoerfer MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_FAMILY));
28925ec8710SThomas Bogendoerfer MODULE_ALIAS("w1-family-" __stringify(W1_DS2505_FAMILY));
29025ec8710SThomas Bogendoerfer MODULE_ALIAS("w1-family-" __stringify(W1_DS2501_UNW_FAMILY));
29125ec8710SThomas Bogendoerfer MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_UNW_FAMILY));
292