diff options
author | Andrey Smirnov <andreysm@charmander.(none)> | 2013-04-18 12:58:29 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2013-04-19 12:37:59 -0400 |
commit | 379550698c1583db63ac62e62f465782f10418c2 (patch) | |
tree | d1eb02364e0ccf72922e753ca49dadd9ecfc1b63 /drivers/mfd | |
parent | 730a30ab7fba60c986e0e4a362bb21d2ed196a29 (diff) |
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>
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/si476x-prop.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/drivers/mfd/si476x-prop.c b/drivers/mfd/si476x-prop.c new file mode 100644 index 000000000000..cfeffa6e15d9 --- /dev/null +++ b/drivers/mfd/si476x-prop.c | |||
@@ -0,0 +1,241 @@ | |||
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); | ||