Nut/OS  4.10.3
API Reference
cy2239x.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2005 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00061 #include <sys/event.h>
00062 #include <dev/twif.h>
00063 #include <dev/cy2239x.h>
00064 
00069 
00073 #ifndef I2C_SLA_PLL
00074 #define I2C_SLA_PLL     0x69
00075 #endif
00076 
00082 #ifndef NUT_PLL_FREF
00083 #define NUT_PLL_FREF   25000000UL
00084 #endif
00085 
00093 static uint32_t PllFreq(uint8_t * reg)
00094 {
00095     uint32_t p;
00096     uint32_t pt;
00097     uint32_t qt;
00098 
00099     /* The 11-bit P value is stored in two registers. */
00100     p = (uint32_t) (reg[2] & 0x03) << 8 | reg[1];
00101     /* Calculate Pt = (2 x (P + 3)) + PO. */
00102     pt = 2 * (p + 3) + ((reg[2] >> 2) & 1);
00103     /* Calculate Qt = Q + 2. */
00104     qt = reg[0] + 2;
00105     /* Calculate Fpll = Fref x (Pt / Qt) */
00106     return (((NUT_PLL_FREF * 10UL + 5UL) / qt) * pt) / 10UL;
00107 }
00108 
00124 int Cy2239xGetPll(int clk)
00125 {
00126     int rc = -1;
00127     uint8_t loc = 0x0E;
00128     uint8_t reg;
00129 
00130     /* ClkE is fixed to PLL1. */
00131     if (clk == CY2239X_CLKE) {
00132         rc = 1;
00133     } 
00134     /* Register 0x0E contains the PLL index for ClkA-ClkD. */
00135     else if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) == 1) {
00136         rc = (reg >> (2 * clk)) & 0x03;
00137     }
00138     return rc;
00139 }
00140 
00159 int Cy2239xSetPll(int clk, int pll)
00160 {
00161     uint8_t reg[2];
00162     uint8_t msk = 0x03;
00163 
00164     /* ClkE is fixed to PLL1. */
00165     if (clk >= CY2239X_CLKE) {
00166         if (pll != CY2239X_PLL1) {
00167             return -1;
00168         }
00169         return 0;
00170     }
00171 
00172     if ((pll | msk) == msk) {
00173 
00174         /* Register 0x0E contains the PLL index for ClkA-ClkD. */
00175         reg[0] = 0x0E;
00176         if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
00177             clk <<= 1;
00178             reg[1] &= ~(msk << clk);
00179             reg[1] |= pll << clk;
00180             TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
00181             return 0;
00182         }
00183     }
00184     return -1;
00185 }
00186 
00199 int Cy2239xGetDivider(int clk, int fctrl)
00200 {
00201     int rc = -1;
00202     int idx;
00203     uint8_t loc;
00204     uint8_t reg;
00205 
00206     /*
00207      * Clock E has a simple divider only.
00208      */
00209     if (clk == CY2239X_CLKE) {
00210         /* The two lower bits of register 0x0F define the Clock E divider. */
00211         loc = 0x0F;
00212         if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) == 1) {
00213             rc = reg & 3;
00214             if (rc == 1) {
00215                 rc = 4;
00216             }
00217         }
00218     }
00219     else {
00220         /*
00221          * Clock A and B have two dividers, which are indirectly selected 
00222          * by the frequency control inputs.
00223          */
00224         if (clk <= CY2239X_CLKB) {
00225             /* Read the DivSel bit. */
00226             loc = 0x42 + fctrl * 3;
00227             if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) != 1) {
00228                 return -1;
00229             }
00230             idx = clk * 2 + (reg >> 7);
00231         }
00232         else {
00233             idx = clk + 2;
00234         }
00235         loc = 0x08 + idx;
00236         if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) == 1) {
00237             rc = reg & 0x7F;
00238         }
00239     }
00240     return rc;
00241 }
00242 
00262 int Cy2239xSetDivider(int clk, int sel, int val)
00263 {
00264     uint8_t reg[2];
00265 
00266     /* Clock E has a simple divider only. */
00267     if (clk == CY2239X_CLKE) {
00268         if (val == 0 || (val >= 2 && val <= 4)) {
00269             if (val == 4) {
00270                 val = 1;
00271             }
00272             /* The two lower bits of register 0x0F define the Clock E divider. */
00273             reg[0] = 0x0F;
00274             if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
00275                 reg[1] &= ~0x03;
00276                 reg[1] |= (uint8_t) val;
00277                 TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
00278                 return 0;
00279             }
00280         }
00281         return -1;
00282     }
00283 
00284     if (val > 0 && val < 128) {
00285         /* Clock A and B have two selectable dividers. */
00286         if (clk <= CY2239X_CLKB) {
00287             reg[0] = 0x08 + clk * 2 + sel;
00288         }
00289         else {
00290             reg[0] = 0x08 + clk + 2;
00291         }
00292         if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
00293             reg[1] &= ~0x7F;
00294             reg[1] |= (uint8_t) val;
00295             TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
00296             return 0;
00297         }
00298     }
00299     return -1;
00300 }
00301 
00328 int Cy2239xPllEnable(int pll, int fctrl, int ena)
00329 {
00330     int rc = -1;
00331     uint8_t reg[2];
00332 
00333     if (pll) {
00334         if (pll == CY2239X_PLL1) {
00335             /* PLL1 can be switched by the external control inputs. */
00336             reg[0] = 0x42 + fctrl * 3;
00337         }
00338         else if (pll == CY2239X_PLL2) {
00339             reg[0] = 0x13;
00340         }
00341         else if (pll == CY2239X_PLL3) {
00342             reg[0] = 0x16;
00343         }
00344 
00345         /* Set bit 6 of the third PLL register. */
00346         if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
00347             rc = (reg[1] & 0x40) != 0;
00348             if (ena == 1) {
00349                 reg[1] |= 0x40;
00350             }
00351             else if (ena == 0) {
00352                 reg[1] &= ~0x40;
00353             }
00354             else {
00355                 return rc;
00356             }
00357             TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
00358         }
00359     }
00360     return rc;
00361 }
00362 
00383 uint32_t Cy2239xPllGetFreq(int pll, int fctrl)
00384 {
00385     uint32_t rc = NUT_PLL_FREF;
00386     uint8_t loc;
00387     uint8_t reg[3];
00388 
00389     if (pll) {
00390         if (pll == CY2239X_PLL1) {
00391             /* PLL1 can be switched by the external control inputs. */
00392             loc = 0x40 + fctrl * 3;
00393         }
00394         else if (pll == CY2239X_PLL2) {
00395             loc = 0x11;
00396         }
00397         else if (pll == CY2239X_PLL3) {
00398             loc = 0x14;
00399         }
00400         /* Read the three PLL specific registers. */
00401         if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, reg, 3, NUT_WAIT_INFINITE) != 3) {
00402             rc = 0;
00403         }
00404         else {
00405             rc = PllFreq(reg);
00406         }
00407     }
00408     return rc;
00409 }
00410 
00440 int Cy2239xPllSetFreq(int pll, int fctrl, unsigned int pval, unsigned int poff, unsigned int qval, unsigned int fval)
00441 {
00442     uint8_t reg[4];
00443     int ena;
00444 
00445     if (pll) {
00446         /* Determine the location of the PLL specific registers. */
00447         if (pll == CY2239X_PLL1) {
00448             reg[0] = 0x40 + fctrl * 3;
00449         }
00450         else if (pll == CY2239X_PLL2) {
00451             reg[0] = 0x11;
00452         }
00453         else if (pll == CY2239X_PLL3) {
00454             reg[0] = 0x14;
00455         }
00456 
00457         /* Read the three PLL specific registers. */
00458         if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 3, NUT_WAIT_INFINITE) == 3) {
00459             /* The register is updated immediately. Disable the PLL to avoid
00460              * out of bounds settings. */
00461             if ((ena = Cy2239xPllEnable(pll, fctrl, 0)) >= 0) {
00462                 reg[1] = (uint8_t) qval;
00463                 reg[2] = (uint8_t) pval;
00464                 reg[3] &= 0x80; /* Clear all except the divider select. */
00465                 reg[3] |= (uint8_t)(pval >> 8) & 0x03;
00466                 reg[3] |= (poff & 1) << 2;
00467                 reg[3] |= (fval & 7) << 3;
00468                 TwMasterTransact(I2C_SLA_PLL, reg, 4, 0, 0, NUT_WAIT_INFINITE);
00469                 Cy2239xPllEnable(pll, fctrl, ena);
00470             }
00471             return 0;
00472         }
00473     }
00474     return -1;
00475 }
00476 
00506 uint32_t Cy2239xGetFreq(int clk, int fctrl)
00507 {
00508     uint32_t rc;
00509     uint32_t d;
00510     uint8_t loc;
00511     uint8_t reg;
00512     uint8_t clk_reg[8];
00513     uint8_t pll_reg[3];
00514     int pll;
00515 
00516     /* Read clock registers 0x08 - 0x0F. */
00517     loc = 0x08;
00518     if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, clk_reg, 8, NUT_WAIT_INFINITE) != 8) {
00519         return 0;
00520     }
00521 
00522     /* 
00523      * Get the PLL index for the specified output divider. Index 0 
00524      * specifies the reference clock, while 1 to 3 specify PLL1 to 
00525      * PLL3 resp.
00526      */
00527     if (clk == CY2239X_CLKE) {
00528         /* ClkE is fixed to PLL1. */
00529         pll = CY2239X_PLL1;
00530     } else {
00531         /* Register 0x0E contains the PLL index for ClkA-ClkD. */
00532         pll = (clk_reg[6] >> (2 * clk)) & 0x03;
00533     }
00534 
00535     /*
00536      * We got the PLL index. Now let's determine the PLL frequency.
00537      */
00538     if (pll == CY2239X_REF) {
00539         /* Index 0 means reference clock. */
00540         rc = NUT_PLL_FREF;
00541     } else {
00542         if (pll == CY2239X_PLL1) {
00543             /* PLL1 can be switched by the external control inputs. */
00544             loc = 0x40 + fctrl * 3;
00545         }
00546         else if (pll == CY2239X_PLL2) {
00547             loc = 0x11;
00548         }
00549         else if (pll == CY2239X_PLL3) {
00550             loc = 0x14;
00551         }
00552         /* Read the three PLL specific registers. */
00553         if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, pll_reg, 3, NUT_WAIT_INFINITE) != 3) {
00554             return 0;
00555         }
00556         rc = PllFreq(pll_reg);
00557     }
00558 
00559     /*
00560      * At this point we got the divider input frequency. Now we retrieve 
00561      * the divider value.
00562      */
00563     if (clk <= CY2239X_CLKB) {
00564         /* Clock A and B provide two dividers, selected by the frequency
00565          * control input. */
00566         if (pll == CY2239X_PLL1) {
00567             /* For PLL1 we already read the register. */
00568             reg = pll_reg[2];
00569         }
00570         else {
00571             /* For PLL2 and PLL3 we read the register now. */
00572             loc = 0x42 + fctrl * 3;
00573             if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) != 1) {
00574                 return 0;
00575             }
00576         }
00577         d = clk_reg[clk * 2 + (reg >> 7)] & 0x7F;
00578     }
00579     else if (clk == CY2239X_CLKE) {
00580         /* Get divider for Clock E. */
00581         d = clk_reg[7] & 3;
00582         if (d == 1) {
00583             d = 4;
00584         }
00585     }
00586     else {
00587         /* Get divider for Clock C and D. */
00588         d = clk_reg[clk + 2] & 0x7F;
00589     }
00590 
00591     /*
00592      * Finally divide the input frequency. A divider value of zero means
00593      * that the output is switched off.
00594      */
00595     if (d) {
00596         rc /= d;
00597     } else {
00598         rc = 0;
00599     }
00600     return rc;
00601 }
00602