atmel-smc.c 11 KB

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