1 /*
2  * Register map access API - SPMI support
3  *
4  * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
5  *
6  * Based on regmap-i2c.c:
7  * Copyright 2011 Wolfson Microelectronics plc
8  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 and
12  * only version 2 as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  */
20 #include <linux/regmap.h>
21 #include <linux/spmi.h>
22 #include <linux/module.h>
23 #include <linux/init.h>
24 
25 static int regmap_spmi_base_read(void *context,
26 				 const void *reg, size_t reg_size,
27 				 void *val, size_t val_size)
28 {
29 	u8 addr = *(u8 *)reg;
30 	int err = 0;
31 
32 	BUG_ON(reg_size != 1);
33 
34 	while (val_size-- && !err)
35 		err = spmi_register_read(context, addr++, val++);
36 
37 	return err;
38 }
39 
40 static int regmap_spmi_base_gather_write(void *context,
41 					 const void *reg, size_t reg_size,
42 					 const void *val, size_t val_size)
43 {
44 	const u8 *data = val;
45 	u8 addr = *(u8 *)reg;
46 	int err = 0;
47 
48 	BUG_ON(reg_size != 1);
49 
50 	/*
51 	 * SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence,
52 	 * use it when possible.
53 	 */
54 	if (addr == 0 && val_size) {
55 		err = spmi_register_zero_write(context, *data);
56 		if (err)
57 			goto err_out;
58 
59 		data++;
60 		addr++;
61 		val_size--;
62 	}
63 
64 	while (val_size) {
65 		err = spmi_register_write(context, addr, *data);
66 		if (err)
67 			goto err_out;
68 
69 		data++;
70 		addr++;
71 		val_size--;
72 	}
73 
74 err_out:
75 	return err;
76 }
77 
78 static int regmap_spmi_base_write(void *context, const void *data,
79 				  size_t count)
80 {
81 	BUG_ON(count < 1);
82 	return regmap_spmi_base_gather_write(context, data, 1, data + 1,
83 					     count - 1);
84 }
85 
86 static const struct regmap_bus regmap_spmi_base = {
87 	.read				= regmap_spmi_base_read,
88 	.write				= regmap_spmi_base_write,
89 	.gather_write			= regmap_spmi_base_gather_write,
90 	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE,
91 	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE,
92 };
93 
94 struct regmap *__regmap_init_spmi_base(struct spmi_device *sdev,
95 				       const struct regmap_config *config,
96 				       struct lock_class_key *lock_key,
97 				       const char *lock_name)
98 {
99 	return __regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config,
100 			     lock_key, lock_name);
101 }
102 EXPORT_SYMBOL_GPL(__regmap_init_spmi_base);
103 
104 struct regmap *__devm_regmap_init_spmi_base(struct spmi_device *sdev,
105 					    const struct regmap_config *config,
106 					    struct lock_class_key *lock_key,
107 					    const char *lock_name)
108 {
109 	return __devm_regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config,
110 				  lock_key, lock_name);
111 }
112 EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_base);
113 
114 static int regmap_spmi_ext_read(void *context,
115 				const void *reg, size_t reg_size,
116 				void *val, size_t val_size)
117 {
118 	int err = 0;
119 	size_t len;
120 	u16 addr;
121 
122 	BUG_ON(reg_size != 2);
123 
124 	addr = *(u16 *)reg;
125 
126 	/*
127 	 * Split accesses into two to take advantage of the more
128 	 * bandwidth-efficient 'Extended Register Read' command when possible
129 	 */
130 	while (addr <= 0xFF && val_size) {
131 		len = min_t(size_t, val_size, 16);
132 
133 		err = spmi_ext_register_read(context, addr, val, len);
134 		if (err)
135 			goto err_out;
136 
137 		addr += len;
138 		val += len;
139 		val_size -= len;
140 	}
141 
142 	while (val_size) {
143 		len = min_t(size_t, val_size, 8);
144 
145 		err = spmi_ext_register_readl(context, addr, val, len);
146 		if (err)
147 			goto err_out;
148 
149 		addr += len;
150 		val += len;
151 		val_size -= len;
152 	}
153 
154 err_out:
155 	return err;
156 }
157 
158 static int regmap_spmi_ext_gather_write(void *context,
159 					const void *reg, size_t reg_size,
160 					const void *val, size_t val_size)
161 {
162 	int err = 0;
163 	size_t len;
164 	u16 addr;
165 
166 	BUG_ON(reg_size != 2);
167 
168 	addr = *(u16 *)reg;
169 
170 	while (addr <= 0xFF && val_size) {
171 		len = min_t(size_t, val_size, 16);
172 
173 		err = spmi_ext_register_write(context, addr, val, len);
174 		if (err)
175 			goto err_out;
176 
177 		addr += len;
178 		val += len;
179 		val_size -= len;
180 	}
181 
182 	while (val_size) {
183 		len = min_t(size_t, val_size, 8);
184 
185 		err = spmi_ext_register_writel(context, addr, val, len);
186 		if (err)
187 			goto err_out;
188 
189 		addr += len;
190 		val += len;
191 		val_size -= len;
192 	}
193 
194 err_out:
195 	return err;
196 }
197 
198 static int regmap_spmi_ext_write(void *context, const void *data,
199 				 size_t count)
200 {
201 	BUG_ON(count < 2);
202 	return regmap_spmi_ext_gather_write(context, data, 2, data + 2,
203 					    count - 2);
204 }
205 
206 static const struct regmap_bus regmap_spmi_ext = {
207 	.read				= regmap_spmi_ext_read,
208 	.write				= regmap_spmi_ext_write,
209 	.gather_write			= regmap_spmi_ext_gather_write,
210 	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE,
211 	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE,
212 };
213 
214 struct regmap *__regmap_init_spmi_ext(struct spmi_device *sdev,
215 				      const struct regmap_config *config,
216 				      struct lock_class_key *lock_key,
217 				      const char *lock_name)
218 {
219 	return __regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config,
220 			     lock_key, lock_name);
221 }
222 EXPORT_SYMBOL_GPL(__regmap_init_spmi_ext);
223 
224 struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev,
225 					   const struct regmap_config *config,
226 					   struct lock_class_key *lock_key,
227 					   const char *lock_name)
228 {
229 	return __devm_regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config,
230 				  lock_key, lock_name);
231 }
232 EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext);
233 
234 MODULE_LICENSE("GPL");
235