1*37613fa5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2*37613fa5SGreg Kroah-Hartman //
3*37613fa5SGreg Kroah-Hartman // Register map access API - SPMI support
4*37613fa5SGreg Kroah-Hartman //
5*37613fa5SGreg Kroah-Hartman // Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
6*37613fa5SGreg Kroah-Hartman //
7*37613fa5SGreg Kroah-Hartman // Based on regmap-i2c.c:
8*37613fa5SGreg Kroah-Hartman // Copyright 2011 Wolfson Microelectronics plc
9*37613fa5SGreg Kroah-Hartman // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
10*37613fa5SGreg Kroah-Hartman
11a01779f8SJosh Cartwright #include <linux/regmap.h>
12a01779f8SJosh Cartwright #include <linux/spmi.h>
13a01779f8SJosh Cartwright #include <linux/module.h>
14a01779f8SJosh Cartwright #include <linux/init.h>
15a01779f8SJosh Cartwright
regmap_spmi_base_read(void * context,const void * reg,size_t reg_size,void * val,size_t val_size)16c9afbb05SJosh Cartwright static int regmap_spmi_base_read(void *context,
17a01779f8SJosh Cartwright const void *reg, size_t reg_size,
18a01779f8SJosh Cartwright void *val, size_t val_size)
19a01779f8SJosh Cartwright {
20c9afbb05SJosh Cartwright u8 addr = *(u8 *)reg;
21c9afbb05SJosh Cartwright int err = 0;
22c9afbb05SJosh Cartwright
23c9afbb05SJosh Cartwright BUG_ON(reg_size != 1);
24c9afbb05SJosh Cartwright
25c9afbb05SJosh Cartwright while (val_size-- && !err)
26c9afbb05SJosh Cartwright err = spmi_register_read(context, addr++, val++);
27c9afbb05SJosh Cartwright
28c9afbb05SJosh Cartwright return err;
29a01779f8SJosh Cartwright }
30a01779f8SJosh Cartwright
regmap_spmi_base_gather_write(void * context,const void * reg,size_t reg_size,const void * val,size_t val_size)31c9afbb05SJosh Cartwright static int regmap_spmi_base_gather_write(void *context,
32a01779f8SJosh Cartwright const void *reg, size_t reg_size,
33a01779f8SJosh Cartwright const void *val, size_t val_size)
34a01779f8SJosh Cartwright {
35c9afbb05SJosh Cartwright const u8 *data = val;
36c9afbb05SJosh Cartwright u8 addr = *(u8 *)reg;
37c9afbb05SJosh Cartwright int err = 0;
38c9afbb05SJosh Cartwright
39c9afbb05SJosh Cartwright BUG_ON(reg_size != 1);
40c9afbb05SJosh Cartwright
41c9afbb05SJosh Cartwright /*
42c9afbb05SJosh Cartwright * SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence,
43c9afbb05SJosh Cartwright * use it when possible.
44c9afbb05SJosh Cartwright */
45c9afbb05SJosh Cartwright if (addr == 0 && val_size) {
46c9afbb05SJosh Cartwright err = spmi_register_zero_write(context, *data);
47c9afbb05SJosh Cartwright if (err)
48c9afbb05SJosh Cartwright goto err_out;
49c9afbb05SJosh Cartwright
50c9afbb05SJosh Cartwright data++;
51c9afbb05SJosh Cartwright addr++;
52c9afbb05SJosh Cartwright val_size--;
53a01779f8SJosh Cartwright }
54a01779f8SJosh Cartwright
55c9afbb05SJosh Cartwright while (val_size) {
56c9afbb05SJosh Cartwright err = spmi_register_write(context, addr, *data);
57c9afbb05SJosh Cartwright if (err)
58c9afbb05SJosh Cartwright goto err_out;
59c9afbb05SJosh Cartwright
60c9afbb05SJosh Cartwright data++;
61c9afbb05SJosh Cartwright addr++;
62c9afbb05SJosh Cartwright val_size--;
63c9afbb05SJosh Cartwright }
64c9afbb05SJosh Cartwright
65c9afbb05SJosh Cartwright err_out:
66c9afbb05SJosh Cartwright return err;
67c9afbb05SJosh Cartwright }
68c9afbb05SJosh Cartwright
regmap_spmi_base_write(void * context,const void * data,size_t count)69c9afbb05SJosh Cartwright static int regmap_spmi_base_write(void *context, const void *data,
70a01779f8SJosh Cartwright size_t count)
71a01779f8SJosh Cartwright {
72c9afbb05SJosh Cartwright BUG_ON(count < 1);
73c9afbb05SJosh Cartwright return regmap_spmi_base_gather_write(context, data, 1, data + 1,
74c9afbb05SJosh Cartwright count - 1);
75a01779f8SJosh Cartwright }
76a01779f8SJosh Cartwright
779c2e5cb3SJulia Lawall static const struct regmap_bus regmap_spmi_base = {
78c9afbb05SJosh Cartwright .read = regmap_spmi_base_read,
79c9afbb05SJosh Cartwright .write = regmap_spmi_base_write,
80c9afbb05SJosh Cartwright .gather_write = regmap_spmi_base_gather_write,
81a01779f8SJosh Cartwright .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
82a01779f8SJosh Cartwright .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
83a01779f8SJosh Cartwright };
84a01779f8SJosh Cartwright
__regmap_init_spmi_base(struct spmi_device * sdev,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)853cfe7a74SNicolas Boichat struct regmap *__regmap_init_spmi_base(struct spmi_device *sdev,
863cfe7a74SNicolas Boichat const struct regmap_config *config,
873cfe7a74SNicolas Boichat struct lock_class_key *lock_key,
883cfe7a74SNicolas Boichat const char *lock_name)
89a01779f8SJosh Cartwright {
903cfe7a74SNicolas Boichat return __regmap_init(&sdev->dev, ®map_spmi_base, sdev, config,
913cfe7a74SNicolas Boichat lock_key, lock_name);
92a01779f8SJosh Cartwright }
933cfe7a74SNicolas Boichat EXPORT_SYMBOL_GPL(__regmap_init_spmi_base);
94a01779f8SJosh Cartwright
__devm_regmap_init_spmi_base(struct spmi_device * sdev,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)953cfe7a74SNicolas Boichat struct regmap *__devm_regmap_init_spmi_base(struct spmi_device *sdev,
963cfe7a74SNicolas Boichat const struct regmap_config *config,
973cfe7a74SNicolas Boichat struct lock_class_key *lock_key,
983cfe7a74SNicolas Boichat const char *lock_name)
99a01779f8SJosh Cartwright {
1003cfe7a74SNicolas Boichat return __devm_regmap_init(&sdev->dev, ®map_spmi_base, sdev, config,
1013cfe7a74SNicolas Boichat lock_key, lock_name);
102a01779f8SJosh Cartwright }
1033cfe7a74SNicolas Boichat EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_base);
104c9afbb05SJosh Cartwright
regmap_spmi_ext_read(void * context,const void * reg,size_t reg_size,void * val,size_t val_size)105c9afbb05SJosh Cartwright static int regmap_spmi_ext_read(void *context,
106c9afbb05SJosh Cartwright const void *reg, size_t reg_size,
107c9afbb05SJosh Cartwright void *val, size_t val_size)
108c9afbb05SJosh Cartwright {
109c9afbb05SJosh Cartwright int err = 0;
110c9afbb05SJosh Cartwright size_t len;
111c9afbb05SJosh Cartwright u16 addr;
112c9afbb05SJosh Cartwright
113c9afbb05SJosh Cartwright BUG_ON(reg_size != 2);
114c9afbb05SJosh Cartwright
115c9afbb05SJosh Cartwright addr = *(u16 *)reg;
116c9afbb05SJosh Cartwright
117c9afbb05SJosh Cartwright /*
118c9afbb05SJosh Cartwright * Split accesses into two to take advantage of the more
119c9afbb05SJosh Cartwright * bandwidth-efficient 'Extended Register Read' command when possible
120c9afbb05SJosh Cartwright */
121c9afbb05SJosh Cartwright while (addr <= 0xFF && val_size) {
122c9afbb05SJosh Cartwright len = min_t(size_t, val_size, 16);
123c9afbb05SJosh Cartwright
124c9afbb05SJosh Cartwright err = spmi_ext_register_read(context, addr, val, len);
125c9afbb05SJosh Cartwright if (err)
126c9afbb05SJosh Cartwright goto err_out;
127c9afbb05SJosh Cartwright
128c9afbb05SJosh Cartwright addr += len;
129c9afbb05SJosh Cartwright val += len;
130c9afbb05SJosh Cartwright val_size -= len;
131c9afbb05SJosh Cartwright }
132c9afbb05SJosh Cartwright
133c9afbb05SJosh Cartwright while (val_size) {
134c9afbb05SJosh Cartwright len = min_t(size_t, val_size, 8);
135c9afbb05SJosh Cartwright
136dec8e8f6SJack Pham err = spmi_ext_register_readl(context, addr, val, len);
137c9afbb05SJosh Cartwright if (err)
138c9afbb05SJosh Cartwright goto err_out;
139c9afbb05SJosh Cartwright
140c9afbb05SJosh Cartwright addr += len;
141c9afbb05SJosh Cartwright val += len;
142c9afbb05SJosh Cartwright val_size -= len;
143c9afbb05SJosh Cartwright }
144c9afbb05SJosh Cartwright
145c9afbb05SJosh Cartwright err_out:
146c9afbb05SJosh Cartwright return err;
147c9afbb05SJosh Cartwright }
148c9afbb05SJosh Cartwright
regmap_spmi_ext_gather_write(void * context,const void * reg,size_t reg_size,const void * val,size_t val_size)149c9afbb05SJosh Cartwright static int regmap_spmi_ext_gather_write(void *context,
150c9afbb05SJosh Cartwright const void *reg, size_t reg_size,
151c9afbb05SJosh Cartwright const void *val, size_t val_size)
152c9afbb05SJosh Cartwright {
153c9afbb05SJosh Cartwright int err = 0;
154c9afbb05SJosh Cartwright size_t len;
155c9afbb05SJosh Cartwright u16 addr;
156c9afbb05SJosh Cartwright
157c9afbb05SJosh Cartwright BUG_ON(reg_size != 2);
158c9afbb05SJosh Cartwright
159c9afbb05SJosh Cartwright addr = *(u16 *)reg;
160c9afbb05SJosh Cartwright
161c9afbb05SJosh Cartwright while (addr <= 0xFF && val_size) {
162c9afbb05SJosh Cartwright len = min_t(size_t, val_size, 16);
163c9afbb05SJosh Cartwright
164c9afbb05SJosh Cartwright err = spmi_ext_register_write(context, addr, val, len);
165c9afbb05SJosh Cartwright if (err)
166c9afbb05SJosh Cartwright goto err_out;
167c9afbb05SJosh Cartwright
168c9afbb05SJosh Cartwright addr += len;
169c9afbb05SJosh Cartwright val += len;
170c9afbb05SJosh Cartwright val_size -= len;
171c9afbb05SJosh Cartwright }
172c9afbb05SJosh Cartwright
173c9afbb05SJosh Cartwright while (val_size) {
174c9afbb05SJosh Cartwright len = min_t(size_t, val_size, 8);
175c9afbb05SJosh Cartwright
176c9afbb05SJosh Cartwright err = spmi_ext_register_writel(context, addr, val, len);
177c9afbb05SJosh Cartwright if (err)
178c9afbb05SJosh Cartwright goto err_out;
179c9afbb05SJosh Cartwright
180c9afbb05SJosh Cartwright addr += len;
181c9afbb05SJosh Cartwright val += len;
182c9afbb05SJosh Cartwright val_size -= len;
183c9afbb05SJosh Cartwright }
184c9afbb05SJosh Cartwright
185c9afbb05SJosh Cartwright err_out:
186c9afbb05SJosh Cartwright return err;
187c9afbb05SJosh Cartwright }
188c9afbb05SJosh Cartwright
regmap_spmi_ext_write(void * context,const void * data,size_t count)189c9afbb05SJosh Cartwright static int regmap_spmi_ext_write(void *context, const void *data,
190c9afbb05SJosh Cartwright size_t count)
191c9afbb05SJosh Cartwright {
192c9afbb05SJosh Cartwright BUG_ON(count < 2);
193c9afbb05SJosh Cartwright return regmap_spmi_ext_gather_write(context, data, 2, data + 2,
194c9afbb05SJosh Cartwright count - 2);
195c9afbb05SJosh Cartwright }
196c9afbb05SJosh Cartwright
1979c2e5cb3SJulia Lawall static const struct regmap_bus regmap_spmi_ext = {
198c9afbb05SJosh Cartwright .read = regmap_spmi_ext_read,
199c9afbb05SJosh Cartwright .write = regmap_spmi_ext_write,
200c9afbb05SJosh Cartwright .gather_write = regmap_spmi_ext_gather_write,
201c9afbb05SJosh Cartwright .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
202c9afbb05SJosh Cartwright .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
203c9afbb05SJosh Cartwright };
204c9afbb05SJosh Cartwright
__regmap_init_spmi_ext(struct spmi_device * sdev,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)2053cfe7a74SNicolas Boichat struct regmap *__regmap_init_spmi_ext(struct spmi_device *sdev,
2063cfe7a74SNicolas Boichat const struct regmap_config *config,
2073cfe7a74SNicolas Boichat struct lock_class_key *lock_key,
2083cfe7a74SNicolas Boichat const char *lock_name)
209c9afbb05SJosh Cartwright {
2103cfe7a74SNicolas Boichat return __regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config,
2113cfe7a74SNicolas Boichat lock_key, lock_name);
212c9afbb05SJosh Cartwright }
2133cfe7a74SNicolas Boichat EXPORT_SYMBOL_GPL(__regmap_init_spmi_ext);
214c9afbb05SJosh Cartwright
__devm_regmap_init_spmi_ext(struct spmi_device * sdev,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)2153cfe7a74SNicolas Boichat struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev,
2163cfe7a74SNicolas Boichat const struct regmap_config *config,
2173cfe7a74SNicolas Boichat struct lock_class_key *lock_key,
2183cfe7a74SNicolas Boichat const char *lock_name)
219c9afbb05SJosh Cartwright {
2203cfe7a74SNicolas Boichat return __devm_regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config,
2213cfe7a74SNicolas Boichat lock_key, lock_name);
222c9afbb05SJosh Cartwright }
2233cfe7a74SNicolas Boichat EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext);
224a01779f8SJosh Cartwright
225a01779f8SJosh Cartwright MODULE_LICENSE("GPL");
226