This patch add support of hsdk platform-specific commands: hsdk_clock set - set clock from axi_freq, cpu_freq and tun_freq environment variables/command line arguments hsdk_clock get - save clock frequencies to axi_freq, cpu_freq and tun_freq environment variables hsdk_clock print - show CPU, AXI, DDR and TUNNEL current clock frequencies. hsdk_clock print_all - show all currently used clock frequencies. hsdk_init - setup board HW in one of pre-defined configuration (hsdk_hs34 / hsdk_hs36 / hsdk_hs36_ccm / hsdk_hs38 / hsdk_hs38_ccm / hsdk_hs38x2 / hsdk_hs38x3 / hsdk_hs38x4) hsdk_go - run baremetal application on hsdk configured by hsdk_init command. This patch changes default behaviour of 'bootm' command: now we are able to set number of CPUs to be kicked by setting 'core_mask' environment variable before 'bootm' command run. Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>master
parent
1e43118560
commit
ada8affdfe
@ -1,5 +1,5 @@ |
|||||||
AXS10X BOARD |
HSDK BOARD |
||||||
M: Alexey Brodkin <abrodkin@synopsys.com> |
M: Eugeniy Paltsev <paltsev@synopsys.com> |
||||||
S: Maintained |
S: Maintained |
||||||
F: board/synopsys/hsdk/ |
F: board/synopsys/hsdk/ |
||||||
F: configs/hsdk_defconfig |
F: configs/hsdk_defconfig |
||||||
|
@ -0,0 +1,75 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Synopsys, Inc. All rights reserved. |
||||||
|
* Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <clk.h> |
||||||
|
#include <dm/device.h> |
||||||
|
|
||||||
|
#include "clk-lib.h" |
||||||
|
|
||||||
|
#define HZ_IN_MHZ 1000000 |
||||||
|
#define ceil(x, y) ({ ulong __x = (x), __y = (y); (__x + __y - 1) / __y; }) |
||||||
|
|
||||||
|
int soc_clk_ctl(const char *name, ulong *rate, enum clk_ctl_ops ctl) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
ulong mhz_rate, priv_rate; |
||||||
|
struct clk clk; |
||||||
|
|
||||||
|
/* Dummy fmeas device, just to be able to use standard clk_* api */ |
||||||
|
struct udevice fmeas = { |
||||||
|
.name = "clk-fmeas", |
||||||
|
.node = ofnode_path("/clk-fmeas"), |
||||||
|
}; |
||||||
|
|
||||||
|
ret = clk_get_by_name(&fmeas, name, &clk); |
||||||
|
if (ret) { |
||||||
|
pr_err("clock '%s' not found, err=%d\n", name, ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
if (ctl & CLK_ON) { |
||||||
|
ret = clk_enable(&clk); |
||||||
|
if (ret && ret != -ENOSYS && ret != -ENOTSUPP) |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
if ((ctl & CLK_SET) && rate) { |
||||||
|
priv_rate = ctl & CLK_MHZ ? (*rate) * HZ_IN_MHZ : *rate; |
||||||
|
ret = clk_set_rate(&clk, priv_rate); |
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
if (ctl & CLK_OFF) { |
||||||
|
ret = clk_disable(&clk); |
||||||
|
if (ret) { |
||||||
|
pr_err("clock '%s' can't be disabled, err=%d\n", name, ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
priv_rate = clk_get_rate(&clk); |
||||||
|
|
||||||
|
clk_free(&clk); |
||||||
|
|
||||||
|
mhz_rate = ceil(priv_rate, HZ_IN_MHZ); |
||||||
|
|
||||||
|
if (ctl & CLK_MHZ) |
||||||
|
priv_rate = mhz_rate; |
||||||
|
|
||||||
|
if ((ctl & CLK_GET) && rate) |
||||||
|
*rate = priv_rate; |
||||||
|
|
||||||
|
if ((ctl & CLK_PRINT) && (ctl & CLK_MHZ)) |
||||||
|
printf("HSDK: clock '%s' rate %lu MHz\n", name, priv_rate); |
||||||
|
else if (ctl & CLK_PRINT) |
||||||
|
printf("HSDK: clock '%s' rate %lu Hz\n", name, priv_rate); |
||||||
|
else |
||||||
|
debug("HSDK: clock '%s' rate %lu MHz\n", name, mhz_rate); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Synopsys, Inc. All rights reserved. |
||||||
|
* Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __BOARD_CLK_LIB_H |
||||||
|
#define __BOARD_CLK_LIB_H |
||||||
|
|
||||||
|
#include <common.h> |
||||||
|
|
||||||
|
enum clk_ctl_ops { |
||||||
|
CLK_SET = BIT(0), /* set frequency */ |
||||||
|
CLK_GET = BIT(1), /* get frequency */ |
||||||
|
CLK_ON = BIT(2), /* enable clock */ |
||||||
|
CLK_OFF = BIT(3), /* disable clock */ |
||||||
|
CLK_PRINT = BIT(4), /* print frequency */ |
||||||
|
CLK_MHZ = BIT(5) /* all values in MHZ instead of HZ */ |
||||||
|
}; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Depending on the clk_ctl_ops enable / disable / |
||||||
|
* set clock rate from 'rate' argument / read clock to 'rate' argument / |
||||||
|
* print clock rate. If CLK_MHZ flag set in clk_ctl_ops 'rate' is in MHz, |
||||||
|
* otherwise - in Hz. |
||||||
|
* |
||||||
|
* This function expects "clk-fmeas" node in device tree: |
||||||
|
* / { |
||||||
|
* clk-fmeas { |
||||||
|
* clocks = <&cpu_pll>, <&sys_pll>; |
||||||
|
* clock-names = "cpu-pll", "sys-pll"; |
||||||
|
* }; |
||||||
|
* }; |
||||||
|
*/ |
||||||
|
int soc_clk_ctl(const char *name, ulong *rate, enum clk_ctl_ops ctl); |
||||||
|
|
||||||
|
#endif /* __BOARD_CLK_LIB_H */ |
@ -0,0 +1,302 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Synopsys, Inc. All rights reserved. |
||||||
|
* Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "env-lib.h" |
||||||
|
|
||||||
|
#define MAX_CMD_LEN 25 |
||||||
|
|
||||||
|
static void env_clear_common(u32 index, const struct env_map_common *map) |
||||||
|
{ |
||||||
|
map[index].val->val = 0; |
||||||
|
map[index].val->set = false; |
||||||
|
} |
||||||
|
|
||||||
|
static int env_read_common(u32 index, const struct env_map_common *map) |
||||||
|
{ |
||||||
|
u32 val; |
||||||
|
|
||||||
|
if (!env_get_yesno(map[index].env_name)) { |
||||||
|
if (map[index].type == ENV_HEX) { |
||||||
|
val = (u32)env_get_hex(map[index].env_name, 0); |
||||||
|
debug("ENV: %s: = %#x\n", map[index].env_name, val); |
||||||
|
} else { |
||||||
|
val = (u32)env_get_ulong(map[index].env_name, 10, 0); |
||||||
|
debug("ENV: %s: = %d\n", map[index].env_name, val); |
||||||
|
} |
||||||
|
|
||||||
|
map[index].val->val = val; |
||||||
|
map[index].val->set = true; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void env_clear_core(u32 index, const struct env_map_percpu *map) |
||||||
|
{ |
||||||
|
for (u32 i = 0; i < NR_CPUS; i++) { |
||||||
|
(*map[index].val)[i].val = 0; |
||||||
|
(*map[index].val)[i].set = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static int env_read_core(u32 index, const struct env_map_percpu *map) |
||||||
|
{ |
||||||
|
u32 val; |
||||||
|
char command[MAX_CMD_LEN]; |
||||||
|
|
||||||
|
for (u32 i = 0; i < NR_CPUS; i++) { |
||||||
|
sprintf(command, "%s_%u", map[index].env_name, i); |
||||||
|
if (!env_get_yesno(command)) { |
||||||
|
if (map[index].type == ENV_HEX) { |
||||||
|
val = (u32)env_get_hex(command, 0); |
||||||
|
debug("ENV: %s: = %#x\n", command, val); |
||||||
|
} else { |
||||||
|
val = (u32)env_get_ulong(command, 10, 0); |
||||||
|
debug("ENV: %s: = %d\n", command, val); |
||||||
|
} |
||||||
|
|
||||||
|
(*map[index].val)[i].val = val; |
||||||
|
(*map[index].val)[i].set = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int env_validate_common(u32 index, const struct env_map_common *map) |
||||||
|
{ |
||||||
|
u32 value = map[index].val->val; |
||||||
|
bool set = map[index].val->set; |
||||||
|
u32 min = map[index].min; |
||||||
|
u32 max = map[index].max; |
||||||
|
|
||||||
|
/* Check if environment is mandatory */ |
||||||
|
if (map[index].mandatory && !set) { |
||||||
|
pr_err("Variable \'%s\' is mandatory, but it is not defined\n", |
||||||
|
map[index].env_name); |
||||||
|
|
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
/* Check environment boundary */ |
||||||
|
if (set && (value < min || value > max)) { |
||||||
|
if (map[index].type == ENV_HEX) |
||||||
|
pr_err("Variable \'%s\' must be between %#x and %#x\n", |
||||||
|
map[index].env_name, min, max); |
||||||
|
else |
||||||
|
pr_err("Variable \'%s\' must be between %u and %u\n", |
||||||
|
map[index].env_name, min, max); |
||||||
|
|
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int env_validate_core(u32 index, const struct env_map_percpu *map, |
||||||
|
bool (*cpu_used)(u32)) |
||||||
|
{ |
||||||
|
u32 value; |
||||||
|
bool set; |
||||||
|
bool mandatory = map[index].mandatory; |
||||||
|
u32 min, max; |
||||||
|
|
||||||
|
for (u32 i = 0; i < NR_CPUS; i++) { |
||||||
|
set = (*map[index].val)[i].set; |
||||||
|
value = (*map[index].val)[i].val; |
||||||
|
|
||||||
|
/* Check if environment is mandatory */ |
||||||
|
if (cpu_used(i) && mandatory && !set) { |
||||||
|
pr_err("CPU %u is used, but \'%s_%u\' is not defined\n", |
||||||
|
i, map[index].env_name, i); |
||||||
|
|
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
min = map[index].min[i]; |
||||||
|
max = map[index].max[i]; |
||||||
|
|
||||||
|
/* Check environment boundary */ |
||||||
|
if (set && (value < min || value > max)) { |
||||||
|
if (map[index].type == ENV_HEX) |
||||||
|
pr_err("Variable \'%s_%u\' must be between %#x and %#x\n", |
||||||
|
map[index].env_name, i, min, max); |
||||||
|
else |
||||||
|
pr_err("Variable \'%s_%u\' must be between %d and %d\n", |
||||||
|
map[index].env_name, i, min, max); |
||||||
|
|
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
void envs_cleanup_core(const struct env_map_percpu *map) |
||||||
|
{ |
||||||
|
/* Cleanup env struct first */ |
||||||
|
for (u32 i = 0; map[i].env_name; i++) |
||||||
|
env_clear_core(i, map); |
||||||
|
} |
||||||
|
|
||||||
|
void envs_cleanup_common(const struct env_map_common *map) |
||||||
|
{ |
||||||
|
/* Cleanup env struct first */ |
||||||
|
for (u32 i = 0; map[i].env_name; i++) |
||||||
|
env_clear_common(i, map); |
||||||
|
} |
||||||
|
|
||||||
|
int envs_read_common(const struct env_map_common *map) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
|
||||||
|
for (u32 i = 0; map[i].env_name; i++) { |
||||||
|
ret = env_read_common(i, map); |
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int envs_validate_common(const struct env_map_common *map) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
|
||||||
|
for (u32 i = 0; map[i].env_name; i++) { |
||||||
|
ret = env_validate_common(i, map); |
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int envs_read_validate_common(const struct env_map_common *map) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
|
||||||
|
envs_cleanup_common(map); |
||||||
|
|
||||||
|
ret = envs_read_common(map); |
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
|
||||||
|
ret = envs_validate_common(map); |
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int envs_read_validate_core(const struct env_map_percpu *map, |
||||||
|
bool (*cpu_used)(u32)) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
|
||||||
|
envs_cleanup_core(map); |
||||||
|
|
||||||
|
for (u32 i = 0; map[i].env_name; i++) { |
||||||
|
ret = env_read_core(i, map); |
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
for (u32 i = 0; map[i].env_name; i++) { |
||||||
|
ret = env_validate_core(i, map, cpu_used); |
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int envs_process_and_validate(const struct env_map_common *common, |
||||||
|
const struct env_map_percpu *core, |
||||||
|
bool (*cpu_used)(u32)) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
|
||||||
|
ret = envs_read_validate_common(common); |
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
|
||||||
|
ret = envs_read_validate_core(core, cpu_used); |
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int args_envs_read_search(const struct env_map_common *map, |
||||||
|
int argc, char *const argv[]) |
||||||
|
{ |
||||||
|
for (int i = 0; map[i].env_name; i++) { |
||||||
|
if (!strcmp(argv[0], map[i].env_name)) |
||||||
|
return i; |
||||||
|
} |
||||||
|
|
||||||
|
pr_err("Unexpected argument '%s', can't parse\n", argv[0]); |
||||||
|
|
||||||
|
return -ENOENT; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_read_set(const struct env_map_common *map, u32 i, int argc, |
||||||
|
char *const argv[]) |
||||||
|
{ |
||||||
|
char *endp = argv[1]; |
||||||
|
|
||||||
|
if (map[i].type == ENV_HEX) |
||||||
|
map[i].val->val = simple_strtoul(argv[1], &endp, 16); |
||||||
|
else |
||||||
|
map[i].val->val = simple_strtoul(argv[1], &endp, 10); |
||||||
|
|
||||||
|
map[i].val->set = true; |
||||||
|
|
||||||
|
if (*endp == '\0') |
||||||
|
return 0; |
||||||
|
|
||||||
|
pr_err("Unexpected argument '%s', can't parse\n", argv[1]); |
||||||
|
|
||||||
|
map[i].val->set = false; |
||||||
|
|
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
int args_envs_enumerate(const struct env_map_common *map, int enum_by, |
||||||
|
int argc, char *const argv[]) |
||||||
|
{ |
||||||
|
u32 i; |
||||||
|
|
||||||
|
if (argc % enum_by) { |
||||||
|
pr_err("unexpected argument number: %d\n", argc); |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
while (argc > 0) { |
||||||
|
i = args_envs_read_search(map, argc, argv); |
||||||
|
if (i < 0) |
||||||
|
return i; |
||||||
|
|
||||||
|
debug("ARG: found '%s' with index %d\n", map[i].env_name, i); |
||||||
|
|
||||||
|
if (i < 0) { |
||||||
|
pr_err("unknown arg: %s\n", argv[0]); |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
if (arg_read_set(map, i, argc, argv)) |
||||||
|
return -EINVAL; |
||||||
|
|
||||||
|
debug("ARG: value.s '%s' == %#x\n", argv[1], map[i].val->val); |
||||||
|
|
||||||
|
argc -= enum_by; |
||||||
|
argv += enum_by; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Synopsys, Inc. All rights reserved. |
||||||
|
* Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __BOARD_ENV_LIB_H |
||||||
|
#define __BOARD_ENV_LIB_H |
||||||
|
|
||||||
|
#include <common.h> |
||||||
|
#include <config.h> |
||||||
|
#include <linux/kernel.h> |
||||||
|
|
||||||
|
enum env_type { |
||||||
|
ENV_DEC, |
||||||
|
ENV_HEX |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
u32 val; |
||||||
|
bool set; |
||||||
|
} u32_env; |
||||||
|
|
||||||
|
struct env_map_common { |
||||||
|
const char *const env_name; |
||||||
|
enum env_type type; |
||||||
|
bool mandatory; |
||||||
|
u32 min; |
||||||
|
u32 max; |
||||||
|
u32_env *val; |
||||||
|
}; |
||||||
|
|
||||||
|
struct env_map_percpu { |
||||||
|
const char *const env_name; |
||||||
|
enum env_type type; |
||||||
|
bool mandatory; |
||||||
|
u32 min[NR_CPUS]; |
||||||
|
u32 max[NR_CPUS]; |
||||||
|
u32_env (*val)[NR_CPUS]; |
||||||
|
}; |
||||||
|
|
||||||
|
void envs_cleanup_common(const struct env_map_common *map); |
||||||
|
int envs_read_common(const struct env_map_common *map); |
||||||
|
int envs_validate_common(const struct env_map_common *map); |
||||||
|
int envs_read_validate_common(const struct env_map_common *map); |
||||||
|
|
||||||
|
void envs_cleanup_core(const struct env_map_percpu *map); |
||||||
|
int envs_read_validate_core(const struct env_map_percpu *map, |
||||||
|
bool (*cpu_used)(u32)); |
||||||
|
int envs_process_and_validate(const struct env_map_common *common, |
||||||
|
const struct env_map_percpu *core, |
||||||
|
bool (*cpu_used)(u32)); |
||||||
|
|
||||||
|
int args_envs_enumerate(const struct env_map_common *map, |
||||||
|
int enum_by, int argc, char *const argv[]); |
||||||
|
|
||||||
|
#endif /* __BOARD_ENV_LIB_H */ |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue