xref: /openbmc/linux/drivers/w1/slaves/w1_ds2406.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
140b0b3f8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
294859308SScott Alfter /*
394859308SScott Alfter  * w1_ds2406.c - w1 family 12 (DS2406) driver
494859308SScott Alfter  * based on w1_ds2413.c by Mariusz Bialonczyk <manio@skyboo.net>
594859308SScott Alfter  *
694859308SScott Alfter  * Copyright (c) 2014 Scott Alfter <scott@alfter.us>
794859308SScott Alfter  */
894859308SScott Alfter 
994859308SScott Alfter #include <linux/kernel.h>
1094859308SScott Alfter #include <linux/module.h>
1194859308SScott Alfter #include <linux/moduleparam.h>
1294859308SScott Alfter #include <linux/device.h>
1394859308SScott Alfter #include <linux/types.h>
1494859308SScott Alfter #include <linux/delay.h>
1594859308SScott Alfter #include <linux/slab.h>
1694859308SScott Alfter #include <linux/crc16.h>
1794859308SScott Alfter 
18de0d6dbdSAndrew F. Davis #include <linux/w1.h>
19de0d6dbdSAndrew F. Davis 
20de0d6dbdSAndrew F. Davis #define W1_FAMILY_DS2406	0x12
2194859308SScott Alfter 
2294859308SScott Alfter #define W1_F12_FUNC_READ_STATUS		   0xAA
2394859308SScott Alfter #define W1_F12_FUNC_WRITE_STATUS	   0x55
2494859308SScott Alfter 
w1_f12_read_state(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)2594859308SScott Alfter static ssize_t w1_f12_read_state(
2694859308SScott Alfter 	struct file *filp, struct kobject *kobj,
2794859308SScott Alfter 	struct bin_attribute *bin_attr,
2894859308SScott Alfter 	char *buf, loff_t off, size_t count)
2994859308SScott Alfter {
3094859308SScott Alfter 	u8 w1_buf[6] = {W1_F12_FUNC_READ_STATUS, 7, 0, 0, 0, 0};
3194859308SScott Alfter 	struct w1_slave *sl = kobj_to_w1_slave(kobj);
3294859308SScott Alfter 	u16 crc = 0;
3394859308SScott Alfter 	int i;
3494859308SScott Alfter 	ssize_t rtnval = 1;
3594859308SScott Alfter 
3694859308SScott Alfter 	if (off != 0)
3794859308SScott Alfter 		return 0;
3894859308SScott Alfter 	if (!buf)
3994859308SScott Alfter 		return -EINVAL;
4094859308SScott Alfter 
4194859308SScott Alfter 	mutex_lock(&sl->master->bus_mutex);
4294859308SScott Alfter 
4394859308SScott Alfter 	if (w1_reset_select_slave(sl)) {
4494859308SScott Alfter 		mutex_unlock(&sl->master->bus_mutex);
4594859308SScott Alfter 		return -EIO;
4694859308SScott Alfter 	}
4794859308SScott Alfter 
4894859308SScott Alfter 	w1_write_block(sl->master, w1_buf, 3);
4994859308SScott Alfter 	w1_read_block(sl->master, w1_buf+3, 3);
5094859308SScott Alfter 	for (i = 0; i < 6; i++)
5194859308SScott Alfter 		crc = crc16_byte(crc, w1_buf[i]);
5294859308SScott Alfter 	if (crc == 0xb001) /* good read? */
5394859308SScott Alfter 		*buf = ((w1_buf[3]>>5)&3)|0x30;
5494859308SScott Alfter 	else
5594859308SScott Alfter 		rtnval = -EIO;
5694859308SScott Alfter 
5794859308SScott Alfter 	mutex_unlock(&sl->master->bus_mutex);
5894859308SScott Alfter 
5994859308SScott Alfter 	return rtnval;
6094859308SScott Alfter }
6194859308SScott Alfter 
w1_f12_write_output(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)6294859308SScott Alfter static ssize_t w1_f12_write_output(
6394859308SScott Alfter 	struct file *filp, struct kobject *kobj,
6494859308SScott Alfter 	struct bin_attribute *bin_attr,
6594859308SScott Alfter 	char *buf, loff_t off, size_t count)
6694859308SScott Alfter {
6794859308SScott Alfter 	struct w1_slave *sl = kobj_to_w1_slave(kobj);
6894859308SScott Alfter 	u8 w1_buf[6] = {W1_F12_FUNC_WRITE_STATUS, 7, 0, 0, 0, 0};
6994859308SScott Alfter 	u16 crc = 0;
7094859308SScott Alfter 	int i;
7194859308SScott Alfter 	ssize_t rtnval = 1;
7294859308SScott Alfter 
7394859308SScott Alfter 	if (count != 1 || off != 0)
7494859308SScott Alfter 		return -EFAULT;
7594859308SScott Alfter 
7694859308SScott Alfter 	mutex_lock(&sl->master->bus_mutex);
7794859308SScott Alfter 
7894859308SScott Alfter 	if (w1_reset_select_slave(sl)) {
7994859308SScott Alfter 		mutex_unlock(&sl->master->bus_mutex);
8094859308SScott Alfter 		return -EIO;
8194859308SScott Alfter 	}
8294859308SScott Alfter 
8394859308SScott Alfter 	w1_buf[3] = (((*buf)&3)<<5)|0x1F;
8494859308SScott Alfter 	w1_write_block(sl->master, w1_buf, 4);
8594859308SScott Alfter 	w1_read_block(sl->master, w1_buf+4, 2);
8694859308SScott Alfter 	for (i = 0; i < 6; i++)
8794859308SScott Alfter 		crc = crc16_byte(crc, w1_buf[i]);
8894859308SScott Alfter 	if (crc == 0xb001) /* good read? */
8994859308SScott Alfter 		w1_write_8(sl->master, 0xFF);
9094859308SScott Alfter 	else
9194859308SScott Alfter 		rtnval = -EIO;
9294859308SScott Alfter 
9394859308SScott Alfter 	mutex_unlock(&sl->master->bus_mutex);
9494859308SScott Alfter 	return rtnval;
9594859308SScott Alfter }
9694859308SScott Alfter 
9794859308SScott Alfter #define NB_SYSFS_BIN_FILES 2
9894859308SScott Alfter static struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = {
9994859308SScott Alfter 	{
10094859308SScott Alfter 		.attr = {
10194859308SScott Alfter 			.name = "state",
102*921e0f2fSKrzysztof Kozlowski 			.mode = 0444,
10394859308SScott Alfter 		},
10494859308SScott Alfter 		.size = 1,
10594859308SScott Alfter 		.read = w1_f12_read_state,
10694859308SScott Alfter 	},
10794859308SScott Alfter 	{
10894859308SScott Alfter 		.attr = {
10994859308SScott Alfter 			.name = "output",
110*921e0f2fSKrzysztof Kozlowski 			.mode = 0664,
11194859308SScott Alfter 		},
11294859308SScott Alfter 		.size = 1,
11394859308SScott Alfter 		.write = w1_f12_write_output,
11494859308SScott Alfter 	}
11594859308SScott Alfter };
11694859308SScott Alfter 
w1_f12_add_slave(struct w1_slave * sl)11794859308SScott Alfter static int w1_f12_add_slave(struct w1_slave *sl)
11894859308SScott Alfter {
11994859308SScott Alfter 	int err = 0;
12094859308SScott Alfter 	int i;
12194859308SScott Alfter 
12294859308SScott Alfter 	for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i)
12394859308SScott Alfter 		err = sysfs_create_bin_file(
12494859308SScott Alfter 			&sl->dev.kobj,
12594859308SScott Alfter 			&(w1_f12_sysfs_bin_files[i]));
12694859308SScott Alfter 	if (err)
12794859308SScott Alfter 		while (--i >= 0)
12894859308SScott Alfter 			sysfs_remove_bin_file(&sl->dev.kobj,
12994859308SScott Alfter 				&(w1_f12_sysfs_bin_files[i]));
13094859308SScott Alfter 	return err;
13194859308SScott Alfter }
13294859308SScott Alfter 
w1_f12_remove_slave(struct w1_slave * sl)13394859308SScott Alfter static void w1_f12_remove_slave(struct w1_slave *sl)
13494859308SScott Alfter {
13594859308SScott Alfter 	int i;
136ad9c36beSKrzysztof Kozlowski 
13794859308SScott Alfter 	for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i)
13894859308SScott Alfter 		sysfs_remove_bin_file(&sl->dev.kobj,
13994859308SScott Alfter 			&(w1_f12_sysfs_bin_files[i]));
14094859308SScott Alfter }
14194859308SScott Alfter 
14257de2dfcSRikard Falkeborn static const struct w1_family_ops w1_f12_fops = {
14394859308SScott Alfter 	.add_slave      = w1_f12_add_slave,
14494859308SScott Alfter 	.remove_slave   = w1_f12_remove_slave,
14594859308SScott Alfter };
14694859308SScott Alfter 
14794859308SScott Alfter static struct w1_family w1_family_12 = {
14894859308SScott Alfter 	.fid = W1_FAMILY_DS2406,
14994859308SScott Alfter 	.fops = &w1_f12_fops,
15094859308SScott Alfter };
151939fc832SAndrew F. Davis module_w1_family(w1_family_12);
15250fa2951SAndrew F. Davis 
15350fa2951SAndrew F. Davis MODULE_AUTHOR("Scott Alfter <scott@alfter.us>");
15450fa2951SAndrew F. Davis MODULE_DESCRIPTION("w1 family 12 driver for DS2406 2 Pin IO");
15550fa2951SAndrew F. Davis MODULE_LICENSE("GPL");
156