Skip to content

Commit fe9d7cb

Browse files
Boris BrezillonLee Jones
authored andcommitted
mfd: syscon: atmel-smc: Add new helpers to ease SMC regs manipulation
These new helpers + macro definitions are meant to replace the old ones which are unpractical to use. Note that the macros and function prefixes have been intentionally changed to ATMEL_[H]SMC_XX and atmel_[h]smc_ to reflect the fact that this IP is also embedded in avr32 SoCs (and not only in at91 ones). Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com> Signed-off-by: Lee Jones <lee.jones@linaro.org>
1 parent 119d53d commit fe9d7cb

4 files changed

Lines changed: 406 additions & 0 deletions

File tree

drivers/mfd/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ config MFD_ATMEL_HLCDC
121121
additional drivers must be enabled in order to use the
122122
functionality of the device.
123123

124+
config MFD_ATMEL_SMC
125+
bool
126+
select MFD_SYSCON
127+
124128
config MFD_BCM590XX
125129
tristate "Broadcom BCM590xx PMUs"
126130
select MFD_CORE

drivers/mfd/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ obj-$(CONFIG_MFD_TPS65090) += tps65090.o
185185
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
186186
obj-$(CONFIG_MFD_ATMEL_FLEXCOM) += atmel-flexcom.o
187187
obj-$(CONFIG_MFD_ATMEL_HLCDC) += atmel-hlcdc.o
188+
obj-$(CONFIG_MFD_ATMEL_SMC) += atmel-smc.o
188189
obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o
189190
obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o
190191
obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o

drivers/mfd/atmel-smc.c

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
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+
16+
/**
17+
* atmel_smc_cs_conf_init - initialize a SMC CS conf
18+
* @conf: the SMC CS conf to initialize
19+
*
20+
* Set all fields to 0 so that one can start defining a new config.
21+
*/
22+
void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf)
23+
{
24+
memset(conf, 0, sizeof(*conf));
25+
}
26+
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_init);
27+
28+
/**
29+
* atmel_smc_cs_encode_ncycles - encode a number of MCK clk cycles in the
30+
* format expected by the SMC engine
31+
* @ncycles: number of MCK clk cycles
32+
* @msbpos: position of the MSB part of the timing field
33+
* @msbwidth: width of the MSB part of the timing field
34+
* @msbfactor: factor applied to the MSB
35+
* @encodedval: param used to store the encoding result
36+
*
37+
* This function encodes the @ncycles value as described in the datasheet
38+
* (section "SMC Setup/Pulse/Cycle/Timings Register"). This is a generic
39+
* helper which called with different parameter depending on the encoding
40+
* scheme.
41+
*
42+
* If the @ncycles value is too big to be encoded, -ERANGE is returned and
43+
* the encodedval is contains the maximum val. Otherwise, 0 is returned.
44+
*/
45+
static int atmel_smc_cs_encode_ncycles(unsigned int ncycles,
46+
unsigned int msbpos,
47+
unsigned int msbwidth,
48+
unsigned int msbfactor,
49+
unsigned int *encodedval)
50+
{
51+
unsigned int lsbmask = GENMASK(msbpos - 1, 0);
52+
unsigned int msbmask = GENMASK(msbwidth - 1, 0);
53+
unsigned int msb, lsb;
54+
int ret = 0;
55+
56+
msb = ncycles / msbfactor;
57+
lsb = ncycles % msbfactor;
58+
59+
if (lsb > lsbmask) {
60+
lsb = 0;
61+
msb++;
62+
}
63+
64+
/*
65+
* Let's just put the maximum we can if the requested setting does
66+
* not fit in the register field.
67+
* We still return -ERANGE in case the caller cares.
68+
*/
69+
if (msb > msbmask) {
70+
msb = msbmask;
71+
lsb = lsbmask;
72+
ret = -ERANGE;
73+
}
74+
75+
*encodedval = (msb << msbpos) | lsb;
76+
77+
return ret;
78+
}
79+
80+
/**
81+
* atmel_smc_cs_conf_set_timing - set the SMC CS conf Txx parameter to a
82+
* specific value
83+
* @conf: SMC CS conf descriptor
84+
* @shift: the position of the Txx field in the TIMINGS register
85+
* @ncycles: value (expressed in MCK clk cycles) to assign to this Txx
86+
* parameter
87+
*
88+
* This function encodes the @ncycles value as described in the datasheet
89+
* (section "SMC Timings Register"), and then stores the result in the
90+
* @conf->timings field at @shift position.
91+
*
92+
* Returns -EINVAL if shift is invalid, -ERANGE if ncycles does not fit in
93+
* the field, and 0 otherwise.
94+
*/
95+
int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
96+
unsigned int shift, unsigned int ncycles)
97+
{
98+
unsigned int val;
99+
int ret;
100+
101+
if (shift != ATMEL_HSMC_TIMINGS_TCLR_SHIFT &&
102+
shift != ATMEL_HSMC_TIMINGS_TADL_SHIFT &&
103+
shift != ATMEL_HSMC_TIMINGS_TAR_SHIFT &&
104+
shift != ATMEL_HSMC_TIMINGS_TRR_SHIFT &&
105+
shift != ATMEL_HSMC_TIMINGS_TWB_SHIFT)
106+
return -EINVAL;
107+
108+
/*
109+
* The formula described in atmel datasheets (section "HSMC Timings
110+
* Register"):
111+
*
112+
* ncycles = (Txx[3] * 64) + Txx[2:0]
113+
*/
114+
ret = atmel_smc_cs_encode_ncycles(ncycles, 3, 1, 64, &val);
115+
conf->timings &= ~GENMASK(shift + 3, shift);
116+
conf->timings |= val << shift;
117+
118+
return ret;
119+
}
120+
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_timing);
121+
122+
/**
123+
* atmel_smc_cs_conf_set_setup - set the SMC CS conf xx_SETUP parameter to a
124+
* specific value
125+
* @conf: SMC CS conf descriptor
126+
* @shift: the position of the xx_SETUP field in the SETUP register
127+
* @ncycles: value (expressed in MCK clk cycles) to assign to this xx_SETUP
128+
* parameter
129+
*
130+
* This function encodes the @ncycles value as described in the datasheet
131+
* (section "SMC Setup Register"), and then stores the result in the
132+
* @conf->setup field at @shift position.
133+
*
134+
* Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
135+
* the field, and 0 otherwise.
136+
*/
137+
int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
138+
unsigned int shift, unsigned int ncycles)
139+
{
140+
unsigned int val;
141+
int ret;
142+
143+
if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
144+
shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
145+
return -EINVAL;
146+
147+
/*
148+
* The formula described in atmel datasheets (section "SMC Setup
149+
* Register"):
150+
*
151+
* ncycles = (128 * xx_SETUP[5]) + xx_SETUP[4:0]
152+
*/
153+
ret = atmel_smc_cs_encode_ncycles(ncycles, 5, 1, 128, &val);
154+
conf->setup &= ~GENMASK(shift + 7, shift);
155+
conf->setup |= val << shift;
156+
157+
return ret;
158+
}
159+
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_setup);
160+
161+
/**
162+
* atmel_smc_cs_conf_set_pulse - set the SMC CS conf xx_PULSE parameter to a
163+
* specific value
164+
* @conf: SMC CS conf descriptor
165+
* @shift: the position of the xx_PULSE field in the PULSE register
166+
* @ncycles: value (expressed in MCK clk cycles) to assign to this xx_PULSE
167+
* parameter
168+
*
169+
* This function encodes the @ncycles value as described in the datasheet
170+
* (section "SMC Pulse Register"), and then stores the result in the
171+
* @conf->setup field at @shift position.
172+
*
173+
* Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
174+
* the field, and 0 otherwise.
175+
*/
176+
int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
177+
unsigned int shift, unsigned int ncycles)
178+
{
179+
unsigned int val;
180+
int ret;
181+
182+
if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
183+
shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
184+
return -EINVAL;
185+
186+
/*
187+
* The formula described in atmel datasheets (section "SMC Pulse
188+
* Register"):
189+
*
190+
* ncycles = (256 * xx_PULSE[6]) + xx_PULSE[5:0]
191+
*/
192+
ret = atmel_smc_cs_encode_ncycles(ncycles, 6, 1, 256, &val);
193+
conf->pulse &= ~GENMASK(shift + 7, shift);
194+
conf->pulse |= val << shift;
195+
196+
return ret;
197+
}
198+
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_pulse);
199+
200+
/**
201+
* atmel_smc_cs_conf_set_cycle - set the SMC CS conf xx_CYCLE parameter to a
202+
* specific value
203+
* @conf: SMC CS conf descriptor
204+
* @shift: the position of the xx_CYCLE field in the CYCLE register
205+
* @ncycles: value (expressed in MCK clk cycles) to assign to this xx_CYCLE
206+
* parameter
207+
*
208+
* This function encodes the @ncycles value as described in the datasheet
209+
* (section "SMC Pulse Register"), and then stores the result in the
210+
* @conf->setup field at @shift position.
211+
*
212+
* Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
213+
* the field, and 0 otherwise.
214+
*/
215+
int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
216+
unsigned int shift, unsigned int ncycles)
217+
{
218+
unsigned int val;
219+
int ret;
220+
221+
if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NRD_SHIFT)
222+
return -EINVAL;
223+
224+
/*
225+
* The formula described in atmel datasheets (section "SMC Cycle
226+
* Register"):
227+
*
228+
* ncycles = (xx_CYCLE[8:7] * 256) + xx_CYCLE[6:0]
229+
*/
230+
ret = atmel_smc_cs_encode_ncycles(ncycles, 7, 2, 256, &val);
231+
conf->cycle &= ~GENMASK(shift + 15, shift);
232+
conf->cycle |= val << shift;
233+
234+
return ret;
235+
}
236+
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle);
237+
238+
/**
239+
* atmel_smc_cs_conf_apply - apply an SMC CS conf
240+
* @regmap: the SMC regmap
241+
* @cs: the CS id
242+
* @conf the SMC CS conf to apply
243+
*
244+
* Applies an SMC CS configuration.
245+
* Only valid on at91sam9/avr32 SoCs.
246+
*/
247+
void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
248+
const struct atmel_smc_cs_conf *conf)
249+
{
250+
regmap_write(regmap, ATMEL_SMC_SETUP(cs), conf->setup);
251+
regmap_write(regmap, ATMEL_SMC_PULSE(cs), conf->pulse);
252+
regmap_write(regmap, ATMEL_SMC_CYCLE(cs), conf->cycle);
253+
regmap_write(regmap, ATMEL_SMC_MODE(cs), conf->mode);
254+
}
255+
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply);
256+
257+
/**
258+
* atmel_hsmc_cs_conf_apply - apply an SMC CS conf
259+
* @regmap: the HSMC regmap
260+
* @cs: the CS id
261+
* @conf the SMC CS conf to apply
262+
*
263+
* Applies an SMC CS configuration.
264+
* Only valid on post-sama5 SoCs.
265+
*/
266+
void atmel_hsmc_cs_conf_apply(struct regmap *regmap, int cs,
267+
const struct atmel_smc_cs_conf *conf)
268+
{
269+
regmap_write(regmap, ATMEL_HSMC_SETUP(cs), conf->setup);
270+
regmap_write(regmap, ATMEL_HSMC_PULSE(cs), conf->pulse);
271+
regmap_write(regmap, ATMEL_HSMC_CYCLE(cs), conf->cycle);
272+
regmap_write(regmap, ATMEL_HSMC_TIMINGS(cs), conf->timings);
273+
regmap_write(regmap, ATMEL_HSMC_MODE(cs), conf->mode);
274+
}
275+
EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply);
276+
277+
/**
278+
* atmel_smc_cs_conf_get - retrieve the current SMC CS conf
279+
* @regmap: the SMC regmap
280+
* @cs: the CS id
281+
* @conf: the SMC CS conf object to store the current conf
282+
*
283+
* Retrieve the SMC CS configuration.
284+
* Only valid on at91sam9/avr32 SoCs.
285+
*/
286+
void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
287+
struct atmel_smc_cs_conf *conf)
288+
{
289+
regmap_read(regmap, ATMEL_SMC_SETUP(cs), &conf->setup);
290+
regmap_read(regmap, ATMEL_SMC_PULSE(cs), &conf->pulse);
291+
regmap_read(regmap, ATMEL_SMC_CYCLE(cs), &conf->cycle);
292+
regmap_read(regmap, ATMEL_SMC_MODE(cs), &conf->mode);
293+
}
294+
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_get);
295+
296+
/**
297+
* atmel_hsmc_cs_conf_get - retrieve the current SMC CS conf
298+
* @regmap: the HSMC regmap
299+
* @cs: the CS id
300+
* @conf: the SMC CS conf object to store the current conf
301+
*
302+
* Retrieve the SMC CS configuration.
303+
* Only valid on post-sama5 SoCs.
304+
*/
305+
void atmel_hsmc_cs_conf_get(struct regmap *regmap, int cs,
306+
struct atmel_smc_cs_conf *conf)
307+
{
308+
regmap_read(regmap, ATMEL_HSMC_SETUP(cs), &conf->setup);
309+
regmap_read(regmap, ATMEL_HSMC_PULSE(cs), &conf->pulse);
310+
regmap_read(regmap, ATMEL_HSMC_CYCLE(cs), &conf->cycle);
311+
regmap_read(regmap, ATMEL_HSMC_TIMINGS(cs), &conf->timings);
312+
regmap_read(regmap, ATMEL_HSMC_MODE(cs), &conf->mode);
313+
}
314+
EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_get);

0 commit comments

Comments
 (0)