162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * w1_ds2406.c - w1 family 12 (DS2406) driver 462306a36Sopenharmony_ci * based on w1_ds2413.c by Mariusz Bialonczyk <manio@skyboo.net> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2014 Scott Alfter <scott@alfter.us> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/moduleparam.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/crc16.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/w1.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define W1_FAMILY_DS2406 0x12 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define W1_F12_FUNC_READ_STATUS 0xAA 2362306a36Sopenharmony_ci#define W1_F12_FUNC_WRITE_STATUS 0x55 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic ssize_t w1_f12_read_state( 2662306a36Sopenharmony_ci struct file *filp, struct kobject *kobj, 2762306a36Sopenharmony_ci struct bin_attribute *bin_attr, 2862306a36Sopenharmony_ci char *buf, loff_t off, size_t count) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci u8 w1_buf[6] = {W1_F12_FUNC_READ_STATUS, 7, 0, 0, 0, 0}; 3162306a36Sopenharmony_ci struct w1_slave *sl = kobj_to_w1_slave(kobj); 3262306a36Sopenharmony_ci u16 crc = 0; 3362306a36Sopenharmony_ci int i; 3462306a36Sopenharmony_ci ssize_t rtnval = 1; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (off != 0) 3762306a36Sopenharmony_ci return 0; 3862306a36Sopenharmony_ci if (!buf) 3962306a36Sopenharmony_ci return -EINVAL; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci mutex_lock(&sl->master->bus_mutex); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (w1_reset_select_slave(sl)) { 4462306a36Sopenharmony_ci mutex_unlock(&sl->master->bus_mutex); 4562306a36Sopenharmony_ci return -EIO; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci w1_write_block(sl->master, w1_buf, 3); 4962306a36Sopenharmony_ci w1_read_block(sl->master, w1_buf+3, 3); 5062306a36Sopenharmony_ci for (i = 0; i < 6; i++) 5162306a36Sopenharmony_ci crc = crc16_byte(crc, w1_buf[i]); 5262306a36Sopenharmony_ci if (crc == 0xb001) /* good read? */ 5362306a36Sopenharmony_ci *buf = ((w1_buf[3]>>5)&3)|0x30; 5462306a36Sopenharmony_ci else 5562306a36Sopenharmony_ci rtnval = -EIO; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci mutex_unlock(&sl->master->bus_mutex); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return rtnval; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic ssize_t w1_f12_write_output( 6362306a36Sopenharmony_ci struct file *filp, struct kobject *kobj, 6462306a36Sopenharmony_ci struct bin_attribute *bin_attr, 6562306a36Sopenharmony_ci char *buf, loff_t off, size_t count) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct w1_slave *sl = kobj_to_w1_slave(kobj); 6862306a36Sopenharmony_ci u8 w1_buf[6] = {W1_F12_FUNC_WRITE_STATUS, 7, 0, 0, 0, 0}; 6962306a36Sopenharmony_ci u16 crc = 0; 7062306a36Sopenharmony_ci int i; 7162306a36Sopenharmony_ci ssize_t rtnval = 1; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (count != 1 || off != 0) 7462306a36Sopenharmony_ci return -EFAULT; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci mutex_lock(&sl->master->bus_mutex); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (w1_reset_select_slave(sl)) { 7962306a36Sopenharmony_ci mutex_unlock(&sl->master->bus_mutex); 8062306a36Sopenharmony_ci return -EIO; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci w1_buf[3] = (((*buf)&3)<<5)|0x1F; 8462306a36Sopenharmony_ci w1_write_block(sl->master, w1_buf, 4); 8562306a36Sopenharmony_ci w1_read_block(sl->master, w1_buf+4, 2); 8662306a36Sopenharmony_ci for (i = 0; i < 6; i++) 8762306a36Sopenharmony_ci crc = crc16_byte(crc, w1_buf[i]); 8862306a36Sopenharmony_ci if (crc == 0xb001) /* good read? */ 8962306a36Sopenharmony_ci w1_write_8(sl->master, 0xFF); 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci rtnval = -EIO; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci mutex_unlock(&sl->master->bus_mutex); 9462306a36Sopenharmony_ci return rtnval; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define NB_SYSFS_BIN_FILES 2 9862306a36Sopenharmony_cistatic struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { 9962306a36Sopenharmony_ci { 10062306a36Sopenharmony_ci .attr = { 10162306a36Sopenharmony_ci .name = "state", 10262306a36Sopenharmony_ci .mode = 0444, 10362306a36Sopenharmony_ci }, 10462306a36Sopenharmony_ci .size = 1, 10562306a36Sopenharmony_ci .read = w1_f12_read_state, 10662306a36Sopenharmony_ci }, 10762306a36Sopenharmony_ci { 10862306a36Sopenharmony_ci .attr = { 10962306a36Sopenharmony_ci .name = "output", 11062306a36Sopenharmony_ci .mode = 0664, 11162306a36Sopenharmony_ci }, 11262306a36Sopenharmony_ci .size = 1, 11362306a36Sopenharmony_ci .write = w1_f12_write_output, 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int w1_f12_add_slave(struct w1_slave *sl) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int err = 0; 12062306a36Sopenharmony_ci int i; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i) 12362306a36Sopenharmony_ci err = sysfs_create_bin_file( 12462306a36Sopenharmony_ci &sl->dev.kobj, 12562306a36Sopenharmony_ci &(w1_f12_sysfs_bin_files[i])); 12662306a36Sopenharmony_ci if (err) 12762306a36Sopenharmony_ci while (--i >= 0) 12862306a36Sopenharmony_ci sysfs_remove_bin_file(&sl->dev.kobj, 12962306a36Sopenharmony_ci &(w1_f12_sysfs_bin_files[i])); 13062306a36Sopenharmony_ci return err; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void w1_f12_remove_slave(struct w1_slave *sl) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci int i; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i) 13862306a36Sopenharmony_ci sysfs_remove_bin_file(&sl->dev.kobj, 13962306a36Sopenharmony_ci &(w1_f12_sysfs_bin_files[i])); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic const struct w1_family_ops w1_f12_fops = { 14362306a36Sopenharmony_ci .add_slave = w1_f12_add_slave, 14462306a36Sopenharmony_ci .remove_slave = w1_f12_remove_slave, 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic struct w1_family w1_family_12 = { 14862306a36Sopenharmony_ci .fid = W1_FAMILY_DS2406, 14962306a36Sopenharmony_ci .fops = &w1_f12_fops, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_cimodule_w1_family(w1_family_12); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciMODULE_AUTHOR("Scott Alfter <scott@alfter.us>"); 15462306a36Sopenharmony_ciMODULE_DESCRIPTION("w1 family 12 driver for DS2406 2 Pin IO"); 15562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 156