diff options
author | Alex A. Mihaylov <minimumlaw@rambler.ru> | 2017-06-02 03:06:27 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2017-06-06 14:47:28 -0400 |
commit | cc5d0db390b0ff0f5da95b643a2b070da15a9c3e (patch) | |
tree | 6e430ec9c71f163abe650adb4d52af594b8214df | |
parent | 2ea659a9ef488125eb46da6eb571de5eae5c43f6 (diff) |
regmap: Add 1-Wire bus support
Add basic support regmap (register map access) API for 1-Wire bus
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | drivers/base/regmap/Kconfig | 6 | ||||
-rw-r--r-- | drivers/base/regmap/Makefile | 1 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-w1.c | 245 | ||||
-rw-r--r-- | include/linux/regmap.h | 34 |
4 files changed, 285 insertions, 1 deletions
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index db9d00c36a3e..413af5f94058 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig | |||
@@ -3,7 +3,7 @@ | |||
3 | # subsystems should select the appropriate symbols. | 3 | # subsystems should select the appropriate symbols. |
4 | 4 | ||
5 | config REGMAP | 5 | config REGMAP |
6 | default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ) | 6 | default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ) |
7 | select LZO_COMPRESS | 7 | select LZO_COMPRESS |
8 | select LZO_DECOMPRESS | 8 | select LZO_DECOMPRESS |
9 | select IRQ_DOMAIN if REGMAP_IRQ | 9 | select IRQ_DOMAIN if REGMAP_IRQ |
@@ -24,6 +24,10 @@ config REGMAP_SPMI | |||
24 | tristate | 24 | tristate |
25 | depends on SPMI | 25 | depends on SPMI |
26 | 26 | ||
27 | config REGMAP_W1 | ||
28 | tristate | ||
29 | depends on W1 | ||
30 | |||
27 | config REGMAP_MMIO | 31 | config REGMAP_MMIO |
28 | tristate | 32 | tristate |
29 | 33 | ||
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index 609e4c84f485..17741ae14ef4 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile | |||
@@ -10,3 +10,4 @@ obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o | |||
10 | obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o | 10 | obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o |
11 | obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o | 11 | obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o |
12 | obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o | 12 | obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o |
13 | obj-$(CONFIG_REGMAP_W1) += regmap-w1.o | ||
diff --git a/drivers/base/regmap/regmap-w1.c b/drivers/base/regmap/regmap-w1.c new file mode 100644 index 000000000000..5f04e7bf063e --- /dev/null +++ b/drivers/base/regmap/regmap-w1.c | |||
@@ -0,0 +1,245 @@ | |||
1 | /* | ||
2 | * Register map access API - W1 (1-Wire) support | ||
3 | * | ||
4 | * Copyright (C) 2017 OAO Radioavionica | ||
5 | * Author: Alex A. Mihaylov <minimumlaw@rambler.ru> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation | ||
10 | */ | ||
11 | |||
12 | #include <linux/regmap.h> | ||
13 | #include <linux/module.h> | ||
14 | #include "../../w1/w1.h" | ||
15 | |||
16 | #include "internal.h" | ||
17 | |||
18 | #define W1_CMD_READ_DATA 0x69 | ||
19 | #define W1_CMD_WRITE_DATA 0x6C | ||
20 | |||
21 | /* | ||
22 | * 1-Wire slaves registers with addess 8 bit and data 8 bit | ||
23 | */ | ||
24 | |||
25 | static int w1_reg_a8_v8_read(void *context, unsigned int reg, unsigned int *val) | ||
26 | { | ||
27 | struct device *dev = context; | ||
28 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); | ||
29 | int ret = 0; | ||
30 | |||
31 | if (reg > 255) | ||
32 | return -EINVAL; | ||
33 | |||
34 | mutex_lock(&sl->master->bus_mutex); | ||
35 | if (!w1_reset_select_slave(sl)) { | ||
36 | w1_write_8(sl->master, W1_CMD_READ_DATA); | ||
37 | w1_write_8(sl->master, reg); | ||
38 | *val = w1_read_8(sl->master); | ||
39 | } else { | ||
40 | ret = -ENODEV; | ||
41 | } | ||
42 | mutex_unlock(&sl->master->bus_mutex); | ||
43 | |||
44 | return ret; | ||
45 | } | ||
46 | |||
47 | static int w1_reg_a8_v8_write(void *context, unsigned int reg, unsigned int val) | ||
48 | { | ||
49 | struct device *dev = context; | ||
50 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); | ||
51 | int ret = 0; | ||
52 | |||
53 | if (reg > 255) | ||
54 | return -EINVAL; | ||
55 | |||
56 | mutex_lock(&sl->master->bus_mutex); | ||
57 | if (!w1_reset_select_slave(sl)) { | ||
58 | w1_write_8(sl->master, W1_CMD_WRITE_DATA); | ||
59 | w1_write_8(sl->master, reg); | ||
60 | w1_write_8(sl->master, val); | ||
61 | } else { | ||
62 | ret = -ENODEV; | ||
63 | } | ||
64 | mutex_unlock(&sl->master->bus_mutex); | ||
65 | |||
66 | return ret; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * 1-Wire slaves registers with addess 8 bit and data 16 bit | ||
71 | */ | ||
72 | |||
73 | static int w1_reg_a8_v16_read(void *context, unsigned int reg, | ||
74 | unsigned int *val) | ||
75 | { | ||
76 | struct device *dev = context; | ||
77 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); | ||
78 | int ret = 0; | ||
79 | |||
80 | if (reg > 255) | ||
81 | return -EINVAL; | ||
82 | |||
83 | mutex_lock(&sl->master->bus_mutex); | ||
84 | if (!w1_reset_select_slave(sl)) { | ||
85 | w1_write_8(sl->master, W1_CMD_READ_DATA); | ||
86 | w1_write_8(sl->master, reg); | ||
87 | *val = w1_read_8(sl->master); | ||
88 | *val |= w1_read_8(sl->master)<<8; | ||
89 | } else { | ||
90 | ret = -ENODEV; | ||
91 | } | ||
92 | mutex_unlock(&sl->master->bus_mutex); | ||
93 | |||
94 | return ret; | ||
95 | } | ||
96 | |||
97 | static int w1_reg_a8_v16_write(void *context, unsigned int reg, | ||
98 | unsigned int val) | ||
99 | { | ||
100 | struct device *dev = context; | ||
101 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); | ||
102 | int ret = 0; | ||
103 | |||
104 | if (reg > 255) | ||
105 | return -EINVAL; | ||
106 | |||
107 | mutex_lock(&sl->master->bus_mutex); | ||
108 | if (!w1_reset_select_slave(sl)) { | ||
109 | w1_write_8(sl->master, W1_CMD_WRITE_DATA); | ||
110 | w1_write_8(sl->master, reg); | ||
111 | w1_write_8(sl->master, val & 0x00FF); | ||
112 | w1_write_8(sl->master, val>>8 & 0x00FF); | ||
113 | } else { | ||
114 | ret = -ENODEV; | ||
115 | } | ||
116 | mutex_unlock(&sl->master->bus_mutex); | ||
117 | |||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * 1-Wire slaves registers with addess 16 bit and data 16 bit | ||
123 | */ | ||
124 | |||
125 | static int w1_reg_a16_v16_read(void *context, unsigned int reg, | ||
126 | unsigned int *val) | ||
127 | { | ||
128 | struct device *dev = context; | ||
129 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); | ||
130 | int ret = 0; | ||
131 | |||
132 | if (reg > 65535) | ||
133 | return -EINVAL; | ||
134 | |||
135 | mutex_lock(&sl->master->bus_mutex); | ||
136 | if (!w1_reset_select_slave(sl)) { | ||
137 | w1_write_8(sl->master, W1_CMD_READ_DATA); | ||
138 | w1_write_8(sl->master, reg & 0x00FF); | ||
139 | w1_write_8(sl->master, reg>>8 & 0x00FF); | ||
140 | *val = w1_read_8(sl->master); | ||
141 | *val |= w1_read_8(sl->master)<<8; | ||
142 | } else { | ||
143 | ret = -ENODEV; | ||
144 | } | ||
145 | mutex_unlock(&sl->master->bus_mutex); | ||
146 | |||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | static int w1_reg_a16_v16_write(void *context, unsigned int reg, | ||
151 | unsigned int val) | ||
152 | { | ||
153 | struct device *dev = context; | ||
154 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); | ||
155 | int ret = 0; | ||
156 | |||
157 | if (reg > 65535) | ||
158 | return -EINVAL; | ||
159 | |||
160 | mutex_lock(&sl->master->bus_mutex); | ||
161 | if (!w1_reset_select_slave(sl)) { | ||
162 | w1_write_8(sl->master, W1_CMD_WRITE_DATA); | ||
163 | w1_write_8(sl->master, reg & 0x00FF); | ||
164 | w1_write_8(sl->master, reg>>8 & 0x00FF); | ||
165 | w1_write_8(sl->master, val & 0x00FF); | ||
166 | w1_write_8(sl->master, val>>8 & 0x00FF); | ||
167 | } else { | ||
168 | ret = -ENODEV; | ||
169 | } | ||
170 | mutex_unlock(&sl->master->bus_mutex); | ||
171 | |||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * Various types of supported bus addressing | ||
177 | */ | ||
178 | |||
179 | static struct regmap_bus regmap_w1_bus_a8_v8 = { | ||
180 | .reg_read = w1_reg_a8_v8_read, | ||
181 | .reg_write = w1_reg_a8_v8_write, | ||
182 | }; | ||
183 | |||
184 | static struct regmap_bus regmap_w1_bus_a8_v16 = { | ||
185 | .reg_read = w1_reg_a8_v16_read, | ||
186 | .reg_write = w1_reg_a8_v16_write, | ||
187 | }; | ||
188 | |||
189 | static struct regmap_bus regmap_w1_bus_a16_v16 = { | ||
190 | .reg_read = w1_reg_a16_v16_read, | ||
191 | .reg_write = w1_reg_a16_v16_write, | ||
192 | }; | ||
193 | |||
194 | static const struct regmap_bus *regmap_get_w1_bus(struct device *w1_dev, | ||
195 | const struct regmap_config *config) | ||
196 | { | ||
197 | if (config->reg_bits == 8 && config->val_bits == 8) | ||
198 | return ®map_w1_bus_a8_v8; | ||
199 | |||
200 | if (config->reg_bits == 8 && config->val_bits == 16) | ||
201 | return ®map_w1_bus_a8_v16; | ||
202 | |||
203 | if (config->reg_bits == 16 && config->val_bits == 16) | ||
204 | return ®map_w1_bus_a16_v16; | ||
205 | |||
206 | return ERR_PTR(-ENOTSUPP); | ||
207 | } | ||
208 | |||
209 | struct regmap *__regmap_init_w1(struct device *w1_dev, | ||
210 | const struct regmap_config *config, | ||
211 | struct lock_class_key *lock_key, | ||
212 | const char *lock_name) | ||
213 | { | ||
214 | |||
215 | const struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config); | ||
216 | |||
217 | if (IS_ERR(bus)) | ||
218 | return ERR_CAST(bus); | ||
219 | |||
220 | return __regmap_init(w1_dev, bus, w1_dev, config, | ||
221 | lock_key, lock_name); | ||
222 | |||
223 | return NULL; | ||
224 | } | ||
225 | EXPORT_SYMBOL_GPL(__regmap_init_w1); | ||
226 | |||
227 | struct regmap *__devm_regmap_init_w1(struct device *w1_dev, | ||
228 | const struct regmap_config *config, | ||
229 | struct lock_class_key *lock_key, | ||
230 | const char *lock_name) | ||
231 | { | ||
232 | |||
233 | const struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config); | ||
234 | |||
235 | if (IS_ERR(bus)) | ||
236 | return ERR_CAST(bus); | ||
237 | |||
238 | return __devm_regmap_init(w1_dev, bus, w1_dev, config, | ||
239 | lock_key, lock_name); | ||
240 | |||
241 | return NULL; | ||
242 | } | ||
243 | EXPORT_SYMBOL_GPL(__devm_regmap_init_w1); | ||
244 | |||
245 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/regmap.h b/include/linux/regmap.h index e88649225a60..86eeacc1425a 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h | |||
@@ -461,6 +461,10 @@ struct regmap *__regmap_init_spmi_ext(struct spmi_device *dev, | |||
461 | const struct regmap_config *config, | 461 | const struct regmap_config *config, |
462 | struct lock_class_key *lock_key, | 462 | struct lock_class_key *lock_key, |
463 | const char *lock_name); | 463 | const char *lock_name); |
464 | struct regmap *__regmap_init_w1(struct device *w1_dev, | ||
465 | const struct regmap_config *config, | ||
466 | struct lock_class_key *lock_key, | ||
467 | const char *lock_name); | ||
464 | struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, | 468 | struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, |
465 | void __iomem *regs, | 469 | void __iomem *regs, |
466 | const struct regmap_config *config, | 470 | const struct regmap_config *config, |
@@ -493,6 +497,10 @@ struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *dev, | |||
493 | const struct regmap_config *config, | 497 | const struct regmap_config *config, |
494 | struct lock_class_key *lock_key, | 498 | struct lock_class_key *lock_key, |
495 | const char *lock_name); | 499 | const char *lock_name); |
500 | struct regmap *__devm_regmap_init_w1(struct device *w1_dev, | ||
501 | const struct regmap_config *config, | ||
502 | struct lock_class_key *lock_key, | ||
503 | const char *lock_name); | ||
496 | struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, | 504 | struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, |
497 | const char *clk_id, | 505 | const char *clk_id, |
498 | void __iomem *regs, | 506 | void __iomem *regs, |
@@ -597,6 +605,19 @@ int regmap_attach_dev(struct device *dev, struct regmap *map, | |||
597 | dev, config) | 605 | dev, config) |
598 | 606 | ||
599 | /** | 607 | /** |
608 | * regmap_init_w1() - Initialise register map | ||
609 | * | ||
610 | * @w1_dev: Device that will be interacted with | ||
611 | * @config: Configuration for register map | ||
612 | * | ||
613 | * The return value will be an ERR_PTR() on error or a valid pointer to | ||
614 | * a struct regmap. | ||
615 | */ | ||
616 | #define regmap_init_w1(w1_dev, config) \ | ||
617 | __regmap_lockdep_wrapper(__regmap_init_w1, #config, \ | ||
618 | w1_dev, config) | ||
619 | |||
620 | /** | ||
600 | * regmap_init_mmio_clk() - Initialise register map with register clock | 621 | * regmap_init_mmio_clk() - Initialise register map with register clock |
601 | * | 622 | * |
602 | * @dev: Device that will be interacted with | 623 | * @dev: Device that will be interacted with |
@@ -712,6 +733,19 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); | |||
712 | dev, config) | 733 | dev, config) |
713 | 734 | ||
714 | /** | 735 | /** |
736 | * devm_regmap_init_w1() - Initialise managed register map | ||
737 | * | ||
738 | * @w1_dev: Device that will be interacted with | ||
739 | * @config: Configuration for register map | ||
740 | * | ||
741 | * The return value will be an ERR_PTR() on error or a valid pointer | ||
742 | * to a struct regmap. The regmap will be automatically freed by the | ||
743 | * device management code. | ||
744 | */ | ||
745 | #define devm_regmap_init_w1(w1_dev, config) \ | ||
746 | __regmap_lockdep_wrapper(__devm_regmap_init_w1, #config, \ | ||
747 | w1_dev, config) | ||
748 | /** | ||
715 | * devm_regmap_init_mmio_clk() - Initialise managed register map with clock | 749 | * devm_regmap_init_mmio_clk() - Initialise managed register map with clock |
716 | * | 750 | * |
717 | * @dev: Device that will be interacted with | 751 | * @dev: Device that will be interacted with |