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); 355