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