mfd: Use dev_get_drvdata() directly
[linux-2.6-microblaze.git] / drivers / mfd / atmel-smc.c
1 /*
2  * Atmel SMC (Static Memory Controller) helper functions.
3  *
4  * Copyright (C) 2017 Atmel
5  * Copyright (C) 2017 Free Electrons
6  *
7  * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  */
13
14 #include <linux/mfd/syscon/atmel-smc.h>
15 #include <linux/string.h>
16
17 /**
18  * atmel_smc_cs_conf_init - initialize a SMC CS conf
19  * @conf: the SMC CS conf to initialize
20  *
21  * Set all fields to 0 so that one can start defining a new config.
22  */
23 void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf)
24 {
25         memset(conf, 0, sizeof(*conf));
26 }
27 EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_init);
28
29 /**
30  * atmel_smc_cs_encode_ncycles - encode a number of MCK clk cycles in the
31  *                               format expected by the SMC engine
32  * @ncycles: number of MCK clk cycles
33  * @msbpos: position of the MSB part of the timing field
34  * @msbwidth: width of the MSB part of the timing field
35  * @msbfactor: factor applied to the MSB
36  * @encodedval: param used to store the encoding result
37  *
38  * This function encodes the @ncycles value as described in the datasheet
39  * (section "SMC Setup/Pulse/Cycle/Timings Register"). This is a generic
40  * helper which called with different parameter depending on the encoding
41  * scheme.
42  *
43  * If the @ncycles value is too big to be encoded, -ERANGE is returned and
44  * the encodedval is contains the maximum val. Otherwise, 0 is returned.
45  */
46 static int atmel_smc_cs_encode_ncycles(unsigned int ncycles,
47                                        unsigned int msbpos,
48                                        unsigned int msbwidth,
49                                        unsigned int msbfactor,
50                                        unsigned int *encodedval)
51 {
52         unsigned int lsbmask = GENMASK(msbpos - 1, 0);
53         unsigned int msbmask = GENMASK(msbwidth - 1, 0);
54         unsigned int msb, lsb;
55         int ret = 0;
56
57         msb = ncycles / msbfactor;
58         lsb = ncycles % msbfactor;
59
60         if (lsb > lsbmask) {
61                 lsb = 0;
62                 msb++;
63         }
64
65         /*
66          * Let's just put the maximum we can if the requested setting does
67          * not fit in the register field.
68          * We still return -ERANGE in case the caller cares.
69          */
70         if (msb > msbmask) {
71                 msb = msbmask;
72                 lsb = lsbmask;
73                 ret = -ERANGE;
74         }
75
76         *encodedval = (msb << msbpos) | lsb;
77
78         return ret;
79 }
80
81 /**
82  * atmel_smc_cs_conf_set_timing - set the SMC CS conf Txx parameter to a
83  *                                specific value
84  * @conf: SMC CS conf descriptor
85  * @shift: the position of the Txx field in the TIMINGS register
86  * @ncycles: value (expressed in MCK clk cycles) to assign to this Txx
87  *           parameter
88  *
89  * This function encodes the @ncycles value as described in the datasheet
90  * (section "SMC Timings Register"), and then stores the result in the
91  * @conf->timings field at @shift position.
92  *
93  * Returns -EINVAL if shift is invalid, -ERANGE if ncycles does not fit in
94  * the field, and 0 otherwise.
95  */
96 int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
97                                  unsigned int shift, unsigned int ncycles)
98 {
99         unsigned int val;
100         int ret;
101
102         if (shift != ATMEL_HSMC_TIMINGS_TCLR_SHIFT &&
103             shift != ATMEL_HSMC_TIMINGS_TADL_SHIFT &&
104             shift != ATMEL_HSMC_TIMINGS_TAR_SHIFT &&
105             shift != ATMEL_HSMC_TIMINGS_TRR_SHIFT &&
106             shift != ATMEL_HSMC_TIMINGS_TWB_SHIFT)
107                 return -EINVAL;
108
109         /*
110          * The formula described in atmel datasheets (section "HSMC Timings
111          * Register"):
112          *
113          * ncycles = (Txx[3] * 64) + Txx[2:0]
114          */
115         ret = atmel_smc_cs_encode_ncycles(ncycles, 3, 1, 64, &val);
116         conf->timings &= ~GENMASK(shift + 3, shift);
117         conf->timings |= val << shift;
118
119         return ret;
120 }
121 EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_timing);
122
123 /**
124  * atmel_smc_cs_conf_set_setup - set the SMC CS conf xx_SETUP parameter to a
125  *                               specific value
126  * @conf: SMC CS conf descriptor
127  * @shift: the position of the xx_SETUP field in the SETUP register
128  * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_SETUP
129  *           parameter
130  *
131  * This function encodes the @ncycles value as described in the datasheet
132  * (section "SMC Setup Register"), and then stores the result in the
133  * @conf->setup field at @shift position.
134  *
135  * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
136  * the field, and 0 otherwise.
137  */
138 int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
139                                 unsigned int shift, unsigned int ncycles)
140 {
141         unsigned int val;
142         int ret;
143
144         if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
145             shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
146                 return -EINVAL;
147
148         /*
149          * The formula described in atmel datasheets (section "SMC Setup
150          * Register"):
151          *
152          * ncycles = (128 * xx_SETUP[5]) + xx_SETUP[4:0]
153          */
154         ret = atmel_smc_cs_encode_ncycles(ncycles, 5, 1, 128, &val);
155         conf->setup &= ~GENMASK(shift + 7, shift);
156         conf->setup |= val << shift;
157
158         return ret;
159 }
160 EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_setup);
161
162 /**
163  * atmel_smc_cs_conf_set_pulse - set the SMC CS conf xx_PULSE parameter to a
164  *                               specific value
165  * @conf: SMC CS conf descriptor
166  * @shift: the position of the xx_PULSE field in the PULSE register
167  * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_PULSE
168  *           parameter
169  *
170  * This function encodes the @ncycles value as described in the datasheet
171  * (section "SMC Pulse Register"), and then stores the result in the
172  * @conf->setup field at @shift position.
173  *
174  * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
175  * the field, and 0 otherwise.
176  */
177 int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
178                                 unsigned int shift, unsigned int ncycles)
179 {
180         unsigned int val;
181         int ret;
182
183         if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
184             shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
185                 return -EINVAL;
186
187         /*
188          * The formula described in atmel datasheets (section "SMC Pulse
189          * Register"):
190          *
191          * ncycles = (256 * xx_PULSE[6]) + xx_PULSE[5:0]
192          */
193         ret = atmel_smc_cs_encode_ncycles(ncycles, 6, 1, 256, &val);
194         conf->pulse &= ~GENMASK(shift + 7, shift);
195         conf->pulse |= val << shift;
196
197         return ret;
198 }
199 EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_pulse);
200
201 /**
202  * atmel_smc_cs_conf_set_cycle - set the SMC CS conf xx_CYCLE parameter to a
203  *                               specific value
204  * @conf: SMC CS conf descriptor
205  * @shift: the position of the xx_CYCLE field in the CYCLE register
206  * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_CYCLE
207  *           parameter
208  *
209  * This function encodes the @ncycles value as described in the datasheet
210  * (section "SMC Cycle Register"), and then stores the result in the
211  * @conf->setup field at @shift position.
212  *
213  * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
214  * the field, and 0 otherwise.
215  */
216 int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
217                                 unsigned int shift, unsigned int ncycles)
218 {
219         unsigned int val;
220         int ret;
221
222         if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NRD_SHIFT)
223                 return -EINVAL;
224
225         /*
226          * The formula described in atmel datasheets (section "SMC Cycle
227          * Register"):
228          *
229          * ncycles = (xx_CYCLE[8:7] * 256) + xx_CYCLE[6:0]
230          */
231         ret = atmel_smc_cs_encode_ncycles(ncycles, 7, 2, 256, &val);
232         conf->cycle &= ~GENMASK(shift + 15, shift);
233         conf->cycle |= val << shift;
234
235         return ret;
236 }
237 EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle);
238
239 /**
240  * atmel_smc_cs_conf_apply - apply an SMC CS conf
241  * @regmap: the SMC regmap
242  * @cs: the CS id
243  * @conf the SMC CS conf to apply
244  *
245  * Applies an SMC CS configuration.
246  * Only valid on at91sam9/avr32 SoCs.
247  */
248 void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
249                              const struct atmel_smc_cs_conf *conf)
250 {
251         regmap_write(regmap, ATMEL_SMC_SETUP(cs), conf->setup);
252         regmap_write(regmap, ATMEL_SMC_PULSE(cs), conf->pulse);
253         regmap_write(regmap, ATMEL_SMC_CYCLE(cs), conf->cycle);
254         regmap_write(regmap, ATMEL_SMC_MODE(cs), conf->mode);
255 }
256 EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply);
257
258 /**
259  * atmel_hsmc_cs_conf_apply - apply an SMC CS conf
260  * @regmap: the HSMC regmap
261  * @cs: the CS id
262  * @layout: the layout of registers
263  * @conf the SMC CS conf to apply
264  *
265  * Applies an SMC CS configuration.
266  * Only valid on post-sama5 SoCs.
267  */
268 void atmel_hsmc_cs_conf_apply(struct regmap *regmap,
269                               const struct atmel_hsmc_reg_layout *layout,
270                               int cs, const struct atmel_smc_cs_conf *conf)
271 {
272         regmap_write(regmap, ATMEL_HSMC_SETUP(layout, cs), conf->setup);
273         regmap_write(regmap, ATMEL_HSMC_PULSE(layout, cs), conf->pulse);
274         regmap_write(regmap, ATMEL_HSMC_CYCLE(layout, cs), conf->cycle);
275         regmap_write(regmap, ATMEL_HSMC_TIMINGS(layout, cs), conf->timings);
276         regmap_write(regmap, ATMEL_HSMC_MODE(layout, cs), conf->mode);
277 }
278 EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply);
279
280 /**
281  * atmel_smc_cs_conf_get - retrieve the current SMC CS conf
282  * @regmap: the SMC regmap
283  * @cs: the CS id
284  * @conf: the SMC CS conf object to store the current conf
285  *
286  * Retrieve the SMC CS configuration.
287  * Only valid on at91sam9/avr32 SoCs.
288  */
289 void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
290                            struct atmel_smc_cs_conf *conf)
291 {
292         regmap_read(regmap, ATMEL_SMC_SETUP(cs), &conf->setup);
293         regmap_read(regmap, ATMEL_SMC_PULSE(cs), &conf->pulse);
294         regmap_read(regmap, ATMEL_SMC_CYCLE(cs), &conf->cycle);
295         regmap_read(regmap, ATMEL_SMC_MODE(cs), &conf->mode);
296 }
297 EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_get);
298
299 /**
300  * atmel_hsmc_cs_conf_get - retrieve the current SMC CS conf
301  * @regmap: the HSMC regmap
302  * @cs: the CS id
303  * @layout: the layout of registers
304  * @conf: the SMC CS conf object to store the current conf
305  *
306  * Retrieve the SMC CS configuration.
307  * Only valid on post-sama5 SoCs.
308  */
309 void atmel_hsmc_cs_conf_get(struct regmap *regmap,
310                             const struct atmel_hsmc_reg_layout *layout,
311                             int cs, struct atmel_smc_cs_conf *conf)
312 {
313         regmap_read(regmap, ATMEL_HSMC_SETUP(layout, cs), &conf->setup);
314         regmap_read(regmap, ATMEL_HSMC_PULSE(layout, cs), &conf->pulse);
315         regmap_read(regmap, ATMEL_HSMC_CYCLE(layout, cs), &conf->cycle);
316         regmap_read(regmap, ATMEL_HSMC_TIMINGS(layout, cs), &conf->timings);
317         regmap_read(regmap, ATMEL_HSMC_MODE(layout, cs), &conf->mode);
318 }
319 EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_get);
320
321 static const struct atmel_hsmc_reg_layout sama5d3_reg_layout = {
322         .timing_regs_offset = 0x600,
323 };
324
325 static const struct atmel_hsmc_reg_layout sama5d2_reg_layout = {
326         .timing_regs_offset = 0x700,
327 };
328
329 static const struct of_device_id atmel_smc_ids[] = {
330         { .compatible = "atmel,at91sam9260-smc", .data = NULL },
331         { .compatible = "atmel,sama5d3-smc", .data = &sama5d3_reg_layout },
332         { .compatible = "atmel,sama5d2-smc", .data = &sama5d2_reg_layout },
333         { /* sentinel */ },
334 };
335
336 /**
337  * atmel_hsmc_get_reg_layout - retrieve the layout of HSMC registers
338  * @np: the HSMC regmap
339  *
340  * Retrieve the layout of HSMC registers.
341  *
342  * Returns NULL in case of SMC, a struct atmel_hsmc_reg_layout pointer
343  * in HSMC case, otherwise ERR_PTR(-EINVAL).
344  */
345 const struct atmel_hsmc_reg_layout *
346 atmel_hsmc_get_reg_layout(struct device_node *np)
347 {
348         const struct of_device_id *match;
349
350         match = of_match_node(atmel_smc_ids, np);
351
352         return match ? match->data : ERR_PTR(-EINVAL);
353 }
354 EXPORT_SYMBOL_GPL(atmel_hsmc_get_reg_layout);