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