satip-axe/kernel/drivers/stm/clocks/clock-stx7108.c

2246 lines
63 KiB
C

/*****************************************************************************
*
* File name : clock-stx7108.c
* Description : Low Level API - HW specific implementation
*
* COPYRIGHT (C) 2009 STMicroelectronics - All Rights Reserved
* May be copied or modified under the terms of the GNU General Public
* License V2 __ONLY__. See linux/COPYING for more information.
*
*****************************************************************************/
/* ----- Modification history (most recent first)----
11/mar/10 fabrice.charpentier@st.com
Added CKGA0 & A1 PLL1 (PLL800) set rate feature.
03/mar/10 fabrice.charpentier@st.com
CLKAx_PLL0, CLKB_FS0, CLKB_FS1, CLKC_FS0 identifiers removed.
CLKA_SH4L2_ICK, CLKA_SH4_ICK, & CLKA_IC_TS_DMA set as ALWAYS ENABLED.
12/feb/10 fabrice.charpentier@st.com
clkgenc_enable()/clkgenc_disable()/clkgenc_xable_fsyn() revisited.
15/dec/09 fabrice.charpent/francesco.virlinzi@st.com
Bug fix in clkgenax_recalc() for CLKA1_PLL0LS.
Replaced REGISTER_OPS by
_CLK_OPS macro. Added LLA_VERSION in 'clock-stx7108.h'.
20/nov/09 francesco.virlinzi@st.com
ClockGenA/B/C managed as bank
03/nov/09 fabrice.charpentier@st.com
Updated clkgenaX_set_rate() to allow PLL0 setup.
clkgenb_observe() bug fix.
06/oct/09 fabrice.charpentier@st.com
Linux feedbacks integration + fixes.
29/sep/09 fabrice.charpentier@st.com
Changes due to tests on first cut1.0 samples.
01/sep/09 fabrice.charpentier@st.com
Revisited on first Linux feedback.
21/aug/09 fabrice.charpentier@st.com
Revisited. Enhancements & fixes.
23/jun/09 fabrice.charpentier@st.com
Revisited. Syconf change to support 7108.
01/may/09 benjamin.colson@st.com
Original version
*/
/* Includes --------------------------------------------------------------- */
#include <linux/stm/stx7108.h>
#include <linux/stm/clk.h>
#include <linux/io.h>
#include <linux/delay.h>
#include "clock-stx7108.h"
#include "clock-regs-stx7108.h"
#include "clock-oslayer.h"
#include "clock-common.h"
#include "clock-utils.h"
static int clkgena1_observe(clk_t *clk_p, unsigned long *div_p);
static int clkgena0_observe(clk_t *clk_p, unsigned long *div_p);
static int clkgenb_observe(clk_t *clk_p, unsigned long *div_p);
static int clkgenax_set_parent(clk_t *clk_p, clk_t *src_p);
static int clkgenb_set_parent(clk_t *clk_p, clk_t *src_p);
static int clkgenc_set_parent(clk_t *clk_p, clk_t *src_p);
static int clkgena1_set_rate(clk_t *clk_p, unsigned long freq);
static int clkgena0_set_rate(clk_t *clk_p, unsigned long freq);
static int clkgenb_set_rate(clk_t *clk_p, unsigned long freq);
static int clkgenc_set_rate(clk_t *clk_p, unsigned long freq);
static int clkgenax_set_div(clk_t *clk_p, unsigned long *div_p);
static int clkgenb_set_div(clk_t *clk_p, unsigned long *div_p);
static int clkgenb_set_fsclock(clk_t *clk_p, unsigned long freq);
static int clkgenax_recalc(clk_t *clk_p);
static int clkgenb_recalc(clk_t *clk_p);
static int clkgenb_fsyn_recalc(clk_t *clk_p);
static int clkgenc_recalc(clk_t *clk_p);
static int clkgend_recalc(clk_t *clk_p);
static int clkgene_recalc(clk_t *clk_p); /* Added to get infos for USB */
static int clkgenax_enable(clk_t *clk_p);
static int clkgenb_enable(clk_t *clk_p);
static int clkgenc_enable(clk_t *clk_p);
static int clkgenax_disable(clk_t *clk_p);
static int clkgenb_disable(clk_t *clk_p);
static int clkgenc_disable(clk_t *clk_p);
static unsigned long clkgena1_get_measure(clk_t *clk_p);
static unsigned long clkgena0_get_measure(clk_t *clk_p);
static int clkgentop_init(clk_t *clk_p);
static int clkgenax_init(clk_t *clk_p);
static int clkgenb_init(clk_t *clk_p);
static int clkgenc_init(clk_t *clk_p);
static int clkgend_init(clk_t *clk_p);
static int clkgene_init(clk_t *clk_p);
/* Boards top input clocks. */
#define SYS_CLKIN 30
#define SYS_CLKALT 30 /* ALT input. Assumed 30Mhz */
_CLK_OPS(clkgentop,
"Top clocks",
clkgentop_init,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL, /* No measure function */
NULL /* No observation point */
);
_CLK_OPS(clkgena0,
"Clockgen A 0/left",
clkgenax_init,
clkgenax_set_parent,
clkgena0_set_rate,
clkgenax_recalc,
clkgenax_enable,
clkgenax_disable,
clkgena0_observe,
clkgena0_get_measure,
"PIO6[4]" /* Observation point */
);
_CLK_OPS(clkgena1,
"Clockgen A 1/right",
clkgenax_init,
clkgenax_set_parent,
clkgena1_set_rate,
clkgenax_recalc,
clkgenax_enable,
clkgenax_disable,
clkgena1_observe,
clkgena1_get_measure,
"PIO6[6]" /* Observation point */
);
_CLK_OPS(clkgenb,
"Clockgen B/video",
clkgenb_init,
clkgenb_set_parent,
clkgenb_set_rate,
clkgenb_recalc,
clkgenb_enable,
clkgenb_disable,
clkgenb_observe,
NULL, /* No measure function */
"PIO23[5]" /* Observation point. There is also PIO23[4] */
);
_CLK_OPS(clkgenc,
"Clockgen C/audio",
clkgenc_init,
clkgenc_set_parent,
clkgenc_set_rate,
clkgenc_recalc,
clkgenc_enable,
clkgenc_disable,
NULL,
NULL, /* No measure function */
NULL /* No observation point */
);
_CLK_OPS(clkgend,
"Clockgen D/DDRSS",
clkgend_init,
NULL,
NULL,
clkgend_recalc,
NULL,
NULL,
NULL,
NULL, /* No measure function */
NULL /* No observation point */
);
_CLK_OPS(clkgene,
"USB",
clkgene_init,
NULL,
NULL,
clkgene_recalc,
NULL,
NULL,
NULL,
NULL, /* No measure function */
NULL /* No observation point */
);
/* Physical clocks description */
clk_t clk_clocks[] = {
/* Top level clocks */
_CLK(CLK_SYSIN, &clkgentop, 0,
CLK_RATE_PROPAGATES | CLK_ALWAYS_ENABLED),
_CLK(CLK_SYSALT, &clkgentop, 0,
CLK_RATE_PROPAGATES | CLK_ALWAYS_ENABLED),
/* Clockgen A 0/Left */
_CLK_P(CLKA0_REF, &clkgena0, 0,
CLK_RATE_PROPAGATES | CLK_ALWAYS_ENABLED, &clk_clocks[CLK_SYSIN]),
_CLK_P(CLKA0_PLL0HS, &clkgena0, 1000000000,
CLK_RATE_PROPAGATES, &clk_clocks[CLKA0_REF]),
_CLK_P(CLKA0_PLL0LS, &clkgena0, 500000000,
CLK_RATE_PROPAGATES, &clk_clocks[CLKA0_PLL0HS]),
_CLK_P(CLKA0_PLL1, &clkgena0, 450000000,
CLK_RATE_PROPAGATES, &clk_clocks[CLKA0_REF]),
_CLK(CLKA_DMU_PREPROC, &clkgena0, 200000000, 0),
_CLK(CLKA_IC_GPU, &clkgena0, 250000000, 0),
_CLK(CLKA_SH4L2_ICK, &clkgena0, 500000000, CLK_ALWAYS_ENABLED),
_CLK(CLKA_SH4_ICK, &clkgena0, 500000000, CLK_ALWAYS_ENABLED),
_CLK(CLKA_LX_DMU_CPU, &clkgena0, 450000000, 0),
_CLK(CLKA_LX_AUD_CPU, &clkgena0, 500000000, 0),
_CLK(CLKA_LX_SEC_CPU, &clkgena0, 500000000, 0),
_CLK(CLKA_IC_CPU, &clkgena0, 500000000, 0),
_CLK(CLKA_SYS_EMISS, &clkgena0, 100000000, 0),
_CLK(CLKA_PCI, &clkgena0, 33000000, 0),
_CLK(CLKA_SYS_MMC_SS, &clkgena0, 100000000, 0),
_CLK(CLKA_CARD_MMC_SS, &clkgena0, 50000000, 0),
_CLK(CLKA_ETH_PHY_1, &clkgena0, 50000000, 0),
_CLK(CLKA_IC_GMAC_1, &clkgena0, 100000000, 0),
_CLK(CLKA_IC_STNOC, &clkgena0, 450000000, 0),
_CLK(CLKA_IC_DMU, &clkgena0, 250000000, 0),
/* Clockgen A 1/Right */
_CLK_P(CLKA1_REF, &clkgena1, 0,
CLK_RATE_PROPAGATES | CLK_ALWAYS_ENABLED, &clk_clocks[CLK_SYSIN]),
_CLK_P(CLKA1_PLL0HS, &clkgena1, 666666666,
CLK_RATE_PROPAGATES, &clk_clocks[CLKA1_REF]),
_CLK_P(CLKA1_PLL0LS, &clkgena1, 333333333,
CLK_RATE_PROPAGATES, &clk_clocks[CLKA1_PLL0HS]),
_CLK_P(CLKA1_PLL1, &clkgena1, 800000000,
CLK_RATE_PROPAGATES, &clk_clocks[CLKA1_REF]),
_CLK(CLKA_SLIM_FDMA_0, &clkgena1, 400000000, 0),
_CLK(CLKA_SLIM_FDMA_1, &clkgena1, 400000000, 0),
_CLK(CLKA_SLIM_FDMA_2, &clkgena1, 400000000, 0),
_CLK(CLKA_HQ_VDP_PROC, &clkgena1, 333000000, 0),
_CLK(CLKA_IC_COMPO_DISP, &clkgena1, 200000000, 0),
_CLK(CLKA_BLIT_PROC, &clkgena1, 266666666, 0),
_CLK(CLKA_SECURE, &clkgena1, 200000000, 0),
_CLK(CLKA_IC_TS_DMA, &clkgena1, 200000000, CLK_ALWAYS_ENABLED),
_CLK(CLKA_ETH_PHY_0, &clkgena1, 50000000, 0),
_CLK(CLKA_IC_GMAC_0, &clkgena1, 100000000, 0),
_CLK(CLKA_IC_REG_LP_OFF, &clkgena1, 100000000, 0),
_CLK(CLKA_IC_REG_LP_ON, &clkgena1, 100000000, CLK_ALWAYS_ENABLED),
_CLK(CLKA_TP, &clkgena1, 333333333, 0),
_CLK(CLKA_AUX_DISP_PIPE, &clkgena1, 200000000, 0),
_CLK(CLKA_PRV_T1_BUS, &clkgena1, 50000000, 0),
_CLK(CLKA_IC_BDISP, &clkgena1, 200000000, 0),
/* Clockgen B */
_CLK(CLKB_REF, &clkgenb, 0,
CLK_RATE_PROPAGATES | CLK_ALWAYS_ENABLED),
_CLK_P(CLKB_FS0_CH1, &clkgenb, 0,
CLK_RATE_PROPAGATES, &clk_clocks[CLKB_REF]),
_CLK_P(CLKB_FS0_CH2, &clkgenb, 36864000,
CLK_RATE_PROPAGATES, &clk_clocks[CLKB_REF]),
_CLK_P(CLKB_FS0_CH3, &clkgenb, 32768000,
CLK_RATE_PROPAGATES, &clk_clocks[CLKB_REF]),
_CLK_P(CLKB_FS0_CH4, &clkgenb, 0,
CLK_RATE_PROPAGATES, &clk_clocks[CLKB_REF]),
_CLK_P(CLKB_FS1_CH1, &clkgenb, 0,
CLK_RATE_PROPAGATES, &clk_clocks[CLKB_REF]),
_CLK_P(CLKB_FS1_CH2, &clkgenb, 0,
CLK_RATE_PROPAGATES, &clk_clocks[CLKB_REF]),
_CLK_P(CLKB_FS1_CH3, &clkgenb, 48000000,
CLK_RATE_PROPAGATES, &clk_clocks[CLKB_REF]),
_CLK_P(CLKB_FS1_CH4, &clkgenb, 48000000,
CLK_RATE_PROPAGATES, &clk_clocks[CLKB_REF]),
_CLK_P(CLKB_FS0_CHAN0, &clkgenb, 0,
0, &clk_clocks[CLKB_FS0_CH1]),
_CLK(CLKB_HD_TO_VID_DIV, &clkgenb, 0, CLK_RATE_PROPAGATES),
_CLK(CLKB_SD_TO_VID_DIV, &clkgenb, 0, CLK_RATE_PROPAGATES),
_CLK_P(CLKB_DSS, &clkgenb, 36864,
0, &clk_clocks[CLKB_FS0_CH2]),
_CLK_P(CLKB_DAA, &clkgenb, 32768,
0, &clk_clocks[CLKB_FS0_CH3]),
_CLK_P(CLKB_CLK48, &clkgenb, 48000000,
0, &clk_clocks[CLKB_FS1_CH3]),
_CLK_P(CLKB_CCSC, &clkgenb, 0,
0, &clk_clocks[CLKB_FS1_CH2]),
_CLK_P(CLKB_LPC, &clkgenb, 46875,
0, &clk_clocks[CLKB_FS1_CH4]),
_CLK(CLKB_PIX_HD, &clkgenb, 0, 0), /* CLK_OUT_0 */
_CLK(CLKB_DISP_HD, &clkgenb, 0, 0), /* CLK_OUT_1 */
_CLK(CLKB_DISP_PIP, &clkgenb, 0, 0), /* CLK_OUT_2 */
_CLK(CLKB_GDP1, &clkgenb, 0, 0), /* CLK_OUT_3 */
_CLK(CLKB_GDP2, &clkgenb, 0, 0), /* CLK_OUT_4 */
_CLK(CLKB_DISP_ID, &clkgenb, 0, 0), /* CLK_OUT_5 */
_CLK(CLKB_PIX_SD, &clkgenb, 0, 0), /* CLK_OUT_6 */
_CLK(CLKB_656, &clkgenb, 0, 0), /* CLK_OUT_7 */
/* Clockgen C (AUDIO) */
_CLK(CLKC_REF, &clkgenc, 0,
CLK_RATE_PROPAGATES | CLK_ALWAYS_ENABLED),
_CLK_P(CLKC_FS0_CH1, &clkgenc, 0, 0, &clk_clocks[CLKC_REF]),
_CLK_P(CLKC_FS0_CH2, &clkgenc, 0, 0, &clk_clocks[CLKC_REF]),
_CLK_P(CLKC_FS0_CH3, &clkgenc, 0, 0, &clk_clocks[CLKC_REF]),
_CLK_P(CLKC_FS0_CH4, &clkgenc, 0, 0, &clk_clocks[CLKC_REF]),
/* Clockgen D (DDR-subsystem) */
_CLK_P(CLKD_REF, &clkgend, 30000000,
CLK_RATE_PROPAGATES | CLK_ALWAYS_ENABLED, &clk_clocks[CLK_SYSIN]),
_CLK_P(CLKD_IC_DDRCTRL, &clkgend, 266000000,
0, &clk_clocks[CLKD_REF]),
_CLK_P(CLKD_DDR, &clkgend, 1066000000,
0, &clk_clocks[CLKD_IC_DDRCTRL]),
/* Clockgen E (USB) */
_CLK_P(CLKE_REF, &clkgene, 30000000,
CLK_ALWAYS_ENABLED, &clk_clocks[CLK_SYSIN]),
};
SYSCONF(SYS_CFG_BANK1, 4, 8, 15);
SYSCONF(SYS_CFG_BANK1, 4, 16, 23);
SYSCONF(SYS_CFG_BANK1, 4, 24, 26);
SYSCONF(SYS_CFG_BANK2, 6, 16, 17);
SYSCONF(SYS_CFG_BANK2, 6, 24, 25);
SYSCONF(SYS_CFG_BANK2, 16, 20, 20);
SYSCONF(SYS_CFG_BANK2, 16, 22, 22);
SYSCONF(SYS_CFG_BANK3, 0, 0, 7); /* stop clock: one bit for each clock */
SYSCONF(SYS_CFG_BANK3, 0, 12, 19); /* sel clock: one bit for each clock */
SYSCONF(SYS_CFG_BANK3, 1, 0, 15); /* div clock: two bits or each clock */
SYSCONF(SYS_CFG_BANK3, 2, 8, 13);/* [c_out_4_7][c_out_0_3][grp_sel][c_selt] */
SYSCONF(SYS_CFG_BANK4, 8, 20, 21);
SYSCONF(SYS_CFG_BANK4, 14, 5, 5);
int __init plat_clk_init(void)
{
int ret = 0;
SYSCONF_CLAIM(SYS_CFG_BANK1, 4, 8, 15);
SYSCONF_CLAIM(SYS_CFG_BANK1, 4, 16, 23);
SYSCONF_CLAIM(SYS_CFG_BANK1, 4, 24, 26);
SYSCONF_CLAIM(SYS_CFG_BANK2, 6, 16, 17);
SYSCONF_CLAIM(SYS_CFG_BANK2, 6, 24, 25);
SYSCONF_CLAIM(SYS_CFG_BANK2, 16, 20, 20);
SYSCONF_CLAIM(SYS_CFG_BANK2, 16, 22, 22);
/* stop clock: 1 bit for each clock */
SYSCONF_CLAIM(SYS_CFG_BANK3, 0, 0, 7);
/* sel clock: 2 bits for each clock */
SYSCONF_CLAIM(SYS_CFG_BANK3, 0, 12, 19);
/* div clock: two bits or each clock */
SYSCONF_CLAIM(SYS_CFG_BANK3, 1, 0, 15);
/* Clock_select */
SYSCONF_CLAIM(SYS_CFG_BANK3, 2, 8, 13);
SYSCONF_CLAIM(SYS_CFG_BANK4, 8, 20, 21);
SYSCONF_CLAIM(SYS_CFG_BANK4, 14, 5, 5);
ret = clk_register_table(clk_clocks, CLKB_REF, 1);
ret |= clk_register_table(&clk_clocks[CLKB_REF],
ARRAY_SIZE(clk_clocks) - CLKB_REF, 0);
return ret;
}
/******************************************************************************
Top level clocks group
******************************************************************************/
/* ========================================================================
Name: clkgentop_init
Description: Read HW status to initialize 'clk_t' structure.
Returns: 'clk_err_t' error code.
======================================================================== */
static int clkgentop_init(clk_t *clk_p)
{
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
/* Top recalc function */
switch (clk_p->id) {
case CLK_SYSIN:
clk_p->rate = SYS_CLKIN * 1000000;
break;
case CLK_SYSALT:
clk_p->rate = SYS_CLKALT * 1000000;
break;
default:
return CLK_ERR_BAD_PARAMETER;
}
clk_p->parent = NULL;
return CLK_ERR_NONE;
}
/******************************************************************************
CLOCKGEN A (CPU+interco+comms) clocks group
******************************************************************************/
static inline int clkgenax_get_bank(int clk_id)
{
return ((clk_id >= CLKA1_REF) ? 1 : 0);
}
static inline unsigned long clkgenax_get_base_address(int clk_id)
{
return (clkgenax_get_bank(clk_id) == 0 ?
CKGA0_BASE_ADDRESS : CKGA1_BASE_ADDRESS);
}
/* ========================================================================
Name: clkgenax_get_index
Description: Returns index of given clockgenA clock and source reg infos
Returns: idx==-1 if error, else >=0
======================================================================== */
static int clkgenax_get_index(int clkid, unsigned long *srcreg, int *shift)
{
int idx;
/* If 'clkid' is from A1, let's shift to A0 */
clkid = (clkgenax_get_bank(clkid) ?
(clkid - CLKA_NOT_USED_1_00 + CLKA_NOT_USED_0_00) : clkid);
/* Warning: This function assumes clock IDs are perfectly
following real implementation order. Each "hole" has therefore
to be filled with "CLKx_NOT_USED_y" */
if (clkid < CLKA_NOT_USED_0_00 || clkid > CLKA_IC_DMU)
return -1;
idx = clkid - CLKA_NOT_USED_0_00; /* from 0 to 15 are ok */
if (idx <= 15) {
*srcreg = CKGA_CLKOPSRC_SWITCH_CFG;
*shift = idx * 2;
} else {
*srcreg = CKGA_CLKOPSRC_SWITCH_CFG2;
*shift = (idx - 16) * 2;
}
return idx;
}
/* ========================================================================
Name: clkgenax_set_parent
Description: Set clock source for clockgenA when possible
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenax_set_parent(clk_t *clk_p, clk_t *src_p)
{
unsigned long clk_src, val;
int idx, shift;
unsigned long srcreg, base;
if (!clk_p || !src_p)
return CLK_ERR_BAD_PARAMETER;
/* check if they are on the same bank */
if (clkgenax_get_bank(clk_p->id) != clkgenax_get_bank(src_p->id))
return CLK_ERR_BAD_PARAMETER;
switch (src_p->id) {
case CLKA0_REF:
case CLKA1_REF:
clk_src = 0;
break;
case CLKA0_PLL0LS:
case CLKA0_PLL0HS:
case CLKA1_PLL0LS:
case CLKA1_PLL0HS:
clk_src = 1;
break;
case CLKA0_PLL1:
case CLKA1_PLL1:
clk_src = 2;
break;
default:
return CLK_ERR_BAD_PARAMETER;
}
idx = clkgenax_get_index(clk_p->id, &srcreg, &shift);
if (idx == -1)
return CLK_ERR_BAD_PARAMETER;
base = clkgenax_get_base_address(clk_p->id);
val = CLK_READ(base + srcreg) & ~(0x3 << shift);
val = val | (clk_src << shift);
CLK_WRITE(base + srcreg, val);
clk_p->parent = &clk_clocks[src_p->id];
return clkgenax_recalc(clk_p);
}
/* ========================================================================
Name: clkgenax_identify_parent
Description: Identify parent clock for clockgen A clocks.
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenax_identify_parent(clk_t *clk_p)
{
int idx;
unsigned long src_sel;
unsigned long srcreg;
unsigned long base_addr, base_id;
int shift;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if ((clk_p->id >= CLKA0_REF && clk_p->id <= CLKA0_PLL1) ||
(clk_p->id >= CLKA1_REF && clk_p->id <= CLKA1_PLL1))
/* statically initialized */
return 0;
/* Which divider to setup ? */
idx = clkgenax_get_index(clk_p->id, &srcreg, &shift);
if (idx == -1)
return CLK_ERR_BAD_PARAMETER;
/* Identifying source */
base_addr = clkgenax_get_base_address(clk_p->id);
base_id = ((clk_p->id >= CLKA1_REF) ? CLKA1_REF : CLKA0_REF);
src_sel = (CLK_READ(base_addr + srcreg) >> shift) & 0x3;
switch (src_sel) {
case 0:
clk_p->parent = &clk_clocks[base_id + 0]; /* CLKAx_REF */
break;
case 1:
if (idx <= 3)
/* CLKAx_PLL0HS */
clk_p->parent = &clk_clocks[base_id + 1];
else /* CLKAx_PLL0LS */
clk_p->parent = &clk_clocks[base_id + 2];
break;
case 2:
clk_p->parent = &clk_clocks[base_id + 3]; /* CLKAx_PLL1 */
break;
case 3:
clk_p->parent = NULL;
clk_p->rate = 0;
break;
}
return 0;
}
/* ========================================================================
Name: clkgenax_xable_pll
Description: Enable/disable PLL
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenax_xable_pll(clk_t *clk_p, int enable)
{
unsigned long val, base_addr;
int bit, err = 0;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id != CLKA0_PLL0HS && clk_p->id != CLKA0_PLL1 &&
clk_p->id != CLKA1_PLL0HS && clk_p->id != CLKA1_PLL1)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id == CLKA0_PLL1 || clk_p->id == CLKA1_PLL1)
bit = 1;
else
bit = 0;
base_addr = clkgenax_get_base_address(clk_p->id);
val = CLK_READ(base_addr + CKGA_POWER_CFG);
if (enable)
val &= ~(1 << bit);
else
val |= (1 << bit);
CLK_WRITE(base_addr + CKGA_POWER_CFG, val);
if (enable)
err = clkgenax_recalc(clk_p);
else
clk_p->rate = 0;
return err;
}
/* ========================================================================
Name: clkgenax_enable
Description: Enable clock
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenax_enable(clk_t *clk_p)
{
int err;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (!clk_p->parent)
/* Unsupported. Init must be called first. */
return CLK_ERR_BAD_PARAMETER;
/* PLL power up */
if ((clk_p->id >= CLKA0_PLL0HS && clk_p->id <= CLKA0_PLL1) ||
(clk_p->id >= CLKA1_PLL0HS && clk_p->id <= CLKA1_PLL1))
return clkgenax_xable_pll(clk_p, 1);
err = clkgenax_set_parent(clk_p, clk_p->parent);
/* clkgenax_set_parent() is performing also a recalc() */
return err;
}
/* ========================================================================
Name: clkgenax_disable
Description: Disable clock
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenax_disable(clk_t *clk_p)
{
unsigned long val;
int idx, shift;
unsigned long srcreg;
unsigned long base_address;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
/* Can this clock be disabled ? */
if (clk_p->flags & CLK_ALWAYS_ENABLED)
return 0;
/* PLL power down */
if ((clk_p->id >= CLKA0_PLL0HS && clk_p->id <= CLKA0_PLL1) ||
(clk_p->id >= CLKA1_PLL0HS && clk_p->id <= CLKA1_PLL1))
return clkgenax_xable_pll(clk_p, 0);
idx = clkgenax_get_index(clk_p->id, &srcreg, &shift);
if (idx == -1)
return CLK_ERR_BAD_PARAMETER;
/* Disabling clock */
base_address = clkgenax_get_base_address(clk_p->id);
val = CLK_READ(base_address + srcreg) & ~(0x3 << shift);
val = val | (3 << shift); /* 3 = STOP clock */
CLK_WRITE(base_address + srcreg, val);
clk_p->rate = 0;
return 0;
}
/* ========================================================================
Name: clkgenax_set_div
Description: Set divider ratio for clockgenA when possible
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenax_set_div(clk_t *clk_p, unsigned long *div_p)
{
int idx;
unsigned long div_cfg = 0;
unsigned long srcreg, offset;
int shift;
unsigned long base_address;
if (!clk_p || !clk_p->parent)
return CLK_ERR_BAD_PARAMETER;
/* Computing divider config */
div_cfg = (*div_p - 1) & 0x1F;
/* Which divider to setup ? */
idx = clkgenax_get_index(clk_p->id, &srcreg, &shift);
if (idx == -1)
return CLK_ERR_BAD_PARAMETER;
/* Now according to parent, let's write divider ratio */
if (clk_p->parent->id >= CLKA1_REF)
offset = CKGA_SOURCE_CFG(clk_p->parent->id - CLKA1_REF);
else
offset = CKGA_SOURCE_CFG(clk_p->parent->id - CLKA0_REF);
base_address = clkgenax_get_base_address(clk_p->id);
CLK_WRITE(base_address + offset + (4 * idx), div_cfg);
return 0;
}
/* ========================================================================
Name: clkgena0_set_rate
Description: Set clock frequency
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgena0_set_rate(clk_t *clk_p, unsigned long freq)
{
unsigned long div, mdiv, ndiv, pdiv, data;
int err;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id < CLKA0_PLL0HS || clk_p->id > CLKA_IC_DMU)
return CLK_ERR_BAD_PARAMETER;
/* We need a parent for these clocks */
if (!clk_p->parent)
return CLK_ERR_INTERNAL;
switch (clk_p->id) {
case CLKA0_PLL0HS:
case CLKA0_PLL0LS:
if (clk_p->id == CLKA0_PLL0LS)
freq = freq * 2;
err = clk_pll1600_get_params(clk_clocks[CLKA0_REF].rate,
freq, &mdiv, &ndiv);
if (err != 0)
break;
data = CLK_READ(CKGA0_BASE_ADDRESS + CKGA_PLL0_CFG) &
~(0xff << 8) & ~0x7;
data = data | ((ndiv & 0xff) << 8) | (mdiv & 0x7);
CLK_WRITE(CKGA0_BASE_ADDRESS + CKGA_PLL0_CFG, data);
if (clk_p->id == CLKA0_PLL0LS)
err = clkgenax_recalc(&clk_clocks[CLKA0_PLL0HS]);
break;
case CLKA0_PLL1:
err = clk_pll800_get_params(clk_clocks[CLKA0_REF].rate,
freq, &mdiv, &ndiv, &pdiv);
if (err != 0)
break;
data = CLK_READ(CKGA0_BASE_ADDRESS + CKGA_PLL1_CFG)
& 0xfff80000;
data |= (pdiv<<16 | ndiv<<8 | mdiv);
CLK_WRITE(CKGA0_BASE_ADDRESS + CKGA_PLL1_CFG, data);
break;
case CLKA_DMU_PREPROC ... CLKA_IC_DMU:
div = clk_p->parent->rate / freq;
err = clkgenax_set_div(clk_p, &div);
break;
default:
return CLK_ERR_BAD_PARAMETER;
}
if (!err)
err = clkgenax_recalc(clk_p);
return err;
}
/* ========================================================================
Name: clkgenax_recalc
Description: Get CKGA programmed clocks frequencies
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenax_recalc(clk_t *clk_p)
{
unsigned long data, ratio;
unsigned long srcreg, offset, base_address;
int shift, err, idx;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (!clk_p->parent)
return CLK_ERR_INTERNAL;
/* Reading clock programmed value */
base_address = clkgenax_get_base_address(clk_p->id);
switch (clk_p->id) {
case CLKA0_REF: /* Clockgen A reference clock */
case CLKA1_REF: /* Clockgen A reference clock */
clk_p->rate = clk_p->parent->rate;
break;
case CLKA0_PLL0HS:
case CLKA1_PLL0HS:
data = CLK_READ(base_address + CKGA_PLL0_CFG);
err =
clk_pll1600_get_rate(clk_p->parent->rate, data & 0x7,
(data >> 8) & 0xff, &clk_p->rate);
return err;
case CLKA0_PLL0LS:
case CLKA1_PLL0LS:
clk_p->rate = clk_p->parent->rate / 2;
return 0;
case CLKA0_PLL1:
case CLKA1_PLL1:
data = CLK_READ(base_address + CKGA_PLL1_CFG);
return clk_pll800_get_rate(clk_p->parent->rate, data & 0xff,
(data >> 8) & 0xff,
(data >> 16) & 0x7, &clk_p->rate);
default:
idx = clkgenax_get_index(clk_p->id, &srcreg, &shift);
if (idx == -1)
return CLK_ERR_BAD_PARAMETER;
/* Now according to source, let's get divider ratio */
if (clk_p->parent->id >= CLKA1_REF)
offset = CKGA_SOURCE_CFG(clk_p->parent->id - CLKA1_REF);
else
offset = CKGA_SOURCE_CFG(clk_p->parent->id - CLKA0_REF);
data = CLK_READ(base_address + offset + (4 * idx));
ratio = (data & 0x1F) + 1;
clk_p->rate = clk_p->parent->rate / ratio;
break;
}
return 0;
}
/* ========================================================================
Name: clkgena0_observe
Description: allows to observe a clock on a SYSACLK_OUT
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgena0_observe(clk_t *clk_p, unsigned long *div_p)
{
unsigned long src = 0;
unsigned long divcfg;
/* WARNING: the obs_table[] must strictly follows clockgen
* enum order taking into account any "holes" (CLKA0_NOT_USED_y)
* filled with 0xff
*/
static const unsigned char obs_table_a0[] = {
0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11,
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19
};
if (!clk_p || !div_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id < CLKA_DMU_PREPROC || clk_p->id > CLKA_IC_DMU)
return CLK_ERR_BAD_PARAMETER;
src = obs_table_a0[clk_p->id - CLKA_DMU_PREPROC];
if (src == 0xff)
return 0;
switch (*div_p) {
case 2:
divcfg = 0;
break;
case 4:
divcfg = 1;
break;
default:
divcfg = 2;
*div_p = 1;
break;
}
CLK_WRITE((CKGA0_BASE_ADDRESS + CKGA_CLKOBS_MUX1_CFG),
(divcfg << 6) | src);
/* Configuring PIO6[4] for clock output */
/* Selecting alternate 3, sysconf6 bank2 */
SYSCONF_WRITE(SYS_CFG_BANK2, 6, 16, 17, 3);
/* Enabling, sysconf16 bank2 */
SYSCONF_WRITE(SYS_CFG_BANK2, 16, 20, 20, 1);
return 0;
}
/* ========================================================================
Name: clkgena0_get_measure
Description: Use internal HW feature (when avail.) to measure clock
Returns: 'clk_err_t' error code.
======================================================================== */
static unsigned long clkgena0_get_measure(clk_t *clk_p)
{
unsigned long src, data;
unsigned long measure;
/* WARNING: the measure_table[] must strictly follows clockgen
* enum order taking into account any "holes" (CLKA_NOT_USED)
* filled with 0xff
*/
static const unsigned char measure_table_a0[] = {
0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11,
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19
};
int i;
if (!clk_p)
return 0;
if (clk_p->id < CLKA_DMU_PREPROC || clk_p->id > CLKA_IC_DMU)
return 0;
src = measure_table_a0[clk_p->id - CLKA_DMU_PREPROC];
if (src == 0xff)
return 0;
measure = 0;
/* Loading the MAX Count 1000 in 30MHz Oscillator Counter */
CLK_WRITE(CKGA0_BASE_ADDRESS + CKGA_CLKOBS_MASTER_MAXCOUNT, 0x3E8);
CLK_WRITE(CKGA0_BASE_ADDRESS + CKGA_CLKOBS_CMD, 3);
/* Selecting clock to observe */
CLK_WRITE(CKGA0_BASE_ADDRESS + CKGA_CLKOBS_MUX1_CFG, (1 << 7) | src);
/* Start counting */
CLK_WRITE(CKGA0_BASE_ADDRESS + CKGA_CLKOBS_CMD, 0);
for (i = 0; i < 10; i++) {
mdelay(10);
data = CLK_READ(CKGA0_BASE_ADDRESS + CKGA_CLKOBS_STATUS);
if (data & 1)
break; /* IT */
}
if (i == 10)
return 0;
/* Reading value */
data = CLK_READ(CKGA0_BASE_ADDRESS + CKGA_CLKOBS_SLAVE0_COUNT);
measure = 30 * data * 1000;
CLK_WRITE(CKGA0_BASE_ADDRESS + CKGA_CLKOBS_CMD, 3);
return measure;
}
/* ========================================================================
Name: clkgenax_init
Description: Read HW status to initialize 'clk_t' structure.
Returns: 'clk_err_t' error code.
======================================================================== */
static int clkgenax_init(clk_t *clk_p)
{
int err;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
err = clkgenax_identify_parent(clk_p);
if (!err)
err = clkgenax_recalc(clk_p);
return err;
}
/******************************************************************************
CLOCKGEN A1 (A right) clocks group
******************************************************************************/
/* ========================================================================
Name: clkgena1_set_rate
Description: Set clock frequency
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgena1_set_rate(clk_t *clk_p, unsigned long freq)
{
unsigned long div, mdiv, ndiv, pdiv, data;
int err;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if ((clk_p->id < CLKA1_PLL0HS) || (clk_p->id > CLKA_IC_BDISP))
return CLK_ERR_BAD_PARAMETER;
/* We need a parent for these clocks */
if (!clk_p->parent)
return CLK_ERR_INTERNAL;
switch (clk_p->id) {
case CLKA1_PLL0HS:
case CLKA1_PLL0LS:
if (clk_p->id == CLKA1_PLL0LS)
freq = freq * 2;
err = clk_pll1600_get_params(clk_clocks[CLKA1_REF].rate,
freq, &mdiv, &ndiv);
if (err != 0)
break;
data = CLK_READ(CKGA1_BASE_ADDRESS + CKGA_PLL0_CFG) &
~(0xff << 8) & ~0x7;
data = data | ((ndiv & 0xff) << 8) | (mdiv & 0x7);
CLK_WRITE(CKGA1_BASE_ADDRESS + CKGA_PLL0_CFG, data);
if (clk_p->id == CLKA1_PLL0LS)
err = clkgenax_recalc(&clk_clocks[CLKA1_PLL0HS]);
break;
case CLKA1_PLL1:
err = clk_pll800_get_params(clk_clocks[CLKA1_REF].rate,
freq, &mdiv, &ndiv, &pdiv);
if (err != 0)
break;
data = CLK_READ(CKGA1_BASE_ADDRESS + CKGA_PLL1_CFG)
& 0xfff80000;
data |= (pdiv<<16 | ndiv<<8 | mdiv);
CLK_WRITE(CKGA1_BASE_ADDRESS + CKGA_PLL1_CFG, data);
break;
case CLKA_SLIM_FDMA_0 ... CLKA_IC_BDISP:
div = clk_p->parent->rate / freq;
err = clkgenax_set_div(clk_p, &div);
break;
default:
return CLK_ERR_BAD_PARAMETER;
}
if (!err)
err = clkgenax_recalc(clk_p);
return err;
}
/* ========================================================================
Name: clkgena1_observe
Description: allows to observe a clock on a SYSACLK_OUT
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgena1_observe(clk_t *clk_p, unsigned long *div_p)
{
unsigned long src = 0;
unsigned long divcfg;
/* WARNING: the obs_table[] must strictly follows clockgen enum order
* taking into account any "holes" (CLKA1_NOT_USED_y) filled
* with 0xff
*/
static const unsigned char obs_table_a1[] = {
0x9, 0xa, 0xb, 0xc, 0xd, 0xff, 0xf, 0x10, 0x11,
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19
};
if (!clk_p || !div_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id < CLKA_SLIM_FDMA_0 || clk_p->id > CLKA_IC_BDISP)
return CLK_ERR_BAD_PARAMETER;
src = obs_table_a1[clk_p->id - CLKA_SLIM_FDMA_0];
if (src == 0xff)
return 0;
switch (*div_p) {
case 2:
divcfg = 0;
break;
case 4:
divcfg = 1;
break;
default:
divcfg = 2;
*div_p = 1;
break;
}
CLK_WRITE((CKGA1_BASE_ADDRESS + CKGA_CLKOBS_MUX1_CFG),
(divcfg << 6) | src);
/* Configuring PIO6[6] for clock output */
/* Selecting alternate 3, sysconf6 bank2 */
SYSCONF_WRITE(SYS_CFG_BANK2, 6, 24, 25, 3);
/* Enabling, sysconf16 bank2 */
SYSCONF_WRITE(SYS_CFG_BANK2, 16, 22, 22, 1);
return 0;
}
/* ========================================================================
Name: clkgena1_get_measure
Description: Use internal HW feature (when avail.) to measure clock
Returns: 'clk_err_t' error code.
======================================================================== */
static unsigned long clkgena1_get_measure(clk_t *clk_p)
{
unsigned long src, data;
unsigned long measure;
/* WARNING: the measure_table[] must strictly follows clockgen
* enum order taking into account any "holes" (CLKA_NOT_USED)
* filled with 0xff
*/
static const unsigned char measure_table_a1[] = {
0x9, 0xa, 0xb, 0xc, 0xd, 0xff, 0x0f, 0x10, 0x11,
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19
};
int i;
if (!clk_p)
return 0;
if (clk_p->id < CLKA_SLIM_FDMA_0 || clk_p->id > CLKA_IC_BDISP)
return 0;
src = measure_table_a1[clk_p->id - CLKA_SLIM_FDMA_0];
if (src == 0xff)
return 0;
measure = 0;
/* Loading the MAX Count 1000 in 30MHz Oscillator Counter */
CLK_WRITE(CKGA1_BASE_ADDRESS + CKGA_CLKOBS_MASTER_MAXCOUNT, 0x3E8);
CLK_WRITE(CKGA1_BASE_ADDRESS + CKGA_CLKOBS_CMD, 3);
/* Selecting clock to observe */
CLK_WRITE(CKGA1_BASE_ADDRESS + CKGA_CLKOBS_MUX1_CFG, (1 << 7) | src);
/* Start counting */
CLK_WRITE(CKGA1_BASE_ADDRESS + CKGA_CLKOBS_CMD, 0);
for (i = 0; i < 10; i++) {
mdelay(10);
data = CLK_READ(CKGA1_BASE_ADDRESS + CKGA_CLKOBS_STATUS);
if (data & 1)
break; /* IT */
}
if (i == 10)
return 0;
/* Reading value */
data = CLK_READ(CKGA1_BASE_ADDRESS + CKGA_CLKOBS_SLAVE0_COUNT);
measure = 30 * data * 1000;
CLK_WRITE(CKGA1_BASE_ADDRESS + CKGA_CLKOBS_CMD, 3);
return measure;
}
/******************************************************************************
CLOCKGEN B/Video
******************************************************************************/
static void clkgenb_unlock(void)
{
CLK_WRITE(CKGB_BASE_ADDRESS + CKGB_LOCK, 0xc0de);
}
static void clkgenb_lock(void)
{
CLK_WRITE(CKGB_BASE_ADDRESS + CKGB_LOCK, 0xc1a0);
}
/* ========================================================================
Name: clkgenb_xable_fsyn
Description: Enable/disable FSYN
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenb_xable_fsyn(clk_t *clk_p, unsigned long enable)
{
unsigned long val, clkout, ctrl, bit, ctrlval;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id < CLKB_FS0_CH1 || clk_p->id > CLKB_FS1_CH4)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id < CLKB_FS1_CH1) {
clkout = CKGB_FS0_CLKOUT_CTRL;
ctrl = CKGB_FS0_CTRL;
} else {
clkout = CKGB_FS1_CLKOUT_CTRL;
ctrl = CKGB_FS1_CTRL;
}
clkgenb_unlock();
/* Powering down/up digital part */
val = CLK_READ(CKGB_BASE_ADDRESS + clkout);
bit = (clk_p->id - CLKB_FS0_CH1) % 4;
if (enable)
val |= (1 << bit);
else
val &= ~(1 << bit);
CLK_WRITE(CKGB_BASE_ADDRESS + clkout, val);
/* Powering down/up analog part */
ctrlval = CLK_READ(CKGB_BASE_ADDRESS + ctrl);
if (enable)
ctrlval |= (1 << 4);
else {
/* If all channels are off then power down FSx */
if ((val & 0xf) == 0)
ctrlval &= ~(1 << 4);
}
CLK_WRITE(CKGB_BASE_ADDRESS + ctrl, ctrlval);
clkgenb_lock();
/* Freq recalc required only if a channel is enabled */
if (enable)
return clkgenb_fsyn_recalc(clk_p);
else
clk_p->rate = 0;
return 0;
}
/* ========================================================================
Name: clkgenb_xable_clock
Description: Enable/disable clock (Clockgen B)
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenb_xable_clock(clk_t *clk_p, unsigned long enable)
{
unsigned long val, bit;
int err = 0;
/* Power en/dis table for clkgen B.
WARNING: enum order !! */
static const unsigned char power_table[] = {
3, 9, 1, 0, 12, 11, 13
};
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id >= CLKB_PIX_HD && clk_p->id <= CLKB_656) {
/* Clocks from "Video Clock Controller".
STOP clock controlled thru SYSTEM_CONFIG 0 of bank3 */
unsigned long tmp = SYSCONF_READ(SYS_CFG_BANK3, 0, 0, 7);
bit = clk_p->id - CLKB_PIX_HD;
if (enable)
tmp &= ~(1 << bit);
else
tmp |= (1 << bit);
SYSCONF_WRITE(SYS_CFG_BANK3, 0, 0, 7, tmp);
} else if (clk_p->id >= CLKB_HD_TO_VID_DIV && clk_p->id <= CLKB_LPC) {
/* Clocks from clockgen B master */
bit = power_table[clk_p->id - CLKB_HD_TO_VID_DIV];
val = CLK_READ(CKGB_BASE_ADDRESS + CKGB_POWER_ENABLE);
if (enable)
val |= (1 << bit);
else
val &= ~(1 << bit);
clkgenb_unlock();
CLK_WRITE(CKGB_BASE_ADDRESS + CKGB_POWER_ENABLE, val);
clkgenb_lock();
} else
return CLK_ERR_BAD_PARAMETER;
if (enable)
err = clkgenb_recalc(clk_p);
else
clk_p->rate = 0;
return err;
}
/* ========================================================================
Name: clkgenb_enable
Description: Enable clock or FSYN (clockgen B)
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenb_enable(clk_t *clk_p)
{
int err;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id >= CLKB_FS0_CH1 && clk_p->id <= CLKB_FS1_CH4)
err = clkgenb_xable_fsyn(clk_p, 1);
else
err = clkgenb_xable_clock(clk_p, 1);
return err;
}
/* ========================================================================
Name: clkgenb_disable
Description: Disable clock
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenb_disable(clk_t *clk_p)
{
int err;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id >= CLKB_FS0_CH1 && clk_p->id <= CLKB_FS1_CH4)
err = clkgenb_xable_fsyn(clk_p, 0);
else
err = clkgenb_xable_clock(clk_p, 0);
return err;
}
/* ========================================================================
Name: clkgenb_set_parent
Description: Set clock source for clockgenB when possible
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenb_set_parent(clk_t *clk_p, clk_t *parent_p)
{
unsigned long set = 0; /* Each bit set to 1 will be SETTED */
unsigned long reset = 0; /* Each bit set to 1 will be RESETTED */
unsigned long reg; /* Register address */
unsigned long val, chan;
if (!clk_p || !parent_p)
return CLK_ERR_BAD_PARAMETER;
switch (clk_p->id) {
case CLKB_REF:
switch (parent_p->id) {
case CLK_SYSIN:
reset = 3;
break;
case CLK_SYSALT:
reset = 2;
set = 1;
break;
default:
return CLK_ERR_BAD_PARAMETER;
}
reg = CKGB_BASE_ADDRESS + CKGB_CRISTAL_SEL;
clkgenb_unlock();
val = CLK_READ(reg);
val = val & ~reset;
val = val | set;
CLK_WRITE(reg, val);
clkgenb_lock();
break;
/* Clockgen B master clocks have no source muxing
capability. */
/* Clocks from "Video Clock Controller".
Parent controlled from SYSCONF0 of bank 3,
SEL_CLK field: 0=HD, 1=SD */
case CLKB_PIX_HD ... CLKB_656:
chan = clk_p->id - CLKB_PIX_HD;
val = SYSCONF_READ(SYS_CFG_BANK3, 0, 12, 19) & ~(1 << chan);
switch (parent_p->id) {
case CLKB_HD_TO_VID_DIV:
val = val | (0 << chan);
break;
case CLKB_SD_TO_VID_DIV:
val = val | (1 << chan);
break;
default:
return CLK_ERR_BAD_PARAMETER;
}
/* Set bank3, SYSCONFIG0 [19:12]: sel_clk */
SYSCONF_WRITE(SYS_CFG_BANK3, 0, 12, 19, val);
break;
default:
return CLK_ERR_BAD_PARAMETER;
}
clk_p->parent = parent_p;
return clkgenb_recalc(clk_p);
}
/* ========================================================================
Name: clkgenb_set_rate
Description: Set clock frequency
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenb_set_rate(clk_t *clk_p, unsigned long freq)
{
unsigned long div;
int err;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (!clk_p->parent)
return CLK_ERR_INTERNAL;
if (clk_p->id < CLKB_FS0_CH1 || clk_p->id > CLKB_656)
return CLK_ERR_BAD_PARAMETER;
switch (clk_p->id) {
case CLKB_FS0_CH1 ... CLKB_FS1_CH4:
err = clkgenb_set_fsclock(clk_p, freq);
break;
/* Following clocks have a fixed parent */
case CLKB_FS0_CHAN0:
case CLKB_DSS:
case CLKB_DAA:
case CLKB_CLK48:
err = clkgenb_set_rate(clk_p->parent, freq);
break;
default:
/* Other clocks are assumed to be from Video Clock Controller */
div = clk_p->parent->rate / freq + (((freq/2) > (clk_p->parent->rate % freq))?0:1);
err = clkgenb_set_div(clk_p, &div);
break;
}
if (!err)
err = clkgenb_recalc(clk_p);
return err;
}
/* ========================================================================
Name: clkgenb_set_fsclock
Description: Set FS clock
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenb_set_fsclock(clk_t *clk_p, unsigned long freq)
{
unsigned long md, pe, sdiv;
int bank, channel;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (!clk_p->parent)
return CLK_ERR_INTERNAL;
if (clk_p->id < CLKB_FS0_CH1 || clk_p->id > CLKB_FS1_CH4)
return CLK_ERR_BAD_PARAMETER;
/* Computing FSyn params. Should be common function with FSyn type */
if (clk_fsyn_get_params(clk_p->parent->rate, freq, &md, &pe, &sdiv))
return CLK_ERR_BAD_PARAMETER;
bank = (clk_p->id - CLKB_FS0_CH1) / 4;
channel = (clk_p->id - CLKB_FS0_CH1) % 4;
clkgenb_unlock();
CLK_WRITE(CKGB_BASE_ADDRESS + CKGB_FS_MD(bank, channel), md);
CLK_WRITE(CKGB_BASE_ADDRESS + CKGB_FS_PE(bank, channel), pe);
CLK_WRITE(CKGB_BASE_ADDRESS + CKGB_FS_SDIV(bank, channel), sdiv);
CLK_WRITE(CKGB_BASE_ADDRESS + CKGB_FS_EN_PRG(bank, channel), 0x1);
clkgenb_lock();
return 0;
}
/* ========================================================================
Name: clkgenb_set_div
Description: Set divider ratio for clockgenB when possible
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenb_set_div(clk_t *clk_p, unsigned long *div_p)
{
unsigned long set = 0; /* Each bit set to 1 will be SETTED */
unsigned long reset = 0;/* Each bit set to 1 will be RESETTED */
unsigned long val, chan, tmp;
static const unsigned char div_table[] = {
/* 1 2 3 4 5 6 7 8 */
0, 1, 0xff, 2, 0xff, 0xff, 0xff, 3 };
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
switch (clk_p->id) {
case CLKB_HD_TO_VID_DIV:
if (*div_p == 1024)
set = 1 << 3;
else {
*div_p = 1; /* Wrong div defaulted to 1 */
reset = 1 << 3;
}
val = CLK_READ(CKGB_BASE_ADDRESS + CKGB_POWER_DOWN);
val = (val & ~reset) | set;
clkgenb_unlock();
CLK_WRITE(CKGB_BASE_ADDRESS + CKGB_POWER_DOWN, val);
clkgenb_lock();
break;
case CLKB_SD_TO_VID_DIV:
if (*div_p == 1024)
set = 1 << 7;
else
reset = 1 << 7;
val = CLK_READ(CKGB_BASE_ADDRESS + CKGB_POWER_DOWN);
val = (val & ~reset) | set;
clkgenb_unlock();
CLK_WRITE(CKGB_BASE_ADDRESS + CKGB_POWER_DOWN, val);
clkgenb_lock();
if (CLK_READ(CKGB_BASE_ADDRESS + CKGB_FS_SELECT) & 0x2) {
/* clk_pix_sd sourced from Fsyn1 */
switch (*div_p) {
case 2:
reset = 0x3 << 10;
break;
case 4:
reset = 1 << 13;
set = 1 << 12;
break;
case 8:
set = 1 << 13;
reset = 1 << 12;
break;
default: /* Wrong div defaulted to 1 */
case 1:
set = 0x3 << 12;
*div_p = 1;
break;
}
} else { /* clk_pix_sd sourced from Fsyn0 */
switch (*div_p) {
case 4:
reset = 1 << 11;
set = 1 << 10;
break;
case 8:
set = 1 << 13;
reset = 1 << 12;
break;
default: /* Wrong div defaulted to 2 */
case 2:
reset = 0x3 << 10;
*div_p = 2;
break;
}
}
val = CLK_READ(CKGB_BASE_ADDRESS + CKGB_DISPLAY_CFG);
val = (val & ~reset) | set;
clkgenb_unlock();
CLK_WRITE(CKGB_BASE_ADDRESS + CKGB_DISPLAY_CFG, val);
clkgenb_lock();
break;
case CLKB_CCSC:
if (*div_p == 1024)
set = 1 << 8;
else {
*div_p = 1; /* Wrong div defaulted to 1 */
reset = 1 << 8;
}
val = CLK_READ(CKGB_BASE_ADDRESS + CKGB_POWER_DOWN);
val = (val & ~reset) | set;
clkgenb_unlock();
CLK_WRITE(CKGB_BASE_ADDRESS + CKGB_POWER_DOWN, val);
clkgenb_lock();
break;
case CLKB_LPC:
*div_p = 1024;
break;
/* Clocks from "Video Clock Controller". */
case CLKB_PIX_HD ... CLKB_656:
if (*div_p < 1 || *div_p > 8)
return CLK_ERR_BAD_PARAMETER;
set = div_table[*div_p - 1];
if (set == 0xff)
return CLK_ERR_BAD_PARAMETER;
chan = clk_p->id - CLKB_PIX_HD;
/* Set Bank 3, SYSCONFIG 1 [15:0]: div_mode
* (2bits per channel)
*/
tmp = SYSCONF_READ(SYS_CFG_BANK3, 1, 0, 15);
tmp &= ~(3 << (chan * 2));
tmp |= set << (chan * 2);
SYSCONF_WRITE(SYS_CFG_BANK3, 1, 0, 15, tmp);
break;
default:
return CLK_ERR_BAD_PARAMETER;
}
return 0;
}
/* ========================================================================
Name: clkgenb_observe
Description: Allows to observe a clock on a PIO5_2
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenb_observe(clk_t *clk_p, unsigned long *div_p)
{
unsigned long out0, out1 = 0;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id < CLKB_FS0_CHAN0 || clk_p->id > CLKB_656)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id >= CLKB_FS0_CHAN0 && clk_p->id <= CLKB_LPC) {
/* ClkgenB master clocks.
WARNING: enum order from CLKB_FS0_CHAN0 to CLKB_LPC !! */
static const unsigned char observe_table[] = {
0, 3, 8, 9, 10, 12, 11, 13 };
out0 = observe_table[clk_p->id - CLKB_FS0_CHAN0];
if (out0 == 0xff)
return CLK_ERR_FEATURE_NOT_SUPPORTED;
if (clk_p->id == CLKB_CLK48)
out1 = 11;
clkgenb_unlock();
CLK_WRITE((CKGB_BASE_ADDRESS + CKGB_OUT_CTRL),
(out1 << 4) | out0);
clkgenb_lock();
/* Note: to have all clockgen B clocks visible on the same
output choosing PIO23[5] even if clockgen B master clocks
only can be observed on PIO23[4] */
SYSCONF_WRITE(SYS_CFG_BANK3, 2, 8, 13, 0);
} else if (clk_p->id >= CLKB_PIX_HD && clk_p->id <= CLKB_656) {
/* Video Clock Controler clocks.
WARNING: enum order from CLKB_PIX_HD to CLKB_656 !! */
/* Group (0=channels 0->3, 1=channels 4->7 */
static const unsigned char observe_table2[] = {
0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12, 0x13 };
unsigned long group, out;
group = observe_table2[clk_p->id - CLKB_PIX_HD] >> 4;
out = observe_table2[clk_p->id - CLKB_PIX_HD] & 0xf;
/* Selection clock for observation, sysconf2 bank3 */
SYSCONF_WRITE(SYS_CFG_BANK3, 2, 8, 13,
(out << 4) | (out << 2) | (group << 1) | 1);
}
/* Selecting alternate mode 2 for PIO23[5] */
SYSCONF_WRITE(SYS_CFG_BANK4, 8, 20, 21, 2);
SYSCONF_WRITE(SYS_CFG_BANK4, 14, 5, 5, 1);
/* No possible predivider on clockgen B */
*div_p = 1;
return 0;
}
/* ========================================================================
Name: clkgenb_fsyn_recalc
Description: Check FSYN & channels status... active, disabled, standby
'clk_p->rate' is updated accordingly.
Returns: Error code.
======================================================================== */
static int clkgenb_fsyn_recalc(clk_t *clk_p)
{
unsigned long val, bit;
unsigned long clkout = CKGB_FS0_CLKOUT_CTRL, ctrl = CKGB_FS0_CTRL;
unsigned long pe, md, sdiv;
int bank, channel;
if (!clk_p || !clk_p->parent)
return CLK_ERR_BAD_PARAMETER;
bank = (clk_p->id - CLKB_FS0_CH1) / 4;
channel = (clk_p->id - CLKB_FS0_CH1) % 4;
/* Which FSYN control registers to use ? */
if (clk_p->id >= CLKB_FS0_CH1 && clk_p->id <= CLKB_FS1_CH4) {
clkout += (CKGB_FS1_CLKOUT_CTRL - CKGB_FS0_CLKOUT_CTRL) * bank;
ctrl += (CKGB_FS1_CTRL - CKGB_FS0_CTRL) * bank;
} else
return CLK_ERR_BAD_PARAMETER;
/* Is FSYN analog part UP ? */
val = CLK_READ(CKGB_BASE_ADDRESS + ctrl);
if ((val & (1 << 4)) == 0) { /* NO. Analog part is powered down */
clk_p->rate = 0;
return 0;
}
/* Is FSYN digital part UP ? */
bit = (clk_p->id - CLKB_FS0_CH1) % 4;
val = CLK_READ(CKGB_BASE_ADDRESS + clkout);
if ((val & (1 << bit)) == 0) {
/* Digital standby */
clk_p->rate = 0;
return 0;
}
/* FSYN is up and running.
Now computing frequency */
pe = CLK_READ(CKGB_BASE_ADDRESS + CKGB_FS_PE(bank, channel));
md = CLK_READ(CKGB_BASE_ADDRESS + CKGB_FS_MD(bank, channel));
sdiv = CLK_READ(CKGB_BASE_ADDRESS + CKGB_FS_SDIV(bank, channel));
return clk_fsyn_get_rate(clk_p->parent->rate,
pe, md, sdiv, &clk_p->rate);
}
/* ========================================================================
Name: clkgenb_recalc
Description: Get CKGB clocks frequencies function
Returns: 'clk_err_t' error code
======================================================================== */
/* Check clock enable value for clockgen B.
Returns: 1=RUNNING, 0=DISABLED */
static int clkgenb_is_running(int bit)
{
unsigned long power;
power = CLK_READ(CKGB_BASE_ADDRESS + CKGB_POWER_ENABLE);
if (power & (1 << bit))
return 1;
return 0;
}
static int clkgenb_recalc(clk_t *clk_p)
{
unsigned long displaycfg, powerdown, fs_sel;
unsigned long chan, val;
static unsigned char tab2481[] = { 2, 4, 8, 1 };
static unsigned char tab2482[] = { 2, 4, 8, 2 };
static unsigned char tab1248[] = { 1, 2, 4, 8 };
/* Power en/dis table for clkgen B.
WARNING: enum order !! */
static unsigned char power_table[] = { 3, 9, 1, 0, 12, 11, 13 };
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (!clk_p->parent)
return CLK_ERR_INTERNAL;
/* Read muxes */
displaycfg = CLK_READ(CKGB_BASE_ADDRESS + CKGB_DISPLAY_CFG);
powerdown = CLK_READ(CKGB_BASE_ADDRESS + CKGB_POWER_DOWN);
fs_sel = CLK_READ(CKGB_BASE_ADDRESS + CKGB_FS_SELECT);
switch (clk_p->id) {
case CLKB_REF:
case CLKB_FS0_CHAN0:
clk_p->rate = clk_p->parent->rate;
break;
case CLKB_FS0_CH1 ... CLKB_FS1_CH4:
return clkgenb_fsyn_recalc(clk_p);
case CLKB_HD_TO_VID_DIV: /* pix_hd */
if (!clkgenb_is_running
(power_table[clk_p->id - CLKB_HD_TO_VID_DIV]))
clk_p->rate = 0;
else if (powerdown & (1 << 3))
clk_p->rate = clk_p->parent->rate / 1024;
else
clk_p->rate = clk_p->parent->rate;
break;
case CLKB_SD_TO_VID_DIV: /* pix_sd */
if (!clkgenb_is_running
(power_table[clk_p->id - CLKB_HD_TO_VID_DIV]))
clk_p->rate = 0;
else if (powerdown & (1 << 7))
clk_p->rate = clk_p->parent->rate / 1024;
else if (fs_sel & (1 << 1)) /* Source is FSYN 1 */
clk_p->rate =
clk_p->parent->rate /
tab2481[(displaycfg >> 12) & 0x3];
else /* Source is FSYN 0 */
clk_p->rate =
clk_p->parent->rate /
tab2482[(displaycfg >> 10) & 0x3];
break;
case CLKB_CCSC:
if (!clkgenb_is_running
(power_table[clk_p->id - CLKB_HD_TO_VID_DIV]))
clk_p->rate = 0;
else if (powerdown & (1 << 8))
clk_p->rate = clk_p->parent->rate / 1024;
else
clk_p->rate = clk_p->parent->rate;
break;
case CLKB_LPC:
if (!clkgenb_is_running
(power_table[clk_p->id - CLKB_HD_TO_VID_DIV]))
clk_p->rate = 0;
else
clk_p->rate = clk_p->parent->rate / 1024;
break;
case CLKB_DSS:
case CLKB_DAA:
case CLKB_CLK48:
if (!clkgenb_is_running
(power_table[clk_p->id - CLKB_HD_TO_VID_DIV]))
clk_p->rate = 0;
else
clk_p->rate = clk_p->parent->rate;
break;
/* Clocks from "Video Clock Controller" */
case CLKB_PIX_HD ... CLKB_656:
chan = clk_p->id - CLKB_PIX_HD;
/* Is the channel stopped ? */
val = (SYSCONF_READ(SYS_CFG_BANK3, 0, 0, 7) >> chan) & 1;
if (val)
clk_p->rate = 0;
else {
/* What is the divider ratio ? */
val = (SYSCONF_READ(SYS_CFG_BANK3, 1, 0, 15)
>> (chan * 2)) & 3;
clk_p->rate = clk_p->parent->rate / tab1248[val];
}
break;
default:
return CLK_ERR_BAD_PARAMETER;
}
return 0;
}
/* ========================================================================
Name: clkgenb_identify_parent
Description: Identify parent clock
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenb_identify_parent(clk_t *clk_p)
{
unsigned long sel, fs_sel, val;
unsigned long displaycfg, chan;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id < CLKB_REF || clk_p->id > CLKB_656)
return CLK_ERR_BAD_PARAMETER;
fs_sel = CLK_READ(CKGB_BASE_ADDRESS + CKGB_FS_SELECT);
displaycfg = CLK_READ(CKGB_BASE_ADDRESS + CKGB_DISPLAY_CFG);
switch (clk_p->id) {
case CLKB_REF: /* What is clockgen B ref clock ? */
sel = CLK_READ(CKGB_BASE_ADDRESS + CKGB_CRISTAL_SEL);
switch (sel & 0x3) {
case 0:
clk_p->parent = &clk_clocks[CLK_SYSIN];
clk_p->rate = clk_p->parent->rate;
break;
case 1:
clk_p->parent = &clk_clocks[CLK_SYSALT];
clk_p->rate = clk_p->parent->rate;
break;
default:
clk_p->parent = NULL;
break;
}
break;
case CLKB_HD_TO_VID_DIV: /* pix_hd */
if (displaycfg & (1 << 14)) /* Source = FSYN 1 */
clk_p->parent = &clk_clocks[CLKB_FS1_CH1];
else
clk_p->parent = &clk_clocks[CLKB_FS0_CH1];
break;
case CLKB_SD_TO_VID_DIV: /* pix_sd */
if (fs_sel & (1 << 1)) /* Source = FSYN 1 */
clk_p->parent = &clk_clocks[CLKB_FS1_CH1];
else
clk_p->parent = &clk_clocks[CLKB_FS0_CH1];
break;
/* Clocks from "Video Clock Controller".
WARNING: must follow enum order !! */
case CLKB_PIX_HD ... CLKB_656:
chan = clk_p->id - CLKB_PIX_HD;
val = SYSCONF_READ(SYS_CFG_BANK3, 0, 12, 19) & (1 << chan);
if (val == 0)
clk_p->parent = &clk_clocks[CLKB_HD_TO_VID_DIV];
else
clk_p->parent = &clk_clocks[CLKB_SD_TO_VID_DIV];
break;
/* Other clockgen B clocks are statically initialized
thanks to _CLK_P() macro */
}
return 0;
}
/* ========================================================================
Name: clkgenb_init
Description: Read HW status to initialize 'clk_t' structure.
Returns: 'clk_err_t' error code.
======================================================================== */
static int clkgenb_init(clk_t *clk_p)
{
int err;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
err = clkgenb_identify_parent(clk_p);
if (!err)
err = clkgenb_recalc(clk_p);
return err;
}
/******************************************************************************
CLOCKGEN C (audio)
******************************************************************************/
/* Clockgen C infos
Channel 0 => PCM0 (tvout ss)
Channel 1 => PCM1 (analog out)
Channel 2 => SPDIF (tvout ss)
Channel 3 => PCM2 (digital out)
*/
/* ========================================================================
Name: clkgenc_fsyn_recalc
Description: Get CKGC FSYN clocks frequencies function
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenc_fsyn_recalc(clk_t *clk_p)
{
unsigned long cfg, dig_bit;
unsigned long pe, md, sdiv;
int channel, err = 0;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id < CLKC_FS0_CH1 || clk_p->id > CLKC_FS0_CH4)
return CLK_ERR_BAD_PARAMETER;
/* Checking FSYN analog status */
cfg = CLK_READ(CKGC_BASE_ADDRESS + CKGC_FS0_CFG);
if (!(cfg & (1 << 14))) { /* Analog power down */
clk_p->rate = 0;
return 0;
}
/* Checking FSYN digital part */
dig_bit = (clk_p->id - CLKC_FS0_CH1) + 10;
if ((cfg & (1 << dig_bit)) == 0) { /* digital part in standby */
clk_p->rate = 0;
return 0;
}
/* FSYN up & running.
Computing frequency */
channel = (clk_p->id - CLKC_FS0_CH1) % 4;
pe = CLK_READ(CKGC_BASE_ADDRESS + CKGC_FS_PE(0, channel));
md = CLK_READ(CKGC_BASE_ADDRESS + CKGC_FS_MD(0, channel));
sdiv = CLK_READ(CKGC_BASE_ADDRESS + CKGC_FS_SDIV(0, channel));
err = clk_fsyn_get_rate(clk_p->parent->rate, pe, md,
sdiv, &clk_p->rate);
return err;
}
/* ========================================================================
Name: clkgenc_recalc
Description: Get CKGC clocks frequencies function
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenc_recalc(clk_t *clk_p)
{
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
switch (clk_p->id) {
case CLKC_REF:
clk_p->rate = clk_p->parent->rate;
break;
case CLKC_FS0_CH1 ... CLKC_FS0_CH4: /* FS0 clocks */
return clkgenc_fsyn_recalc(clk_p);
default:
return CLK_ERR_BAD_PARAMETER;
}
return 0;
}
/* ========================================================================
Name: clkgenc_set_rate
Description: Set CKGC clocks frequencies
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenc_set_rate(clk_t *clk_p, unsigned long freq)
{
unsigned long md, pe, sdiv;
unsigned long reg_value = 0;
int channel;
static const unsigned char set_rate_table[] = {
0x06, 0x0A, 0x12, 0x22 };
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if ((clk_p->id < CLKC_FS0_CH1) || (clk_p->id > CLKC_FS0_CH4))
return CLK_ERR_BAD_PARAMETER;
/* Computing FSyn params. Should be common function with FSyn type */
if (clk_fsyn_get_params(clk_p->parent->rate, freq, &md, &pe, &sdiv))
return CLK_ERR_BAD_PARAMETER;
reg_value = CLK_READ(CKGC_BASE_ADDRESS + CKGC_FS0_CFG);
channel = (clk_p->id - CLKC_FS0_CH1) % 4;
reg_value |= set_rate_table[clk_p->id - CLKC_FS0_CH1];
reg_value |=1; /*LILLE FIX*/
/* Select FS clock only for the clock specified */
CLK_WRITE(CKGC_BASE_ADDRESS + CKGC_FS0_CFG, reg_value);
CLK_WRITE(CKGC_BASE_ADDRESS + CKGC_FS_PE(0, channel), pe);
CLK_WRITE(CKGC_BASE_ADDRESS + CKGC_FS_MD(0, channel), md);
CLK_WRITE(CKGC_BASE_ADDRESS + CKGC_FS_SDIV(0, channel), sdiv);
CLK_WRITE(CKGC_BASE_ADDRESS + CKGC_FS_EN_PRG(0, channel), 0x01);
CLK_WRITE(CKGC_BASE_ADDRESS + CKGC_FS_EN_PRG(0, channel), 0x00);
return clkgenc_recalc(clk_p);
}
/* ========================================================================
Name: clkgenc_identify_parent
Description: Identify parent clock
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenc_identify_parent(clk_t *clk_p)
{
unsigned long sel;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id != CLKC_REF)
/* already done statically */
return 0;
sel = CLK_READ(CKGC_BASE_ADDRESS + CKGC_FS0_CFG) >> 23;
switch (sel & 0x3) {
case 0:
clk_p->parent = &clk_clocks[CLK_SYSIN];
break;
case 1:
clk_p->parent = &clk_clocks[CLK_SYSALT];
break;
default:
clk_p->parent = NULL;
break;
}
return 0;
}
/* ========================================================================
Name: clkgenc_set_parent
Description: Set parent clock
Returns: 'clk_err_t' error code.
======================================================================== */
static int clkgenc_set_parent(clk_t *clk_p, clk_t *parent_p)
{
unsigned long sel, data;
if (!clk_p || !parent_p)
return CLK_ERR_BAD_PARAMETER;
/* Only CLKC_REF's parent can be changed */
if (clk_p->id != CLKC_REF)
return CLK_ERR_BAD_PARAMETER;
switch (parent_p->id) {
case CLK_SYSIN:
sel = 0;
break;
case CLK_SYSALT:
sel = 1;
break;
default:
return CLK_ERR_BAD_PARAMETER;
}
data = CLK_READ(CKGC_BASE_ADDRESS + CKGC_FS0_CFG) & ~(0x3 << 23);
CLK_WRITE(CKGC_BASE_ADDRESS + CKGC_FS0_CFG, data | (sel << 23));
clk_p->parent = parent_p;
clk_p->rate = parent_p->rate;
return 0;
}
/* ========================================================================
Name: clkgenc_init
Description: Read HW status to initialize 'clk_t' structure.
Returns: 'clk_err_t' error code.
======================================================================== */
static int clkgenc_init(clk_t *clk_p)
{
int err;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id == CLKC_REF) {
unsigned long data = CLK_READ(CKGC_BASE_ADDRESS + CKGC_FS0_CFG);
data |= (1 << 0); /* reset NOT active */
CLK_WRITE(CKGC_BASE_ADDRESS + CKGC_FS0_CFG, data);
}
err = clkgenc_identify_parent(clk_p);
if (!err)
err = clkgenc_recalc(clk_p);
return err;
}
/* ========================================================================
Name: clkgenc_xable_fsyn
Description: Enable/Disable FSYN. If all channels OFF, FSYN is powered
down.
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgenc_xable_fsyn(clk_t *clk_p, unsigned long enable)
{
unsigned long val;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id < CLKC_FS0_CH1 || clk_p->id > CLKC_FS0_CH4)
return CLK_ERR_BAD_PARAMETER;
val = CLK_READ(CKGC_BASE_ADDRESS + CKGC_FS0_CFG);
/* Powering */
if (enable) {
/* Powering digital parts */
val |= (1 << (10 + (clk_p->id - CLKC_FS0_CH1)));
/*Put FS out of reset */
val |= 1;
/* Set the freq source */
val |= (1 << (2 + (clk_p->id - CLKC_FS0_CH1)));
/* Enable FS too */
val |= (1 << (6 + (clk_p->id - CLKC_FS0_CH1)));
/* Powering analog part */
val |= (1 << 14);
} else {
val &= ~(1 << (10 + (clk_p->id - CLKC_FS0_CH1)));
/* If all channels are off then power down FS0 */
if ((val & 0x3c00) == 0)
val &= ~(1 << 14);
}
CLK_WRITE(CKGC_BASE_ADDRESS + CKGC_FS0_CFG, val);
/* Freq recalc required only if a channel is enabled */
if (enable)
return clkgenc_fsyn_recalc(clk_p);
else
clk_p->rate = 0;
return 0;
}
/* ========================================================================
Name: clkgenc_enable
Description: Enable clock
Returns: 'clk_err_t' error code.
======================================================================== */
static int clkgenc_enable(clk_t *clk_p)
{
return clkgenc_xable_fsyn(clk_p, 1);
}
/* ========================================================================
Name: clkgenc_disable
Description: Disable clock
Returns: 'clk_err_t' error code.
======================================================================== */
static int clkgenc_disable(clk_t *clk_p)
{
return clkgenc_xable_fsyn(clk_p, 0);
}
/******************************************************************************
CLOCKGEN D (DDR sub-systems)
******************************************************************************/
/* ========================================================================
Name: clkgend_recalc
Description: Get CKGD (LMI) clocks frequencies (in Hz)
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgend_recalc(clk_t *clk_p)
{
unsigned long pdiv, ndiv, mdiv;
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id == CLKD_REF)
clk_p->rate = clk_p->parent->rate;
else if (clk_p->id == CLKD_IC_DDRCTRL) {
/* Accessing SYSCONF_CFG4 of BANK1 */
pdiv = SYSCONF_READ(SYS_CFG_BANK1, 4, 24, 26);
ndiv = SYSCONF_READ(SYS_CFG_BANK1, 4, 16, 23);
mdiv = SYSCONF_READ(SYS_CFG_BANK1, 4, 8, 15);
return clk_pll800_get_rate
(clk_p->parent->rate, mdiv, ndiv, pdiv,
&(clk_p->rate));
} else if (clk_p->id == CLKD_DDR)
clk_p->rate = clk_p->parent->rate * 4;
else
return CLK_ERR_BAD_PARAMETER; /* Unknown clock */
return 0;
}
/* ========================================================================
Name: clkgend_init
Description: Read HW status to initialize 'clk_t' structure.
Returns: 'clk_err_t' error code.
======================================================================== */
static int clkgend_init(clk_t *clk_p)
{
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
/* Parents are static. No idenfication required */
return clkgend_recalc(clk_p);
}
/******************************************************************************
CLOCKGEN E (USB)
******************************************************************************/
/* ========================================================================
Name: clkgene_recalc
Description: Get CKGE (USB) clocks frequencies (in Hz)
Returns: 'clk_err_t' error code
======================================================================== */
static int clkgene_recalc(clk_t *clk_p)
{
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
if (clk_p->id != CLKE_REF)
return CLK_ERR_BAD_PARAMETER;
clk_p->rate = clk_p->parent->rate;
return 0;
}
/* ========================================================================
Name: clkgene_init
Description: Read HW status to initialize 'clk_t' structure.
Returns: 'clk_err_t' error code.
======================================================================== */
static int clkgene_init(clk_t *clk_p)
{
if (!clk_p)
return CLK_ERR_BAD_PARAMETER;
/* Parents are static. No idenfication required */
return clkgene_recalc(clk_p);
}