Skip to content

Commit 5a376eb

Browse files
widgetiiclaude
andcommitted
clocks: brief survey output, drop family/notes/non-decoded entries
PR review feedback (#163): the YAML emitted by the no-arg `ipctool` survey was too verbose for a daily-driver summary, and the previous commit's `note:` fields + `family:` line added noise without helping anyone read the output. Changes: - `clocks_build_json()` gains a `bool brief` parameter. - `brief = true` (used by `ipctool` no-arg survey): emits only `cpu_pll.freq_mhz`, `ddr.data_rate_mbps`, `hpm.bin`. - `brief = false` (used by `ipctool clocks`): emits the full register-by-register YAML the subcommand has always produced. - Remove the `family:` line; the chip name is already in `chip.model` one level up. - Remove every `note:` field. They were either workarounds for "we haven't fully decoded this register" or restated info already obvious from sibling fields. - Remove `pll_shadow_0c/14` (#162) and `ddr_pll/eth_pll/video_pll/ pll_lock_status` (this PR's earlier commit). Their FBDIV bit layout on V4 differs from APLL (field-shaped bytes sit at [23:16] rather than [11:0]) and the REFDIV/POSTDIV positions aren't confirmed -- shipping the raw words with a "decode TBD" note was noise. Per review: decypher or remove totally -- removed until a DDR-throughput probe and ethernet PHY rate cross-check anchor the layout. - Keep the lock-bit signal that IS reliable: APLL lock from PERI_CRG_PLL122 bit 0. Expose it as `cpu_pll.locked: true/false` in the full output (bit 0 = APLL on V4 confirmed across all three lab boards; other bits' assignment on V4 still TBD). Survey output is now (Goke V300 example): clocks: cpu_pll: freq_mhz: 900 ddr: data_rate_mbps: 1800 hpm: bin: mid Verified end-to-end on all three lab boards (hi3516ev300 OpenIPC, gk7205v300 OpenIPC, gk7205v300 XM Sofia): identical 8-line `clocks:` section in survey mode; full `ipctool clocks` output unchanged except for the dropped notes/family and the added cpu_pll.locked. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent fe47c5c commit 5a376eb

4 files changed

Lines changed: 104 additions & 202 deletions

File tree

src/clocks.c

Lines changed: 67 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ static uint32_t extract_field(uint32_t raw, uint8_t shift, uint8_t width) {
4141
return (raw >> shift) & mask;
4242
}
4343

44-
static cJSON *decode_pll(const struct pll_info *pll) {
44+
static cJSON *decode_pll(const struct pll_info *pll, bool brief) {
4545
cJSON *j_inner = cJSON_CreateObject();
4646

4747
/* Read ctrl_reg1 (FRACDIV / POSTDIV1 / POSTDIV2) */
@@ -56,9 +56,6 @@ static cJSON *decode_pll(const struct pll_info *pll) {
5656

5757
if (!ok1 || !ok2) {
5858
ADD_PARAM("error", "register read failed");
59-
ADD_PARAM_FMT("ctrl_reg1", "0x%08x", pll->ctrl_reg1);
60-
if (pll->ctrl_reg2)
61-
ADD_PARAM_FMT("ctrl_reg2", "0x%08x", pll->ctrl_reg2);
6259
return j_inner;
6360
}
6461

@@ -76,6 +73,23 @@ static cJSON *decode_pll(const struct pll_info *pll) {
7673
if (pll->refdiv_width == 0)
7774
refdiv = 1;
7875

76+
/* Compute frequency. f = input * (FBDIV + FRACDIV/2^frac_width) /
77+
* (REFDIV * POSTDIV1 * POSTDIV2). */
78+
double freq_mhz = 0.0;
79+
uint32_t denom = refdiv * pdiv1 * pdiv2;
80+
if (fbdiv != 0 && denom != 0) {
81+
uint64_t numer_khz = (uint64_t)pll->input_khz * fbdiv;
82+
if (pll->frac_width)
83+
numer_khz +=
84+
((uint64_t)pll->input_khz * fracdiv) >> pll->frac_width;
85+
freq_mhz = (double)numer_khz / (double)denom / 1000.0;
86+
}
87+
88+
if (brief) {
89+
ADD_PARAM_NUM("freq_mhz", freq_mhz);
90+
return j_inner;
91+
}
92+
7993
ADD_PARAM_FMT("ctrl_reg1", "0x%08x", pll->ctrl_reg1);
8094
ADD_PARAM_FMT("ctrl_reg1_raw", "0x%08x", raw1);
8195
if (pll->ctrl_reg2 && pll->ctrl_reg2 != pll->ctrl_reg1) {
@@ -88,70 +102,28 @@ static cJSON *decode_pll(const struct pll_info *pll) {
88102
ADD_PARAM_NUM("postdiv2", pdiv2);
89103
if (pll->frac_width)
90104
ADD_PARAM_FMT("fracdiv", "0x%06x", fracdiv);
91-
92-
if (fbdiv == 0) {
93-
ADD_PARAM("note", "PLL gated (FBDIV=0)");
94-
ADD_PARAM_NUM("freq_mhz", 0);
95-
return j_inner;
96-
}
97-
uint32_t denom = refdiv * pdiv1 * pdiv2;
98-
if (denom == 0) {
99-
ADD_PARAM("note", "invalid divisor (REFDIV*POSTDIV1*POSTDIV2 = 0)");
100-
ADD_PARAM_NUM("freq_mhz", 0);
101-
return j_inner;
102-
}
103-
/* f = input * (FBDIV + FRACDIV/2^24) / (REFDIV * POSTDIV1 * POSTDIV2)
104-
* Compute in microhertz to keep the integer part exact. */
105-
uint64_t numer_khz = (uint64_t)pll->input_khz * fbdiv;
106-
if (pll->frac_width)
107-
numer_khz += ((uint64_t)pll->input_khz * fracdiv) >> pll->frac_width;
108-
double freq_mhz = (double)numer_khz / (double)denom / 1000.0;
109105
ADD_PARAM_NUM("freq_mhz", freq_mhz);
110-
return j_inner;
111-
}
112106

113-
static cJSON *decode_raw(const struct raw_reg_info *r) {
114-
cJSON *j_inner = cJSON_CreateObject();
115-
uint32_t raw1;
116-
if (!mem_reg(r->reg, &raw1, OP_READ)) {
117-
ADD_PARAM("error", "register read failed");
118-
ADD_PARAM_FMT("reg", "0x%08x", r->reg);
119-
return j_inner;
120-
}
121-
if (r->reg2) {
122-
/* Two-register PLL config pair: report both raws. */
123-
uint32_t raw2;
124-
bool ok2 = mem_reg(r->reg2, &raw2, OP_READ);
125-
ADD_PARAM_FMT("ctrl_reg1", "0x%08x", r->reg);
126-
ADD_PARAM_FMT("ctrl_reg1_raw", "0x%08x", raw1);
127-
ADD_PARAM_FMT("ctrl_reg2", "0x%08x", r->reg2);
128-
if (ok2)
129-
ADD_PARAM_FMT("ctrl_reg2_raw", "0x%08x", raw2);
130-
else
131-
ADD_PARAM("ctrl_reg2_raw", "<read failed>");
132-
} else {
133-
ADD_PARAM_FMT("reg", "0x%08x", r->reg);
134-
ADD_PARAM_FMT("raw", "0x%08x", raw1);
107+
/* Optional lock-bit check (e.g. PERI_CRG_PLL122 bit 0 = APLL on V4). */
108+
if (pll->lock_reg) {
109+
uint32_t lock_raw;
110+
if (mem_reg(pll->lock_reg, &lock_raw, OP_READ)) {
111+
bool locked = (lock_raw >> pll->lock_bit) & 1u;
112+
cJSON_AddItemToObject(j_inner, "locked", cJSON_CreateBool(locked));
113+
}
135114
}
136-
if (r->note)
137-
ADD_PARAM("note", r->note);
138115
return j_inner;
139116
}
140117

141-
static cJSON *decode_mux(const struct mux_info *mux) {
118+
static cJSON *decode_mux(const struct mux_info *mux, bool brief) {
142119
cJSON *j_inner = cJSON_CreateObject();
143120
uint32_t raw;
144121
if (!mem_reg(mux->reg, &raw, OP_READ)) {
145122
ADD_PARAM("error", "register read failed");
146-
ADD_PARAM_FMT("reg", "0x%08x", mux->reg);
147123
return j_inner;
148124
}
149125
uint8_t sel = (raw >> mux->sel_shift) & mux->sel_mask;
150126

151-
ADD_PARAM_FMT("reg", "0x%08x", mux->reg);
152-
ADD_PARAM_FMT("raw", "0x%08x", raw);
153-
ADD_PARAM_NUM("cksel", sel);
154-
155127
uint16_t mhz = 0;
156128
bool found = false;
157129
for (size_t i = 0; i < mux->table_len; i++) {
@@ -161,12 +133,23 @@ static cJSON *decode_mux(const struct mux_info *mux) {
161133
break;
162134
}
163135
}
136+
137+
if (brief) {
138+
if (mux->rate_mult)
139+
ADD_PARAM_NUM("data_rate_mbps",
140+
(uint32_t)mhz * (found ? mux->rate_mult : 0));
141+
else
142+
ADD_PARAM_NUM("freq_mhz", mhz);
143+
return j_inner;
144+
}
145+
146+
ADD_PARAM_FMT("reg", "0x%08x", mux->reg);
147+
ADD_PARAM_FMT("raw", "0x%08x", raw);
148+
ADD_PARAM_NUM("cksel", sel);
164149
if (found) {
165150
ADD_PARAM_NUM("freq_mhz", mhz);
166151
if (mux->rate_mult)
167152
ADD_PARAM_NUM("data_rate_mbps", (uint32_t)mhz * mux->rate_mult);
168-
} else {
169-
ADD_PARAM("note", "cksel value not in known table");
170153
}
171154
return j_inner;
172155
}
@@ -188,19 +171,23 @@ static const char *hpm_bin(uint16_t v, const struct hpm_info *h) {
188171
return "high";
189172
}
190173

191-
static cJSON *decode_hpm(const struct hpm_info *h) {
174+
static cJSON *decode_hpm(const struct hpm_info *h, bool brief) {
192175
cJSON *j_inner = cJSON_CreateObject();
193176
uint32_t raw;
194177
if (!mem_reg(h->reg, &raw, OP_READ) || raw == 0xFFFFFFFF) {
195-
/* HPM register absent on this variant — caller should treat NULL-ish
196-
* by simply omitting; we return an empty object and let the parent
197-
* decide. */
178+
/* HPM register absent on this variant — caller treats NULL by
179+
* omitting the section entirely. */
198180
cJSON_Delete(j_inner);
199181
return NULL;
200182
}
201183
uint16_t value = (raw >> h->value_shift) & h->value_mask;
202184
const char *bin = hpm_bin(value, h);
203185

186+
if (brief) {
187+
ADD_PARAM("bin", bin);
188+
return j_inner;
189+
}
190+
204191
ADD_PARAM_FMT("reg", "0x%08x", h->reg);
205192
ADD_PARAM_FMT("raw", "0x%08x", raw);
206193
ADD_PARAM_NUM("value", value);
@@ -211,12 +198,6 @@ static cJSON *decode_hpm(const struct hpm_info *h) {
211198
cJSON_AddItemToArray(window, cJSON_CreateNumber(h->bin_max));
212199
cJSON_AddItemToObject(j_inner, "binning_window", window);
213200

214-
if (!strcmp(bin, "low") || !strcmp(bin, "below_window")) {
215-
ADD_PARAM("note",
216-
"low-bin silicon; mask ROM may have selected a reduced PLL "
217-
"multiplier at boot");
218-
}
219-
220201
if (h->aux_reg) {
221202
uint32_t aux;
222203
if (mem_reg(h->aux_reg, &aux, OP_READ)) {
@@ -278,36 +259,7 @@ static cJSON *build_cpu_running(void) {
278259
return j_inner;
279260
}
280261

281-
static void add_plls(cJSON *parent, const struct clock_family *fam) {
282-
for (size_t i = 0; i < fam->n_plls; i++) {
283-
cJSON *p = decode_pll(&fam->plls[i]);
284-
cJSON_AddItemToObject(parent, fam->plls[i].name, p);
285-
}
286-
}
287-
288-
static void add_muxes(cJSON *parent, const struct clock_family *fam) {
289-
for (size_t i = 0; i < fam->n_muxes; i++) {
290-
cJSON *m = decode_mux(&fam->muxes[i]);
291-
cJSON_AddItemToObject(parent, fam->muxes[i].name, m);
292-
}
293-
}
294-
295-
static void add_hpms(cJSON *parent, const struct clock_family *fam) {
296-
for (size_t i = 0; i < fam->n_hpms; i++) {
297-
cJSON *h = decode_hpm(&fam->hpms[i]);
298-
if (h)
299-
cJSON_AddItemToObject(parent, fam->hpms[i].name, h);
300-
}
301-
}
302-
303-
static void add_raws(cJSON *parent, const struct clock_family *fam) {
304-
for (size_t i = 0; i < fam->n_raws; i++) {
305-
cJSON *r = decode_raw(&fam->raws[i]);
306-
cJSON_AddItemToObject(parent, fam->raws[i].name, r);
307-
}
308-
}
309-
310-
cJSON *clocks_build_json(void) {
262+
cJSON *clocks_build_json(bool brief) {
311263
/* Make sure chip detection has run and chip_generation is populated. */
312264
if (!getchipname())
313265
return NULL;
@@ -317,17 +269,26 @@ cJSON *clocks_build_json(void) {
317269
return NULL;
318270

319271
cJSON *j_inner = cJSON_CreateObject();
320-
ADD_PARAM("family", fam->label);
321-
322-
add_plls(j_inner, fam);
323-
add_muxes(j_inner, fam);
324-
add_hpms(j_inner, fam);
325-
add_raws(j_inner, fam);
326272

327-
cJSON *running = build_cpu_running();
328-
if (running)
329-
cJSON_AddItemToObject(j_inner, "cpu_running", running);
273+
for (size_t i = 0; i < fam->n_plls; i++) {
274+
cJSON *p = decode_pll(&fam->plls[i], brief);
275+
cJSON_AddItemToObject(j_inner, fam->plls[i].name, p);
276+
}
277+
for (size_t i = 0; i < fam->n_muxes; i++) {
278+
cJSON *m = decode_mux(&fam->muxes[i], brief);
279+
cJSON_AddItemToObject(j_inner, fam->muxes[i].name, m);
280+
}
281+
for (size_t i = 0; i < fam->n_hpms; i++) {
282+
cJSON *h = decode_hpm(&fam->hpms[i], brief);
283+
if (h)
284+
cJSON_AddItemToObject(j_inner, fam->hpms[i].name, h);
285+
}
330286

287+
if (!brief) {
288+
cJSON *running = build_cpu_running();
289+
if (running)
290+
cJSON_AddItemToObject(j_inner, "cpu_running", running);
291+
}
331292
return j_inner;
332293
}
333294

@@ -382,7 +343,7 @@ int clocks_cmd(int argc, char **argv) {
382343
return EXIT_FAILURE;
383344
}
384345

385-
cJSON *clocks = clocks_build_json();
346+
cJSON *clocks = clocks_build_json(false);
386347
if (!clocks) {
387348
fprintf(stderr, "clocks: failed to build clock info\n");
388349
return EXIT_FAILURE;

src/clocks.h

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef CLOCKS_H
22
#define CLOCKS_H
33

4+
#include <stdbool.h>
45
#include <stddef.h>
56
#include <stdint.h>
67

@@ -32,6 +33,8 @@ struct pll_info {
3233
uint8_t refdiv_shift;
3334
uint8_t refdiv_width; /* 0 = refdiv fixed to 1 */
3435
uint32_t input_khz; /* crystal frequency; 24000 on V4 */
36+
uint32_t lock_reg; /* 0 = no lock-bit check (e.g. PERI_CRG_PLL122) */
37+
uint8_t lock_bit; /* bit index in lock_reg */
3538
};
3639

3740
struct mux_entry {
@@ -64,22 +67,6 @@ struct hpm_info {
6467
const char *aux_name;
6568
};
6669

67-
/* Raw register dump entry. Two flavours:
68-
* - single register: reg2 = 0
69-
* - two-register PLL config pair (e.g. {APLL,DPLL,EPLL,VPLL}_CONFIG_0/1
70-
* in the HiSilicon CRG): set both reg (CONFIG_0) and reg2 (CONFIG_1)
71-
*
72-
* The two-register form is for PLLs whose FBDIV/REFDIV bit layout we
73-
* haven't verified yet — we dump the raw words so users can correlate
74-
* with vendor docs without us shipping a wrong decode. */
75-
struct raw_reg_info {
76-
const char *name;
77-
const char *label;
78-
uint32_t reg;
79-
uint32_t reg2; /* 0 = single-register entry */
80-
const char *note;
81-
};
82-
8370
struct clock_family {
8471
int chip_id; /* matches chip_generation, e.g. HISI_V4 */
8572
const char *label;
@@ -89,14 +76,17 @@ struct clock_family {
8976
size_t n_muxes;
9077
const struct hpm_info *hpms;
9178
size_t n_hpms;
92-
const struct raw_reg_info *raws;
93-
size_t n_raws;
9479
};
9580

9681
/* Builds the cJSON tree for the current chip. Returns NULL on unsupported
97-
* chip family. Used by both the `clocks`/`freq` subcommand and the default
98-
* `ipctool` YAML survey. */
99-
cJSON *clocks_build_json(void);
82+
* chip family.
83+
*
84+
* brief = true : only the headline numbers (cpu_pll.freq_mhz,
85+
* ddr.data_rate_mbps, hpm.bin) -- used by the default
86+
* `ipctool` no-arg survey to keep its YAML compact.
87+
* brief = false : full detail (raw register values, all PLL fields,
88+
* HPM aux register, etc.) -- used by `ipctool clocks`. */
89+
cJSON *clocks_build_json(bool brief);
10090

10191
int clocks_cmd(int argc, char **argv);
10292

0 commit comments

Comments
 (0)