diff options
Diffstat (limited to 'drivers/base/regmap/regmap-w1.c')
-rw-r--r-- | drivers/base/regmap/regmap-w1.c | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/drivers/base/regmap/regmap-w1.c b/drivers/base/regmap/regmap-w1.c new file mode 100644 index 000000000..1fbaaad71 --- /dev/null +++ b/drivers/base/regmap/regmap-w1.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Register map access API - W1 (1-Wire) support +// +// Copyright (c) 2017 Radioavionica Corporation +// Author: Alex A. Mihaylov <minimumlaw@rambler.ru> + +#include <linux/regmap.h> +#include <linux/module.h> +#include <linux/w1.h> + +#include "internal.h" + +#define W1_CMD_READ_DATA 0x69 +#define W1_CMD_WRITE_DATA 0x6C + +/* + * 1-Wire slaves registers with addess 8 bit and data 8 bit + */ + +static int w1_reg_a8_v8_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + int ret = 0; + + if (reg > 255) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + if (!w1_reset_select_slave(sl)) { + w1_write_8(sl->master, W1_CMD_READ_DATA); + w1_write_8(sl->master, reg); + *val = w1_read_8(sl->master); + } else { + ret = -ENODEV; + } + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} + +static int w1_reg_a8_v8_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + int ret = 0; + + if (reg > 255) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + if (!w1_reset_select_slave(sl)) { + w1_write_8(sl->master, W1_CMD_WRITE_DATA); + w1_write_8(sl->master, reg); + w1_write_8(sl->master, val); + } else { + ret = -ENODEV; + } + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} + +/* + * 1-Wire slaves registers with addess 8 bit and data 16 bit + */ + +static int w1_reg_a8_v16_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct device *dev = context; + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + int ret = 0; + + if (reg > 255) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + if (!w1_reset_select_slave(sl)) { + w1_write_8(sl->master, W1_CMD_READ_DATA); + w1_write_8(sl->master, reg); + *val = w1_read_8(sl->master); + *val |= w1_read_8(sl->master)<<8; + } else { + ret = -ENODEV; + } + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} + +static int w1_reg_a8_v16_write(void *context, unsigned int reg, + unsigned int val) +{ + struct device *dev = context; + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + int ret = 0; + + if (reg > 255) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + if (!w1_reset_select_slave(sl)) { + w1_write_8(sl->master, W1_CMD_WRITE_DATA); + w1_write_8(sl->master, reg); + w1_write_8(sl->master, val & 0x00FF); + w1_write_8(sl->master, val>>8 & 0x00FF); + } else { + ret = -ENODEV; + } + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} + +/* + * 1-Wire slaves registers with addess 16 bit and data 16 bit + */ + +static int w1_reg_a16_v16_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct device *dev = context; + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + int ret = 0; + + if (reg > 65535) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + if (!w1_reset_select_slave(sl)) { + w1_write_8(sl->master, W1_CMD_READ_DATA); + w1_write_8(sl->master, reg & 0x00FF); + w1_write_8(sl->master, reg>>8 & 0x00FF); + *val = w1_read_8(sl->master); + *val |= w1_read_8(sl->master)<<8; + } else { + ret = -ENODEV; + } + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} + +static int w1_reg_a16_v16_write(void *context, unsigned int reg, + unsigned int val) +{ + struct device *dev = context; + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + int ret = 0; + + if (reg > 65535) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + if (!w1_reset_select_slave(sl)) { + w1_write_8(sl->master, W1_CMD_WRITE_DATA); + w1_write_8(sl->master, reg & 0x00FF); + w1_write_8(sl->master, reg>>8 & 0x00FF); + w1_write_8(sl->master, val & 0x00FF); + w1_write_8(sl->master, val>>8 & 0x00FF); + } else { + ret = -ENODEV; + } + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} + +/* + * Various types of supported bus addressing + */ + +static struct regmap_bus regmap_w1_bus_a8_v8 = { + .reg_read = w1_reg_a8_v8_read, + .reg_write = w1_reg_a8_v8_write, +}; + +static struct regmap_bus regmap_w1_bus_a8_v16 = { + .reg_read = w1_reg_a8_v16_read, + .reg_write = w1_reg_a8_v16_write, +}; + +static struct regmap_bus regmap_w1_bus_a16_v16 = { + .reg_read = w1_reg_a16_v16_read, + .reg_write = w1_reg_a16_v16_write, +}; + +static const struct regmap_bus *regmap_get_w1_bus(struct device *w1_dev, + const struct regmap_config *config) +{ + if (config->reg_bits == 8 && config->val_bits == 8) + return ®map_w1_bus_a8_v8; + + if (config->reg_bits == 8 && config->val_bits == 16) + return ®map_w1_bus_a8_v16; + + if (config->reg_bits == 16 && config->val_bits == 16) + return ®map_w1_bus_a16_v16; + + return ERR_PTR(-ENOTSUPP); +} + +struct regmap *__regmap_init_w1(struct device *w1_dev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) +{ + + const struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config); + + if (IS_ERR(bus)) + return ERR_CAST(bus); + + return __regmap_init(w1_dev, bus, w1_dev, config, + lock_key, lock_name); +} +EXPORT_SYMBOL_GPL(__regmap_init_w1); + +struct regmap *__devm_regmap_init_w1(struct device *w1_dev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) +{ + + const struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config); + + if (IS_ERR(bus)) + return ERR_CAST(bus); + + return __devm_regmap_init(w1_dev, bus, w1_dev, config, + lock_key, lock_name); +} +EXPORT_SYMBOL_GPL(__devm_regmap_init_w1); + +MODULE_LICENSE("GPL"); |