/*
* Clock tree for CSR SiRFprimaII
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
#define SIRFSOC_CLKC_CLK_EN0 0x0000
#define SIRFSOC_CLKC_CLK_EN1 0x0004
#define SIRFSOC_CLKC_REF_CFG 0x0014
#define SIRFSOC_CLKC_CPU_CFG 0x0018
#define SIRFSOC_CLKC_MEM_CFG 0x001c
#define SIRFSOC_CLKC_SYS_CFG 0x0020
#define SIRFSOC_CLKC_IO_CFG 0x0024
#define SIRFSOC_CLKC_DSP_CFG 0x0028
#define SIRFSOC_CLKC_GFX_CFG 0x002c
#define SIRFSOC_CLKC_MM_CFG 0x0030
#define SIRFSOC_CLKC_LCD_CFG 0x0034
#define SIRFSOC_CLKC_MMC_CFG 0x0038
#define SIRFSOC_CLKC_PLL1_CFG0 0x0040
#define SIRFSOC_CLKC_PLL2_CFG0 0x0044
#define SIRFSOC_CLKC_PLL3_CFG0 0x0048
#define SIRFSOC_CLKC_PLL1_CFG1 0x004c
#define SIRFSOC_CLKC_PLL2_CFG1 0x0050
#define SIRFSOC_CLKC_PLL3_CFG1 0x0054
#define SIRFSOC_CLKC_PLL1_CFG2 0x0058
#define SIRFSOC_CLKC_PLL2_CFG2 0x005c
#define SIRFSOC_CLKC_PLL3_CFG2 0x0060
#define SIRFSOC_USBPHY_PLL_CTRL 0x0008
#define SIRFSOC_USBPHY_PLL_POWERDOWN BIT(1)
#define SIRFSOC_USBPHY_PLL_BYPASS BIT(2)
#define SIRFSOC_USBPHY_PLL_LOCK BIT(3)
static void *sirfsoc_clk_vbase, *sirfsoc_rsc_vbase;
#define KHZ 1000
#define MHZ (KHZ * KHZ)
/*
* SiRFprimaII clock controller
* - 2 oscillators: osc-26MHz, rtc-32.768KHz
* - 3 standard configurable plls: pll1, pll2 & pll3
* - 2 exclusive plls: usb phy pll and sata phy pll
* - 8 clock domains: cpu/cpudiv, mem/memdiv, sys/io, dsp, graphic, multimedia,
* display and sdphy.
* Each clock domain can select its own clock source from five clock sources,
* X_XIN, X_XINW, PLL1, PLL2 and PLL3. The domain clock is used as the source
* clock of the group clock.
* - dsp domain: gps, mf
* - io domain: dmac, nand, audio, uart, i2c, spi, usp, pwm, pulse
* - sys domain: security
*/
struct clk_pll {
struct clk_hw hw;
unsigned short regofs; /* register offset */
};
#define to_pllclk(_hw) container_of(_hw, struct clk_pll, hw)
struct clk_dmn {
struct clk_hw hw;
signed char enable_bit; /* enable bit: 0 ~ 63 */
unsigned short regofs; /* register offset */
};
#define to_dmnclk(_hw) container_of(_hw, struct clk_dmn, hw)
struct clk_std {
struct clk_hw hw;
signed char enable_bit; /* enable bit: 0 ~ 63 */
};
#define to_stdclk(_hw) container_of(_hw, struct clk_std, hw)
static int std_clk_is_enabled(struct clk_hw *hw);
static int std_clk_enable(struct clk_hw *hw);
static void std_clk_disable(struct clk_hw *hw);
static inline unsigned long clkc_readl(unsigned reg)
{
return readl(sirfsoc_clk_vbase + reg);
}
static inline void clkc_writel(u32 val, unsigned reg)
{
writel(val, sirfsoc_clk_vbase + reg);
}
/*
* std pll
*/
static unsigned long pll_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
unsigned long fin = parent_rate;
struct clk_pll *clk = to_pllclk(hw);
u32 regcfg2 = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 -
SIRFSOC_CLKC_PLL1_CFG0;
if (clkc_readl(regcfg2) & BIT(2)) {
/* pll bypass mode */
return fin;
} else {
/* fout = fin * nf / nr / od */
u32 cfg0 = clkc_readl(clk->regofs);
u32 nf = (cfg0 & (BIT(13) - 1)) + 1;
u32 nr = ((cfg0 >> 13) & (BIT(6) - 1)) + 1;
u32 od = ((cfg0 >> 19) & (BIT(4) - 1)) + 1;
WARN_ON(fin % MHZ);
return fin / MHZ * nf / nr / od * MHZ;
}
}
static long pll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long fin, nf, nr, od;
/*
* fout = fin * nf / (nr * od);
* set od = 1, nr = fin/MHz, so fout = nf * MHz
*/
rate = rate -<