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