xref: /openbmc/linux/drivers/mfd/si476x-prop.c (revision eb3fcf00)
1 /*
2  * drivers/mfd/si476x-prop.c -- Subroutines to access
3  * properties of si476x chips
4  *
5  * Copyright (C) 2012 Innovative Converged Devices(ICD)
6  * Copyright (C) 2013 Andrey Smirnov
7  *
8  * Author: Andrey Smirnov <andrew.smirnov@gmail.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 as published by
12  * the Free Software Foundation; version 2 of the License.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  */
19 #include <linux/module.h>
20 
21 #include <linux/mfd/si476x-core.h>
22 
23 struct si476x_property_range {
24 	u16 low, high;
25 };
26 
27 static bool si476x_core_element_is_in_array(u16 element,
28 					    const u16 array[],
29 					    size_t size)
30 {
31 	int i;
32 
33 	for (i = 0; i < size; i++)
34 		if (element == array[i])
35 			return true;
36 
37 	return false;
38 }
39 
40 static bool si476x_core_element_is_in_range(u16 element,
41 					    const struct si476x_property_range range[],
42 					    size_t size)
43 {
44 	int i;
45 
46 	for (i = 0; i < size; i++)
47 		if (element <= range[i].high && element >= range[i].low)
48 			return true;
49 
50 	return false;
51 }
52 
53 static bool si476x_core_is_valid_property_a10(struct si476x_core *core,
54 					      u16 property)
55 {
56 	static const u16 valid_properties[] = {
57 		0x0000,
58 		0x0500, 0x0501,
59 		0x0600,
60 		0x0709, 0x070C, 0x070D, 0x70E, 0x710,
61 		0x0718,
62 		0x1207, 0x1208,
63 		0x2007,
64 		0x2300,
65 	};
66 
67 	static const struct si476x_property_range valid_ranges[] = {
68 		{ 0x0200, 0x0203 },
69 		{ 0x0300, 0x0303 },
70 		{ 0x0400, 0x0404 },
71 		{ 0x0700, 0x0707 },
72 		{ 0x1100, 0x1102 },
73 		{ 0x1200, 0x1204 },
74 		{ 0x1300, 0x1306 },
75 		{ 0x2000, 0x2005 },
76 		{ 0x2100, 0x2104 },
77 		{ 0x2106, 0x2106 },
78 		{ 0x2200, 0x220E },
79 		{ 0x3100, 0x3104 },
80 		{ 0x3207, 0x320F },
81 		{ 0x3300, 0x3304 },
82 		{ 0x3500, 0x3517 },
83 		{ 0x3600, 0x3617 },
84 		{ 0x3700, 0x3717 },
85 		{ 0x4000, 0x4003 },
86 	};
87 
88 	return	si476x_core_element_is_in_range(property, valid_ranges,
89 						ARRAY_SIZE(valid_ranges)) ||
90 		si476x_core_element_is_in_array(property, valid_properties,
91 						ARRAY_SIZE(valid_properties));
92 }
93 
94 static bool si476x_core_is_valid_property_a20(struct si476x_core *core,
95 					      u16 property)
96 {
97 	static const u16 valid_properties[] = {
98 		0x071B,
99 		0x1006,
100 		0x2210,
101 		0x3401,
102 	};
103 
104 	static const struct si476x_property_range valid_ranges[] = {
105 		{ 0x2215, 0x2219 },
106 	};
107 
108 	return	si476x_core_is_valid_property_a10(core, property) ||
109 		si476x_core_element_is_in_range(property, valid_ranges,
110 						ARRAY_SIZE(valid_ranges))  ||
111 		si476x_core_element_is_in_array(property, valid_properties,
112 						ARRAY_SIZE(valid_properties));
113 }
114 
115 static bool si476x_core_is_valid_property_a30(struct si476x_core *core,
116 					      u16 property)
117 {
118 	static const u16 valid_properties[] = {
119 		0x071C, 0x071D,
120 		0x1007, 0x1008,
121 		0x220F, 0x2214,
122 		0x2301,
123 		0x3105, 0x3106,
124 		0x3402,
125 	};
126 
127 	static const struct si476x_property_range valid_ranges[] = {
128 		{ 0x0405, 0x0411 },
129 		{ 0x2008, 0x200B },
130 		{ 0x2220, 0x2223 },
131 		{ 0x3100, 0x3106 },
132 	};
133 
134 	return	si476x_core_is_valid_property_a20(core, property) ||
135 		si476x_core_element_is_in_range(property, valid_ranges,
136 						ARRAY_SIZE(valid_ranges)) ||
137 		si476x_core_element_is_in_array(property, valid_properties,
138 						ARRAY_SIZE(valid_properties));
139 }
140 
141 typedef bool (*valid_property_pred_t) (struct si476x_core *, u16);
142 
143 static bool si476x_core_is_valid_property(struct si476x_core *core,
144 					  u16 property)
145 {
146 	static const valid_property_pred_t is_valid_property[] = {
147 		[SI476X_REVISION_A10] = si476x_core_is_valid_property_a10,
148 		[SI476X_REVISION_A20] = si476x_core_is_valid_property_a20,
149 		[SI476X_REVISION_A30] = si476x_core_is_valid_property_a30,
150 	};
151 
152 	BUG_ON(core->revision > SI476X_REVISION_A30 ||
153 	       core->revision == -1);
154 	return is_valid_property[core->revision](core, property);
155 }
156 
157 
158 static bool si476x_core_is_readonly_property(struct si476x_core *core,
159 					     u16 property)
160 {
161 	BUG_ON(core->revision > SI476X_REVISION_A30 ||
162 	       core->revision == -1);
163 
164 	switch (core->revision) {
165 	case SI476X_REVISION_A10:
166 		return (property == 0x3200);
167 	case SI476X_REVISION_A20:
168 		return (property == 0x1006 ||
169 			property == 0x2210 ||
170 			property == 0x3200);
171 	case SI476X_REVISION_A30:
172 		return false;
173 	}
174 
175 	return false;
176 }
177 
178 static bool si476x_core_regmap_readable_register(struct device *dev,
179 						 unsigned int reg)
180 {
181 	struct i2c_client *client = to_i2c_client(dev);
182 	struct si476x_core *core = i2c_get_clientdata(client);
183 
184 	return si476x_core_is_valid_property(core, (u16) reg);
185 
186 }
187 
188 static bool si476x_core_regmap_writable_register(struct device *dev,
189 						 unsigned int reg)
190 {
191 	struct i2c_client *client = to_i2c_client(dev);
192 	struct si476x_core *core = i2c_get_clientdata(client);
193 
194 	return si476x_core_is_valid_property(core, (u16) reg) &&
195 		!si476x_core_is_readonly_property(core, (u16) reg);
196 }
197 
198 
199 static int si476x_core_regmap_write(void *context, unsigned int reg,
200 				    unsigned int val)
201 {
202 	return si476x_core_cmd_set_property(context, reg, val);
203 }
204 
205 static int si476x_core_regmap_read(void *context, unsigned int reg,
206 				   unsigned *val)
207 {
208 	struct si476x_core *core = context;
209 	int err;
210 
211 	err = si476x_core_cmd_get_property(core, reg);
212 	if (err < 0)
213 		return err;
214 
215 	*val = err;
216 
217 	return 0;
218 }
219 
220 
221 static const struct regmap_config si476x_regmap_config = {
222 	.reg_bits = 16,
223 	.val_bits = 16,
224 
225 	.max_register = 0x4003,
226 
227 	.writeable_reg = si476x_core_regmap_writable_register,
228 	.readable_reg = si476x_core_regmap_readable_register,
229 
230 	.reg_read = si476x_core_regmap_read,
231 	.reg_write = si476x_core_regmap_write,
232 
233 	.cache_type = REGCACHE_RBTREE,
234 };
235 
236 struct regmap *devm_regmap_init_si476x(struct si476x_core *core)
237 {
238 	return devm_regmap_init(&core->client->dev, NULL,
239 				core, &si476x_regmap_config);
240 }
241 EXPORT_SYMBOL_GPL(devm_regmap_init_si476x);
242