cy2239x.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. /*
  2. * Copyright (C) 2005 by egnite Software GmbH. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * For additional information see http://www.ethernut.de/
  31. *
  32. */
  33. /*!
  34. * \file dev/cy2239x.c
  35. * \brief Routines for the Cypress CY22393/4/5 clock chips.
  36. *
  37. * \verbatim
  38. *
  39. * $Log$
  40. * Revision 1.5 2009/01/17 11:26:46 haraldkipp
  41. * Getting rid of two remaining BSD types in favor of stdint.
  42. * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
  43. *
  44. * Revision 1.4 2008/08/11 06:59:41 haraldkipp
  45. * BSD types replaced by stdint types (feature request #1282721).
  46. *
  47. * Revision 1.3 2006/01/23 17:32:11 haraldkipp
  48. * Avoid hang-ups caused by debug leftovers.
  49. *
  50. * Revision 1.2 2006/01/05 16:51:06 haraldkipp
  51. * Several new functions added to query and modify the clock settings.
  52. *
  53. * Revision 1.1 2005/10/24 10:21:57 haraldkipp
  54. * Initial check in.
  55. *
  56. *
  57. * \endverbatim
  58. */
  59. #include <sys/event.h>
  60. #include <dev/twif.h>
  61. #include <dev/cy2239x.h>
  62. /*!
  63. * \addtogroup xgCy2239x
  64. */
  65. /*@{*/
  66. /*!
  67. * \brief I2C address.
  68. */
  69. #ifndef I2C_SLA_PLL
  70. #define I2C_SLA_PLL 0x69
  71. #endif
  72. /*!
  73. * \brief Base frequency.
  74. *
  75. * Frequency of the attached crystal. On Ethernut 3 a 25 MHz crystal is used.
  76. */
  77. #ifndef NUT_PLL_FREF
  78. #define NUT_PLL_FREF 25000000UL
  79. #endif
  80. /*!
  81. * \brief Calculate PLL frequency.
  82. *
  83. * \parm reg Points to an array with the three PLL registers.
  84. *
  85. * \return Frequency in Hertz.
  86. */
  87. static uint32_t PllFreq(uint8_t * reg)
  88. {
  89. uint32_t p;
  90. uint32_t pt;
  91. uint32_t qt;
  92. /* The 11-bit P value is stored in two registers. */
  93. p = (uint32_t) (reg[2] & 0x03) << 8 | reg[1];
  94. /* Calculate Pt = (2 x (P + 3)) + PO. */
  95. pt = 2 * (p + 3) + ((reg[2] >> 2) & 1);
  96. /* Calculate Qt = Q + 2. */
  97. qt = reg[0] + 2;
  98. /* Calculate Fpll = Fref x (Pt / Qt) */
  99. return (((NUT_PLL_FREF * 10UL + 5UL) / qt) * pt) / 10UL;
  100. }
  101. /*!
  102. * \brief Get the PLL connected to the specified output.
  103. *
  104. * \param clk Specifies the output. 0 = ClkA, 1 = ClkB etc.
  105. *
  106. * \return 0 for the reference clock, 1 for PLL1, 2 for PLL2 or 3 for PLL3.
  107. * In case of an error -1 is returned.
  108. *
  109. * The following code fragment stores the number of the PLL connected
  110. * to Clock Output C in the variable pll.
  111. * \code
  112. * int pll;
  113. * pll = Cy2239xGetPll(CY2239X_CLKC);
  114. * \endcode
  115. */
  116. int Cy2239xGetPll(int clk)
  117. {
  118. int rc = -1;
  119. uint8_t loc = 0x0E;
  120. uint8_t reg;
  121. /* ClkE is fixed to PLL1. */
  122. if (clk == CY2239X_CLKE) {
  123. rc = 1;
  124. }
  125. /* Register 0x0E contains the PLL index for ClkA-ClkD. */
  126. else if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) == 1) {
  127. rc = (reg >> (2 * clk)) & 0x03;
  128. }
  129. return rc;
  130. }
  131. /*!
  132. * \brief Connect a specified PLL to a specified output.
  133. *
  134. * \note Use with great care. This function may destroy your hardware.
  135. *
  136. * \param clk Specifies the output. 0 = ClkA, 1 = ClkB etc.
  137. * \param pll 1 - 3 for PLL1 - PLL3 resp. or 0 to select the reference
  138. * clock.
  139. *
  140. * \return 0 on success, -1 otherwise.
  141. *
  142. * The following code can be used to connect PLL3 to Clock Output B.
  143. * \code
  144. * if (Cy2239xSetPll(CY2239X_CLKB, CY2239X_PLL3)) {
  145. * printf("Failed to select PLL3 for Clock B\n");
  146. * }
  147. * \endcode
  148. */
  149. int Cy2239xSetPll(int clk, int pll)
  150. {
  151. uint8_t reg[2];
  152. uint8_t msk = 0x03;
  153. /* ClkE is fixed to PLL1. */
  154. if (clk >= CY2239X_CLKE) {
  155. if (pll != CY2239X_PLL1) {
  156. return -1;
  157. }
  158. return 0;
  159. }
  160. if ((pll | msk) == msk) {
  161. /* Register 0x0E contains the PLL index for ClkA-ClkD. */
  162. reg[0] = 0x0E;
  163. if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
  164. clk <<= 1;
  165. reg[1] &= ~(msk << clk);
  166. reg[1] |= pll << clk;
  167. TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
  168. return 0;
  169. }
  170. }
  171. return -1;
  172. }
  173. /*!
  174. * \brief Get the current divider value of the specified output.
  175. *
  176. * \param clk Specifies the output. 0 = ClkA, 1 = ClkB etc.
  177. * \param fctrl Frequency control input status, only valid for clock A
  178. * and B. Otherwise this parameter is ignored. For
  179. * Ethernut 3 the inputs S0 and S1 are high and S2 is
  180. * connected to the CPLD. Thus, this value is either 3 if
  181. * control input S2 is low or 7 if S2 is set high (default).
  182. *
  183. * \return Clock's divider value. 0 means, that the output is switched off.
  184. */
  185. int Cy2239xGetDivider(int clk, int fctrl)
  186. {
  187. int rc = -1;
  188. int idx;
  189. uint8_t loc;
  190. uint8_t reg;
  191. /*
  192. * Clock E has a simple divider only.
  193. */
  194. if (clk == CY2239X_CLKE) {
  195. /* The two lower bits of register 0x0F define the Clock E divider. */
  196. loc = 0x0F;
  197. if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) == 1) {
  198. rc = reg & 3;
  199. if (rc == 1) {
  200. rc = 4;
  201. }
  202. }
  203. }
  204. else {
  205. /*
  206. * Clock A and B have two dividers, which are indirectly selected
  207. * by the frequency control inputs.
  208. */
  209. if (clk <= CY2239X_CLKB) {
  210. /* Read the DivSel bit. */
  211. loc = 0x42 + fctrl * 3;
  212. if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) != 1) {
  213. return -1;
  214. }
  215. idx = clk * 2 + (reg >> 7);
  216. }
  217. else {
  218. idx = clk + 2;
  219. }
  220. loc = 0x08 + idx;
  221. if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) == 1) {
  222. rc = reg & 0x7F;
  223. }
  224. }
  225. return rc;
  226. }
  227. /*!
  228. * \brief Set the divider value of the specified output.
  229. *
  230. * Clock A through D provide a 7-bit output divider, while the Clock E
  231. * divider is fixed to 0 (off), 2, 3 or 4.
  232. *
  233. * Changing the divider value of an active output may cause a glitch on
  234. * the output.
  235. *
  236. * \note Use with great care. This function may destroy your hardware.
  237. *
  238. * \param clk Specifies the output. 0 = ClkA, 1 = ClkB etc.
  239. * \param sel Divider select for Clock A and B, either 0 or 1. For other
  240. * outputs this parameter is ignored.
  241. * \param val New divider value to set. A value of zero powers down the
  242. * divider and forces the output to three-state.
  243. *
  244. * \return 0 on success, -1 otherwise.
  245. */
  246. int Cy2239xSetDivider(int clk, int sel, int val)
  247. {
  248. uint8_t reg[2];
  249. /* Clock E has a simple divider only. */
  250. if (clk == CY2239X_CLKE) {
  251. if (val == 0 || (val >= 2 && val <= 4)) {
  252. if (val == 4) {
  253. val = 1;
  254. }
  255. /* The two lower bits of register 0x0F define the Clock E divider. */
  256. reg[0] = 0x0F;
  257. if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
  258. reg[1] &= ~0x03;
  259. reg[1] |= (uint8_t) val;
  260. TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
  261. return 0;
  262. }
  263. }
  264. return -1;
  265. }
  266. if (val > 0 && val < 128) {
  267. /* Clock A and B have two selectable dividers. */
  268. if (clk <= CY2239X_CLKB) {
  269. reg[0] = 0x08 + clk * 2 + sel;
  270. }
  271. else {
  272. reg[0] = 0x08 + clk + 2;
  273. }
  274. if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
  275. reg[1] &= ~0x7F;
  276. reg[1] |= (uint8_t) val;
  277. TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
  278. return 0;
  279. }
  280. }
  281. return -1;
  282. }
  283. /*!
  284. * \brief Enable or disable a specified PLL.
  285. *
  286. * \param pll Set to 1 - 3 for PLL1 - PLL3 resp.
  287. * \param fctrl Frequency control input status, only used for PLL1.
  288. * Otherwise this parameter is ignored. For Ethernut 3 the
  289. * inputs S0 and S1 are high and S2 is connected to the
  290. * CPLD. Thus, this value is either 3 if control input S2
  291. * is low or 7 if S2 is set to high.
  292. * \param ena 0 will disable the PLL, 1 will enable it. Any other value
  293. * may be used to query the current status.
  294. *
  295. * \return 0 if the PLL had been previously disabled, 1 if it had been
  296. * enabled or -1 in case of an error.
  297. *
  298. * The following code shuts down PLL2 and Clock Output A. On Ethernut 3
  299. * this will remove the clock from the Ethernet Controller.
  300. * \code
  301. * if (Cy2239xSetDivider(CY2239X_CLKA, 1, 0)) {
  302. * printf("Failed to disable ClkA Divider 1\n");
  303. * }
  304. * if (Cy2239xPllEnable(CY2239X_PLL2, 7, 0)) {
  305. * printf("Failed to disable PLL2\n");
  306. * }
  307. * \endcode
  308. */
  309. int Cy2239xPllEnable(int pll, int fctrl, int ena)
  310. {
  311. int rc = -1;
  312. uint8_t reg[2];
  313. if (pll) {
  314. if (pll == CY2239X_PLL1) {
  315. /* PLL1 can be switched by the external control inputs. */
  316. reg[0] = 0x42 + fctrl * 3;
  317. }
  318. else if (pll == CY2239X_PLL2) {
  319. reg[0] = 0x13;
  320. }
  321. else if (pll == CY2239X_PLL3) {
  322. reg[0] = 0x16;
  323. }
  324. /* Set bit 6 of the third PLL register. */
  325. if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
  326. rc = (reg[1] & 0x40) != 0;
  327. if (ena == 1) {
  328. reg[1] |= 0x40;
  329. }
  330. else if (ena == 0) {
  331. reg[1] &= ~0x40;
  332. }
  333. else {
  334. return rc;
  335. }
  336. TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
  337. }
  338. }
  339. return rc;
  340. }
  341. /*!
  342. * \brief Get the current frequency of a specified PLL.
  343. *
  344. * \param pll 1 - 3 for PLL1 - PLL3 resp. or 0 to query the reference
  345. * clock.
  346. * \param fctrl Frequency control input status, only used for PLL1.
  347. * Otherwise this parameter is ignored. For Ethernut 3 the
  348. * inputs S0 and S1 are high and S2 is connected to the
  349. * CPLD. Thus, this value is either 3 if control input S2
  350. * is low or 7 if S2 is set to high.
  351. *
  352. * \return Frequency in Hertz.
  353. *
  354. * Use the following code to retrieve the reference clock:
  355. * \code
  356. * uint32_t fref;
  357. *
  358. * fref = Cy2239xPllGetFreq(CY2239X_REF, 7);
  359. * \endcode
  360. */
  361. uint32_t Cy2239xPllGetFreq(int pll, int fctrl)
  362. {
  363. uint32_t rc = NUT_PLL_FREF;
  364. uint8_t loc;
  365. uint8_t reg[3];
  366. if (pll) {
  367. if (pll == CY2239X_PLL1) {
  368. /* PLL1 can be switched by the external control inputs. */
  369. loc = 0x40 + fctrl * 3;
  370. }
  371. else if (pll == CY2239X_PLL2) {
  372. loc = 0x11;
  373. }
  374. else if (pll == CY2239X_PLL3) {
  375. loc = 0x14;
  376. }
  377. /* Read the three PLL specific registers. */
  378. if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, reg, 3, NUT_WAIT_INFINITE) != 3) {
  379. rc = 0;
  380. }
  381. else {
  382. rc = PllFreq(reg);
  383. }
  384. }
  385. return rc;
  386. }
  387. /*!
  388. * \brief Set the frequency of a specified PLL.
  389. *
  390. * If the PLL is enabled, it will be temporarily disabled.
  391. *
  392. * The resulting frequency can calculated by
  393. * \code
  394. * F = Fref * (2 * (Pval + 3) + Poff) / (Qval + 2)
  395. * \endcode
  396. *
  397. * It is recommended to use Cypress' CyClocksRT utility for calculating
  398. * the best parameter values.
  399. *
  400. * \note Use with great care. This function may destroy your hardware.
  401. *
  402. * \param pll 1 - 3 for PLL1 - PLL3 resp.
  403. * \param fctrl Frequency control input status, only valid for clock A
  404. * and B. Otherwise this parameter is ignored. For
  405. * Ethernut 3 the inputs S0 and S1 are high and S2 is
  406. * connected to the CPLD. Thus, this value is either 3 if
  407. * control input S2 is low or 7 if S2 is set to high.
  408. * \param pval 10-bit multiplier.
  409. * \param poff 1-bit offset.
  410. * \param qval 8-bit divider.
  411. * \param fval 2-bit loop filter value. This value corresponds to the
  412. * multiplier value and guarantess the PLL stability.
  413. *
  414. * \return 0 on success, -1 otherwise.
  415. */
  416. int Cy2239xPllSetFreq(int pll, int fctrl, unsigned int pval, unsigned int poff, unsigned int qval, unsigned int fval)
  417. {
  418. uint8_t reg[4];
  419. int ena;
  420. if (pll) {
  421. /* Determine the location of the PLL specific registers. */
  422. if (pll == CY2239X_PLL1) {
  423. reg[0] = 0x40 + fctrl * 3;
  424. }
  425. else if (pll == CY2239X_PLL2) {
  426. reg[0] = 0x11;
  427. }
  428. else if (pll == CY2239X_PLL3) {
  429. reg[0] = 0x14;
  430. }
  431. /* Read the three PLL specific registers. */
  432. if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 3, NUT_WAIT_INFINITE) == 3) {
  433. /* The register is updated immediately. Disable the PLL to avoid
  434. * out of bounds settings. */
  435. if ((ena = Cy2239xPllEnable(pll, fctrl, 0)) >= 0) {
  436. reg[1] = (uint8_t) qval;
  437. reg[2] = (uint8_t) pval;
  438. reg[3] &= 0x80; /* Clear all except the divider select. */
  439. reg[3] |= (uint8_t)(pval >> 8) & 0x03;
  440. reg[3] |= (poff & 1) << 2;
  441. reg[3] |= (fval & 7) << 3;
  442. TwMasterTransact(I2C_SLA_PLL, reg, 4, 0, 0, NUT_WAIT_INFINITE);
  443. Cy2239xPllEnable(pll, fctrl, ena);
  444. }
  445. return 0;
  446. }
  447. }
  448. return -1;
  449. }
  450. /*!
  451. * \brief Get the frequency of a specified output.
  452. *
  453. * On Ethernut 3 Clock A provides the Ethernet Controller clock and
  454. * Clock C is used as the CPU clock.
  455. *
  456. * PLL1 provides two configurations, selectable by the external input
  457. * S2, which might be controlled by the Ethernut 3 CPLD. However, NPL
  458. * version 2 doesn't allow to modify the S2 line, but holds it at a
  459. * fixed high level.
  460. *
  461. * \param clk Specifies the output. 0 = ClkA, 1 = ClkB etc.
  462. * \param fctrl Frequency control input status, only valid for clock A
  463. * and B. Otherwise this parameter is ignored. For
  464. * Ethernut 3 the inputs S0 and S1 are high and S2 is
  465. * connected to the CPLD. Thus, this value is either 3 if
  466. * control input S2 is low or 7 if S2 is set to high.
  467. *
  468. * \return Frequency in Hertz. 0 is returned if the clock output is
  469. * disabled or if an error occured.
  470. *
  471. * The following code can be used to query the main CPU clock on the
  472. * Ethernut 3 board.
  473. * \code
  474. * uint32_t fcpu;
  475. *
  476. * fcpu = Cy2239xGetFreq(CY2239X_CLKC, 7);
  477. * \endcode
  478. */
  479. uint32_t Cy2239xGetFreq(int clk, int fctrl)
  480. {
  481. uint32_t rc;
  482. uint32_t d;
  483. uint8_t loc;
  484. uint8_t reg;
  485. uint8_t clk_reg[8];
  486. uint8_t pll_reg[3];
  487. int pll;
  488. /* Read clock registers 0x08 - 0x0F. */
  489. loc = 0x08;
  490. if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, clk_reg, 8, NUT_WAIT_INFINITE) != 8) {
  491. return 0;
  492. }
  493. /*
  494. * Get the PLL index for the specified output divider. Index 0
  495. * specifies the reference clock, while 1 to 3 specify PLL1 to
  496. * PLL3 resp.
  497. */
  498. if (clk == CY2239X_CLKE) {
  499. /* ClkE is fixed to PLL1. */
  500. pll = CY2239X_PLL1;
  501. } else {
  502. /* Register 0x0E contains the PLL index for ClkA-ClkD. */
  503. pll = (clk_reg[6] >> (2 * clk)) & 0x03;
  504. }
  505. /*
  506. * We got the PLL index. Now let's determine the PLL frequency.
  507. */
  508. if (pll == CY2239X_REF) {
  509. /* Index 0 means reference clock. */
  510. rc = NUT_PLL_FREF;
  511. } else {
  512. if (pll == CY2239X_PLL1) {
  513. /* PLL1 can be switched by the external control inputs. */
  514. loc = 0x40 + fctrl * 3;
  515. }
  516. else if (pll == CY2239X_PLL2) {
  517. loc = 0x11;
  518. }
  519. else if (pll == CY2239X_PLL3) {
  520. loc = 0x14;
  521. }
  522. /* Read the three PLL specific registers. */
  523. if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, pll_reg, 3, NUT_WAIT_INFINITE) != 3) {
  524. return 0;
  525. }
  526. rc = PllFreq(pll_reg);
  527. }
  528. /*
  529. * At this point we got the divider input frequency. Now we retrieve
  530. * the divider value.
  531. */
  532. if (clk <= CY2239X_CLKB) {
  533. /* Clock A and B provide two dividers, selected by the frequency
  534. * control input. */
  535. if (pll == CY2239X_PLL1) {
  536. /* For PLL1 we already read the register. */
  537. reg = pll_reg[2];
  538. }
  539. else {
  540. /* For PLL2 and PLL3 we read the register now. */
  541. loc = 0x42 + fctrl * 3;
  542. if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) != 1) {
  543. return 0;
  544. }
  545. }
  546. d = clk_reg[clk * 2 + (reg >> 7)] & 0x7F;
  547. }
  548. else if (clk == CY2239X_CLKE) {
  549. /* Get divider for Clock E. */
  550. d = clk_reg[7] & 3;
  551. if (d == 1) {
  552. d = 4;
  553. }
  554. }
  555. else {
  556. /* Get divider for Clock C and D. */
  557. d = clk_reg[clk + 2] & 0x7F;
  558. }
  559. /*
  560. * Finally divide the input frequency. A divider value of zero means
  561. * that the output is switched off.
  562. */
  563. if (d) {
  564. rc /= d;
  565. } else {
  566. rc = 0;
  567. }
  568. return rc;
  569. }
  570. /*@}*/