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 |
||||
M: Alexey Brodkin <abrodkin@synopsys.com> |
||||
HSDK BOARD |
||||
M: Eugeniy Paltsev <paltsev@synopsys.com> |
||||
S: Maintained |
||||
F: board/synopsys/hsdk/ |
||||
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