Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  w1_ds2405.c
0004  *
0005  * Copyright (c) 2017 Maciej S. Szmigiero <mail@maciej.szmigiero.name>
0006  * Based on w1_therm.c copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
0007  */
0008 
0009 #include <linux/device.h>
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/moduleparam.h>
0013 #include <linux/mutex.h>
0014 #include <linux/string.h>
0015 #include <linux/types.h>
0016 
0017 #include <linux/w1.h>
0018 
0019 #define W1_FAMILY_DS2405    0x05
0020 
0021 MODULE_LICENSE("GPL");
0022 MODULE_AUTHOR("Maciej S. Szmigiero <mail@maciej.szmigiero.name>");
0023 MODULE_DESCRIPTION("Driver for 1-wire Dallas DS2405 PIO.");
0024 MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2405));
0025 
0026 static int w1_ds2405_select(struct w1_slave *sl, bool only_active)
0027 {
0028     struct w1_master *dev = sl->master;
0029 
0030     u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num);
0031     unsigned int bit_ctr;
0032 
0033     if (w1_reset_bus(dev) != 0)
0034         return 0;
0035 
0036     /*
0037      * We cannot use a normal Match ROM command
0038      * since doing so would toggle PIO state
0039      */
0040     w1_write_8(dev, only_active ? W1_ALARM_SEARCH : W1_SEARCH);
0041 
0042     for (bit_ctr = 0; bit_ctr < 64; bit_ctr++) {
0043         int bit2send = !!(dev_addr & BIT(bit_ctr));
0044         u8 ret;
0045 
0046         ret = w1_triplet(dev, bit2send);
0047 
0048         if ((ret & (BIT(0) | BIT(1))) ==
0049             (BIT(0) | BIT(1))) /* no devices found */
0050             return 0;
0051 
0052         if (!!(ret & BIT(2)) != bit2send)
0053             /* wrong direction taken - no such device */
0054             return 0;
0055     }
0056 
0057     return 1;
0058 }
0059 
0060 static int w1_ds2405_read_pio(struct w1_slave *sl)
0061 {
0062     if (w1_ds2405_select(sl, true))
0063         return 0; /* "active" means PIO is low */
0064 
0065     if (w1_ds2405_select(sl, false))
0066         return 1;
0067 
0068     return -ENODEV;
0069 }
0070 
0071 static ssize_t state_show(struct device *device,
0072               struct device_attribute *attr, char *buf)
0073 {
0074     struct w1_slave *sl = dev_to_w1_slave(device);
0075     struct w1_master *dev = sl->master;
0076 
0077     int ret;
0078     ssize_t f_retval;
0079     u8 state;
0080 
0081     ret = mutex_lock_interruptible(&dev->bus_mutex);
0082     if (ret)
0083         return ret;
0084 
0085     if (!w1_ds2405_select(sl, false)) {
0086         f_retval = -ENODEV;
0087         goto out_unlock;
0088     }
0089 
0090     state = w1_read_8(dev);
0091     if (state != 0 &&
0092         state != 0xff) {
0093         dev_err(device, "non-consistent state %x\n", state);
0094         f_retval = -EIO;
0095         goto out_unlock;
0096     }
0097 
0098     *buf = state ? '1' : '0';
0099     f_retval = 1;
0100 
0101 out_unlock:
0102     w1_reset_bus(dev);
0103     mutex_unlock(&dev->bus_mutex);
0104 
0105     return f_retval;
0106 }
0107 
0108 static ssize_t output_show(struct device *device,
0109                struct device_attribute *attr, char *buf)
0110 {
0111     struct w1_slave *sl = dev_to_w1_slave(device);
0112     struct w1_master *dev = sl->master;
0113 
0114     int ret;
0115     ssize_t f_retval;
0116 
0117     ret = mutex_lock_interruptible(&dev->bus_mutex);
0118     if (ret)
0119         return ret;
0120 
0121     ret = w1_ds2405_read_pio(sl);
0122     if (ret < 0) {
0123         f_retval = ret;
0124         goto out_unlock;
0125     }
0126 
0127     *buf = ret ? '1' : '0';
0128     f_retval = 1;
0129 
0130 out_unlock:
0131     w1_reset_bus(dev);
0132     mutex_unlock(&dev->bus_mutex);
0133 
0134     return f_retval;
0135 }
0136 
0137 static ssize_t output_store(struct device *device,
0138                 struct device_attribute *attr,
0139                 const char *buf, size_t count)
0140 {
0141     struct w1_slave *sl = dev_to_w1_slave(device);
0142     struct w1_master *dev = sl->master;
0143 
0144     int ret, current_pio;
0145     unsigned int val;
0146     ssize_t f_retval;
0147 
0148     if (count < 1)
0149         return -EINVAL;
0150 
0151     if (sscanf(buf, " %u%n", &val, &ret) < 1)
0152         return -EINVAL;
0153 
0154     if (val != 0 && val != 1)
0155         return -EINVAL;
0156 
0157     f_retval = ret;
0158 
0159     ret = mutex_lock_interruptible(&dev->bus_mutex);
0160     if (ret)
0161         return ret;
0162 
0163     current_pio = w1_ds2405_read_pio(sl);
0164     if (current_pio < 0) {
0165         f_retval = current_pio;
0166         goto out_unlock;
0167     }
0168 
0169     if (current_pio == val)
0170         goto out_unlock;
0171 
0172     if (w1_reset_bus(dev) != 0) {
0173         f_retval = -ENODEV;
0174         goto out_unlock;
0175     }
0176 
0177     /*
0178      * can't use w1_reset_select_slave() here since it uses Skip ROM if
0179      * there is only one device on bus
0180      */
0181     do {
0182         u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num);
0183         u8 cmd[9];
0184 
0185         cmd[0] = W1_MATCH_ROM;
0186         memcpy(&cmd[1], &dev_addr, sizeof(dev_addr));
0187 
0188         w1_write_block(dev, cmd, sizeof(cmd));
0189     } while (0);
0190 
0191 out_unlock:
0192     w1_reset_bus(dev);
0193     mutex_unlock(&dev->bus_mutex);
0194 
0195     return f_retval;
0196 }
0197 
0198 static DEVICE_ATTR_RO(state);
0199 static DEVICE_ATTR_RW(output);
0200 
0201 static struct attribute *w1_ds2405_attrs[] = {
0202     &dev_attr_state.attr,
0203     &dev_attr_output.attr,
0204     NULL
0205 };
0206 
0207 ATTRIBUTE_GROUPS(w1_ds2405);
0208 
0209 static const struct w1_family_ops w1_ds2405_fops = {
0210     .groups = w1_ds2405_groups
0211 };
0212 
0213 static struct w1_family w1_family_ds2405 = {
0214     .fid = W1_FAMILY_DS2405,
0215     .fops = &w1_ds2405_fops
0216 };
0217 
0218 module_w1_family(w1_family_ds2405);