Commit 379550698c1583db63ac62e62f465782f10418c2
Committed by
Samuel Ortiz
1 parent
730a30ab7f
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
mfd: si476x: Add chip properties handling code
This patch adds code related to manipulation of the properties of SI476X chips. Acked-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Showing 1 changed file with 241 additions and 0 deletions Side-by-side Diff
drivers/mfd/si476x-prop.c
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); |