18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * w1_ds2406.c - w1 family 12 (DS2406) driver 48c2ecf20Sopenharmony_ci * based on w1_ds2413.c by Mariusz Bialonczyk <manio@skyboo.net> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2014 Scott Alfter <scott@alfter.us> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/crc16.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/w1.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define W1_FAMILY_DS2406 0x12 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define W1_F12_FUNC_READ_STATUS 0xAA 238c2ecf20Sopenharmony_ci#define W1_F12_FUNC_WRITE_STATUS 0x55 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic ssize_t w1_f12_read_state( 268c2ecf20Sopenharmony_ci struct file *filp, struct kobject *kobj, 278c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, 288c2ecf20Sopenharmony_ci char *buf, loff_t off, size_t count) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci u8 w1_buf[6]={W1_F12_FUNC_READ_STATUS, 7, 0, 0, 0, 0}; 318c2ecf20Sopenharmony_ci struct w1_slave *sl = kobj_to_w1_slave(kobj); 328c2ecf20Sopenharmony_ci u16 crc=0; 338c2ecf20Sopenharmony_ci int i; 348c2ecf20Sopenharmony_ci ssize_t rtnval=1; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (off != 0) 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci if (!buf) 398c2ecf20Sopenharmony_ci return -EINVAL; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci mutex_lock(&sl->master->bus_mutex); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (w1_reset_select_slave(sl)) { 448c2ecf20Sopenharmony_ci mutex_unlock(&sl->master->bus_mutex); 458c2ecf20Sopenharmony_ci return -EIO; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci w1_write_block(sl->master, w1_buf, 3); 498c2ecf20Sopenharmony_ci w1_read_block(sl->master, w1_buf+3, 3); 508c2ecf20Sopenharmony_ci for (i=0; i<6; i++) 518c2ecf20Sopenharmony_ci crc=crc16_byte(crc, w1_buf[i]); 528c2ecf20Sopenharmony_ci if (crc==0xb001) /* good read? */ 538c2ecf20Sopenharmony_ci *buf=((w1_buf[3]>>5)&3)|0x30; 548c2ecf20Sopenharmony_ci else 558c2ecf20Sopenharmony_ci rtnval=-EIO; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci mutex_unlock(&sl->master->bus_mutex); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return rtnval; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic ssize_t w1_f12_write_output( 638c2ecf20Sopenharmony_ci struct file *filp, struct kobject *kobj, 648c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, 658c2ecf20Sopenharmony_ci char *buf, loff_t off, size_t count) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct w1_slave *sl = kobj_to_w1_slave(kobj); 688c2ecf20Sopenharmony_ci u8 w1_buf[6]={W1_F12_FUNC_WRITE_STATUS, 7, 0, 0, 0, 0}; 698c2ecf20Sopenharmony_ci u16 crc=0; 708c2ecf20Sopenharmony_ci int i; 718c2ecf20Sopenharmony_ci ssize_t rtnval=1; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (count != 1 || off != 0) 748c2ecf20Sopenharmony_ci return -EFAULT; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci mutex_lock(&sl->master->bus_mutex); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (w1_reset_select_slave(sl)) { 798c2ecf20Sopenharmony_ci mutex_unlock(&sl->master->bus_mutex); 808c2ecf20Sopenharmony_ci return -EIO; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci w1_buf[3] = (((*buf)&3)<<5)|0x1F; 848c2ecf20Sopenharmony_ci w1_write_block(sl->master, w1_buf, 4); 858c2ecf20Sopenharmony_ci w1_read_block(sl->master, w1_buf+4, 2); 868c2ecf20Sopenharmony_ci for (i=0; i<6; i++) 878c2ecf20Sopenharmony_ci crc=crc16_byte(crc, w1_buf[i]); 888c2ecf20Sopenharmony_ci if (crc==0xb001) /* good read? */ 898c2ecf20Sopenharmony_ci w1_write_8(sl->master, 0xFF); 908c2ecf20Sopenharmony_ci else 918c2ecf20Sopenharmony_ci rtnval=-EIO; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci mutex_unlock(&sl->master->bus_mutex); 948c2ecf20Sopenharmony_ci return rtnval; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define NB_SYSFS_BIN_FILES 2 988c2ecf20Sopenharmony_cistatic struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { 998c2ecf20Sopenharmony_ci { 1008c2ecf20Sopenharmony_ci .attr = { 1018c2ecf20Sopenharmony_ci .name = "state", 1028c2ecf20Sopenharmony_ci .mode = S_IRUGO, 1038c2ecf20Sopenharmony_ci }, 1048c2ecf20Sopenharmony_ci .size = 1, 1058c2ecf20Sopenharmony_ci .read = w1_f12_read_state, 1068c2ecf20Sopenharmony_ci }, 1078c2ecf20Sopenharmony_ci { 1088c2ecf20Sopenharmony_ci .attr = { 1098c2ecf20Sopenharmony_ci .name = "output", 1108c2ecf20Sopenharmony_ci .mode = S_IRUGO | S_IWUSR | S_IWGRP, 1118c2ecf20Sopenharmony_ci }, 1128c2ecf20Sopenharmony_ci .size = 1, 1138c2ecf20Sopenharmony_ci .write = w1_f12_write_output, 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int w1_f12_add_slave(struct w1_slave *sl) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int err = 0; 1208c2ecf20Sopenharmony_ci int i; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i) 1238c2ecf20Sopenharmony_ci err = sysfs_create_bin_file( 1248c2ecf20Sopenharmony_ci &sl->dev.kobj, 1258c2ecf20Sopenharmony_ci &(w1_f12_sysfs_bin_files[i])); 1268c2ecf20Sopenharmony_ci if (err) 1278c2ecf20Sopenharmony_ci while (--i >= 0) 1288c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&sl->dev.kobj, 1298c2ecf20Sopenharmony_ci &(w1_f12_sysfs_bin_files[i])); 1308c2ecf20Sopenharmony_ci return err; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void w1_f12_remove_slave(struct w1_slave *sl) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int i; 1368c2ecf20Sopenharmony_ci for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i) 1378c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&sl->dev.kobj, 1388c2ecf20Sopenharmony_ci &(w1_f12_sysfs_bin_files[i])); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic const struct w1_family_ops w1_f12_fops = { 1428c2ecf20Sopenharmony_ci .add_slave = w1_f12_add_slave, 1438c2ecf20Sopenharmony_ci .remove_slave = w1_f12_remove_slave, 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic struct w1_family w1_family_12 = { 1478c2ecf20Sopenharmony_ci .fid = W1_FAMILY_DS2406, 1488c2ecf20Sopenharmony_ci .fops = &w1_f12_fops, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_cimodule_w1_family(w1_family_12); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Scott Alfter <scott@alfter.us>"); 1538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("w1 family 12 driver for DS2406 2 Pin IO"); 1548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 155