目次: ROCK64/ROCKPro64
ASUS TinkerBoardで動かしているlinux-nextを最新版に更新したところ、下記のようなエラーを出力して起動しなくなりました。
[ 0.000000] __clk_core_init: Failed to get phase for clk 'sdmmc_drv' [ 0.000000] rockchip_clk_register_branches: failed to register clock sdmmc_drv: -22 [ 0.000000] rockchip_clk_register_branches: failed to register clock sdmmc_sample: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock sdio0_drv: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock sdio0_sample: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock sdio1_drv: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock sdio1_sample: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock emmc_drv: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock emmc_sample: -17
エラーメッセージで検索しても、特にめぼしい報告やパッチに引っかかりません。うーん。困りましたね。
仕方ないので、更新履歴をgit bisectすると2/13から2/14の間にクロックドライバが変更されたことが原因だとわかりました。
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index dc8bdfbd6a0c..ed1797857bae 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -3457,7 +3457,12 @@ static int __clk_core_init(struct clk_core *core)
* Since a phase is by definition relative to its parent, just
* query the current clock phase, or just assume it's in phase.
*/
- clk_core_get_phase(core);
+ ret = clk_core_get_phase(core);
+ if (ret < 0) {
+ pr_warn("%s: Failed to get phase for clk '%s'\n", __func__,
+ core->name);
+ goto out;
+ }
/*
* Set clk's duty cycle.
しかしこの変更は真っ当に見えます。今までエラーを無視していたところをエラーチェックするようにしているだけだからです。
どの辺りでエラーが出るか調べるためprintkを適当に入れたりしながら追ってみると、下記の場所で躓いていました。
// drivers/clk/clk.c
static int __clk_core_init(struct clk_core *core)
{
...
/*
* Set clk's phase by clk_core_get_phase() caching the phase.
* Since a phase is by definition relative to its parent, just
* query the current clock phase, or just assume it's in phase.
*/
phase = clk_core_get_phase(core); //★★変更が影響している場所
if (phase < 0) {
ret = phase;
pr_warn("%s: Failed to get phase for clk '%s'\n", __func__,
core->name);
//goto out;
}
// drivers/clk/clk.c
static int clk_core_get_phase(struct clk_core *core)
{
int ret;
lockdep_assert_held(&prepare_lock);
if (!core->ops->get_phase)
return 0;
/* Always try to update cached phase if possible */
ret = core->ops->get_phase(core->hw); //★★ここでエラーが発生していた
if (ret >= 0)
core->phase = ret;
return ret;
}
独自のget_phaseを持っているドライバがエラーを返すと、clk_core_get_phase() もエラーを返し、先程のエラーメッセージが表示されるみたいです。しかし、この処理自体におかしな箇所はないように思います。真っ当です。
おそらく実装がおかしいのはRockchipのMMCドライバのクロック周りの方でしょう。エラーメッセージに出ているsdmmc_drvを頼りにコードを追います。
// drivers/clk/rockchip/clk-rk3228.c
PNAME(mux_mmc_src_p) = { "cpll", "gpll", "xin24m", "usb480m" };
...
COMPOSITE(SCLK_SDMMC, "sclk_sdmmc", mux_mmc_src_p, 0,
RK2928_CLKSEL_CON(11), 8, 2, MFLAGS, 0, 8, DFLAGS,
RK2928_CLKGATE_CON(2), 11, GFLAGS),
...
//★★sdmmc_drvはこの部分由来です
MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc", RK3228_SDMMC_CON0, 1),
// drivers/clk/rockchip/clk.h
#define MMC(_id, cname, pname, offset, shift) \
{ \
.id = _id, \
.branch_type = branch_mmc, \ //★★この値がカギかな?
.name = cname, \
.parent_names = (const char *[]){ pname }, \
.num_parents = 1, \
.muxdiv_offset = offset, \
.div_shift = shift, \
}
// drivers/clk/rockchip/clk-rk3228.c
static void __init rk3228_clk_init(struct device_node *np)
{
...
rockchip_clk_register_branches(ctx, rk3228_clk_branches, //★★ここでbranch_typeを見ている箇所がある
ARRAY_SIZE(rk3228_clk_branches));
...
}
//★★注: rk3228_clk_initはRK3228のクロックコア初期化関数
// デバイスツリー内のcompatible = "rockchip,rk3228-cru" を持つノードに応じて呼ばれる
CLK_OF_DECLARE(rk3228_cru, "rockchip,rk3228-cru", rk3228_clk_init);
// drivers/clk/rockchip/clk.c
void __init rockchip_clk_register_branches(
struct rockchip_clk_provider *ctx,
struct rockchip_clk_branch *list,
unsigned int nr_clk)
{
...
for (idx = 0; idx < nr_clk; idx++, list++) {
flags = list->flags;
/* catch simple muxes */
switch (list->branch_type) {
...
case branch_mmc: //★★branch_type == branch_mmcだったら
clk = rockchip_clk_register_mmc( //★★クロックの登録をしている
list->name,
list->parent_names, list->num_parents,
ctx->reg_base + list->muxdiv_offset,
list->div_shift
);
break;
...
// drivers/clk/rockchip/clk-mmc-phase.c
struct clk *rockchip_clk_register_mmc(const char *name,
const char *const *parent_names, u8 num_parents,
void __iomem *reg, int shift)
{
...
init.name = name;
init.flags = 0;
init.num_parents = num_parents;
init.parent_names = parent_names;
init.ops = &rockchip_mmc_clk_ops; //★★クロックの操作関数を登録している
...
// drivers/clk/rockchip/clk-mmc-phase.c
static const struct clk_ops rockchip_mmc_clk_ops = {
.recalc_rate = rockchip_mmc_recalc,
.get_phase = rockchip_mmc_get_phase, //★★get_phaseはこの関数
.set_phase = rockchip_mmc_set_phase,
};
// drivers/clk/rockchip/clk-mmc-phase.c
static int rockchip_mmc_get_phase(struct clk_hw *hw)
{
struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw);
unsigned long rate = clk_hw_get_rate(hw);
u32 raw_value;
u16 degrees;
u32 delay_num = 0;
/* See the comment for rockchip_mmc_set_phase below */
if (!rate)
return -EINVAL; //★★エラーを返している
...
原因らしき箇所が見つかりました。クロック周波数(rate)が設定されていなくても、エラーを返す必要はなく、phaseの初期値0を返せば良いはずですから、return -EINVALをreturn 0にすれば良さそうです。
変更すると無事linux-nextが起動できるようになりました。良かった良かった。
こんなバグを2週間以上放置するなんてlinuxらしくないなと思って、パッチを送ろうとしましたが……、なんだかとっても嫌な予感がしたので、rochchip_mmc_get_phaseでLKMLを検索してみました。
検索してびっくり、なんと、全く同じ指摘をしているパッチが先週LKMLに送られています(LKMLへのリンク)。しかも既にclk-nextに取り込まれているじゃないですか。ですがlinux-nextへの反映はまだのようです。
明日まで待っていればclk-nextがlinux-nextに取り込まれるので、何もしなくても解決していたんです。パッチ投稿が3/4なので、今回は本当に気づくタイミングが悪かったです。
Author: Jerome Brunet < >
Date: Tue Mar 3 20:29:56 2020 +0100
clk: rockchip: fix mmc get phase
If the mmc clock has no rate, it can be assumed to be constant.
In such case, there is no measurable phase shift. Just return 0
in this case instead of returning an error.
Fixes: 2760878662a2 ("clk: Bail out when calculating phase fails during clk
registration")
Tested-by: Markus Reichl <m.reichl@fivetechno.de>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c
index 4abe7ff31f53..975454a3dd72 100644
--- a/drivers/clk/rockchip/clk-mmc-phase.c
+++ b/drivers/clk/rockchip/clk-mmc-phase.c
@@ -51,9 +51,9 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw)
u16 degrees;
u32 delay_num = 0;
- /* See the comment for rockchip_mmc_set_phase below */
+ /* Constant signal, no measurable phase shift */
if (!rate)
- return -EINVAL;
+ return 0;
raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift);
前にも、原因を突き止めたら、解決済みだったというパターン(2018年12月4日の日記、2019年9月18日の日記)がありました。
またこの「鶏と卵のパターン」にやられました。ついてない。
コードに特攻する前に、ちゃんと探せば?って思われるかもしれませんが、困ったことにエラーメッセージで検索してもパッチに辿り着けないんです。調べに調べて原因と対策がわかった後に、はぁ?もうパッチあるじゃん!?と気づくから、徒労感が激しい……。
< | 2020 | > | ||||
<< | < | 03 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | - | - | - | - |
合計:
本日:
管理者: Katsuhiro Suzuki(katsuhiro( a t )katsuster.net)
This is Simple Diary 1.0
Copyright(C) Katsuhiro Suzuki 2006-2023.
Powered by PHP 8.2.18.
using GD 2.3.3(png support.)