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 
00054 #include <sys/event.h>
00055 #include <dev/twif.h>
00056 #include <dev/cy2239x.h>
00057 
00062 
00066 #ifndef I2C_SLA_PLL
00067 #define I2C_SLA_PLL     0x69
00068 #endif
00069 
00075 #ifndef NUT_PLL_FREF
00076 #define NUT_PLL_FREF   25000000UL
00077 #endif
00078 
00086 static u_long PllFreq(u_char * reg)
00087 {
00088     u_long p;
00089     u_long pt;
00090     u_long qt;
00091 
00092     /* The 11-bit P value is stored in two registers. */
00093     p = (u_long) (reg[2] & 0x03) << 8 | reg[1];
00094     /* Calculate Pt = (2 x (P + 3)) + PO. */
00095     pt = 2 * (p + 3) + ((reg[2] >> 2) & 1);
00096     /* Calculate Qt = Q + 2. */
00097     qt = reg[0] + 2;
00098     /* Calculate Fpll = Fref x (Pt / Qt) */
00099     return (((NUT_PLL_FREF * 10UL + 5UL) / qt) * pt) / 10UL;
00100 }
00101 
00117 int Cy2239xGetPll(int clk)
00118 {
00119     int rc = -1;
00120     u_char loc = 0x0E;
00121     u_char reg;
00122 
00123     /* ClkE is fixed to PLL1. */
00124     if (clk == CY2239X_CLKE) {
00125         rc = 1;
00126     } 
00127     /* Register 0x0E contains the PLL index for ClkA-ClkD. */
00128     else if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) == 1) {
00129         rc = (reg >> (2 * clk)) & 0x03;
00130     }
00131     return rc;
00132 }
00133 
00152 int Cy2239xSetPll(int clk, int pll)
00153 {
00154     u_char reg[2];
00155     u_char msk = 0x03;
00156 
00157     /* ClkE is fixed to PLL1. */
00158     if (clk >= CY2239X_CLKE) {
00159         if (pll != CY2239X_PLL1) {
00160             return -1;
00161         }
00162         return 0;
00163     }
00164 
00165     if ((pll | msk) == msk) {
00166 
00167         /* Register 0x0E contains the PLL index for ClkA-ClkD. */
00168         reg[0] = 0x0E;
00169         if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
00170             clk <<= 1;
00171             reg[1] &= ~(msk << clk);
00172             reg[1] |= pll << clk;
00173             TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
00174             return 0;
00175         }
00176     }
00177     return -1;
00178 }
00179 
00192 int Cy2239xGetDivider(int clk, int fctrl)
00193 {
00194     int rc = -1;
00195     int idx;
00196     u_char loc;
00197     u_char reg;
00198 
00199     /*
00200      * Clock E has a simple divider only.
00201      */
00202     if (clk == CY2239X_CLKE) {
00203         /* The two lower bits of register 0x0F define the Clock E divider. */
00204         loc = 0x0F;
00205         if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) == 1) {
00206             rc = reg & 3;
00207             if (rc == 1) {
00208                 rc = 4;
00209             }
00210         }
00211     }
00212     else {
00213         /*
00214          * Clock A and B have two dividers, which are indirectly selected 
00215          * by the frequency control inputs.
00216          */
00217         if (clk <= CY2239X_CLKB) {
00218             /* Read the DivSel bit. */
00219             loc = 0x42 + fctrl * 3;
00220             if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) != 1) {
00221                 return -1;
00222             }
00223             idx = clk * 2 + (reg >> 7);
00224         }
00225         else {
00226             idx = clk + 2;
00227         }
00228         loc = 0x08 + idx;
00229         if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) == 1) {
00230             rc = reg & 0x7F;
00231         }
00232     }
00233     return rc;
00234 }
00235 
00255 int Cy2239xSetDivider(int clk, int sel, int val)
00256 {
00257     u_char reg[2];
00258 
00259     /* Clock E has a simple divider only. */
00260     if (clk == CY2239X_CLKE) {
00261         if (val == 0 || (val >= 2 && val <= 4)) {
00262             if (val == 4) {
00263                 val = 1;
00264             }
00265             /* The two lower bits of register 0x0F define the Clock E divider. */
00266             reg[0] = 0x0F;
00267             if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
00268                 reg[1] &= ~0x03;
00269                 reg[1] |= (u_char) val;
00270                 TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
00271                 return 0;
00272             }
00273         }
00274         return -1;
00275     }
00276 
00277     if (val > 0 && val < 128) {
00278         /* Clock A and B have two selectable dividers. */
00279         if (clk <= CY2239X_CLKB) {
00280             reg[0] = 0x08 + clk * 2 + sel;
00281         }
00282         else {
00283             reg[0] = 0x08 + clk + 2;
00284         }
00285         if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
00286             reg[1] &= ~0x7F;
00287             reg[1] |= (u_char) val;
00288             TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
00289             return 0;
00290         }
00291     }
00292     return -1;
00293 }
00294 
00321 int Cy2239xPllEnable(int pll, int fctrl, int ena)
00322 {
00323     int rc = -1;
00324     u_char reg[2];
00325 
00326     if (pll) {
00327         if (pll == CY2239X_PLL1) {
00328             /* PLL1 can be switched by the external control inputs. */
00329             reg[0] = 0x42 + fctrl * 3;
00330         }
00331         else if (pll == CY2239X_PLL2) {
00332             reg[0] = 0x13;
00333         }
00334         else if (pll == CY2239X_PLL3) {
00335             reg[0] = 0x16;
00336         }
00337 
00338         /* Set bit 6 of the third PLL register. */
00339         if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 1, NUT_WAIT_INFINITE) == 1) {
00340             rc = (reg[1] & 0x40) != 0;
00341             if (ena == 1) {
00342                 reg[1] |= 0x40;
00343             }
00344             else if (ena == 0) {
00345                 reg[1] &= ~0x40;
00346             }
00347             else {
00348                 return rc;
00349             }
00350             TwMasterTransact(I2C_SLA_PLL, reg, 2, 0, 0, NUT_WAIT_INFINITE);
00351         }
00352     }
00353     return rc;
00354 }
00355 
00376 u_long Cy2239xPllGetFreq(int pll, int fctrl)
00377 {
00378     u_long rc = NUT_PLL_FREF;
00379     u_char loc;
00380     u_char reg[3];
00381 
00382     if (pll) {
00383         if (pll == CY2239X_PLL1) {
00384             /* PLL1 can be switched by the external control inputs. */
00385             loc = 0x40 + fctrl * 3;
00386         }
00387         else if (pll == CY2239X_PLL2) {
00388             loc = 0x11;
00389         }
00390         else if (pll == CY2239X_PLL3) {
00391             loc = 0x14;
00392         }
00393         /* Read the three PLL specific registers. */
00394         if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, reg, 3, NUT_WAIT_INFINITE) != 3) {
00395             rc = 0;
00396         }
00397         else {
00398             rc = PllFreq(reg);
00399         }
00400     }
00401     return rc;
00402 }
00403 
00433 int Cy2239xPllSetFreq(int pll, int fctrl, u_int pval, u_int poff, u_int qval, u_int fval)
00434 {
00435     u_char reg[4];
00436     int ena;
00437 
00438     if (pll) {
00439         /* Determine the location of the PLL specific registers. */
00440         if (pll == CY2239X_PLL1) {
00441             reg[0] = 0x40 + fctrl * 3;
00442         }
00443         else if (pll == CY2239X_PLL2) {
00444             reg[0] = 0x11;
00445         }
00446         else if (pll == CY2239X_PLL3) {
00447             reg[0] = 0x14;
00448         }
00449 
00450         /* Read the three PLL specific registers. */
00451         if (TwMasterTransact(I2C_SLA_PLL, reg, 1, &reg[1], 3, NUT_WAIT_INFINITE) == 3) {
00452             /* The register is updated immediately. Disable the PLL to avoid
00453              * out of bounds settings. */
00454             if ((ena = Cy2239xPllEnable(pll, fctrl, 0)) >= 0) {
00455                 reg[1] = (u_char) qval;
00456                 reg[2] = (u_char) pval;
00457                 reg[3] &= 0x80; /* Clear all except the divider select. */
00458                 reg[3] |= (u_char)(pval >> 8) & 0x03;
00459                 reg[3] |= (poff & 1) << 2;
00460                 reg[3] |= (fval & 7) << 3;
00461                 TwMasterTransact(I2C_SLA_PLL, reg, 4, 0, 0, NUT_WAIT_INFINITE);
00462                 Cy2239xPllEnable(pll, fctrl, ena);
00463             }
00464             return 0;
00465         }
00466     }
00467     return -1;
00468 }
00469 
00499 u_long Cy2239xGetFreq(int clk, int fctrl)
00500 {
00501     u_long rc;
00502     u_long d;
00503     u_char loc;
00504     u_char reg;
00505     u_char clk_reg[8];
00506     u_char pll_reg[3];
00507     int pll;
00508 
00509     /* Read clock registers 0x08 - 0x0F. */
00510     loc = 0x08;
00511     if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, clk_reg, 8, NUT_WAIT_INFINITE) != 8) {
00512         return 0;
00513     }
00514 
00515     /* 
00516      * Get the PLL index for the specified output divider. Index 0 
00517      * specifies the reference clock, while 1 to 3 specify PLL1 to 
00518      * PLL3 resp.
00519      */
00520     if (clk == CY2239X_CLKE) {
00521         /* ClkE is fixed to PLL1. */
00522         pll = CY2239X_PLL1;
00523     } else {
00524         /* Register 0x0E contains the PLL index for ClkA-ClkD. */
00525         pll = (clk_reg[6] >> (2 * clk)) & 0x03;
00526     }
00527 
00528     /*
00529      * We got the PLL index. Now let's determine the PLL frequency.
00530      */
00531     if (pll == CY2239X_REF) {
00532         /* Index 0 means reference clock. */
00533         rc = NUT_PLL_FREF;
00534     } else {
00535         if (pll == CY2239X_PLL1) {
00536             /* PLL1 can be switched by the external control inputs. */
00537             loc = 0x40 + fctrl * 3;
00538         }
00539         else if (pll == CY2239X_PLL2) {
00540             loc = 0x11;
00541         }
00542         else if (pll == CY2239X_PLL3) {
00543             loc = 0x14;
00544         }
00545         /* Read the three PLL specific registers. */
00546         if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, pll_reg, 3, NUT_WAIT_INFINITE) != 3) {
00547             return 0;
00548         }
00549         rc = PllFreq(pll_reg);
00550     }
00551 
00552     /*
00553      * At this point we got the divider input frequency. Now we retrieve 
00554      * the divider value.
00555      */
00556     if (clk <= CY2239X_CLKB) {
00557         /* Clock A and B provide two dividers, selected by the frequency
00558          * control input. */
00559         if (pll == CY2239X_PLL1) {
00560             /* For PLL1 we already read the register. */
00561             reg = pll_reg[2];
00562         }
00563         else {
00564             /* For PLL2 and PLL3 we read the register now. */
00565             loc = 0x42 + fctrl * 3;
00566             if (TwMasterTransact(I2C_SLA_PLL, &loc, 1, &reg, 1, NUT_WAIT_INFINITE) != 1) {
00567                 return 0;
00568             }
00569         }
00570         d = clk_reg[clk * 2 + (reg >> 7)] & 0x7F;
00571     }
00572     else if (clk == CY2239X_CLKE) {
00573         /* Get divider for Clock E. */
00574         d = clk_reg[7] & 3;
00575         if (d == 1) {
00576             d = 4;
00577         }
00578     }
00579     else {
00580         /* Get divider for Clock C and D. */
00581         d = clk_reg[clk + 2] & 0x7F;
00582     }
00583 
00584     /*
00585      * Finally divide the input frequency. A divider value of zero means
00586      * that the output is switched off.
00587      */
00588     if (d) {
00589         rc /= d;
00590     } else {
00591         rc = 0;
00592     }
00593     return rc;
00594 }
00595 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/