xlnx_vcu.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Xilinx VCU Init
  4. *
  5. * Copyright (C) 2016 - 2017 Xilinx, Inc.
  6. *
  7. * Contacts Dhaval Shah <dshah@xilinx.com>
  8. */
  9. #include <linux/clk.h>
  10. #include <linux/device.h>
  11. #include <linux/errno.h>
  12. #include <linux/io.h>
  13. #include <linux/module.h>
  14. #include <linux/of_platform.h>
  15. #include <linux/platform_device.h>
  16. /* Address map for different registers implemented in the VCU LogiCORE IP. */
  17. #define VCU_ECODER_ENABLE 0x00
  18. #define VCU_DECODER_ENABLE 0x04
  19. #define VCU_MEMORY_DEPTH 0x08
  20. #define VCU_ENC_COLOR_DEPTH 0x0c
  21. #define VCU_ENC_VERTICAL_RANGE 0x10
  22. #define VCU_ENC_FRAME_SIZE_X 0x14
  23. #define VCU_ENC_FRAME_SIZE_Y 0x18
  24. #define VCU_ENC_COLOR_FORMAT 0x1c
  25. #define VCU_ENC_FPS 0x20
  26. #define VCU_MCU_CLK 0x24
  27. #define VCU_CORE_CLK 0x28
  28. #define VCU_PLL_BYPASS 0x2c
  29. #define VCU_ENC_CLK 0x30
  30. #define VCU_PLL_CLK 0x34
  31. #define VCU_ENC_VIDEO_STANDARD 0x38
  32. #define VCU_STATUS 0x3c
  33. #define VCU_AXI_ENC_CLK 0x40
  34. #define VCU_AXI_DEC_CLK 0x44
  35. #define VCU_AXI_MCU_CLK 0x48
  36. #define VCU_DEC_VIDEO_STANDARD 0x4c
  37. #define VCU_DEC_FRAME_SIZE_X 0x50
  38. #define VCU_DEC_FRAME_SIZE_Y 0x54
  39. #define VCU_DEC_FPS 0x58
  40. #define VCU_BUFFER_B_FRAME 0x5c
  41. #define VCU_WPP_EN 0x60
  42. #define VCU_PLL_CLK_DEC 0x64
  43. #define VCU_GASKET_INIT 0x74
  44. #define VCU_GASKET_VALUE 0x03
  45. /* vcu slcr registers, bitmask and shift */
  46. #define VCU_PLL_CTRL 0x24
  47. #define VCU_PLL_CTRL_RESET_MASK 0x01
  48. #define VCU_PLL_CTRL_RESET_SHIFT 0
  49. #define VCU_PLL_CTRL_BYPASS_MASK 0x01
  50. #define VCU_PLL_CTRL_BYPASS_SHIFT 3
  51. #define VCU_PLL_CTRL_FBDIV_MASK 0x7f
  52. #define VCU_PLL_CTRL_FBDIV_SHIFT 8
  53. #define VCU_PLL_CTRL_POR_IN_MASK 0x01
  54. #define VCU_PLL_CTRL_POR_IN_SHIFT 1
  55. #define VCU_PLL_CTRL_PWR_POR_MASK 0x01
  56. #define VCU_PLL_CTRL_PWR_POR_SHIFT 2
  57. #define VCU_PLL_CTRL_CLKOUTDIV_MASK 0x03
  58. #define VCU_PLL_CTRL_CLKOUTDIV_SHIFT 16
  59. #define VCU_PLL_CTRL_DEFAULT 0
  60. #define VCU_PLL_DIV2 2
  61. #define VCU_PLL_CFG 0x28
  62. #define VCU_PLL_CFG_RES_MASK 0x0f
  63. #define VCU_PLL_CFG_RES_SHIFT 0
  64. #define VCU_PLL_CFG_CP_MASK 0x0f
  65. #define VCU_PLL_CFG_CP_SHIFT 5
  66. #define VCU_PLL_CFG_LFHF_MASK 0x03
  67. #define VCU_PLL_CFG_LFHF_SHIFT 10
  68. #define VCU_PLL_CFG_LOCK_CNT_MASK 0x03ff
  69. #define VCU_PLL_CFG_LOCK_CNT_SHIFT 13
  70. #define VCU_PLL_CFG_LOCK_DLY_MASK 0x7f
  71. #define VCU_PLL_CFG_LOCK_DLY_SHIFT 25
  72. #define VCU_ENC_CORE_CTRL 0x30
  73. #define VCU_ENC_MCU_CTRL 0x34
  74. #define VCU_DEC_CORE_CTRL 0x38
  75. #define VCU_DEC_MCU_CTRL 0x3c
  76. #define VCU_PLL_DIVISOR_MASK 0x3f
  77. #define VCU_PLL_DIVISOR_SHIFT 4
  78. #define VCU_SRCSEL_MASK 0x01
  79. #define VCU_SRCSEL_SHIFT 0
  80. #define VCU_SRCSEL_PLL 1
  81. #define VCU_PLL_STATUS 0x60
  82. #define VCU_PLL_STATUS_LOCK_STATUS_MASK 0x01
  83. #define MHZ 1000000
  84. #define FVCO_MIN (1500U * MHZ)
  85. #define FVCO_MAX (3000U * MHZ)
  86. #define DIVISOR_MIN 0
  87. #define DIVISOR_MAX 63
  88. #define FRAC 100
  89. #define LIMIT (10 * MHZ)
  90. /**
  91. * struct xvcu_device - Xilinx VCU init device structure
  92. * @dev: Platform device
  93. * @pll_ref: pll ref clock source
  94. * @aclk: axi clock source
  95. * @logicore_reg_ba: logicore reg base address
  96. * @vcu_slcr_ba: vcu_slcr Register base address
  97. * @coreclk: core clock frequency
  98. */
  99. struct xvcu_device {
  100. struct device *dev;
  101. struct clk *pll_ref;
  102. struct clk *aclk;
  103. void __iomem *logicore_reg_ba;
  104. void __iomem *vcu_slcr_ba;
  105. u32 coreclk;
  106. };
  107. /**
  108. * struct xvcu_pll_cfg - Helper data
  109. * @fbdiv: The integer portion of the feedback divider to the PLL
  110. * @cp: PLL charge pump control
  111. * @res: PLL loop filter resistor control
  112. * @lfhf: PLL loop filter high frequency capacitor control
  113. * @lock_dly: Lock circuit configuration settings for lock windowsize
  114. * @lock_cnt: Lock circuit counter setting
  115. */
  116. struct xvcu_pll_cfg {
  117. u32 fbdiv;
  118. u32 cp;
  119. u32 res;
  120. u32 lfhf;
  121. u32 lock_dly;
  122. u32 lock_cnt;
  123. };
  124. static const struct xvcu_pll_cfg xvcu_pll_cfg[] = {
  125. { 25, 3, 10, 3, 63, 1000 },
  126. { 26, 3, 10, 3, 63, 1000 },
  127. { 27, 4, 6, 3, 63, 1000 },
  128. { 28, 4, 6, 3, 63, 1000 },
  129. { 29, 4, 6, 3, 63, 1000 },
  130. { 30, 4, 6, 3, 63, 1000 },
  131. { 31, 6, 1, 3, 63, 1000 },
  132. { 32, 6, 1, 3, 63, 1000 },
  133. { 33, 4, 10, 3, 63, 1000 },
  134. { 34, 5, 6, 3, 63, 1000 },
  135. { 35, 5, 6, 3, 63, 1000 },
  136. { 36, 5, 6, 3, 63, 1000 },
  137. { 37, 5, 6, 3, 63, 1000 },
  138. { 38, 5, 6, 3, 63, 975 },
  139. { 39, 3, 12, 3, 63, 950 },
  140. { 40, 3, 12, 3, 63, 925 },
  141. { 41, 3, 12, 3, 63, 900 },
  142. { 42, 3, 12, 3, 63, 875 },
  143. { 43, 3, 12, 3, 63, 850 },
  144. { 44, 3, 12, 3, 63, 850 },
  145. { 45, 3, 12, 3, 63, 825 },
  146. { 46, 3, 12, 3, 63, 800 },
  147. { 47, 3, 12, 3, 63, 775 },
  148. { 48, 3, 12, 3, 63, 775 },
  149. { 49, 3, 12, 3, 63, 750 },
  150. { 50, 3, 12, 3, 63, 750 },
  151. { 51, 3, 2, 3, 63, 725 },
  152. { 52, 3, 2, 3, 63, 700 },
  153. { 53, 3, 2, 3, 63, 700 },
  154. { 54, 3, 2, 3, 63, 675 },
  155. { 55, 3, 2, 3, 63, 675 },
  156. { 56, 3, 2, 3, 63, 650 },
  157. { 57, 3, 2, 3, 63, 650 },
  158. { 58, 3, 2, 3, 63, 625 },
  159. { 59, 3, 2, 3, 63, 625 },
  160. { 60, 3, 2, 3, 63, 625 },
  161. { 61, 3, 2, 3, 63, 600 },
  162. { 62, 3, 2, 3, 63, 600 },
  163. { 63, 3, 2, 3, 63, 600 },
  164. { 64, 3, 2, 3, 63, 600 },
  165. { 65, 3, 2, 3, 63, 600 },
  166. { 66, 3, 2, 3, 63, 600 },
  167. { 67, 3, 2, 3, 63, 600 },
  168. { 68, 3, 2, 3, 63, 600 },
  169. { 69, 3, 2, 3, 63, 600 },
  170. { 70, 3, 2, 3, 63, 600 },
  171. { 71, 3, 2, 3, 63, 600 },
  172. { 72, 3, 2, 3, 63, 600 },
  173. { 73, 3, 2, 3, 63, 600 },
  174. { 74, 3, 2, 3, 63, 600 },
  175. { 75, 3, 2, 3, 63, 600 },
  176. { 76, 3, 2, 3, 63, 600 },
  177. { 77, 3, 2, 3, 63, 600 },
  178. { 78, 3, 2, 3, 63, 600 },
  179. { 79, 3, 2, 3, 63, 600 },
  180. { 80, 3, 2, 3, 63, 600 },
  181. { 81, 3, 2, 3, 63, 600 },
  182. { 82, 3, 2, 3, 63, 600 },
  183. { 83, 4, 2, 3, 63, 600 },
  184. { 84, 4, 2, 3, 63, 600 },
  185. { 85, 4, 2, 3, 63, 600 },
  186. { 86, 4, 2, 3, 63, 600 },
  187. { 87, 4, 2, 3, 63, 600 },
  188. { 88, 4, 2, 3, 63, 600 },
  189. { 89, 4, 2, 3, 63, 600 },
  190. { 90, 4, 2, 3, 63, 600 },
  191. { 91, 4, 2, 3, 63, 600 },
  192. { 92, 4, 2, 3, 63, 600 },
  193. { 93, 4, 2, 3, 63, 600 },
  194. { 94, 4, 2, 3, 63, 600 },
  195. { 95, 4, 2, 3, 63, 600 },
  196. { 96, 4, 2, 3, 63, 600 },
  197. { 97, 4, 2, 3, 63, 600 },
  198. { 98, 4, 2, 3, 63, 600 },
  199. { 99, 4, 2, 3, 63, 600 },
  200. { 100, 4, 2, 3, 63, 600 },
  201. { 101, 4, 2, 3, 63, 600 },
  202. { 102, 4, 2, 3, 63, 600 },
  203. { 103, 5, 2, 3, 63, 600 },
  204. { 104, 5, 2, 3, 63, 600 },
  205. { 105, 5, 2, 3, 63, 600 },
  206. { 106, 5, 2, 3, 63, 600 },
  207. { 107, 3, 4, 3, 63, 600 },
  208. { 108, 3, 4, 3, 63, 600 },
  209. { 109, 3, 4, 3, 63, 600 },
  210. { 110, 3, 4, 3, 63, 600 },
  211. { 111, 3, 4, 3, 63, 600 },
  212. { 112, 3, 4, 3, 63, 600 },
  213. { 113, 3, 4, 3, 63, 600 },
  214. { 114, 3, 4, 3, 63, 600 },
  215. { 115, 3, 4, 3, 63, 600 },
  216. { 116, 3, 4, 3, 63, 600 },
  217. { 117, 3, 4, 3, 63, 600 },
  218. { 118, 3, 4, 3, 63, 600 },
  219. { 119, 3, 4, 3, 63, 600 },
  220. { 120, 3, 4, 3, 63, 600 },
  221. { 121, 3, 4, 3, 63, 600 },
  222. { 122, 3, 4, 3, 63, 600 },
  223. { 123, 3, 4, 3, 63, 600 },
  224. { 124, 3, 4, 3, 63, 600 },
  225. { 125, 3, 4, 3, 63, 600 },
  226. };
  227. /**
  228. * xvcu_read - Read from the VCU register space
  229. * @iomem: vcu reg space base address
  230. * @offset: vcu reg offset from base
  231. *
  232. * Return: Returns 32bit value from VCU register specified
  233. *
  234. */
  235. static inline u32 xvcu_read(void __iomem *iomem, u32 offset)
  236. {
  237. return ioread32(iomem + offset);
  238. }
  239. /**
  240. * xvcu_write - Write to the VCU register space
  241. * @iomem: vcu reg space base address
  242. * @offset: vcu reg offset from base
  243. * @value: Value to write
  244. */
  245. static inline void xvcu_write(void __iomem *iomem, u32 offset, u32 value)
  246. {
  247. iowrite32(value, iomem + offset);
  248. }
  249. /**
  250. * xvcu_write_field_reg - Write to the vcu reg field
  251. * @iomem: vcu reg space base address
  252. * @offset: vcu reg offset from base
  253. * @field: vcu reg field to write to
  254. * @mask: vcu reg mask
  255. * @shift: vcu reg number of bits to shift the bitfield
  256. */
  257. static void xvcu_write_field_reg(void __iomem *iomem, int offset,
  258. u32 field, u32 mask, int shift)
  259. {
  260. u32 val = xvcu_read(iomem, offset);
  261. val &= ~(mask << shift);
  262. val |= (field & mask) << shift;
  263. xvcu_write(iomem, offset, val);
  264. }
  265. /**
  266. * xvcu_set_vcu_pll_info - Set the VCU PLL info
  267. * @xvcu: Pointer to the xvcu_device structure
  268. *
  269. * Programming the VCU PLL based on the user configuration
  270. * (ref clock freq, core clock freq, mcu clock freq).
  271. * Core clock frequency has higher priority than mcu clock frequency
  272. * Errors in following cases
  273. * - When mcu or clock clock get from logicoreIP is 0
  274. * - When VCU PLL DIV related bits value other than 1
  275. * - When proper data not found for given data
  276. * - When sis570_1 clocksource related operation failed
  277. *
  278. * Return: Returns status, either success or error+reason
  279. */
  280. static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu)
  281. {
  282. u32 refclk, coreclk, mcuclk, inte, deci;
  283. u32 divisor_mcu, divisor_core, fvco;
  284. u32 clkoutdiv, vcu_pll_ctrl, pll_clk;
  285. u32 cfg_val, mod, ctrl;
  286. int ret, i;
  287. const struct xvcu_pll_cfg *found = NULL;
  288. inte = xvcu_read(xvcu->logicore_reg_ba, VCU_PLL_CLK);
  289. deci = xvcu_read(xvcu->logicore_reg_ba, VCU_PLL_CLK_DEC);
  290. coreclk = xvcu_read(xvcu->logicore_reg_ba, VCU_CORE_CLK) * MHZ;
  291. mcuclk = xvcu_read(xvcu->logicore_reg_ba, VCU_MCU_CLK) * MHZ;
  292. if (!mcuclk || !coreclk) {
  293. dev_err(xvcu->dev, "Invalid mcu and core clock data\n");
  294. return -EINVAL;
  295. }
  296. refclk = (inte * MHZ) + (deci * (MHZ / FRAC));
  297. dev_dbg(xvcu->dev, "Ref clock from logicoreIP is %uHz\n", refclk);
  298. dev_dbg(xvcu->dev, "Core clock from logicoreIP is %uHz\n", coreclk);
  299. dev_dbg(xvcu->dev, "Mcu clock from logicoreIP is %uHz\n", mcuclk);
  300. clk_disable_unprepare(xvcu->pll_ref);
  301. ret = clk_set_rate(xvcu->pll_ref, refclk);
  302. if (ret)
  303. dev_warn(xvcu->dev, "failed to set logicoreIP refclk rate\n");
  304. ret = clk_prepare_enable(xvcu->pll_ref);
  305. if (ret) {
  306. dev_err(xvcu->dev, "failed to enable pll_ref clock source\n");
  307. return ret;
  308. }
  309. refclk = clk_get_rate(xvcu->pll_ref);
  310. /*
  311. * The divide-by-2 should be always enabled (==1)
  312. * to meet the timing in the design.
  313. * Otherwise, it's an error
  314. */
  315. vcu_pll_ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_PLL_CTRL);
  316. clkoutdiv = vcu_pll_ctrl >> VCU_PLL_CTRL_CLKOUTDIV_SHIFT;
  317. clkoutdiv = clkoutdiv & VCU_PLL_CTRL_CLKOUTDIV_MASK;
  318. if (clkoutdiv != 1) {
  319. dev_err(xvcu->dev, "clkoutdiv value is invalid\n");
  320. return -EINVAL;
  321. }
  322. for (i = ARRAY_SIZE(xvcu_pll_cfg) - 1; i >= 0; i--) {
  323. const struct xvcu_pll_cfg *cfg = &xvcu_pll_cfg[i];
  324. fvco = cfg->fbdiv * refclk;
  325. if (fvco >= FVCO_MIN && fvco <= FVCO_MAX) {
  326. pll_clk = fvco / VCU_PLL_DIV2;
  327. if (fvco % VCU_PLL_DIV2 != 0)
  328. pll_clk++;
  329. mod = pll_clk % coreclk;
  330. if (mod < LIMIT) {
  331. divisor_core = pll_clk / coreclk;
  332. } else if (coreclk - mod < LIMIT) {
  333. divisor_core = pll_clk / coreclk;
  334. divisor_core++;
  335. } else {
  336. continue;
  337. }
  338. if (divisor_core >= DIVISOR_MIN &&
  339. divisor_core <= DIVISOR_MAX) {
  340. found = cfg;
  341. divisor_mcu = pll_clk / mcuclk;
  342. mod = pll_clk % mcuclk;
  343. if (mcuclk - mod < LIMIT)
  344. divisor_mcu++;
  345. break;
  346. }
  347. }
  348. }
  349. if (!found) {
  350. dev_err(xvcu->dev, "Invalid clock combination.\n");
  351. return -EINVAL;
  352. }
  353. xvcu->coreclk = pll_clk / divisor_core;
  354. mcuclk = pll_clk / divisor_mcu;
  355. dev_dbg(xvcu->dev, "Actual Ref clock freq is %uHz\n", refclk);
  356. dev_dbg(xvcu->dev, "Actual Core clock freq is %uHz\n", xvcu->coreclk);
  357. dev_dbg(xvcu->dev, "Actual Mcu clock freq is %uHz\n", mcuclk);
  358. vcu_pll_ctrl &= ~(VCU_PLL_CTRL_FBDIV_MASK << VCU_PLL_CTRL_FBDIV_SHIFT);
  359. vcu_pll_ctrl |= (found->fbdiv & VCU_PLL_CTRL_FBDIV_MASK) <<
  360. VCU_PLL_CTRL_FBDIV_SHIFT;
  361. vcu_pll_ctrl &= ~(VCU_PLL_CTRL_POR_IN_MASK <<
  362. VCU_PLL_CTRL_POR_IN_SHIFT);
  363. vcu_pll_ctrl |= (VCU_PLL_CTRL_DEFAULT & VCU_PLL_CTRL_POR_IN_MASK) <<
  364. VCU_PLL_CTRL_POR_IN_SHIFT;
  365. vcu_pll_ctrl &= ~(VCU_PLL_CTRL_PWR_POR_MASK <<
  366. VCU_PLL_CTRL_PWR_POR_SHIFT);
  367. vcu_pll_ctrl |= (VCU_PLL_CTRL_DEFAULT & VCU_PLL_CTRL_PWR_POR_MASK) <<
  368. VCU_PLL_CTRL_PWR_POR_SHIFT;
  369. xvcu_write(xvcu->vcu_slcr_ba, VCU_PLL_CTRL, vcu_pll_ctrl);
  370. /* Set divisor for the core and mcu clock */
  371. ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_ENC_CORE_CTRL);
  372. ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
  373. ctrl |= (divisor_core & VCU_PLL_DIVISOR_MASK) <<
  374. VCU_PLL_DIVISOR_SHIFT;
  375. ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
  376. ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
  377. xvcu_write(xvcu->vcu_slcr_ba, VCU_ENC_CORE_CTRL, ctrl);
  378. ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_DEC_CORE_CTRL);
  379. ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
  380. ctrl |= (divisor_core & VCU_PLL_DIVISOR_MASK) <<
  381. VCU_PLL_DIVISOR_SHIFT;
  382. ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
  383. ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
  384. xvcu_write(xvcu->vcu_slcr_ba, VCU_DEC_CORE_CTRL, ctrl);
  385. ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_ENC_MCU_CTRL);
  386. ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
  387. ctrl |= (divisor_mcu & VCU_PLL_DIVISOR_MASK) << VCU_PLL_DIVISOR_SHIFT;
  388. ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
  389. ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
  390. xvcu_write(xvcu->vcu_slcr_ba, VCU_ENC_MCU_CTRL, ctrl);
  391. ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_DEC_MCU_CTRL);
  392. ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
  393. ctrl |= (divisor_mcu & VCU_PLL_DIVISOR_MASK) << VCU_PLL_DIVISOR_SHIFT;
  394. ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
  395. ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
  396. xvcu_write(xvcu->vcu_slcr_ba, VCU_DEC_MCU_CTRL, ctrl);
  397. /* Set RES, CP, LFHF, LOCK_CNT and LOCK_DLY cfg values */
  398. cfg_val = (found->res << VCU_PLL_CFG_RES_SHIFT) |
  399. (found->cp << VCU_PLL_CFG_CP_SHIFT) |
  400. (found->lfhf << VCU_PLL_CFG_LFHF_SHIFT) |
  401. (found->lock_cnt << VCU_PLL_CFG_LOCK_CNT_SHIFT) |
  402. (found->lock_dly << VCU_PLL_CFG_LOCK_DLY_SHIFT);
  403. xvcu_write(xvcu->vcu_slcr_ba, VCU_PLL_CFG, cfg_val);
  404. return 0;
  405. }
  406. /**
  407. * xvcu_set_pll - PLL init sequence
  408. * @xvcu: Pointer to the xvcu_device structure
  409. *
  410. * Call the api to set the PLL info and once that is done then
  411. * init the PLL sequence to make the PLL stable.
  412. *
  413. * Return: Returns status, either success or error+reason
  414. */
  415. static int xvcu_set_pll(struct xvcu_device *xvcu)
  416. {
  417. u32 lock_status;
  418. unsigned long timeout;
  419. int ret;
  420. ret = xvcu_set_vcu_pll_info(xvcu);
  421. if (ret) {
  422. dev_err(xvcu->dev, "failed to set pll info\n");
  423. return ret;
  424. }
  425. xvcu_write_field_reg(xvcu->vcu_slcr_ba, VCU_PLL_CTRL,
  426. 1, VCU_PLL_CTRL_BYPASS_MASK,
  427. VCU_PLL_CTRL_BYPASS_SHIFT);
  428. xvcu_write_field_reg(xvcu->vcu_slcr_ba, VCU_PLL_CTRL,
  429. 1, VCU_PLL_CTRL_RESET_MASK,
  430. VCU_PLL_CTRL_RESET_SHIFT);
  431. xvcu_write_field_reg(xvcu->vcu_slcr_ba, VCU_PLL_CTRL,
  432. 0, VCU_PLL_CTRL_RESET_MASK,
  433. VCU_PLL_CTRL_RESET_SHIFT);
  434. /*
  435. * Defined the timeout for the max time to wait the
  436. * PLL_STATUS to be locked.
  437. */
  438. timeout = jiffies + msecs_to_jiffies(2000);
  439. do {
  440. lock_status = xvcu_read(xvcu->vcu_slcr_ba, VCU_PLL_STATUS);
  441. if (lock_status & VCU_PLL_STATUS_LOCK_STATUS_MASK) {
  442. xvcu_write_field_reg(xvcu->vcu_slcr_ba, VCU_PLL_CTRL,
  443. 0, VCU_PLL_CTRL_BYPASS_MASK,
  444. VCU_PLL_CTRL_BYPASS_SHIFT);
  445. return 0;
  446. }
  447. } while (!time_after(jiffies, timeout));
  448. /* PLL is not locked even after the timeout of the 2sec */
  449. dev_err(xvcu->dev, "PLL is not locked\n");
  450. return -ETIMEDOUT;
  451. }
  452. /**
  453. * xvcu_probe - Probe existence of the logicoreIP
  454. * and initialize PLL
  455. *
  456. * @pdev: Pointer to the platform_device structure
  457. *
  458. * Return: Returns 0 on success
  459. * Negative error code otherwise
  460. */
  461. static int xvcu_probe(struct platform_device *pdev)
  462. {
  463. struct resource *res;
  464. struct xvcu_device *xvcu;
  465. int ret;
  466. xvcu = devm_kzalloc(&pdev->dev, sizeof(*xvcu), GFP_KERNEL);
  467. if (!xvcu)
  468. return -ENOMEM;
  469. xvcu->dev = &pdev->dev;
  470. res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vcu_slcr");
  471. if (!res) {
  472. dev_err(&pdev->dev, "get vcu_slcr memory resource failed.\n");
  473. return -ENODEV;
  474. }
  475. xvcu->vcu_slcr_ba = devm_ioremap_nocache(&pdev->dev, res->start,
  476. resource_size(res));
  477. if (!xvcu->vcu_slcr_ba) {
  478. dev_err(&pdev->dev, "vcu_slcr register mapping failed.\n");
  479. return -ENOMEM;
  480. }
  481. res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "logicore");
  482. if (!res) {
  483. dev_err(&pdev->dev, "get logicore memory resource failed.\n");
  484. return -ENODEV;
  485. }
  486. xvcu->logicore_reg_ba = devm_ioremap_nocache(&pdev->dev, res->start,
  487. resource_size(res));
  488. if (!xvcu->logicore_reg_ba) {
  489. dev_err(&pdev->dev, "logicore register mapping failed.\n");
  490. return -ENOMEM;
  491. }
  492. xvcu->aclk = devm_clk_get(&pdev->dev, "aclk");
  493. if (IS_ERR(xvcu->aclk)) {
  494. dev_err(&pdev->dev, "Could not get aclk clock\n");
  495. return PTR_ERR(xvcu->aclk);
  496. }
  497. xvcu->pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
  498. if (IS_ERR(xvcu->pll_ref)) {
  499. dev_err(&pdev->dev, "Could not get pll_ref clock\n");
  500. return PTR_ERR(xvcu->pll_ref);
  501. }
  502. ret = clk_prepare_enable(xvcu->aclk);
  503. if (ret) {
  504. dev_err(&pdev->dev, "aclk clock enable failed\n");
  505. return ret;
  506. }
  507. ret = clk_prepare_enable(xvcu->pll_ref);
  508. if (ret) {
  509. dev_err(&pdev->dev, "pll_ref clock enable failed\n");
  510. goto error_aclk;
  511. }
  512. /*
  513. * Do the Gasket isolation and put the VCU out of reset
  514. * Bit 0 : Gasket isolation
  515. * Bit 1 : put VCU out of reset
  516. */
  517. xvcu_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, VCU_GASKET_VALUE);
  518. /* Do the PLL Settings based on the ref clk,core and mcu clk freq */
  519. ret = xvcu_set_pll(xvcu);
  520. if (ret) {
  521. dev_err(&pdev->dev, "Failed to set the pll\n");
  522. goto error_pll_ref;
  523. }
  524. dev_set_drvdata(&pdev->dev, xvcu);
  525. dev_info(&pdev->dev, "%s: Probed successfully\n", __func__);
  526. return 0;
  527. error_pll_ref:
  528. clk_disable_unprepare(xvcu->pll_ref);
  529. error_aclk:
  530. clk_disable_unprepare(xvcu->aclk);
  531. return ret;
  532. }
  533. /**
  534. * xvcu_remove - Insert gasket isolation
  535. * and disable the clock
  536. * @pdev: Pointer to the platform_device structure
  537. *
  538. * Return: Returns 0 on success
  539. * Negative error code otherwise
  540. */
  541. static int xvcu_remove(struct platform_device *pdev)
  542. {
  543. struct xvcu_device *xvcu;
  544. xvcu = platform_get_drvdata(pdev);
  545. if (!xvcu)
  546. return -ENODEV;
  547. /* Add the the Gasket isolation and put the VCU in reset. */
  548. xvcu_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0);
  549. clk_disable_unprepare(xvcu->pll_ref);
  550. clk_disable_unprepare(xvcu->aclk);
  551. return 0;
  552. }
  553. static const struct of_device_id xvcu_of_id_table[] = {
  554. { .compatible = "xlnx,vcu" },
  555. { .compatible = "xlnx,vcu-logicoreip-1.0" },
  556. { }
  557. };
  558. MODULE_DEVICE_TABLE(of, xvcu_of_id_table);
  559. static struct platform_driver xvcu_driver = {
  560. .driver = {
  561. .name = "xilinx-vcu",
  562. .of_match_table = xvcu_of_id_table,
  563. },
  564. .probe = xvcu_probe,
  565. .remove = xvcu_remove,
  566. };
  567. module_platform_driver(xvcu_driver);
  568. MODULE_AUTHOR("Dhaval Shah <dshah@xilinx.com>");
  569. MODULE_DESCRIPTION("Xilinx VCU init Driver");
  570. MODULE_LICENSE("GPL v2");