Merge tag 'multiplatform-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / 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);