137613fa5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
237613fa5SGreg Kroah-Hartman //
337613fa5SGreg Kroah-Hartman // Register map access API - SPMI support
437613fa5SGreg Kroah-Hartman //
537613fa5SGreg Kroah-Hartman // Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
637613fa5SGreg Kroah-Hartman //
737613fa5SGreg Kroah-Hartman // Based on regmap-i2c.c:
837613fa5SGreg Kroah-Hartman // Copyright 2011 Wolfson Microelectronics plc
937613fa5SGreg Kroah-Hartman // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
1037613fa5SGreg 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, &regmap_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, &regmap_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, &regmap_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, &regmap_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