/* $Id: clock_prep.c,v 1.3 2013/10/07 17:36:40 matt Exp $ */
/*
* Copyright (c) 2012 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Petri Laakso.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/types.h>
#include <arm/imx/imx23_clkctrlreg.h>
#include <lib/libsa/stand.h>
#include "common.h"
#define CLKCTRL_HBUS (HW_CLKCTRL_BASE + HW_CLKCTRL_HBUS)
#define CLKCTRL_PLL0 (HW_CLKCTRL_BASE + HW_CLKCTRL_PLLCTRL0)
#define CLKCTRL_PLL0_S (HW_CLKCTRL_BASE + HW_CLKCTRL_PLLCTRL0_SET)
#define CLKCTRL_PLL0_C (HW_CLKCTRL_BASE + HW_CLKCTRL_PLLCTRL0_CLR)
#define CLKCTRL_PLL1 (HW_CLKCTRL_BASE + HW_CLKCTRL_PLLCTRL1)
#define CLKCTRL_FRAC (HW_CLKCTRL_BASE + HW_CLKCTRL_FRAC)
#define CLKCTRL_FRAC_S (HW_CLKCTRL_BASE + HW_CLKCTRL_FRAC_SET)
#define CLKCTRL_FRAC_C (HW_CLKCTRL_BASE + HW_CLKCTRL_FRAC_CLR)
#define CLKCTRL_SEQ (HW_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ)
#define CLKCTRL_SEQ_S (HW_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ_SET)
#define CLKCTRL_SEQ_C (HW_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ_CLR)
#define CLKCTRL_EMI (HW_CLKCTRL_BASE + HW_CLKCTRL_EMI)
#define CLKCTRL_SSP (HW_CLKCTRL_BASE + HW_CLKCTRL_SSP)
void en_pll(void);
void set_hbus_div(unsigned int);
void set_cpu_frac(unsigned int);
void bypass_cpu(void);
void set_emi_div(unsigned int);
void set_emi_frac(unsigned int);
void bypass_emi(void);
/*
* Power on 480 MHz PLL.
*/
void
en_pll(void)
{
REG_WR(CLKCTRL_PLL0_S, HW_CLKCTRL_PLLCTRL0_POWER);
while(!(REG_RD(CLKCTRL_PLL1) & HW_CLKCTRL_PLLCTRL1))
;
return;
}
void
bypass_cpu(void)
{
REG_WR(CLKCTRL_SEQ_C, HW_CLKCTRL_CLKSEQ_BYPASS_CPU);
return;
}
void
bypass_emi(void)
{
REG_WR(CLKCTRL_SEQ_C, HW_CLKCTRL_CLKSEQ_BYPASS_EMI);
return;
}
void
bypass_ssp(void)
{
REG_WR(CLKCTRL_SEQ_C, HW_CLKCTRL_CLKSEQ_BYPASS_SSP);
return;
}
void
bypass_saif(void)
{
REG_WR(CLKCTRL_SEQ_C, HW_CLKCTRL_CLKSEQ_BYPASS_SAIF);
return;
}
/*
* Set HBUS divider value.
*/
void
set_hbus_div(unsigned int div)
{
uint32_t tmp_r;
while (REG_RD(CLKCTRL_HBUS) & HW_CLKCTRL_HBUS_BUSY)
;
tmp_r = REG_RD(CLKCTRL_HBUS);
tmp_r &= ~HW_CLKCTRL_HBUS_DIV;
tmp_r |= __SHIFTIN(div, HW_CLKCTRL_HBUS_DIV);
REG_WR(CLKCTRL_HBUS, tmp_r);
while (REG_RD(CLKCTRL_HBUS) & HW_CLKCTRL_HBUS_BUSY)
;
return;
}
/*
* Set CPU frac.
*/
void
set_cpu_frac(unsigned int frac)
{
uint8_t tmp_r;
tmp_r = REG_RD_BYTE(CLKCTRL_FRAC);
tmp_r &= ~(HW_CLKCTRL_FRAC_CLKGATECPU | HW_CLKCTRL_FRAC_CPUFRAC);
tmp_r |= __SHIFTIN(frac, HW_CLKCTRL_FRAC_CPUFRAC);
REG_WR_BYTE(CLKCTRL_FRAC, tmp_r);
return;
}
/*
* Set EMI frac.
*/
void
set_emi_frac(unsigned int frac)
{
uint8_t *emi_frac;
uint16_t tmp_r;
emi_frac = (uint8_t *)(CLKCTRL_FRAC);
emi_frac++;
tmp_r = *emi_frac<<8;
tmp_r &= ~(HW_CLKCTRL_FRAC_CLKGATEEMI | HW_CLKCTRL_FRAC_EMIFRAC);
tmp_r |= __SHIFTIN(frac, HW_CLKCTRL_FRAC_EMIFRAC);
*emi_frac = tmp_r>>8;
return;
}
/*
* Set EMU divider value.
*/
void
set_emi_div(unsigned int div)
{
uint32_t tmp_r;
while (REG_RD(CLKCTRL_EMI) &
(HW_CLKCTRL_EMI_BUSY_REF_XTAL | HW_CLKCTRL_EMI_BUSY_REF_EMI))
;
tmp_r = REG_RD(CLKCTRL_EMI);
tmp_r &= ~(HW_CLKCTRL_EMI_CLKGATE | HW_CLKCTRL_EMI_DIV_EMI);
tmp_r |= __SHIFTIN(div, HW_CLKCTRL_EMI_DIV_EMI);
REG_WR(CLKCTRL_EMI, tmp_r);
return;
}
/*
* Set SSP divider value.
*/
void
set_ssp_div(unsigned int div)
{
uint32_t tmp_r;
tmp_r = REG_RD(CLKCTRL_SSP);
tmp_r &= ~HW_CLKCTRL_SSP_CLKGATE;
REG_WR(CLKCTRL_SSP, tmp_r);
while (REG_RD(CLKCTRL_SSP) & HW_CLKCTRL_SSP_BUSY)
;
tmp_r = REG_RD(CLKCTRL_SSP);
tmp_r &= ~HW_CLKCTRL_SSP_DIV;
tmp_r |= __SHIFTIN(div, HW_CLKCTRL_SSP_DIV);
REG_WR(CLKCTRL_SSP, tmp_r);
while (REG_RD(CLKCTRL_SSP) & HW_CLKCTRL_SSP_BUSY)
;
return;
}
/*
* Set IO frac.
*/
void
set_io_frac(unsigned int frac)
{
uint8_t *io_frac;
uint32_t tmp_r;
io_frac = (uint8_t *)(CLKCTRL_FRAC);
io_frac++; /* emi */
io_frac++; /* pix */
io_frac++; /* io */
tmp_r = (*io_frac)<<24;
tmp_r &= ~(HW_CLKCTRL_FRAC_CLKGATEIO | HW_CLKCTRL_FRAC_IOFRAC);
tmp_r |= __SHIFTIN(frac, HW_CLKCTRL_FRAC_IOFRAC);
*io_frac = (uint8_t)(tmp_r>>24);
return;
}