Files
openloongarch-linux-6.12/drivers/clk/loongson/clk-loongson2k500.c
T
maoxiaochuan 7ffd18ab9d Update to 14fc3074cfe039
1. 添加带irq处理的gpio驱动
2. 龙芯2k系列添加mmc驱动支持
3. 添加看门狗驱动
4. dtb支持从command line中的board_name字段判断
5. 支持内存分布描述数据从固件传入的数据获取
6. 支持从bootcmdline获取ramdisk启动的地址和所用大小
7. 添加2k300的i2c总线驱动
8. 2k300使用新的spi 和 spi-io 驱动
9. CPU芯片温度传感器支持2k300的计算方式
10. 添加2k300的adc驱动
11. pwm: update default pwm freq to 50Mhz
12. loongson pwm 驱动支持dts使用clock-frequency形式声明频率
13. loongson pwm 确保disable时输出的电平为空闲电平
14. 2k300编译配置使能pwm leds功能
15. 2k300的gmac的phy-mode默认使用rgmii-id
16. 2k300先锋派设置YT8531的内部延迟
17. YT8531 phy 支持设置led的工作模式
18. LCD屏幕 支持cmdline的panel属性选择某一款板卡的dts配置,实现屏幕切换
19. 添加脚本用于快速选择板卡配置文件
20. 添加CAN驱动
21. 添加音频驱动
22. 优化OTG兼容性

Signed-off-by: maoxiaochuan <maoxiaochuan@loongson.cn>
2025-02-13 16:03:32 +08:00

768 lines
25 KiB
C

/*
* Copyright (c) 2022
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <loongson-2k.h>
#include <dt-bindings/clock/loongson2k500-clock.h>
#include "clk.h"
#define REF_CLK_NAME "ref_clk"
#define NODE_CLK_FREQ_DEF 500000000
static unsigned long refclk_rate;
static void __iomem *reg;
static struct clk** clks = NULL;
#ifdef CONFIG_LOONGSON2_SYSCLK_SOFT
#if 0
#define DEBUG_INFO(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__)
#else
#define DEBUG_INFO(fmt, ...)
#endif
#define PARENT_BRANCH 0
#define PARENT_CLONE 1
struct ls2k_reg_field_desc {
unsigned int reg_offset;
unsigned int bit_offset;
unsigned int bit_mask;
};
typedef unsigned long (*clock_magnification_calu_func)(struct ls2k_reg_field_desc*);
/*
* 本驱动中用于描述时钟结构的单个时钟的结构体,其组成的数组用于描述整个时钟结构
* 其中不同的变量的值用于表示这个时钟的一些定义
* name: 这是时钟的名字
* parent_name: 这是时钟的上一层时钟的名字,比如DDR PLL的时钟 分频后 就是 CPU的时钟 那么可以说 CPU的 parent_name 是 Node PLL
* parent_relevant: 和上一层时钟的关系,这个变量用于 fixed 为 1的时候的处理流程,主要是为了区分 clks* 怎么来
* PARENT_BRANCH 代表是一个分支,PARENT_CLONE 代表是同一个时钟,而定义是不是分支主要是看两个时钟之间有没有分频或倍频
* 比如 DRR_PLL的时钟 分频后 获得 HDA 的时钟 那么就是 PARENT_BRANCH
* 比如 BOOT 的时钟 直接引到 BOOT SPI里面 那么就是 代表是一个分支, PARENT_CLONE
* dts_desc: 这是一个重要的变量 为1 的时候,意味着要去dts里面找这个时钟的声明,比如参考时钟
* essential_n: 在 dts_desc 为 1的时候,如果找不到该时钟,如果这个值为 1 将不会报错,只是会提示,不会影响该时钟管理驱动的初始化。例如 PCI的外部时钟,不提供也没什么事
* fixed: 这个值为1的时候,代表这个时钟在本内核中是固定不变的 不为1的时候,就可以改变
* *_reg_set 和 *_calu_func: 在 fixed == 1 的时候有效 *_reg_set 用于登记 分频 和 倍频的寄存器位域
* *calu_func是怎么处理这些寄存器的值的函数(就是怎么算系数) 不过我们也有提供通用的 ls2k_mul_calu ls2k_div_calu 等函数
* clk_ops: 这个值就是一个结构体 需要填充 recalc_rate round_rate set_rate 函数的实现(linux这边要求的) 详见 ls2k500_node_clk_ops 和 ls2k500_cpu_clk_ops
*
* 所以 需要注意
* name parent_name 是必填的
* dts_desc 为 1的时候 相关的要填的变量是 essential_n(可选 不填的话 static 下 自动为0) 其他变量不起效
* parent_relevant 为 PARENT_BRANCH 的时候
* fixed 为 1的时候 *_reg_set 和 *_calu_func(可选 不填的话 static 下 自动为0) 其他变量不起效
* fixed 为 0的时候 clk_ops(一定要实现) 其他变量不起效
* parent_relevant 为 PARENT_CLONE
* parent_name 不能为空 其他变量不起效
*
* *_reg_set 声明的时候 必须在在末尾添加 {.bit_mask = 0} 这是判断数组结束的标志
* *_calu_func 可以为空
*
* static 声明 ls2k_clock_desc数组时, 可不填的变量 就是 0
*/
struct ls2k_clock_desc {
char* name;
char* parent_name;
int parent_relevant; //branch or clone if branch reg_set or clk_ops enable if clone just clockA == clockB
int dts_desc;
int essential_n;
int fixed; // if fixed reg_set enable or clk_ops enable
struct ls2k_reg_field_desc* mul_reg_set;
clock_magnification_calu_func mul_calu_func;
struct ls2k_reg_field_desc* div_reg_set;
clock_magnification_calu_func div_calu_func;
struct ls2k_reg_field_desc* freqscale_reg_set;
clock_magnification_calu_func freqscale_mul_calu_func;
clock_magnification_calu_func freqscale_div_calu_func;
struct clk_ops* clk_ops;
};
struct pll_config {
unsigned int l1_div_ref;
unsigned int l1_loopc;
unsigned int l1_odiv;
};
// 系统时钟需满足如下几个限制:
// 1.可配分频器的输出 refclk/L1_div_ref 在 20~40MHz 范围内
// 2.PLL 倍频值 refclk/L1_div_ref*L1_loopc 需要在 1.2GHz~3.2GHz
static int ls2k_caculate_freq(unsigned long freq, struct pll_config *pll_cfg)
{
unsigned long pll_in_clk;
unsigned long loopc;
unsigned long div;
int found = 0;
if (!pll_cfg)
return -EINVAL;
// Keep refclk/L1_div_ref in range 20MHz ~ 40Mhz
pll_cfg->l1_div_ref = 4;
pll_in_clk = refclk_rate >> 2; // 25MHz // 100/4 = 25
// 2*7 = 14 1.4Ghz 3*7 = 21 2.1GHz
// 3*6 = 18 1.8GHz
if(freq >= 700000000)
div = 2;
else
div = 3;
//keep 1.2 ~ 3.2
// and keep 1.2 ~ 2.1 will be better
loopc = (freq * div) / pll_in_clk;
if (!found) {
pll_cfg->l1_div_ref = 0;
pll_cfg->l1_loopc = 0;
pll_cfg->l1_odiv = 0;
return -EINVAL;
}
pll_cfg->l1_loopc = loopc;
pll_cfg->l1_odiv = div;
return 0;
}
//PLL 寄存器设置 用于 时钟频率设置生效
/*
* 注意手册上写的 3.5.2 软件配置
* 第二步和第三步是一起的,也就是代码中
* li.w t1, (NODE_DIV << 24) | (NODE_LOOPC << 16) | (NODE_REFC << 8)
* 没有管 pd_pll 会被设置成 0 的原因
*/
static void ls2k_config_pll(void __iomem *pll_base, struct pll_config *pll_cfg)
{
int timeout = 200;
u32 val;
/* pd_pll 1 */
val = __raw_readl(pll_base);
val |= BIT(5);
__raw_writel(val, pll_base);
/* 除了 pll_sel_* [2:0] 设置为0 pll_soft_set (3) 设置为0 其他的寄存器按需设置*/
/* pd_pll 0 */
val = (pll_cfg->l1_odiv << PLL_GENERIC_0_ODIV_SHIFT) |
(pll_cfg->l1_loopc << PLL_GENERIC_0_LOOPC_SHIFT) |
(pll_cfg->l1_div_ref << PLL_GENERIC_0_REFC_SHIFT);
__raw_writel(val, pll_base);
/* 其他信号不变 pll_soft_set (3) 信号设置为 1 */
val = __raw_readl(pll_base);
val |= BIT(3);
__raw_writel(val, pll_base);
/* 等待 pll_lock (7) 锁定 也就是变成 1 */
while(!(__raw_readl(pll_base) & BIT(7)) && (timeout-- > 0)) {
udelay(1);
}
/* pll_sel_* [2:0] 设置为1 */
val = __raw_readl(pll_base);
val |= 0x7;
__raw_writel(val, pll_base);
}
static unsigned long ls2k500_node_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
u32 ctrl;
unsigned int div_loopc, div_ref, odiv;
unsigned int mult, div;
unsigned long rate;
/* node cpu clk */
ctrl = __raw_readl(reg + PLL_NODE_0_OFFSET);
div_loopc = (ctrl >> PLL_NODE_0_LOOPC_SHIFT) & PLL_NODE_0_LOOPC_MASK;
div_ref = (ctrl >> PLL_NODE_0_REFC_SHIFT) & PLL_NODE_0_REFC_MASK;
odiv = (ctrl >> PLL_NODE_0_ODIV_SHIFT) & PLL_NODE_0_ODIV_MASK;
mult = div_loopc;
div = div_ref * odiv;
if (div == 0) {
rate = NODE_CLK_FREQ_DEF;
} else {
rate = refclk_rate * mult / div;
}
return rate;
}
static long ls2k500_node_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct pll_config pll_cfg;
unsigned int mult, div;
if (rate == 0 || *parent_rate == 0)
return 0;
if (ls2k_caculate_freq(rate, &pll_cfg)) {
return NODE_CLK_FREQ_DEF;
}
mult = pll_cfg.l1_loopc;
div = pll_cfg.l1_div_ref * pll_cfg.l1_odiv;
return refclk_rate * mult / div;
}
static int ls2k500_node_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct pll_config pll_cfg;
if (rate == 0 || parent_rate == 0)
return -EINVAL;
if (ls2k_caculate_freq(rate, &pll_cfg)) {
return -EINVAL;
}
ls2k_config_pll(reg + PLL_NODE_0_OFFSET, &pll_cfg);
return 0;
}
static struct clk_ops ls2k500_node_clk_ops = {
.recalc_rate = ls2k500_node_recalc_rate,
.round_rate = ls2k500_node_round_rate,
.set_rate = ls2k500_node_set_rate,
};
static unsigned long ls2k500_cpu_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
unsigned int freqscale;
u32 val;
val = __raw_readl(reg + PLL_FREQ_SC);
freqscale = (val >> PLL_FREQSCALE_NODE_SHIFT) & PLL_FREQSCALE_NODE_MASK;
++freqscale;
freqscale >>= 3; // /8
return parent_rate / freqscale;
}
static long ls2k500_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
int scale;
if (!rate || !*parent_rate)
return 0;
scale = rate * 8 / (*parent_rate) - 1;
return (*parent_rate) * (scale + 1) / 8;
}
static int ls2k500_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
int scale;
u32 val;
if (!rate || !parent_rate)
return -EINVAL;
scale = rate * 8 / parent_rate - 1;
val = __raw_readl(reg + PLL_FREQ_SC);
val &= (~PLL_FREQSCALE_NODE_MASK) << PLL_FREQSCALE_NODE_SHIFT;
val |= (scale & PLL_FREQSCALE_NODE_MASK) << PLL_FREQSCALE_NODE_SHIFT;
__raw_writel(val, reg + PLL_FREQ_SC);
return 0;
}
static struct clk_ops ls2k500_cpu_clk_ops = {
.recalc_rate = ls2k500_cpu_recalc_rate,
.round_rate = ls2k500_cpu_round_rate,
.set_rate = ls2k500_cpu_set_rate,
};
//通用的倍率计算函数 不管是乘法还是除法
static unsigned int ls2k_generic_magnification_calu(struct ls2k_reg_field_desc* reg_set)
{
int i = 0;
unsigned int total = 1;
while(1) {
unsigned int reg_value, value;
if(!reg_set[i].bit_mask)
break;
reg_value = __raw_readl(reg + reg_set[i].reg_offset);
value = (reg_value >> reg_set[i].bit_offset) & reg_set[i].bit_mask;
total *= value;
++i;
}
return total;
}
static unsigned int ls2k_mul_calu(struct ls2k_reg_field_desc* reg_set)
{
return ls2k_generic_magnification_calu(reg_set);
}
static unsigned int ls2k_div_calu(struct ls2k_reg_field_desc* reg_set)
{
return ls2k_generic_magnification_calu(reg_set);
}
//通用的freqscale 乘法部分计算 需要 + 1 (7+1)
static unsigned long ls2k_freqscale_mul_calu(struct ls2k_reg_field_desc* reg_set)
{
int i = 0;
unsigned int total = 1;
while(1) {
unsigned int reg_value, value;
if(!reg_set[i].bit_mask)
break;
reg_value = __raw_readl(reg + reg_set[i].reg_offset);
value = (reg_value >> reg_set[i].bit_offset) & reg_set[i].bit_mask;
total *= (value + 1);
++i;
}
return total;
}
//通用的freqscale 除法部分计算 按照位域的最大值 + 1 比如 usb 就是 (freqscale + 1 / 8) 这个8就是返回值
static unsigned long ls2k_freqscale_div_calu(struct ls2k_reg_field_desc* reg_set)
{
int i = 0;
unsigned long total = 1;
while(1) {
if(!reg_set[i].bit_mask)
break;
total *= (reg_set[i].bit_mask + 1);
++i;
}
return total;
}
/*
* @brief: 倍频系数和分频系数的计算函数
* 逻辑上就是对*_reg_set分析 把全部倍频系数 乘起来 把全部分频系数 乘起来
* 然后 赋值到 参数 mul_result 和 div_result
* @param[in]: 参数1(cur_clock): 这个要计算的时钟的描述结构体 里面包含 *_reg_set
* @param[out]: 参数2(mul_result): 倍频的总系数 需要传入一个 unsigned long 的变量的地址 作为结果返回
* @param[out]: 参数3(div_result): 分频的总系数 需要传入一个 unsigned long 的变量的地址 作为结果返回
* @return: 参数传进来的指针有一个为NULL 返回 -EFAULT 否则返回 0 代表计算成功
*/
static int ls2k_mul_div_total_calu(struct ls2k_clock_desc* cur_clock, unsigned long* mul_result, unsigned long* div_result)
{
unsigned long mul = 1, div = 1, freq_sc_mul = 1, freq_sc_div = 1;
if(!mul_result || !div_result)
return -EFAULT;
if(!cur_clock)
return -EFAULT;
if(cur_clock->mul_reg_set)
mul = cur_clock->mul_calu_func ? cur_clock->mul_calu_func(cur_clock->mul_reg_set)
: ls2k_mul_calu(cur_clock->mul_reg_set);
if(cur_clock->div_reg_set)
div = cur_clock->div_calu_func ? cur_clock->div_calu_func(cur_clock->div_reg_set)
: ls2k_div_calu(cur_clock->div_reg_set);
if(cur_clock->freqscale_reg_set) {
freq_sc_mul = cur_clock->freqscale_mul_calu_func ? cur_clock->freqscale_mul_calu_func(cur_clock->freqscale_reg_set)
: ls2k_freqscale_mul_calu(cur_clock->freqscale_reg_set);
freq_sc_div = cur_clock->freqscale_div_calu_func ? cur_clock->freqscale_div_calu_func(cur_clock->freqscale_reg_set)
: ls2k_freqscale_div_calu(cur_clock->freqscale_reg_set);
}
mul *= freq_sc_mul;
div *= freq_sc_div;
mul_result[0] = mul;
div_result[0] = div;
return 0;
}
/*
* 时钟的描述结构体的数组 用于描述 整个时钟结构
* 注意 必须要让父节点先于子节点声明,否则在处理逻辑中会引发bug
* 参考时钟 REF_CLK_NAME 必须是第一个变量
*/
static struct ls2k_clock_desc ls2k_clock_desc_set[CLK_CNT] = {
{
.name = REF_CLK_NAME,
.parent_name = NULL,
.dts_desc = 1,
},
{
.name = "node_clk",
.parent_name = "ref_clk",
.parent_relevant = PARENT_BRANCH,
.clk_ops = &ls2k500_node_clk_ops,
},
{
.name = "cpu_clk",
.parent_name = "node_clk",
.parent_relevant = PARENT_BRANCH,
.clk_ops = &ls2k500_cpu_clk_ops,
},
{
.name = "scache_clk",
.parent_name = "node_clk",
.parent_relevant = PARENT_BRANCH,
.clk_ops = &ls2k500_cpu_clk_ops,
},
{
.name = "iodma_clk",
.parent_name = "node_clk",
.parent_relevant = PARENT_BRANCH,
.clk_ops = &ls2k500_cpu_clk_ops,
},
{
.name = "ddr_p_clk",
.parent_name = "ref_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.mul_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_0_OFFSET, .bit_offset = PLL_DDR_0_LOOPC_SHIFT, .bit_mask = PLL_DDR_0_LOOPC_MASK}, {.bit_mask = 0}},
.div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_0_OFFSET, .bit_offset = PLL_DDR_0_REFC_SHIFT, .bit_mask = PLL_DDR_0_REFC_MASK}, {.bit_mask = 0}},
},
{
.name = "ddr_clk",
.parent_name = "ddr_p_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_0_OFFSET, .bit_offset = PLL_DDR_0_ODIV_SHIFT, .bit_mask = PLL_DDR_0_ODIV_MASK}, {.bit_mask = 0}},
},
{
.name = "hda_clk",
.parent_name = "ddr_p_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_1_OFFSET, .bit_offset = PLL_DDR_1_ODIV_HDA_SHIFT, .bit_mask = PLL_DDR_1_ODIV_HDA_MASK}, {.bit_mask = 0}},
},
{
.name = "net_clk",
.parent_name = "ddr_p_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_1_OFFSET, .bit_offset = PLL_DDR_1_ODIV_NET_SHIFT, .bit_mask = PLL_DDR_1_ODIV_NET_MASK}, {.bit_mask = 0}},
},
{
.name = "print_lsu_clk",
.parent_name = "net_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_PRINT_SHIFT, .bit_mask = PLL_FREQSCALE_PRINT_MASK},
{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_LSU_SHIFT, .bit_mask = PLL_FREQSCALE_LSU_MASK},
{.bit_mask = 0}},
},
{
.name = "dc0_clk",
.parent_name = "ref_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.mul_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX0_0_OFFSET, .bit_offset = PLL_PIX0_0_LOOPC_SHIFT, .bit_mask = PLL_PIX0_0_LOOPC_MASK}, {.bit_mask = 0}},
.div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX0_0_OFFSET, .bit_offset = PLL_PIX0_0_REFC_SHIFT, .bit_mask = PLL_PIX0_0_REFC_MASK},
{.reg_offset = PLL_PIX0_0_OFFSET, .bit_offset = PLL_PIX0_0_ODIV_SHIFT, .bit_mask = PLL_PIX0_0_ODIV_MASK},
{.bit_mask = 0}},
},
{
.name = "dc1_clk",
.parent_name = "ref_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.mul_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX1_0_OFFSET, .bit_offset = PLL_PIX1_0_LOOPC_SHIFT, .bit_mask = PLL_PIX1_0_LOOPC_MASK}, {.bit_mask = 0}},
.div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX1_0_OFFSET, .bit_offset = PLL_PIX1_0_REFC_SHIFT, .bit_mask = PLL_PIX1_0_REFC_MASK},
{.reg_offset = PLL_PIX1_0_OFFSET, .bit_offset = PLL_PIX1_0_ODIV_SHIFT, .bit_mask = PLL_PIX1_0_ODIV_MASK},
{.bit_mask = 0}},
},
{
.name = "soc_clk",
.parent_name = "ref_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.mul_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_SOC_0_OFFSET, .bit_offset = PLL_SOC_0_LOOPC_SHIFT, .bit_mask = PLL_SOC_0_LOOPC_MASK}, {.bit_mask = 0}},
.div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_SOC_0_OFFSET, .bit_offset = PLL_SOC_0_REFC_SHIFT, .bit_mask = PLL_SOC_0_REFC_MASK}, {.bit_mask = 0}},
},
{
.name = "gpu_clk",
.parent_name = "soc_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_SOC_0_OFFSET, .bit_offset = PLL_SOC_0_ODIV_SHIFT, .bit_mask = PLL_SOC_0_ODIV_MASK}, {.bit_mask = 0}},
.freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_GPU_SHIFT, .bit_mask = PLL_FREQSCALE_GPU_MASK}, {.bit_mask = 0}},
},
{
.name = "gmac_clk",
.parent_name = "soc_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_SOC_1_OFFSET, .bit_offset = PLL_SOC_1_ODIV_GMAC_SHIFT, .bit_mask = PLL_SOC_1_ODIV_GMAC_MASK}, {.bit_mask = 0}},
},
{
.name = "sb_clk",
.parent_name = "soc_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_SOC_1_OFFSET, .bit_offset = PLL_SOC_1_ODIV_SB_SHIFT, .bit_mask = PLL_SOC_1_ODIV_SB_MASK}, {.bit_mask = 0}},
},
{
.name = "boot_clk",
.parent_name = "sb_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_SB_SHIFT, .bit_mask = PLL_FREQSCALE_SB_MASK}, {.bit_mask = 0}},
},
{
.name = "sata_clk",
.parent_name = "boot_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_SATA_SHIFT, .bit_mask = PLL_FREQSCALE_SATA_MASK}, {.bit_mask = 0}},
},
{
.name = "usb_clk",
.parent_name = "boot_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_USB_SHIFT, .bit_mask = PLL_FREQSCALE_USB_MASK}, {.bit_mask = 0}},
},
{
.name = "apb_clk",
.parent_name = "boot_clk",
.parent_relevant = PARENT_BRANCH,
.fixed = 1,
.freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_APB_SHIFT, .bit_mask = PLL_FREQSCALE_APB_MASK}, {.bit_mask = 0}},
},
};
/*
* @brief: CONFIG_LOONGSON2_SYSCLK_SOFT 的情况下 ls2k500_clk_setup 函数要做的主要任务
* 扫描上面的 ls2k_clock_desc_set 得到整个时钟结构
* 然而第一个时钟默认不用扫,在 ls2k500_clk_setup 开头就会对 参数时钟中注册
* @param[in]: 参数1(node): 这是dts中 始终管理驱动节点的信息 例如这个 clocks:clock-controller@1fe10400
* 用于扫描dts中指定的外部时钟
* @return: 如果成功返回 0
* clks 为空则返回 -EFAULT
* node 为空 | dts里面没有要扫描的必要的时钟声明 | ls2k_clock_desc_set里面的某一个 PARENT_CLONE 的 父节点不存在 返回 -ENODEV
*/
static int register_clock(struct device_node *node)
{
int i = 1; // ref clk is registered!
struct ls2k_clock_desc* cur_clock;
unsigned long rate;
if(!clks) {
pr_err("%s func %s: ls2k500_clks kalloc failed\n", __FILE__, __func__);
return -EFAULT;
}
for(; i < CLK_CNT; ++i) {
int err;
cur_clock = ls2k_clock_desc_set + i;
clks[i] = NULL;
if(cur_clock->dts_desc) {
//dts 扫描
if(!node && !cur_clock->essential_n) {
pr_err("%s func %s: device node is NULL can't scan dts\n", __FILE__, __func__);
return -ENODEV;
}
clks[i] = of_clk_get_by_name(node, cur_clock->name);
if (IS_ERR(clks[i])) {
pr_err("%s func %s: not found %s clock in device tree or maybe you should add this clock in %s clock-names key\n",
__FILE__, __func__, cur_clock->name, node->full_name);
if(cur_clock->essential_n) {
clks[i] = NULL;
continue;
} else
return -ENODEV;
}
err = clk_register_clkdev(clks[i], cur_clock->name, NULL);
if (err && !cur_clock->essential_n)
return err;
}
else if(cur_clock->parent_relevant == PARENT_BRANCH) {
if(cur_clock->fixed) {
//从父节点有分频倍频而来的的时钟 且固定不变
unsigned long mul_total = 1, div_total = 1;
ls2k_mul_div_total_calu(cur_clock, &mul_total, &div_total);
clks[i] = clk_register_fixed_factor(NULL, cur_clock->name, cur_clock->parent_name, 0, mul_total, div_total);
clk_register_clkdev(clks[i], cur_clock->name, NULL);
}
else {
//从父节点有分频倍频而来的的时钟 且会改变
struct clk_hw *cur_cpu_hw;
cur_cpu_hw = clk_hw_register_pll(NULL, cur_clock->name, cur_clock->parent_name, cur_clock->clk_ops, 0);
clks[i] = cur_cpu_hw->clk;
clk_register_clkdev(clks[i], cur_clock->name, NULL);
}
}
else if(cur_clock->parent_relevant == PARENT_CLONE) {
//从父节点直接使用的时钟 同一个时钟
int parent_index = 0;
//null name not found
if(!cur_clock->parent_name)
parent_index = i;
for(; parent_index < i; ++parent_index) {
if(!strcmp(cur_clock->parent_name, ls2k_clock_desc_set[parent_index].name)) {
clks[i] = clks[parent_index];
clk_register_clkdev(clks[i], cur_clock->name, NULL);
}
}
//not found parent
if(parent_index == i) {
//not found
printk(KERN_INFO "%s func %s: warning: clk not found parent clk, name is %s, parent is %s\n",
__FILE__, __func__, cur_clock->name, cur_clock->parent_name);
if(!cur_clock->essential_n)
return -ENODEV;
}
}
else
printk(KERN_INFO "%s func %s: warning: clk register type unkonwn! name is %s\n", __FILE__, __func__, cur_clock->name);
if(clks[i]) {
rate = clk_get_rate(clks[i]);
//文件开头 #if 判断开的话 就能看到所有扫描的时钟的信息
DEBUG_INFO("cur clock info name:%s\tpname:%s\trate:%ld\tdts_desc:%d\tparent_relevant:%d\tfixed:%d\n",
cur_clock->name, cur_clock->parent_name, rate, cur_clock->dts_desc, cur_clock->parent_relevant, cur_clock->fixed);
}
}
return 0;
}
#endif
static struct clk ** __init ls2k500_clk_setup(struct device_node *node)
{
#ifndef CONFIG_LOONGSON2_SYSCLK_SOFT
int start_ = CLK_NODE;
int end_ = CLK_PCI;
int i = 0;
char *con_ids[] = { REF_CLK_NAME,
"node_clk", "cpu_clk", "scache_clk", "iodma_clk",
"ddr_p_clk", "ddr_clk", "hda_clk", "net_clk", "print_lsu_clk",
"dc0_clk",
"dc1_clk",
"soc_clk", "gpu_clk", "gmac_clk", "sb_clk", "boot_clk", "sata_clk", "usb_clk", "apb_clk",
"pci_clk"};
#endif
struct clk_onecell_data *clk_data;
int ndivs = CLK_CNT;
int err = -EINVAL;
reg = of_iomap(node, 0);
if(!reg) {
pr_err("%s: Could not map registers for divs-clk: %s\n", __func__, node->full_name);
return NULL;
}
pr_debug("lsdbg---> %s reg:0x%lx", __func__, (unsigned long)reg);
clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
if(!clk_data)
goto out_unmap;
clks = kcalloc(ndivs, sizeof(*clks), GFP_KERNEL);
if(!clks)
goto free_clkdata;
clk_data->clks = clks;
clk_data->clk_num = CLK_CNT;
clks[CLK_REF] = of_clk_get_by_name(node, REF_CLK_NAME);
if (IS_ERR(clks[CLK_REF])) {
pr_err("%s func %s: not found %s clock in device tree or maybe you should add this clock in %s clock-names key\n",
__FILE__, __func__, REF_CLK_NAME, node->full_name);
err = -ENODEV;
goto free_clks;
}
err = clk_register_clkdev(clks[CLK_REF], REF_CLK_NAME, NULL);
if (err) {
goto free_clks;
}
refclk_rate = clk_get_rate(clks[CLK_REF]);
pr_debug("lsdbg--->%s refclk rate:%ld", __func__, refclk_rate);
if (!refclk_rate) {
err = -EINVAL;
goto free_clks;
}
#ifdef CONFIG_LOONGSON2_SYSCLK_HW_LOWFREQ
{
int fixed_clks[] = {100000000,
500000000, 500000000, 500000000, 500000000,
480000000, 480000000, 24000000, 320000000, 1250000,
200000000,
200000000,
200000000, 200000000, 125000000, 100000000, 100000000, 100000000, 100000000, 100000000,
33000000};
for (i = start_; i <= end_; i++) {
clks[i] = clk_register_fixed_rate(NULL, con_ids[i], NULL, 0, fixed_clks[i-start_]);
clk_register_clkdev(clks[i], con_ids[i], NULL);
}
}
#endif
#ifdef CONFIG_LOONGSON2_SYSCLK_HW_HIGFREQ
{
int fixed_clks[] = {100000000,
800000000, 800000000, 800000000, 800000000,
600000000, 600000000, 24000000, 400000000, 1562500,
400000000,
400000000,
300000000, 300000000, 125000000, 150000000, 150000000, 150000000, 150000000, 150000000,
33000000};
for (i = start_; i <= end_; i++) {
clks[i] = clk_register_fixed_rate(NULL, con_ids[i], NULL, 0, fixed_clks[i-start_]);
clk_register_clkdev(clks[i], con_ids[i], NULL);
}
}
#endif
#ifdef CONFIG_LOONGSON2_SYSCLK_SOFT
if(register_clock(node))
goto free_clks;
#endif
#ifdef CONFIG_LOONGSON2_SYSCLK_HW_BYPASS
for (i = start_; i <= end_; i++) {
clks[i] = clks[CLK_REF];
clk_register_clkdev(clks[i], con_ids[i], NULL);
}
#endif
if (of_clk_add_provider(node, of_clk_src_onecell_get, clk_data)) {
pr_err("%s: failed to add clock provider for\n",
__func__);
goto free_clks;
}
return clks;
free_clks:
kfree(clks);
free_clkdata:
kfree(clk_data);
out_unmap:
iounmap(reg);
return NULL;
}
static void __init ls2k500_clk_init(struct device_node *node)
{
ls2k500_clk_setup(node);
}
CLK_OF_DECLARE(ls2k500_clk, "loongson,ls2k500-clk", ls2k500_clk_init);