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

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