pax_global_header00006660000000000000000000000064141415222250014510gustar00rootroot0000000000000052 comment=83b2a57edab6c821ccdd701d743bd0a463be12e7 crust-0.5/000077500000000000000000000000001414152222500125145ustar00rootroot00000000000000crust-0.5/.gitignore000066400000000000000000000000501414152222500144770ustar00rootroot00000000000000/..config* /.config* /build/ /defconfig crust-0.5/.travis.yml000066400000000000000000000052171414152222500146320ustar00rootroot00000000000000--- dist: bionic language: c matrix: include: - addons: apt: packages: - codespell env: - MAKEFLAGS=-j$(nproc) install: - git clone --depth 1 -b uncrustify-0.72.0 https://github.com/uncrustify/uncrustify - mkdir -p uncrustify/build - (cd uncrustify/build && cmake -DCMAKE_INSTALL_PREFIX=$PWD -DCMAKE_C_COMPILER=gcc-7 -DCMAKE_CXX_COMPILER=g++-7 ..) - make -C uncrustify/build - export PATH=$PATH:$PWD/uncrustify/build - uncrustify --version name: Run source code checks script: - make check-format - codespell -i 0 -q 4 $(git ls-files) - addons: apt: packages: - doxygen - gcc-aarch64-linux-gnu - graphviz - libc6-dev-arm64-cross env: - CONFIGS="allnoconfig allyesconfig defconfig bananapi_m2_plus_h3_defconfig beelink_x2_defconfig libretech_all_h3_cc_h3_defconfig libretech_all_h3_cc_h5_defconfig orangepi_3_defconfig orangepi_one_defconfig orangepi_pc2_defconfig orangepi_plus2e_defconfig orangepi_win_defconfig orangepi_zero_plus_defconfig pine64_plus_defconfig pine_h64_defconfig pinebook_defconfig pinephone_defconfig tanix_tx6_defconfig tbs_a711_defconfig randconfig randconfig randconfig" - GCC_VER=9.1.1-20190507 - HOST_COMPILE=aarch64-linux-gnu- - MAKEFLAGS=-j$(nproc) install: - curl -LSs https://github.com/stffrdhrn/gcc/releases/download/or1k-$GCC_VER/or1k-linux-musl-$GCC_VER.tar.xz | tar xJf - - export PATH=$PATH:$PWD/or1k-linux-musl/bin name: Build all configs script: - |- set -e cd /tmp ln -s $TRAVIS_BUILD_DIR/Makefile . cat > all.config << 'EOF' CONFIG_COMPILE_TEST=y EOF for config in ${CONFIGS}; do echo ========== begin $config ========== make SRC=$TRAVIS_BUILD_DIR KCONFIG_ALLCONFIG=1 V=1 "$config" make SRC=$TRAVIS_BUILD_DIR TGT="\$(OBJ)/$config" V=1 all check or1k-linux-musl-size build/$config/scp.elf grep __bss_end build/$config/scp.map sha256sum -b build/$config/scp.bin echo =========== end $config =========== done make SRC=$TRAVIS_BUILD_DIR V=1 html ... crust-0.5/.uncrustify000066400000000000000000000567701414152222500147470ustar00rootroot00000000000000align_asm_colon = false align_assign_span = 1 align_assign_thresh = 8 align_enum_equ_span = 3 align_enum_equ_thresh = 0 align_func_params = false align_func_params_gap = 0 align_func_params_span = 0 align_func_params_thresh = 0 align_func_proto_gap = 0 align_func_proto_span = 0 align_keep_extra_space = true align_keep_tabs = false align_left_shift = true align_mix_var_proto = false align_nl_cont = false align_number_right = false align_oc_decl_colon = false align_oc_msg_colon_first = false align_oc_msg_colon_span = 0 align_oc_msg_spec_span = 0 align_on_operator = false align_on_tabstop = false align_pp_define_gap = 0 align_pp_define_span = 3 align_pp_define_together = true align_right_cmt_at_col = 0 align_right_cmt_gap = 0 align_right_cmt_mix = false align_right_cmt_span = 2 align_same_func_call_params = false align_single_line_brace = false align_single_line_brace_gap = 0 align_single_line_func = false align_struct_init_span = 0 align_typedef_amp_style = 2 align_typedef_func = 2 align_typedef_gap = 0 align_typedef_span = 3 align_typedef_star_style = 2 align_var_class_gap = 0 align_var_class_span = 0 align_var_class_thresh = 0 align_var_def_amp_style = 2 align_var_def_attribute = true align_var_def_colon = false align_var_def_colon_gap = 0 align_var_def_gap = 0 align_var_def_inline = true align_var_def_span = 1 align_var_def_star_style = 2 align_var_def_thresh = 1 align_var_struct_gap = 0 align_var_struct_span = 3 align_var_struct_thresh = 0 align_with_tabs = false cmt_c_group = false cmt_c_nl_end = false cmt_c_nl_start = false cmt_convert_tab_to_spaces = false cmt_cpp_group = false cmt_cpp_nl_end = false cmt_cpp_nl_start = false cmt_cpp_to_c = true cmt_indent_multi = true cmt_insert_before_ctor_dtor = false cmt_insert_before_inlines = true cmt_insert_before_preproc = false cmt_insert_class_header = "" cmt_insert_file_footer = "" cmt_insert_file_header = "" cmt_insert_func_header = "" cmt_insert_oc_msg_header = "" cmt_multi_check_last = true cmt_multi_first_len_minimum = 4 cmt_reflow_mode = 0 cmt_sp_after_star_cont = 0 cmt_sp_before_star_cont = 0 cmt_star_cont = true cmt_width = 79 code_width = 79 disable_processing_cmt = "format off" eat_blanks_after_open_brace = true eat_blanks_before_close_brace = true enable_digraphs = false enable_processing_cmt = "format on" force_tab_after_define = false include_category_0 = "<[^/]*>" include_category_1 = "<(?!platform/).*/.*>" include_category_2 = "<.*>" indent_access_spec = 1 indent_access_spec_body = false indent_align_assign = true indent_align_string = true indent_bool_paren = false indent_brace = 0 indent_brace_parent = false indent_braces = false indent_braces_no_class = false indent_braces_no_func = false indent_braces_no_struct = false indent_case_brace = 0 indent_case_shift = 0 indent_class = false indent_class_colon = false indent_class_on_colon = false indent_cmt_with_tabs = false indent_col1_comment = false indent_columns = 8 indent_comma_paren = false indent_constr_colon = false indent_continue = 0 indent_cpp_lambda_body = false indent_cs_delegate_brace = false indent_ctor_init = 0 indent_ctor_init_leading = 2 indent_else_if = false indent_extern = false indent_first_bool_expr = false indent_func_call_param = false indent_func_class_param = false indent_func_const = 0 indent_func_ctor_var_param = false indent_func_def_force_col1 = false indent_func_def_param = false indent_func_param_double = false indent_func_proto_param = false indent_func_throw = 0 indent_ignore_asm_block = true indent_label = 1 indent_member = 0 indent_min_vbrace_open = 0 indent_namespace = false indent_namespace_level = 0 indent_namespace_limit = 0 indent_namespace_single_indent = false indent_oc_block = false indent_oc_block_msg = 0 indent_oc_block_msg_from_brace = false indent_oc_block_msg_from_caret = false indent_oc_block_msg_from_colon = false indent_oc_block_msg_from_keyword = false indent_oc_block_msg_xcode_style = false indent_oc_msg_colon = 0 indent_oc_msg_prioritize_first_colon = true indent_param = 0 indent_paren_after_func_call = false indent_paren_after_func_decl = false indent_paren_after_func_def = false indent_paren_close = 2 indent_paren_nl = false indent_paren_open_brace = false indent_preserve_sql = false indent_relative_single_line_comments = false indent_shift = false indent_sing_line_comments = 0 indent_square_nl = false indent_switch_case = 0 indent_switch_pp = true indent_template_param = false indent_ternary_operator = 2 indent_token_after_brace = true indent_using_block = true indent_var_def_blk = 0 indent_var_def_cont = false indent_vbrace_open_on_tabstop = false indent_with_tabs = 1 indent_xml_string = 0 input_tab_size = 8 ls_code_width = false ls_for_split_full = false ls_func_split_full = false mod_add_long_class_closebrace_comment = 0 mod_add_long_function_closebrace_comment = 0 mod_add_long_ifdef_else_comment = 0 mod_add_long_ifdef_endif_comment = 0 mod_add_long_namespace_closebrace_comment = 0 mod_add_long_switch_closebrace_comment = 0 mod_case_brace = remove mod_full_brace_do = force mod_full_brace_for = remove mod_full_brace_function = ignore mod_full_brace_if = remove mod_full_brace_if_chain = true mod_full_brace_if_chain_only = false mod_full_brace_nl = 2 mod_full_brace_nl_block_rem_mlcond = false mod_full_brace_using = ignore mod_full_brace_while = remove mod_full_paren_if_bool = false mod_move_case_break = false mod_paren_on_return = remove mod_pawn_semicolon = false mod_remove_empty_return = false mod_remove_extra_semicolon = true mod_sort_import = false mod_sort_include = true mod_sort_oc_properties = false mod_sort_oc_property_class_weight = 0 mod_sort_oc_property_getter_weight = 0 mod_sort_oc_property_nullability_weight = 0 mod_sort_oc_property_readwrite_weight = 0 mod_sort_oc_property_reference_weight = 0 mod_sort_oc_property_setter_weight = 0 mod_sort_oc_property_thread_safe_weight = 0 mod_sort_using = false newlines = lf nl_after_access_spec = 0 nl_after_annotation = ignore nl_after_brace_close = true nl_after_brace_open = true nl_after_brace_open_cmt = false nl_after_case = true nl_after_class = 0 nl_after_do = ignore nl_after_for = ignore nl_after_func_body = 2 nl_after_func_body_class = 0 nl_after_func_body_one_liner = 2 nl_after_func_class_proto = 0 nl_after_func_class_proto_group = 0 nl_after_func_proto = 0 nl_after_func_proto_group = 2 nl_after_if = ignore nl_after_label_colon = true nl_after_multiline_comment = true nl_after_return = false nl_after_semicolon = true nl_after_square_assign = ignore nl_after_struct = 2 nl_after_switch = ignore nl_after_synchronized = ignore nl_after_try_catch_finally = 0 nl_after_vbrace_close = false nl_after_vbrace_open = true nl_after_vbrace_open_empty = true nl_after_while = ignore nl_around_cs_property = 0 nl_assign_brace = remove nl_assign_leave_one_liners = true nl_assign_square = ignore nl_before_access_spec = 0 nl_before_block_comment = 0 nl_before_c_comment = 0 nl_before_case = false nl_before_class = 0 nl_before_cpp_comment = 0 nl_before_do = ignore nl_before_for = ignore nl_before_func_body_def = 0 nl_before_func_body_proto = 0 nl_before_func_class_def = 0 nl_before_func_class_proto = 0 nl_before_if = ignore nl_before_if_closing_paren = remove nl_before_return = false nl_before_switch = ignore nl_before_synchronized = ignore nl_before_throw = ignore nl_before_while = ignore nl_between_annotation = ignore nl_between_get_set = 0 nl_brace_brace = ignore nl_brace_catch = ignore nl_brace_else = remove nl_brace_finally = ignore nl_brace_fparen = remove nl_brace_square = ignore nl_brace_struct_var = remove nl_brace_while = remove nl_case_colon_brace = remove nl_catch_brace = ignore nl_class_brace = ignore nl_class_colon = ignore nl_class_init_args = ignore nl_class_leave_one_liners = false nl_collapse_empty_body = false nl_comment_func_def = 1 nl_constr_colon = ignore nl_constr_init_args = ignore nl_cpp_lambda_leave_one_liners = false nl_cpp_ldef_brace = ignore nl_create_for_one_liner = false nl_create_if_one_liner = false nl_create_while_one_liner = false nl_define_macro = true nl_do_brace = remove nl_ds_struct_enum_close_brace = true nl_ds_struct_enum_cmt = false nl_else_brace = remove nl_else_if = remove nl_elseif_brace = ignore nl_end_of_file = force nl_end_of_file_min = 1 nl_enum_brace = remove nl_enum_class = ignore nl_enum_class_identifier = ignore nl_enum_colon_type = ignore nl_enum_identifier_colon = ignore nl_enum_leave_one_liners = false nl_enum_own_lines = ignore nl_fcall_brace = remove nl_fdef_brace = force nl_finally_brace = ignore nl_for_brace = remove nl_func_call_args_multi_line = false nl_func_call_empty = remove nl_func_call_end_multi_line = false nl_func_call_paren = remove nl_func_call_paren_empty = ignore nl_func_call_start_multi_line = false nl_func_class_scope = ignore nl_func_decl_args = ignore nl_func_decl_args_multi_line = false nl_func_decl_empty = remove nl_func_decl_end = remove nl_func_decl_end_multi_line = false nl_func_decl_end_single = ignore nl_func_decl_start = remove nl_func_decl_start_multi_line = false nl_func_decl_start_single = ignore nl_func_def_args = ignore nl_func_def_args_multi_line = false nl_func_def_empty = remove nl_func_def_end = remove nl_func_def_end_multi_line = false nl_func_def_end_single = ignore nl_func_def_paren = remove nl_func_def_paren_empty = ignore nl_func_def_start = remove nl_func_def_start_multi_line = false nl_func_def_start_single = ignore nl_func_leave_one_liners = false nl_func_paren = remove nl_func_paren_empty = ignore nl_func_proto_type_name = remove nl_func_scope_name = ignore nl_func_type_name = force nl_func_type_name_class = ignore nl_func_var_def_blk = 1 nl_getset_brace = ignore nl_getset_leave_one_liners = false nl_if_brace = remove nl_if_leave_one_liners = false nl_max = 2 nl_max_blank_in_func = 2 nl_multi_line_cond = false nl_multi_line_define = false nl_namespace_brace = ignore nl_oc_block_brace = ignore nl_oc_msg_args = false nl_oc_msg_leave_one_liner = false nl_paren_dbrace_open = ignore nl_property_brace = ignore nl_remove_extra_newlines = 0 nl_return_expr = remove nl_scope_brace = ignore nl_split_for_one_liner = true nl_split_if_one_liner = true nl_split_while_one_liner = true nl_squeeze_ifdef = false nl_squeeze_ifdef_top_level = false nl_start_of_file = remove nl_start_of_file_min = 0 nl_struct_brace = remove nl_switch_brace = remove nl_synchronized_brace = ignore nl_template_class = ignore nl_try_brace = ignore nl_type_brace_init_lst = ignore nl_type_brace_init_lst_close = ignore nl_type_brace_init_lst_open = ignore nl_typedef_blk_end = 0 nl_typedef_blk_in = 0 nl_typedef_blk_start = 0 nl_union_brace = remove nl_unittest_brace = ignore nl_using_brace = ignore nl_var_def_blk_end = 0 nl_var_def_blk_in = 2 nl_var_def_blk_start = 0 nl_version_brace = ignore nl_while_brace = remove nl_while_leave_one_liners = false output_tab_size = 8 pos_arith = trail pos_assign = trail pos_bool = trail pos_class_colon = ignore pos_class_comma = ignore pos_comma = trail pos_compare = trail pos_conditional = ignore pos_constr_colon = ignore pos_constr_comma = ignore pos_enum_comma = ignore pp_define_at_level = false pp_if_indent_code = false pp_ignore_define_body = false pp_indent = remove pp_indent_at_level = false pp_indent_brace = true pp_indent_case = true pp_indent_count = 1 pp_indent_extern = true pp_indent_func_def = true pp_indent_if = 0 pp_indent_region = 0 pp_region_indent_code = false pp_space = remove pp_space_count = 0 sp_addr = remove sp_after_angle = ignore sp_after_assign = ignore sp_after_byref = ignore sp_after_byref_func = ignore sp_after_cast = remove sp_after_class_colon = ignore sp_after_comma = force sp_after_constr_colon = ignore sp_after_dc = ignore sp_after_for_colon = ignore sp_after_invariant_paren = ignore sp_after_mdatype_commas = ignore sp_after_new = ignore sp_after_newop_paren = ignore sp_after_oc_at_sel = ignore sp_after_oc_at_sel_parens = ignore sp_after_oc_block_caret = ignore sp_after_oc_colon = ignore sp_after_oc_dict_colon = ignore sp_after_oc_msg_receiver = ignore sp_after_oc_property = ignore sp_after_oc_return_type = ignore sp_after_oc_scope = ignore sp_after_oc_type = ignore sp_after_operator = ignore sp_after_operator_sym = ignore sp_after_operator_sym_empty = ignore sp_after_ptr_star = remove sp_after_ptr_star_func = remove sp_after_ptr_star_qualifier = remove sp_after_semi = remove sp_after_semi_for = force sp_after_semi_for_empty = remove sp_after_send_oc_colon = ignore sp_after_sparen = force sp_after_tag = ignore sp_after_throw = ignore sp_after_tparen_close = remove sp_after_type = force sp_after_type_brace_init_lst_open = ignore sp_angle_colon = ignore sp_angle_paren = ignore sp_angle_paren_empty = ignore sp_angle_shift = add sp_angle_word = ignore sp_annotation_paren = ignore sp_arith = force sp_arith_additive = ignore sp_assign = force sp_assign_default = ignore sp_attribute_paren = remove sp_balance_nested_parens = false sp_before_angle = ignore sp_before_assign = ignore sp_before_byref = ignore sp_before_byref_func = ignore sp_before_case_colon = remove sp_before_class_colon = ignore sp_before_comma = remove sp_before_constr_colon = ignore sp_before_dc = ignore sp_before_ellipsis = ignore sp_before_for_colon = ignore sp_before_mdatype_commas = ignore sp_before_nl_cont = force sp_before_oc_block_caret = ignore sp_before_oc_colon = ignore sp_before_oc_dict_colon = ignore sp_before_pp_stringify = ignore sp_before_ptr_star = force sp_before_ptr_star_func = force sp_before_semi = remove sp_before_semi_for = remove sp_before_semi_for_empty = remove sp_before_send_oc_colon = ignore sp_before_sparen = force sp_before_square = remove sp_before_squares = remove sp_before_template_paren = ignore sp_before_tr_emb_cmt = force sp_before_type_brace_init_lst_close = ignore sp_before_unnamed_byref = ignore sp_before_unnamed_ptr_star = force sp_between_mdatype_commas = ignore sp_between_new_paren = ignore sp_between_ptr_star = remove sp_bool = force sp_brace_catch = ignore sp_brace_else = force sp_brace_finally = ignore sp_brace_typedef = force sp_case_label = force sp_catch_brace = ignore sp_catch_paren = ignore sp_cmt_cpp_doxygen = true sp_cmt_cpp_qttr = true sp_cmt_cpp_start = force sp_compare = force sp_cond_colon = force sp_cond_colon_after = ignore sp_cond_colon_before = ignore sp_cond_question = force sp_cond_question_after = ignore sp_cond_question_before = ignore sp_cond_ternary_short = remove sp_cparen_oparen = remove sp_cpp_cast_paren = ignore sp_cpp_lambda_assign = ignore sp_d_array_colon = ignore sp_defined_paren = remove sp_deref = remove sp_else_brace = force sp_endif_cmt = force sp_enum_after_assign = force sp_enum_assign = ignore sp_enum_before_assign = ignore sp_enum_colon = ignore sp_enum_paren = ignore sp_extern_paren = ignore sp_finally_brace = ignore sp_fparen_brace = force sp_fparen_dbrace = ignore sp_func_call_paren = remove sp_func_call_paren_empty = ignore sp_func_call_user_paren = ignore sp_func_class_paren = ignore sp_func_class_paren_empty = ignore sp_func_def_paren = remove sp_func_def_paren_empty = remove sp_func_proto_paren = remove sp_func_proto_paren_empty = remove sp_getset_brace = ignore sp_incdec = remove sp_inside_angle = ignore sp_inside_braces = force sp_inside_braces_empty = remove sp_inside_braces_enum = force sp_inside_braces_struct = force sp_inside_fparen = remove sp_inside_fparens = remove sp_inside_newop_paren = ignore sp_inside_newop_paren_close = ignore sp_inside_newop_paren_open = ignore sp_inside_oc_at_sel_parens = ignore sp_inside_paren = remove sp_inside_paren_cast = remove sp_inside_sparen = remove sp_inside_sparen_close = ignore sp_inside_sparen_open = ignore sp_inside_square = remove sp_inside_tparen = remove sp_inside_type_brace_init_lst = ignore sp_inv = remove sp_invariant_paren = ignore sp_macro = force sp_macro_func = force sp_member = remove sp_not = remove sp_num_before_tr_emb_cmt = 1 sp_paren_brace = force sp_paren_comma = force sp_paren_paren = remove sp_permit_cpp11_shift = false sp_pp_concat = add sp_pp_stringify = remove sp_ptr_star_paren = remove sp_range = ignore sp_return_paren = force sp_scope_paren = ignore sp_sign = remove sp_sizeof_paren = remove sp_skip_vbrace_tokens = false sp_sparen_brace = force sp_special_semi = remove sp_square_fparen = remove sp_super_paren = remove sp_template_angle = ignore sp_this_paren = remove sp_throw_paren = ignore sp_try_brace = ignore sp_type_brace_init_lst = ignore sp_type_func = force sp_version_paren = ignore sp_word_brace_ns = add string_escape_char = 92 string_escape_char2 = 0 string_replace_tab_chars = true tok_split_gte = false use_indent_continue_only_once = false use_indent_func_call_param = true use_options_overriding_for_qt_macros = true utf8_bom = remove utf8_byte = true utf8_force = true crust-0.5/3rdparty/000077500000000000000000000000001414152222500142645ustar00rootroot00000000000000crust-0.5/3rdparty/Makefile000066400000000000000000000002121414152222500157170ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # hostprogs-y += kconfig/ crust-0.5/3rdparty/kconfig/000077500000000000000000000000001414152222500157045ustar00rootroot00000000000000crust-0.5/3rdparty/kconfig/Makefile000066400000000000000000000034211414152222500173440ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: GPL-2.0-only # # Based in part on scripts/kconfig/Makefile from Linux v4.19. # ccflags-y += -D_GNU_SOURCE \ -std=gnu11 \ -Wno-implicit-fallthrough \ -Wno-pedantic \ -Wno-sign-compare \ -Wno-unused-parameter \ -Wno-vla cppflags-y += -I$(src) BUILDCFLAGS_nconf := $(shell \ pkg-config --cflags menuw panelw ncursesw 2>/dev/null \ || pkg-config --cflags menu panel ncurses 2>/dev/null \ || echo "-D_GNU_SOURCE -I/usr/include/ncurses") BUILDLDLIBS_nconf := $(shell \ pkg-config --libs menuw panelw ncursesw 2>/dev/null \ || pkg-config --libs menu panel ncurses 2>/dev/null \ || echo "-lmenu -lpanel -lncurses") buildprogs-y += conf nconf common-objs := confdata.o expr.o lexer.lex.o parser.tab.o \ preprocess.o symbol.o util.o conf-objs := $(common-objs) conf.o nconf-objs := $(common-objs) nconf.o nconf.gui.o silent := $(if $(filter-out 0,$(V)),,-s) # These targets map 1:1 to the command line options of 'conf'. simple-targets := \ alldefconfig allnoconfig allyesconfig helpnewconfig listnewconfig \ oldconfig olddefconfig randconfig syncconfig $(simple-targets): $(obj)/conf $(Q) $< $(silent) --$@ $(SRC)/Kconfig config: $(obj)/conf $(Q) $< $(silent) --oldaskconfig $(SRC)/Kconfig defconfig: $(obj)/conf $(Q) $< $(silent) --defconfig=$(SRC)/configs/$@ $(SRC)/Kconfig %_defconfig: $(obj)/conf $(Q) $< $(silent) --defconfig=$(SRC)/configs/$@ $(SRC)/Kconfig nconfig: $(obj)/nconf $(Q) $< $(silent) $(SRC)/Kconfig savedefconfig: $(obj)/conf $(Q) $< $(silent) --$@=defconfig $(SRC)/Kconfig $(obj)/lexer.lex.c: | $(obj)/. $(obj)/lexer.lex.o: $(obj)/parser.tab.c $(obj)/parser.tab.c: | $(obj)/. .PHONY: $(simple-targets) config defconfig %_defconfig nconfig savedefconfig crust-0.5/3rdparty/kconfig/conf.c000066400000000000000000000373601414152222500170060ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2002 Roman Zippel */ #include #include #include #include #include #include #include #include #include #include #include #include "lkc.h" static void conf(struct menu *menu); static void check_conf(struct menu *menu); enum input_mode { oldaskconfig, syncconfig, oldconfig, allnoconfig, allyesconfig, allmodconfig, alldefconfig, randconfig, defconfig, savedefconfig, listnewconfig, helpnewconfig, olddefconfig, yes2modconfig, mod2yesconfig, }; static enum input_mode input_mode = oldaskconfig; static int indent = 1; static int tty_stdio; static int sync_kconfig; static int conf_cnt; static char line[PATH_MAX]; static struct menu *rootEntry; static void print_help(struct menu *menu) { struct gstr help = str_new(); menu_get_ext_help(menu, &help); printf("\n%s\n", str_get(&help)); str_free(&help); } static void strip(char *str) { char *p = str; int l; while ((isspace(*p))) p++; l = strlen(p); if (p != str) memmove(str, p, l + 1); if (!l) return; p = str + l - 1; while ((isspace(*p))) *p-- = 0; } /* Helper function to facilitate fgets() by Jean Sacren. */ static void xfgets(char *str, int size, FILE *in) { if (!fgets(str, size, in)) fprintf(stderr, "\nError in reading or end of file.\n"); if (!tty_stdio) printf("%s", str); } static int conf_askvalue(struct symbol *sym, const char *def) { enum symbol_type type = sym_get_type(sym); if (!sym_has_value(sym)) printf("(NEW) "); line[0] = '\n'; line[1] = 0; if (!sym_is_changeable(sym)) { printf("%s\n", def); line[0] = '\n'; line[1] = 0; return 0; } switch (input_mode) { case oldconfig: case syncconfig: if (sym_has_value(sym)) { printf("%s\n", def); return 0; } /* fall through */ case oldaskconfig: fflush(stdout); xfgets(line, sizeof(line), stdin); return 1; default: break; } switch (type) { case S_INT: case S_HEX: case S_STRING: printf("%s\n", def); return 1; default: ; } printf("%s", line); return 1; } static int conf_string(struct menu *menu) { struct symbol *sym = menu->sym; const char *def; while (1) { printf("%*s%s ", indent - 1, "", menu->prompt->text); printf("(%s) ", sym->name); def = sym_get_string_value(sym); if (sym_get_string_value(sym)) printf("[%s] ", def); if (!conf_askvalue(sym, def)) return 0; switch (line[0]) { case '\n': break; case '?': /* print help */ if (line[1] == '\n') { print_help(menu); def = NULL; break; } /* fall through */ default: line[strlen(line)-1] = 0; def = line; } if (def && sym_set_string_value(sym, def)) return 0; } } static int conf_sym(struct menu *menu) { struct symbol *sym = menu->sym; tristate oldval, newval; while (1) { printf("%*s%s ", indent - 1, "", menu->prompt->text); if (sym->name) printf("(%s) ", sym->name); putchar('['); oldval = sym_get_tristate_value(sym); switch (oldval) { case no: putchar('N'); break; case mod: putchar('M'); break; case yes: putchar('Y'); break; } if (oldval != no && sym_tristate_within_range(sym, no)) printf("/n"); if (oldval != mod && sym_tristate_within_range(sym, mod)) printf("/m"); if (oldval != yes && sym_tristate_within_range(sym, yes)) printf("/y"); printf("/?] "); if (!conf_askvalue(sym, sym_get_string_value(sym))) return 0; strip(line); switch (line[0]) { case 'n': case 'N': newval = no; if (!line[1] || !strcmp(&line[1], "o")) break; continue; case 'm': case 'M': newval = mod; if (!line[1]) break; continue; case 'y': case 'Y': newval = yes; if (!line[1] || !strcmp(&line[1], "es")) break; continue; case 0: newval = oldval; break; case '?': goto help; default: continue; } if (sym_set_tristate_value(sym, newval)) return 0; help: print_help(menu); } } static int conf_choice(struct menu *menu) { struct symbol *sym, *def_sym; struct menu *child; bool is_new; sym = menu->sym; is_new = !sym_has_value(sym); if (sym_is_changeable(sym)) { conf_sym(menu); sym_calc_value(sym); switch (sym_get_tristate_value(sym)) { case no: return 1; case mod: return 0; case yes: break; } } else { switch (sym_get_tristate_value(sym)) { case no: return 1; case mod: printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); return 0; case yes: break; } } while (1) { int cnt, def; printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); def_sym = sym_get_choice_value(sym); cnt = def = 0; line[0] = 0; for (child = menu->list; child; child = child->next) { if (!menu_is_visible(child)) continue; if (!child->sym) { printf("%*c %s\n", indent, '*', menu_get_prompt(child)); continue; } cnt++; if (child->sym == def_sym) { def = cnt; printf("%*c", indent, '>'); } else printf("%*c", indent, ' '); printf(" %d. %s", cnt, menu_get_prompt(child)); if (child->sym->name) printf(" (%s)", child->sym->name); if (!sym_has_value(child->sym)) printf(" (NEW)"); printf("\n"); } printf("%*schoice", indent - 1, ""); if (cnt == 1) { printf("[1]: 1\n"); goto conf_childs; } printf("[1-%d?]: ", cnt); switch (input_mode) { case oldconfig: case syncconfig: if (!is_new) { cnt = def; printf("%d\n", cnt); break; } /* fall through */ case oldaskconfig: fflush(stdout); xfgets(line, sizeof(line), stdin); strip(line); if (line[0] == '?') { print_help(menu); continue; } if (!line[0]) cnt = def; else if (isdigit(line[0])) cnt = atoi(line); else continue; break; default: break; } conf_childs: for (child = menu->list; child; child = child->next) { if (!child->sym || !menu_is_visible(child)) continue; if (!--cnt) break; } if (!child) continue; if (line[0] && line[strlen(line) - 1] == '?') { print_help(child); continue; } sym_set_choice_value(sym, child->sym); for (child = child->list; child; child = child->next) { indent += 2; conf(child); indent -= 2; } return 1; } } static void conf(struct menu *menu) { struct symbol *sym; struct property *prop; struct menu *child; if (!menu_is_visible(menu)) return; sym = menu->sym; prop = menu->prompt; if (prop) { const char *prompt; switch (prop->type) { case P_MENU: /* * Except in oldaskconfig mode, we show only menus that * contain new symbols. */ if (input_mode != oldaskconfig && rootEntry != menu) { check_conf(menu); return; } /* fall through */ case P_COMMENT: prompt = menu_get_prompt(menu); if (prompt) printf("%*c\n%*c %s\n%*c\n", indent, '*', indent, '*', prompt, indent, '*'); default: ; } } if (!sym) goto conf_childs; if (sym_is_choice(sym)) { conf_choice(menu); if (sym->curr.tri != mod) return; goto conf_childs; } switch (sym->type) { case S_INT: case S_HEX: case S_STRING: conf_string(menu); break; default: conf_sym(menu); break; } conf_childs: if (sym) indent += 2; for (child = menu->list; child; child = child->next) conf(child); if (sym) indent -= 2; } static void check_conf(struct menu *menu) { struct symbol *sym; struct menu *child; if (!menu_is_visible(menu)) return; sym = menu->sym; if (sym && !sym_has_value(sym)) { if (sym_is_changeable(sym) || (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) { if (input_mode == listnewconfig) { if (sym->name) { const char *str; if (sym->type == S_STRING) { str = sym_get_string_value(sym); str = sym_escape_string_value(str); printf("%s%s=%s\n", CONFIG_, sym->name, str); free((void *)str); } else { str = sym_get_string_value(sym); printf("%s%s=%s\n", CONFIG_, sym->name, str); } } } else if (input_mode == helpnewconfig) { printf("-----\n"); print_help(menu); printf("-----\n"); } else { if (!conf_cnt++) printf("*\n* Restart config...\n*\n"); rootEntry = menu_get_parent_menu(menu); conf(rootEntry); } } } for (child = menu->list; child; child = child->next) check_conf(child); } static struct option long_opts[] = { {"oldaskconfig", no_argument, NULL, oldaskconfig}, {"oldconfig", no_argument, NULL, oldconfig}, {"syncconfig", no_argument, NULL, syncconfig}, {"defconfig", required_argument, NULL, defconfig}, {"savedefconfig", required_argument, NULL, savedefconfig}, {"allnoconfig", no_argument, NULL, allnoconfig}, {"allyesconfig", no_argument, NULL, allyesconfig}, {"allmodconfig", no_argument, NULL, allmodconfig}, {"alldefconfig", no_argument, NULL, alldefconfig}, {"randconfig", no_argument, NULL, randconfig}, {"listnewconfig", no_argument, NULL, listnewconfig}, {"helpnewconfig", no_argument, NULL, helpnewconfig}, {"olddefconfig", no_argument, NULL, olddefconfig}, {"yes2modconfig", no_argument, NULL, yes2modconfig}, {"mod2yesconfig", no_argument, NULL, mod2yesconfig}, {NULL, 0, NULL, 0} }; static void conf_usage(const char *progname) { printf("Usage: %s [-s] [option] \n", progname); printf("[option] is _one_ of the following:\n"); printf(" --listnewconfig List new options\n"); printf(" --helpnewconfig List new options and help text\n"); printf(" --oldaskconfig Start a new configuration using a line-oriented program\n"); printf(" --oldconfig Update a configuration using a provided .config as base\n"); printf(" --syncconfig Similar to oldconfig but generates configuration in\n" " include/{generated/,config/}\n"); printf(" --olddefconfig Same as oldconfig but sets new symbols to their default value\n"); printf(" --defconfig New config with default defined in \n"); printf(" --savedefconfig Save the minimal current configuration to \n"); printf(" --allnoconfig New config where all options are answered with no\n"); printf(" --allyesconfig New config where all options are answered with yes\n"); printf(" --allmodconfig New config where all options are answered with mod\n"); printf(" --alldefconfig New config with all symbols set to default\n"); printf(" --randconfig New config with random answer to all options\n"); printf(" --yes2modconfig Change answers from yes to mod if possible\n"); printf(" --mod2yesconfig Change answers from mod to yes if possible\n"); } int main(int ac, char **av) { const char *progname = av[0]; int opt; const char *name, *defconfig_file = NULL /* gcc uninit */; int no_conf_write = 0; tty_stdio = isatty(0) && isatty(1); while ((opt = getopt_long(ac, av, "s", long_opts, NULL)) != -1) { if (opt == 's') { conf_set_message_callback(NULL); continue; } input_mode = (enum input_mode)opt; switch (opt) { case syncconfig: /* * syncconfig is invoked during the build stage. * Suppress distracting "configuration written to ..." */ conf_set_message_callback(NULL); sync_kconfig = 1; break; case defconfig: case savedefconfig: defconfig_file = optarg; break; case randconfig: { struct timeval now; unsigned int seed; char *seed_env; /* * Use microseconds derived seed, * compensate for systems where it may be zero */ gettimeofday(&now, NULL); seed = (unsigned int)((now.tv_sec + 1) * (now.tv_usec + 1)); seed_env = getenv("KCONFIG_SEED"); if( seed_env && *seed_env ) { char *endp; int tmp = (int)strtol(seed_env, &endp, 0); if (*endp == '\0') { seed = tmp; } } fprintf( stderr, "KCONFIG_SEED=0x%X\n", seed ); srand(seed); break; } case oldaskconfig: case oldconfig: case allnoconfig: case allyesconfig: case allmodconfig: case alldefconfig: case listnewconfig: case helpnewconfig: case olddefconfig: case yes2modconfig: case mod2yesconfig: break; case '?': conf_usage(progname); exit(1); break; } } if (ac == optind) { fprintf(stderr, "%s: Kconfig file missing\n", av[0]); conf_usage(progname); exit(1); } name = av[optind]; conf_parse(name); //zconfdump(stdout); switch (input_mode) { case defconfig: if (conf_read(defconfig_file)) { fprintf(stderr, "***\n" "*** Can't find default configuration \"%s\"!\n" "***\n", defconfig_file); exit(1); } break; case savedefconfig: case syncconfig: case oldaskconfig: case oldconfig: case listnewconfig: case helpnewconfig: case olddefconfig: case yes2modconfig: case mod2yesconfig: conf_read(NULL); break; case allnoconfig: case allyesconfig: case allmodconfig: case alldefconfig: case randconfig: name = getenv("KCONFIG_ALLCONFIG"); if (!name) break; if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) { if (conf_read_simple(name, S_DEF_USER)) { fprintf(stderr, "*** Can't read seed configuration \"%s\"!\n", name); exit(1); } break; } switch (input_mode) { case allnoconfig: name = "allno.config"; break; case allyesconfig: name = "allyes.config"; break; case allmodconfig: name = "allmod.config"; break; case alldefconfig: name = "alldef.config"; break; case randconfig: name = "allrandom.config"; break; default: break; } if (conf_read_simple(name, S_DEF_USER) && conf_read_simple("all.config", S_DEF_USER)) { fprintf(stderr, "*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n", name); exit(1); } break; default: break; } if (sync_kconfig) { name = getenv("KCONFIG_NOSILENTUPDATE"); if (name && *name) { if (conf_get_changed()) { fprintf(stderr, "\n*** The configuration requires explicit update.\n\n"); return 1; } no_conf_write = 1; } } switch (input_mode) { case allnoconfig: conf_set_all_new_symbols(def_no); break; case allyesconfig: conf_set_all_new_symbols(def_yes); break; case allmodconfig: conf_set_all_new_symbols(def_mod); break; case alldefconfig: conf_set_all_new_symbols(def_default); break; case randconfig: /* Really nothing to do in this loop */ while (conf_set_all_new_symbols(def_random)) ; break; case defconfig: conf_set_all_new_symbols(def_default); break; case savedefconfig: break; case yes2modconfig: conf_rewrite_mod_or_yes(def_y2m); break; case mod2yesconfig: conf_rewrite_mod_or_yes(def_m2y); break; case oldaskconfig: rootEntry = &rootmenu; conf(&rootmenu); input_mode = oldconfig; /* fall through */ case oldconfig: case listnewconfig: case helpnewconfig: case syncconfig: /* Update until a loop caused no more changes */ do { conf_cnt = 0; check_conf(&rootmenu); } while (conf_cnt); break; case olddefconfig: default: break; } if (input_mode == savedefconfig) { if (conf_write_defconfig(defconfig_file)) { fprintf(stderr, "n*** Error while saving defconfig to: %s\n\n", defconfig_file); return 1; } } else if (input_mode != listnewconfig && input_mode != helpnewconfig) { if (!no_conf_write && conf_write(NULL)) { fprintf(stderr, "\n*** Error during writing of the configuration.\n\n"); exit(1); } /* * Create auto.conf if it does not exist. * This prevents GNU Make 4.1 or older from emitting * "include/config/auto.conf: No such file or directory" * in the top-level Makefile * * syncconfig always creates or updates auto.conf because it is * used during the build. */ if (conf_write_autoconf(sync_kconfig) && sync_kconfig) { fprintf(stderr, "\n*** Error during sync of the configuration.\n\n"); return 1; } } return 0; } crust-0.5/3rdparty/kconfig/confdata.c000066400000000000000000000664701414152222500176440ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2002 Roman Zippel */ #include #include #include #include #include #include #include #include #include #include #include #include #include "lkc.h" /* return true if 'path' exists, false otherwise */ static bool is_present(const char *path) { struct stat st; return !stat(path, &st); } /* return true if 'path' exists and it is a directory, false otherwise */ static bool is_dir(const char *path) { struct stat st; if (stat(path, &st)) return 0; return S_ISDIR(st.st_mode); } /* return true if the given two files are the same, false otherwise */ static bool is_same(const char *file1, const char *file2) { int fd1, fd2; struct stat st1, st2; void *map1, *map2; bool ret = false; fd1 = open(file1, O_RDONLY); if (fd1 < 0) return ret; fd2 = open(file2, O_RDONLY); if (fd2 < 0) goto close1; ret = fstat(fd1, &st1); if (ret) goto close2; ret = fstat(fd2, &st2); if (ret) goto close2; if (st1.st_size != st2.st_size) goto close2; map1 = mmap(NULL, st1.st_size, PROT_READ, MAP_PRIVATE, fd1, 0); if (map1 == MAP_FAILED) goto close2; map2 = mmap(NULL, st2.st_size, PROT_READ, MAP_PRIVATE, fd2, 0); if (map2 == MAP_FAILED) goto close2; if (bcmp(map1, map2, st1.st_size)) goto close2; ret = true; close2: close(fd2); close1: close(fd1); return ret; } /* * Create the parent directory of the given path. * * For example, if 'include/config/auto.conf' is given, create 'include/config'. */ static int make_parent_dir(const char *path) { char tmp[PATH_MAX + 1]; char *p; strncpy(tmp, path, sizeof(tmp)); tmp[sizeof(tmp) - 1] = 0; /* Remove the base name. Just return if nothing is left */ p = strrchr(tmp, '/'); if (!p) return 0; *(p + 1) = 0; /* Just in case it is an absolute path */ p = tmp; while (*p == '/') p++; while ((p = strchr(p, '/'))) { *p = 0; /* skip if the directory exists */ if (!is_dir(tmp) && mkdir(tmp, 0755)) return -1; *p = '/'; while (*p == '/') p++; } return 0; } static char depfile_path[PATH_MAX]; static size_t depfile_prefix_len; /* touch depfile for symbol 'name' */ static int conf_touch_dep(const char *name) { int fd, ret; const char *s; char *d, c; /* check overflow: prefix + name + ".h" + '\0' must fit in buffer. */ if (depfile_prefix_len + strlen(name) + 3 > sizeof(depfile_path)) return -1; d = depfile_path + depfile_prefix_len; s = name; while ((c = *s++)) *d++ = (c == '_') ? '/' : tolower(c); strcpy(d, ".h"); /* Assume directory path already exists. */ fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { if (errno != ENOENT) return -1; ret = make_parent_dir(depfile_path); if (ret) return ret; /* Try it again. */ fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) return -1; } close(fd); return 0; } struct conf_printer { void (*print_symbol)(FILE *, struct symbol *, const char *, void *); void (*print_comment)(FILE *, const char *, void *); }; static void conf_warning(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); static void conf_message(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); static const char *conf_filename; static int conf_lineno, conf_warnings; static void conf_warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); conf_warnings++; } static void conf_default_message_callback(const char *s) { printf("#\n# "); printf("%s", s); printf("\n#\n"); } static void (*conf_message_callback)(const char *s) = conf_default_message_callback; void conf_set_message_callback(void (*fn)(const char *s)) { conf_message_callback = fn; } static void conf_message(const char *fmt, ...) { va_list ap; char buf[4096]; if (!conf_message_callback) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); conf_message_callback(buf); va_end(ap); } const char *conf_get_configname(void) { char *name = getenv("KCONFIG_CONFIG"); return name ? name : ".config"; } static const char *conf_get_autoconfig_name(void) { char *name = getenv("KCONFIG_AUTOCONFIG"); return name ? name : "include/config/auto.conf"; } static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p) { char *p2; switch (sym->type) { case S_TRISTATE: if (p[0] == 'm') { sym->def[def].tri = mod; sym->flags |= def_flags; break; } /* fall through */ case S_BOOLEAN: if (p[0] == 'y') { sym->def[def].tri = yes; sym->flags |= def_flags; break; } if (p[0] == 'n') { sym->def[def].tri = no; sym->flags |= def_flags; break; } if (def != S_DEF_AUTO) conf_warning("symbol value '%s' invalid for %s", p, sym->name); return 1; case S_STRING: if (*p++ != '"') break; for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) { if (*p2 == '"') { *p2 = 0; break; } memmove(p2, p2 + 1, strlen(p2)); } if (!p2) { if (def != S_DEF_AUTO) conf_warning("invalid string found"); return 1; } /* fall through */ case S_INT: case S_HEX: if (sym_string_valid(sym, p)) { sym->def[def].val = xstrdup(p); sym->flags |= def_flags; } else { if (def != S_DEF_AUTO) conf_warning("symbol value '%s' invalid for %s", p, sym->name); return 1; } break; default: ; } return 0; } #define LINE_GROWTH 16 static int add_byte(int c, char **lineptr, size_t slen, size_t *n) { char *nline; size_t new_size = slen + 1; if (new_size > *n) { new_size += LINE_GROWTH - 1; new_size *= 2; nline = xrealloc(*lineptr, new_size); if (!nline) return -1; *lineptr = nline; *n = new_size; } (*lineptr)[slen] = c; return 0; } static ssize_t compat_getline(char **lineptr, size_t *n, FILE *stream) { char *line = *lineptr; size_t slen = 0; for (;;) { int c = getc(stream); switch (c) { case '\n': if (add_byte(c, &line, slen, n) < 0) goto e_out; slen++; /* fall through */ case EOF: if (add_byte('\0', &line, slen, n) < 0) goto e_out; *lineptr = line; if (slen == 0) return -1; return slen; default: if (add_byte(c, &line, slen, n) < 0) goto e_out; slen++; } } e_out: line[slen-1] = '\0'; *lineptr = line; return -1; } int conf_read_simple(const char *name, int def) { FILE *in = NULL; char *line = NULL; size_t line_asize = 0; char *p, *p2; struct symbol *sym; int i, def_flags; if (name) { in = zconf_fopen(name); } else { struct property *prop; name = conf_get_configname(); in = zconf_fopen(name); if (in) goto load; sym_add_change_count(1); if (!sym_defconfig_list) return 1; for_all_defaults(sym_defconfig_list, prop) { if (expr_calc_value(prop->visible.expr) == no || prop->expr->type != E_SYMBOL) continue; sym_calc_value(prop->expr->left.sym); name = sym_get_string_value(prop->expr->left.sym); in = zconf_fopen(name); if (in) { conf_message("using defaults found in %s", name); goto load; } } } if (!in) return 1; load: conf_filename = name; conf_lineno = 0; conf_warnings = 0; def_flags = SYMBOL_DEF << def; for_all_symbols(i, sym) { sym->flags |= SYMBOL_CHANGED; sym->flags &= ~(def_flags|SYMBOL_VALID); if (sym_is_choice(sym)) sym->flags |= def_flags; switch (sym->type) { case S_INT: case S_HEX: case S_STRING: if (sym->def[def].val) free(sym->def[def].val); /* fall through */ default: sym->def[def].val = NULL; sym->def[def].tri = no; } } while (compat_getline(&line, &line_asize, in) != -1) { conf_lineno++; sym = NULL; if (line[0] == '#') { if (memcmp(line + 2, CONFIG_, strlen(CONFIG_))) continue; p = strchr(line + 2 + strlen(CONFIG_), ' '); if (!p) continue; *p++ = 0; if (strncmp(p, "is not set", 10)) continue; if (def == S_DEF_USER) { sym = sym_find(line + 2 + strlen(CONFIG_)); if (!sym) { sym_add_change_count(1); continue; } } else { sym = sym_lookup(line + 2 + strlen(CONFIG_), 0); if (sym->type == S_UNKNOWN) sym->type = S_BOOLEAN; } if (sym->flags & def_flags) { conf_warning("override: reassigning to symbol %s", sym->name); } switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: sym->def[def].tri = no; sym->flags |= def_flags; break; default: ; } } else if (memcmp(line, CONFIG_, strlen(CONFIG_)) == 0) { p = strchr(line + strlen(CONFIG_), '='); if (!p) continue; *p++ = 0; p2 = strchr(p, '\n'); if (p2) { *p2-- = 0; if (*p2 == '\r') *p2 = 0; } sym = sym_find(line + strlen(CONFIG_)); if (!sym) { if (def == S_DEF_AUTO) /* * Reading from include/config/auto.conf * If CONFIG_FOO previously existed in * auto.conf but it is missing now, * include/config/foo.h must be touched. */ conf_touch_dep(line + strlen(CONFIG_)); else sym_add_change_count(1); continue; } if (sym->flags & def_flags) { conf_warning("override: reassigning to symbol %s", sym->name); } if (conf_set_sym_val(sym, def, def_flags, p)) continue; } else { if (line[0] != '\r' && line[0] != '\n') conf_warning("unexpected data: %.*s", (int)strcspn(line, "\r\n"), line); continue; } if (sym && sym_is_choice_value(sym)) { struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); switch (sym->def[def].tri) { case no: break; case mod: if (cs->def[def].tri == yes) { conf_warning("%s creates inconsistent choice state", sym->name); cs->flags &= ~def_flags; } break; case yes: if (cs->def[def].tri != no) conf_warning("override: %s changes choice state", sym->name); cs->def[def].val = sym; break; } cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri); } } free(line); fclose(in); return 0; } int conf_read(const char *name) { struct symbol *sym; int conf_unsaved = 0; int i; sym_set_change_count(0); if (conf_read_simple(name, S_DEF_USER)) { sym_calc_value(modules_sym); return 1; } sym_calc_value(modules_sym); for_all_symbols(i, sym) { sym_calc_value(sym); if (sym_is_choice(sym) || (sym->flags & SYMBOL_NO_WRITE)) continue; if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) { /* check that calculated value agrees with saved value */ switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: if (sym->def[S_DEF_USER].tri == sym_get_tristate_value(sym)) continue; break; default: if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val)) continue; break; } } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE)) /* no previous value and not saved */ continue; conf_unsaved++; /* maybe print value in verbose mode... */ } for_all_symbols(i, sym) { if (sym_has_value(sym) && !sym_is_choice_value(sym)) { /* Reset values of generates values, so they'll appear * as new, if they should become visible, but that * doesn't quite work if the Kconfig and the saved * configuration disagree. */ if (sym->visible == no && !conf_unsaved) sym->flags &= ~SYMBOL_DEF_USER; switch (sym->type) { case S_STRING: case S_INT: case S_HEX: /* Reset a string value if it's out of range */ if (sym_string_within_range(sym, sym->def[S_DEF_USER].val)) break; sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER); conf_unsaved++; break; default: break; } } } sym_add_change_count(conf_warnings || conf_unsaved); return 0; } /* * Kconfig configuration printer * * This printer is used when generating the resulting configuration after * kconfig invocation and `defconfig' files. Unset symbol might be omitted by * passing a non-NULL argument to the printer. * */ static void kconfig_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) { switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: if (*value == 'n') { bool skip_unset = (arg != NULL); if (!skip_unset) fprintf(fp, "# %s%s is not set\n", CONFIG_, sym->name); return; } break; default: break; } fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, value); } static void kconfig_print_comment(FILE *fp, const char *value, void *arg) { const char *p = value; size_t l; for (;;) { l = strcspn(p, "\n"); fprintf(fp, "#"); if (l) { fprintf(fp, " "); xfwrite(p, l, 1, fp); p += l; } fprintf(fp, "\n"); if (*p++ == '\0') break; } } static struct conf_printer kconfig_printer_cb = { .print_symbol = kconfig_print_symbol, .print_comment = kconfig_print_comment, }; /* * Header printer * * This printer is used when generating the `include/generated/autoconf.h' file. */ static void header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) { switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: { const char *suffix = ""; switch (*value) { case 'n': break; case 'm': suffix = "_MODULE"; /* fall through */ default: fprintf(fp, "#define %s%s%s 1\n", CONFIG_, sym->name, suffix); } break; } case S_HEX: { const char *prefix = ""; if (value[0] != '0' || (value[1] != 'x' && value[1] != 'X')) prefix = "0x"; fprintf(fp, "#define %s%s %s%s\n", CONFIG_, sym->name, prefix, value); break; } case S_STRING: case S_INT: fprintf(fp, "#define %s%s %s\n", CONFIG_, sym->name, value); break; default: break; } } static void header_print_comment(FILE *fp, const char *value, void *arg) { const char *p = value; size_t l; fprintf(fp, "/*\n"); for (;;) { l = strcspn(p, "\n"); fprintf(fp, " *"); if (l) { fprintf(fp, " "); xfwrite(p, l, 1, fp); p += l; } fprintf(fp, "\n"); if (*p++ == '\0') break; } fprintf(fp, " */\n"); } static struct conf_printer header_printer_cb = { .print_symbol = header_print_symbol, .print_comment = header_print_comment, }; static void conf_write_symbol(FILE *fp, struct symbol *sym, struct conf_printer *printer, void *printer_arg) { const char *str; switch (sym->type) { case S_UNKNOWN: break; case S_STRING: str = sym_get_string_value(sym); str = sym_escape_string_value(str); printer->print_symbol(fp, sym, str, printer_arg); free((void *)str); break; default: str = sym_get_string_value(sym); printer->print_symbol(fp, sym, str, printer_arg); } } static void conf_write_heading(FILE *fp, struct conf_printer *printer, void *printer_arg) { char buf[256]; snprintf(buf, sizeof(buf), "\n" "Automatically generated file; DO NOT EDIT.\n" "%s\n", rootmenu.prompt->text); printer->print_comment(fp, buf, printer_arg); } /* * Write out a minimal config. * All values that has default values are skipped as this is redundant. */ int conf_write_defconfig(const char *filename) { struct symbol *sym; struct menu *menu; FILE *out; out = fopen(filename, "w"); if (!out) return 1; sym_clear_all_valid(); /* Traverse all menus to find all relevant symbols */ menu = rootmenu.list; while (menu != NULL) { sym = menu->sym; if (sym == NULL) { if (!menu_is_visible(menu)) goto next_menu; } else if (!sym_is_choice(sym)) { sym_calc_value(sym); if (!(sym->flags & SYMBOL_WRITE)) goto next_menu; sym->flags &= ~SYMBOL_WRITE; /* If we cannot change the symbol - skip */ if (!sym_is_changeable(sym)) goto next_menu; /* If symbol equals to default value - skip */ if (strcmp(sym_get_string_value(sym), sym_get_string_default(sym)) == 0) goto next_menu; /* * If symbol is a choice value and equals to the * default for a choice - skip. * But only if value is bool and equal to "y" and * choice is not "optional". * (If choice is "optional" then all values can be "n") */ if (sym_is_choice_value(sym)) { struct symbol *cs; struct symbol *ds; cs = prop_get_symbol(sym_get_choice_prop(sym)); ds = sym_choice_default(cs); if (!sym_is_optional(cs) && sym == ds) { if ((sym->type == S_BOOLEAN) && sym_get_tristate_value(sym) == yes) goto next_menu; } } conf_write_symbol(out, sym, &kconfig_printer_cb, NULL); } next_menu: if (menu->list != NULL) { menu = menu->list; } else if (menu->next != NULL) { menu = menu->next; } else { while ((menu = menu->parent)) { if (menu->next != NULL) { menu = menu->next; break; } } } } fclose(out); return 0; } int conf_write(const char *name) { FILE *out; struct symbol *sym; struct menu *menu; const char *str; char tmpname[PATH_MAX + 1], oldname[PATH_MAX + 1]; char *env; int i; bool need_newline = false; if (!name) name = conf_get_configname(); if (!*name) { fprintf(stderr, "config name is empty\n"); return -1; } if (is_dir(name)) { fprintf(stderr, "%s: Is a directory\n", name); return -1; } if (make_parent_dir(name)) return -1; env = getenv("KCONFIG_OVERWRITECONFIG"); if (env && *env) { *tmpname = 0; out = fopen(name, "w"); } else { snprintf(tmpname, sizeof(tmpname), "%s.%d.tmp", name, (int)getpid()); out = fopen(tmpname, "w"); } if (!out) return 1; conf_write_heading(out, &kconfig_printer_cb, NULL); if (!conf_get_changed()) sym_clear_all_valid(); menu = rootmenu.list; while (menu) { sym = menu->sym; if (!sym) { if (!menu_is_visible(menu)) goto next; str = menu_get_prompt(menu); fprintf(out, "\n" "#\n" "# %s\n" "#\n", str); need_newline = false; } else if (!(sym->flags & SYMBOL_CHOICE) && !(sym->flags & SYMBOL_WRITTEN)) { sym_calc_value(sym); if (!(sym->flags & SYMBOL_WRITE)) goto next; if (need_newline) { fprintf(out, "\n"); need_newline = false; } sym->flags |= SYMBOL_WRITTEN; conf_write_symbol(out, sym, &kconfig_printer_cb, NULL); } next: if (menu->list) { menu = menu->list; continue; } if (menu->next) menu = menu->next; else while ((menu = menu->parent)) { if (!menu->sym && menu_is_visible(menu) && menu != &rootmenu) { str = menu_get_prompt(menu); fprintf(out, "# end of %s\n", str); need_newline = true; } if (menu->next) { menu = menu->next; break; } } } fclose(out); for_all_symbols(i, sym) sym->flags &= ~SYMBOL_WRITTEN; if (*tmpname) { if (is_same(name, tmpname)) { conf_message("No change to %s", name); unlink(tmpname); sym_set_change_count(0); return 0; } snprintf(oldname, sizeof(oldname), "%s.old", name); rename(name, oldname); if (rename(tmpname, name)) return 1; } conf_message("configuration written to %s", name); sym_set_change_count(0); return 0; } /* write a dependency file as used by kbuild to track dependencies */ static int conf_write_dep(const char *name) { struct file *file; FILE *out; out = fopen("..config.tmp", "w"); if (!out) return 1; fprintf(out, "deps_config := \\\n"); for (file = file_list; file; file = file->next) { if (file->next) fprintf(out, "\t%s \\\n", file->name); else fprintf(out, "\t%s\n", file->name); } fprintf(out, "\n%s: \\\n" "\t$(deps_config)\n\n", conf_get_autoconfig_name()); env_write_dep(out, conf_get_autoconfig_name()); fprintf(out, "\n$(deps_config): ;\n"); fclose(out); if (make_parent_dir(name)) return 1; rename("..config.tmp", name); return 0; } static int conf_touch_deps(void) { const char *name; struct symbol *sym; int res, i; char *s; strcpy(depfile_path, conf_get_autoconfig_name()); if ((s = strrchr(depfile_path, '/'))) *s = '\0'; depfile_prefix_len = strlen(depfile_path); name = conf_get_autoconfig_name(); conf_read_simple(name, S_DEF_AUTO); sym_calc_value(modules_sym); for_all_symbols(i, sym) { sym_calc_value(sym); if ((sym->flags & SYMBOL_NO_WRITE) || !sym->name) continue; if (sym->flags & SYMBOL_WRITE) { if (sym->flags & SYMBOL_DEF_AUTO) { /* * symbol has old and new value, * so compare them... */ switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: if (sym_get_tristate_value(sym) == sym->def[S_DEF_AUTO].tri) continue; break; case S_STRING: case S_HEX: case S_INT: if (!strcmp(sym_get_string_value(sym), sym->def[S_DEF_AUTO].val)) continue; break; default: break; } } else { /* * If there is no old value, only 'no' (unset) * is allowed as new value. */ switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: if (sym_get_tristate_value(sym) == no) continue; break; default: break; } } } else if (!(sym->flags & SYMBOL_DEF_AUTO)) /* There is neither an old nor a new value. */ continue; /* else * There is an old value, but no new value ('no' (unset) * isn't saved in auto.conf, so the old value is always * different from 'no'). */ res = conf_touch_dep(sym->name); if (res) return res; } return 0; } int conf_write_autoconf(int overwrite) { struct symbol *sym; const char *name; const char *autoconf_name = conf_get_autoconfig_name(); char path[PATH_MAX]; FILE *out, *out_h; int i; if (!overwrite && is_present(autoconf_name)) return 0; snprintf(path, sizeof(path), "%s.cmd", autoconf_name); conf_write_dep(path); if (conf_touch_deps()) return 1; out = fopen(".tmpconfig", "w"); if (!out) return 1; out_h = fopen(".tmpconfig.h", "w"); if (!out_h) { fclose(out); return 1; } conf_write_heading(out, &kconfig_printer_cb, NULL); conf_write_heading(out_h, &header_printer_cb, NULL); for_all_symbols(i, sym) { sym_calc_value(sym); if (!(sym->flags & SYMBOL_WRITE) || !sym->name) continue; /* write symbols to auto.conf and autoconf.h */ conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1); conf_write_symbol(out_h, sym, &header_printer_cb, NULL); } fclose(out); fclose(out_h); name = getenv("KCONFIG_AUTOHEADER"); if (!name) name = "include/generated/autoconf.h"; if (make_parent_dir(name)) return 1; if (rename(".tmpconfig.h", name)) return 1; if (make_parent_dir(autoconf_name)) return 1; /* * This must be the last step, kbuild has a dependency on auto.conf * and this marks the successful completion of the previous steps. */ if (rename(".tmpconfig", autoconf_name)) return 1; return 0; } static int sym_change_count; static void (*conf_changed_callback)(void); void sym_set_change_count(int count) { int _sym_change_count = sym_change_count; sym_change_count = count; if (conf_changed_callback && (bool)_sym_change_count != (bool)count) conf_changed_callback(); } void sym_add_change_count(int count) { sym_set_change_count(count + sym_change_count); } bool conf_get_changed(void) { return sym_change_count; } void conf_set_changed_callback(void (*fn)(void)) { conf_changed_callback = fn; } static bool randomize_choice_values(struct symbol *csym) { struct property *prop; struct symbol *sym; struct expr *e; int cnt, def; /* * If choice is mod then we may have more items selected * and if no then no-one. * In both cases stop. */ if (csym->curr.tri != yes) return false; prop = sym_get_choice_prop(csym); /* count entries in choice block */ cnt = 0; expr_list_for_each_sym(prop->expr, e, sym) cnt++; /* * find a random value and set it to yes, * set the rest to no so we have only one set */ def = (rand() % cnt); cnt = 0; expr_list_for_each_sym(prop->expr, e, sym) { if (def == cnt++) { sym->def[S_DEF_USER].tri = yes; csym->def[S_DEF_USER].val = sym; } else { sym->def[S_DEF_USER].tri = no; } sym->flags |= SYMBOL_DEF_USER; /* clear VALID to get value calculated */ sym->flags &= ~SYMBOL_VALID; } csym->flags |= SYMBOL_DEF_USER; /* clear VALID to get value calculated */ csym->flags &= ~(SYMBOL_VALID); return true; } void set_all_choice_values(struct symbol *csym) { struct property *prop; struct symbol *sym; struct expr *e; prop = sym_get_choice_prop(csym); /* * Set all non-assinged choice values to no */ expr_list_for_each_sym(prop->expr, e, sym) { if (!sym_has_value(sym)) sym->def[S_DEF_USER].tri = no; } csym->flags |= SYMBOL_DEF_USER; /* clear VALID to get value calculated */ csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES); } bool conf_set_all_new_symbols(enum conf_def_mode mode) { struct symbol *sym, *csym; int i, cnt, pby, pty, ptm; /* pby: probability of bool = y * pty: probability of tristate = y * ptm: probability of tristate = m */ pby = 50; pty = ptm = 33; /* can't go as the default in switch-case * below, otherwise gcc whines about * -Wmaybe-uninitialized */ if (mode == def_random) { int n, p[3]; char *env = getenv("KCONFIG_PROBABILITY"); n = 0; while( env && *env ) { char *endp; int tmp = strtol( env, &endp, 10 ); if( tmp >= 0 && tmp <= 100 ) { p[n++] = tmp; } else { errno = ERANGE; perror( "KCONFIG_PROBABILITY" ); exit( 1 ); } env = (*endp == ':') ? endp+1 : endp; if( n >=3 ) { break; } } switch( n ) { case 1: pby = p[0]; ptm = pby/2; pty = pby-ptm; break; case 2: pty = p[0]; ptm = p[1]; pby = pty + ptm; break; case 3: pby = p[0]; pty = p[1]; ptm = p[2]; break; } if( pty+ptm > 100 ) { errno = ERANGE; perror( "KCONFIG_PROBABILITY" ); exit( 1 ); } } bool has_changed = false; for_all_symbols(i, sym) { if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID)) continue; switch (sym_get_type(sym)) { case S_BOOLEAN: case S_TRISTATE: has_changed = true; switch (mode) { case def_yes: sym->def[S_DEF_USER].tri = yes; break; case def_mod: sym->def[S_DEF_USER].tri = mod; break; case def_no: if (sym->flags & SYMBOL_ALLNOCONFIG_Y) sym->def[S_DEF_USER].tri = yes; else sym->def[S_DEF_USER].tri = no; break; case def_random: sym->def[S_DEF_USER].tri = no; cnt = rand() % 100; if (sym->type == S_TRISTATE) { if (cnt < pty) sym->def[S_DEF_USER].tri = yes; else if (cnt < (pty+ptm)) sym->def[S_DEF_USER].tri = mod; } else if (cnt < pby) sym->def[S_DEF_USER].tri = yes; break; default: continue; } if (!(sym_is_choice(sym) && mode == def_random)) sym->flags |= SYMBOL_DEF_USER; break; default: break; } } sym_clear_all_valid(); /* * We have different type of choice blocks. * If curr.tri equals to mod then we can select several * choice symbols in one block. * In this case we do nothing. * If curr.tri equals yes then only one symbol can be * selected in a choice block and we set it to yes, * and the rest to no. */ if (mode != def_random) { for_all_symbols(i, csym) { if ((sym_is_choice(csym) && !sym_has_value(csym)) || sym_is_choice_value(csym)) csym->flags |= SYMBOL_NEED_SET_CHOICE_VALUES; } } for_all_symbols(i, csym) { if (sym_has_value(csym) || !sym_is_choice(csym)) continue; sym_calc_value(csym); if (mode == def_random) has_changed |= randomize_choice_values(csym); else { set_all_choice_values(csym); has_changed = true; } } return has_changed; } void conf_rewrite_mod_or_yes(enum conf_def_mode mode) { struct symbol *sym; int i; tristate old_val = (mode == def_y2m) ? yes : mod; tristate new_val = (mode == def_y2m) ? mod : yes; for_all_symbols(i, sym) { if (sym_get_type(sym) == S_TRISTATE && sym->def[S_DEF_USER].tri == old_val) sym->def[S_DEF_USER].tri = new_val; } sym_clear_all_valid(); } crust-0.5/3rdparty/kconfig/expr.c000066400000000000000000000740061414152222500170350ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2002 Roman Zippel */ #include #include #include #include #include #include "lkc.h" #define DEBUG_EXPR 0 static struct expr *expr_eliminate_yn(struct expr *e); struct expr *expr_alloc_symbol(struct symbol *sym) { struct expr *e = xcalloc(1, sizeof(*e)); e->type = E_SYMBOL; e->left.sym = sym; return e; } struct expr *expr_alloc_one(enum expr_type type, struct expr *ce) { struct expr *e = xcalloc(1, sizeof(*e)); e->type = type; e->left.expr = ce; return e; } struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2) { struct expr *e = xcalloc(1, sizeof(*e)); e->type = type; e->left.expr = e1; e->right.expr = e2; return e; } struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2) { struct expr *e = xcalloc(1, sizeof(*e)); e->type = type; e->left.sym = s1; e->right.sym = s2; return e; } struct expr *expr_alloc_and(struct expr *e1, struct expr *e2) { if (!e1) return e2; return e2 ? expr_alloc_two(E_AND, e1, e2) : e1; } struct expr *expr_alloc_or(struct expr *e1, struct expr *e2) { if (!e1) return e2; return e2 ? expr_alloc_two(E_OR, e1, e2) : e1; } struct expr *expr_copy(const struct expr *org) { struct expr *e; if (!org) return NULL; e = xmalloc(sizeof(*org)); memcpy(e, org, sizeof(*org)); switch (org->type) { case E_SYMBOL: e->left = org->left; break; case E_NOT: e->left.expr = expr_copy(org->left.expr); break; case E_EQUAL: case E_GEQ: case E_GTH: case E_LEQ: case E_LTH: case E_UNEQUAL: e->left.sym = org->left.sym; e->right.sym = org->right.sym; break; case E_AND: case E_OR: case E_LIST: e->left.expr = expr_copy(org->left.expr); e->right.expr = expr_copy(org->right.expr); break; default: fprintf(stderr, "can't copy type %d\n", e->type); free(e); e = NULL; break; } return e; } void expr_free(struct expr *e) { if (!e) return; switch (e->type) { case E_SYMBOL: break; case E_NOT: expr_free(e->left.expr); break; case E_EQUAL: case E_GEQ: case E_GTH: case E_LEQ: case E_LTH: case E_UNEQUAL: break; case E_OR: case E_AND: expr_free(e->left.expr); expr_free(e->right.expr); break; default: fprintf(stderr, "how to free type %d?\n", e->type); break; } free(e); } static int trans_count; #define e1 (*ep1) #define e2 (*ep2) /* * expr_eliminate_eq() helper. * * Walks the two expression trees given in 'ep1' and 'ep2'. Any node that does * not have type 'type' (E_OR/E_AND) is considered a leaf, and is compared * against all other leaves. Two equal leaves are both replaced with either 'y' * or 'n' as appropriate for 'type', to be eliminated later. */ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2) { /* Recurse down to leaves */ if (e1->type == type) { __expr_eliminate_eq(type, &e1->left.expr, &e2); __expr_eliminate_eq(type, &e1->right.expr, &e2); return; } if (e2->type == type) { __expr_eliminate_eq(type, &e1, &e2->left.expr); __expr_eliminate_eq(type, &e1, &e2->right.expr); return; } /* e1 and e2 are leaves. Compare them. */ if (e1->type == E_SYMBOL && e2->type == E_SYMBOL && e1->left.sym == e2->left.sym && (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no)) return; if (!expr_eq(e1, e2)) return; /* e1 and e2 are equal leaves. Prepare them for elimination. */ trans_count++; expr_free(e1); expr_free(e2); switch (type) { case E_OR: e1 = expr_alloc_symbol(&symbol_no); e2 = expr_alloc_symbol(&symbol_no); break; case E_AND: e1 = expr_alloc_symbol(&symbol_yes); e2 = expr_alloc_symbol(&symbol_yes); break; default: ; } } /* * Rewrites the expressions 'ep1' and 'ep2' to remove operands common to both. * Example reductions: * * ep1: A && B -> ep1: y * ep2: A && B && C -> ep2: C * * ep1: A || B -> ep1: n * ep2: A || B || C -> ep2: C * * ep1: A && (B && FOO) -> ep1: FOO * ep2: (BAR && B) && A -> ep2: BAR * * ep1: A && (B || C) -> ep1: y * ep2: (C || B) && A -> ep2: y * * Comparisons are done between all operands at the same "level" of && or ||. * For example, in the expression 'e1 && (e2 || e3) && (e4 || e5)', the * following operands will be compared: * * - 'e1', 'e2 || e3', and 'e4 || e5', against each other * - e2 against e3 * - e4 against e5 * * Parentheses are irrelevant within a single level. 'e1 && (e2 && e3)' and * '(e1 && e2) && e3' are both a single level. * * See __expr_eliminate_eq() as well. */ void expr_eliminate_eq(struct expr **ep1, struct expr **ep2) { if (!e1 || !e2) return; switch (e1->type) { case E_OR: case E_AND: __expr_eliminate_eq(e1->type, ep1, ep2); default: ; } if (e1->type != e2->type) switch (e2->type) { case E_OR: case E_AND: __expr_eliminate_eq(e2->type, ep1, ep2); default: ; } e1 = expr_eliminate_yn(e1); e2 = expr_eliminate_yn(e2); } #undef e1 #undef e2 /* * Returns true if 'e1' and 'e2' are equal, after minor simplification. Two * &&/|| expressions are considered equal if every operand in one expression * equals some operand in the other (operands do not need to appear in the same * order), recursively. */ int expr_eq(struct expr *e1, struct expr *e2) { int res, old_count; /* * A NULL expr is taken to be yes, but there's also a different way to * represent yes. expr_is_yes() checks for either representation. */ if (!e1 || !e2) return expr_is_yes(e1) && expr_is_yes(e2); if (e1->type != e2->type) return 0; switch (e1->type) { case E_EQUAL: case E_GEQ: case E_GTH: case E_LEQ: case E_LTH: case E_UNEQUAL: return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym; case E_SYMBOL: return e1->left.sym == e2->left.sym; case E_NOT: return expr_eq(e1->left.expr, e2->left.expr); case E_AND: case E_OR: e1 = expr_copy(e1); e2 = expr_copy(e2); old_count = trans_count; expr_eliminate_eq(&e1, &e2); res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL && e1->left.sym == e2->left.sym); expr_free(e1); expr_free(e2); trans_count = old_count; return res; case E_LIST: case E_RANGE: case E_NONE: /* panic */; } if (DEBUG_EXPR) { expr_fprint(e1, stdout); printf(" = "); expr_fprint(e2, stdout); printf(" ?\n"); } return 0; } /* * Recursively performs the following simplifications in-place (as well as the * corresponding simplifications with swapped operands): * * expr && n -> n * expr && y -> expr * expr || n -> expr * expr || y -> y * * Returns the optimized expression. */ static struct expr *expr_eliminate_yn(struct expr *e) { struct expr *tmp; if (e) switch (e->type) { case E_AND: e->left.expr = expr_eliminate_yn(e->left.expr); e->right.expr = expr_eliminate_yn(e->right.expr); if (e->left.expr->type == E_SYMBOL) { if (e->left.expr->left.sym == &symbol_no) { expr_free(e->left.expr); expr_free(e->right.expr); e->type = E_SYMBOL; e->left.sym = &symbol_no; e->right.expr = NULL; return e; } else if (e->left.expr->left.sym == &symbol_yes) { free(e->left.expr); tmp = e->right.expr; *e = *(e->right.expr); free(tmp); return e; } } if (e->right.expr->type == E_SYMBOL) { if (e->right.expr->left.sym == &symbol_no) { expr_free(e->left.expr); expr_free(e->right.expr); e->type = E_SYMBOL; e->left.sym = &symbol_no; e->right.expr = NULL; return e; } else if (e->right.expr->left.sym == &symbol_yes) { free(e->right.expr); tmp = e->left.expr; *e = *(e->left.expr); free(tmp); return e; } } break; case E_OR: e->left.expr = expr_eliminate_yn(e->left.expr); e->right.expr = expr_eliminate_yn(e->right.expr); if (e->left.expr->type == E_SYMBOL) { if (e->left.expr->left.sym == &symbol_no) { free(e->left.expr); tmp = e->right.expr; *e = *(e->right.expr); free(tmp); return e; } else if (e->left.expr->left.sym == &symbol_yes) { expr_free(e->left.expr); expr_free(e->right.expr); e->type = E_SYMBOL; e->left.sym = &symbol_yes; e->right.expr = NULL; return e; } } if (e->right.expr->type == E_SYMBOL) { if (e->right.expr->left.sym == &symbol_no) { free(e->right.expr); tmp = e->left.expr; *e = *(e->left.expr); free(tmp); return e; } else if (e->right.expr->left.sym == &symbol_yes) { expr_free(e->left.expr); expr_free(e->right.expr); e->type = E_SYMBOL; e->left.sym = &symbol_yes; e->right.expr = NULL; return e; } } break; default: ; } return e; } /* * bool FOO!=n => FOO */ struct expr *expr_trans_bool(struct expr *e) { if (!e) return NULL; switch (e->type) { case E_AND: case E_OR: case E_NOT: e->left.expr = expr_trans_bool(e->left.expr); e->right.expr = expr_trans_bool(e->right.expr); break; case E_UNEQUAL: // FOO!=n -> FOO if (e->left.sym->type == S_TRISTATE) { if (e->right.sym == &symbol_no) { e->type = E_SYMBOL; e->right.sym = NULL; } } break; default: ; } return e; } /* * e1 || e2 -> ? */ static struct expr *expr_join_or(struct expr *e1, struct expr *e2) { struct expr *tmp; struct symbol *sym1, *sym2; if (expr_eq(e1, e2)) return expr_copy(e1); if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) return NULL; if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) return NULL; if (e1->type == E_NOT) { tmp = e1->left.expr; if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) return NULL; sym1 = tmp->left.sym; } else sym1 = e1->left.sym; if (e2->type == E_NOT) { if (e2->left.expr->type != E_SYMBOL) return NULL; sym2 = e2->left.expr->left.sym; } else sym2 = e2->left.sym; if (sym1 != sym2) return NULL; if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) return NULL; if (sym1->type == S_TRISTATE) { if (e1->type == E_EQUAL && e2->type == E_EQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) { // (a='y') || (a='m') -> (a!='n') return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no); } if (e1->type == E_EQUAL && e2->type == E_EQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) { // (a='y') || (a='n') -> (a!='m') return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod); } if (e1->type == E_EQUAL && e2->type == E_EQUAL && ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) { // (a='m') || (a='n') -> (a!='y') return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes); } } if (sym1->type == S_BOOLEAN && sym1 == sym2) { if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) || (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL)) return expr_alloc_symbol(&symbol_yes); } if (DEBUG_EXPR) { printf("optimize ("); expr_fprint(e1, stdout); printf(") || ("); expr_fprint(e2, stdout); printf(")?\n"); } return NULL; } static struct expr *expr_join_and(struct expr *e1, struct expr *e2) { struct expr *tmp; struct symbol *sym1, *sym2; if (expr_eq(e1, e2)) return expr_copy(e1); if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) return NULL; if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) return NULL; if (e1->type == E_NOT) { tmp = e1->left.expr; if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) return NULL; sym1 = tmp->left.sym; } else sym1 = e1->left.sym; if (e2->type == E_NOT) { if (e2->left.expr->type != E_SYMBOL) return NULL; sym2 = e2->left.expr->left.sym; } else sym2 = e2->left.sym; if (sym1 != sym2) return NULL; if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) return NULL; if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) || (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes)) // (a) && (a='y') -> (a='y') return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) || (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no)) // (a) && (a!='n') -> (a) return expr_alloc_symbol(sym1); if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) || (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod)) // (a) && (a!='m') -> (a='y') return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); if (sym1->type == S_TRISTATE) { if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) { // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' sym2 = e1->right.sym; if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) : expr_alloc_symbol(&symbol_no); } if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) { // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' sym2 = e2->right.sym; if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) : expr_alloc_symbol(&symbol_no); } if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) // (a!='y') && (a!='n') -> (a='m') return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod); if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) // (a!='y') && (a!='m') -> (a='n') return expr_alloc_comp(E_EQUAL, sym1, &symbol_no); if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) // (a!='m') && (a!='n') -> (a='m') return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) || (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) || (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) || (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes)) return NULL; } if (DEBUG_EXPR) { printf("optimize ("); expr_fprint(e1, stdout); printf(") && ("); expr_fprint(e2, stdout); printf(")?\n"); } return NULL; } /* * expr_eliminate_dups() helper. * * Walks the two expression trees given in 'ep1' and 'ep2'. Any node that does * not have type 'type' (E_OR/E_AND) is considered a leaf, and is compared * against all other leaves to look for simplifications. */ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2) { #define e1 (*ep1) #define e2 (*ep2) struct expr *tmp; /* Recurse down to leaves */ if (e1->type == type) { expr_eliminate_dups1(type, &e1->left.expr, &e2); expr_eliminate_dups1(type, &e1->right.expr, &e2); return; } if (e2->type == type) { expr_eliminate_dups1(type, &e1, &e2->left.expr); expr_eliminate_dups1(type, &e1, &e2->right.expr); return; } /* e1 and e2 are leaves. Compare and process them. */ if (e1 == e2) return; switch (e1->type) { case E_OR: case E_AND: expr_eliminate_dups1(e1->type, &e1, &e1); default: ; } switch (type) { case E_OR: tmp = expr_join_or(e1, e2); if (tmp) { expr_free(e1); expr_free(e2); e1 = expr_alloc_symbol(&symbol_no); e2 = tmp; trans_count++; } break; case E_AND: tmp = expr_join_and(e1, e2); if (tmp) { expr_free(e1); expr_free(e2); e1 = expr_alloc_symbol(&symbol_yes); e2 = tmp; trans_count++; } break; default: ; } #undef e1 #undef e2 } /* * Rewrites 'e' in-place to remove ("join") duplicate and other redundant * operands. * * Example simplifications: * * A || B || A -> A || B * A && B && A=y -> A=y && B * * Returns the deduplicated expression. */ struct expr *expr_eliminate_dups(struct expr *e) { int oldcount; if (!e) return e; oldcount = trans_count; while (1) { trans_count = 0; switch (e->type) { case E_OR: case E_AND: expr_eliminate_dups1(e->type, &e, &e); default: ; } if (!trans_count) /* No simplifications done in this pass. We're done */ break; e = expr_eliminate_yn(e); } trans_count = oldcount; return e; } /* * Performs various simplifications involving logical operators and * comparisons. * * Allocates and returns a new expression. */ struct expr *expr_transform(struct expr *e) { struct expr *tmp; if (!e) return NULL; switch (e->type) { case E_EQUAL: case E_GEQ: case E_GTH: case E_LEQ: case E_LTH: case E_UNEQUAL: case E_SYMBOL: case E_LIST: break; default: e->left.expr = expr_transform(e->left.expr); e->right.expr = expr_transform(e->right.expr); } switch (e->type) { case E_EQUAL: if (e->left.sym->type != S_BOOLEAN) break; if (e->right.sym == &symbol_no) { e->type = E_NOT; e->left.expr = expr_alloc_symbol(e->left.sym); e->right.sym = NULL; break; } if (e->right.sym == &symbol_mod) { printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name); e->type = E_SYMBOL; e->left.sym = &symbol_no; e->right.sym = NULL; break; } if (e->right.sym == &symbol_yes) { e->type = E_SYMBOL; e->right.sym = NULL; break; } break; case E_UNEQUAL: if (e->left.sym->type != S_BOOLEAN) break; if (e->right.sym == &symbol_no) { e->type = E_SYMBOL; e->right.sym = NULL; break; } if (e->right.sym == &symbol_mod) { printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name); e->type = E_SYMBOL; e->left.sym = &symbol_yes; e->right.sym = NULL; break; } if (e->right.sym == &symbol_yes) { e->type = E_NOT; e->left.expr = expr_alloc_symbol(e->left.sym); e->right.sym = NULL; break; } break; case E_NOT: switch (e->left.expr->type) { case E_NOT: // !!a -> a tmp = e->left.expr->left.expr; free(e->left.expr); free(e); e = tmp; e = expr_transform(e); break; case E_EQUAL: case E_UNEQUAL: // !a='x' -> a!='x' tmp = e->left.expr; free(e); e = tmp; e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL; break; case E_LEQ: case E_GEQ: // !a<='x' -> a>'x' tmp = e->left.expr; free(e); e = tmp; e->type = e->type == E_LEQ ? E_GTH : E_LTH; break; case E_LTH: case E_GTH: // !a<'x' -> a>='x' tmp = e->left.expr; free(e); e = tmp; e->type = e->type == E_LTH ? E_GEQ : E_LEQ; break; case E_OR: // !(a || b) -> !a && !b tmp = e->left.expr; e->type = E_AND; e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); tmp->type = E_NOT; tmp->right.expr = NULL; e = expr_transform(e); break; case E_AND: // !(a && b) -> !a || !b tmp = e->left.expr; e->type = E_OR; e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); tmp->type = E_NOT; tmp->right.expr = NULL; e = expr_transform(e); break; case E_SYMBOL: if (e->left.expr->left.sym == &symbol_yes) { // !'y' -> 'n' tmp = e->left.expr; free(e); e = tmp; e->type = E_SYMBOL; e->left.sym = &symbol_no; break; } if (e->left.expr->left.sym == &symbol_mod) { // !'m' -> 'm' tmp = e->left.expr; free(e); e = tmp; e->type = E_SYMBOL; e->left.sym = &symbol_mod; break; } if (e->left.expr->left.sym == &symbol_no) { // !'n' -> 'y' tmp = e->left.expr; free(e); e = tmp; e->type = E_SYMBOL; e->left.sym = &symbol_yes; break; } break; default: ; } break; default: ; } return e; } int expr_contains_symbol(struct expr *dep, struct symbol *sym) { if (!dep) return 0; switch (dep->type) { case E_AND: case E_OR: return expr_contains_symbol(dep->left.expr, sym) || expr_contains_symbol(dep->right.expr, sym); case E_SYMBOL: return dep->left.sym == sym; case E_EQUAL: case E_GEQ: case E_GTH: case E_LEQ: case E_LTH: case E_UNEQUAL: return dep->left.sym == sym || dep->right.sym == sym; case E_NOT: return expr_contains_symbol(dep->left.expr, sym); default: ; } return 0; } bool expr_depends_symbol(struct expr *dep, struct symbol *sym) { if (!dep) return false; switch (dep->type) { case E_AND: return expr_depends_symbol(dep->left.expr, sym) || expr_depends_symbol(dep->right.expr, sym); case E_SYMBOL: return dep->left.sym == sym; case E_EQUAL: if (dep->left.sym == sym) { if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod) return true; } break; case E_UNEQUAL: if (dep->left.sym == sym) { if (dep->right.sym == &symbol_no) return true; } break; default: ; } return false; } /* * Inserts explicit comparisons of type 'type' to symbol 'sym' into the * expression 'e'. * * Examples transformations for type == E_UNEQUAL, sym == &symbol_no: * * A -> A!=n * !A -> A=n * A && B -> !(A=n || B=n) * A || B -> !(A=n && B=n) * A && (B || C) -> !(A=n || (B=n && C=n)) * * Allocates and returns a new expression. */ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym) { struct expr *e1, *e2; if (!e) { e = expr_alloc_symbol(sym); if (type == E_UNEQUAL) e = expr_alloc_one(E_NOT, e); return e; } switch (e->type) { case E_AND: e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); if (sym == &symbol_yes) e = expr_alloc_two(E_AND, e1, e2); if (sym == &symbol_no) e = expr_alloc_two(E_OR, e1, e2); if (type == E_UNEQUAL) e = expr_alloc_one(E_NOT, e); return e; case E_OR: e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); if (sym == &symbol_yes) e = expr_alloc_two(E_OR, e1, e2); if (sym == &symbol_no) e = expr_alloc_two(E_AND, e1, e2); if (type == E_UNEQUAL) e = expr_alloc_one(E_NOT, e); return e; case E_NOT: return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym); case E_UNEQUAL: case E_LTH: case E_LEQ: case E_GTH: case E_GEQ: case E_EQUAL: if (type == E_EQUAL) { if (sym == &symbol_yes) return expr_copy(e); if (sym == &symbol_mod) return expr_alloc_symbol(&symbol_no); if (sym == &symbol_no) return expr_alloc_one(E_NOT, expr_copy(e)); } else { if (sym == &symbol_yes) return expr_alloc_one(E_NOT, expr_copy(e)); if (sym == &symbol_mod) return expr_alloc_symbol(&symbol_yes); if (sym == &symbol_no) return expr_copy(e); } break; case E_SYMBOL: return expr_alloc_comp(type, e->left.sym, sym); case E_LIST: case E_RANGE: case E_NONE: /* panic */; } return NULL; } enum string_value_kind { k_string, k_signed, k_unsigned, }; union string_value { unsigned long long u; signed long long s; }; static enum string_value_kind expr_parse_string(const char *str, enum symbol_type type, union string_value *val) { char *tail; enum string_value_kind kind; errno = 0; switch (type) { case S_BOOLEAN: case S_TRISTATE: val->s = !strcmp(str, "n") ? 0 : !strcmp(str, "m") ? 1 : !strcmp(str, "y") ? 2 : -1; return k_signed; case S_INT: val->s = strtoll(str, &tail, 10); kind = k_signed; break; case S_HEX: val->u = strtoull(str, &tail, 16); kind = k_unsigned; break; default: val->s = strtoll(str, &tail, 0); kind = k_signed; break; } return !errno && !*tail && tail > str && isxdigit(tail[-1]) ? kind : k_string; } tristate expr_calc_value(struct expr *e) { tristate val1, val2; const char *str1, *str2; enum string_value_kind k1 = k_string, k2 = k_string; union string_value lval = {}, rval = {}; int res; if (!e) return yes; switch (e->type) { case E_SYMBOL: sym_calc_value(e->left.sym); return e->left.sym->curr.tri; case E_AND: val1 = expr_calc_value(e->left.expr); val2 = expr_calc_value(e->right.expr); return EXPR_AND(val1, val2); case E_OR: val1 = expr_calc_value(e->left.expr); val2 = expr_calc_value(e->right.expr); return EXPR_OR(val1, val2); case E_NOT: val1 = expr_calc_value(e->left.expr); return EXPR_NOT(val1); case E_EQUAL: case E_GEQ: case E_GTH: case E_LEQ: case E_LTH: case E_UNEQUAL: break; default: printf("expr_calc_value: %d?\n", e->type); return no; } sym_calc_value(e->left.sym); sym_calc_value(e->right.sym); str1 = sym_get_string_value(e->left.sym); str2 = sym_get_string_value(e->right.sym); if (e->left.sym->type != S_STRING || e->right.sym->type != S_STRING) { k1 = expr_parse_string(str1, e->left.sym->type, &lval); k2 = expr_parse_string(str2, e->right.sym->type, &rval); } if (k1 == k_string || k2 == k_string) res = strcmp(str1, str2); else if (k1 == k_unsigned || k2 == k_unsigned) res = (lval.u > rval.u) - (lval.u < rval.u); else /* if (k1 == k_signed && k2 == k_signed) */ res = (lval.s > rval.s) - (lval.s < rval.s); switch(e->type) { case E_EQUAL: return res ? no : yes; case E_GEQ: return res >= 0 ? yes : no; case E_GTH: return res > 0 ? yes : no; case E_LEQ: return res <= 0 ? yes : no; case E_LTH: return res < 0 ? yes : no; case E_UNEQUAL: return res ? yes : no; default: printf("expr_calc_value: relation %d?\n", e->type); return no; } } static int expr_compare_type(enum expr_type t1, enum expr_type t2) { if (t1 == t2) return 0; switch (t1) { case E_LEQ: case E_LTH: case E_GEQ: case E_GTH: if (t2 == E_EQUAL || t2 == E_UNEQUAL) return 1; case E_EQUAL: case E_UNEQUAL: if (t2 == E_NOT) return 1; case E_NOT: if (t2 == E_AND) return 1; case E_AND: if (t2 == E_OR) return 1; case E_OR: if (t2 == E_LIST) return 1; case E_LIST: if (t2 == 0) return 1; default: return -1; } printf("[%dgt%d?]", t1, t2); return 0; } void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken) { if (!e) { fn(data, NULL, "y"); return; } if (expr_compare_type(prevtoken, e->type) > 0) fn(data, NULL, "("); switch (e->type) { case E_SYMBOL: if (e->left.sym->name) fn(data, e->left.sym, e->left.sym->name); else fn(data, NULL, ""); break; case E_NOT: fn(data, NULL, "!"); expr_print(e->left.expr, fn, data, E_NOT); break; case E_EQUAL: if (e->left.sym->name) fn(data, e->left.sym, e->left.sym->name); else fn(data, NULL, ""); fn(data, NULL, "="); fn(data, e->right.sym, e->right.sym->name); break; case E_LEQ: case E_LTH: if (e->left.sym->name) fn(data, e->left.sym, e->left.sym->name); else fn(data, NULL, ""); fn(data, NULL, e->type == E_LEQ ? "<=" : "<"); fn(data, e->right.sym, e->right.sym->name); break; case E_GEQ: case E_GTH: if (e->left.sym->name) fn(data, e->left.sym, e->left.sym->name); else fn(data, NULL, ""); fn(data, NULL, e->type == E_GEQ ? ">=" : ">"); fn(data, e->right.sym, e->right.sym->name); break; case E_UNEQUAL: if (e->left.sym->name) fn(data, e->left.sym, e->left.sym->name); else fn(data, NULL, ""); fn(data, NULL, "!="); fn(data, e->right.sym, e->right.sym->name); break; case E_OR: expr_print(e->left.expr, fn, data, E_OR); fn(data, NULL, " || "); expr_print(e->right.expr, fn, data, E_OR); break; case E_AND: expr_print(e->left.expr, fn, data, E_AND); fn(data, NULL, " && "); expr_print(e->right.expr, fn, data, E_AND); break; case E_LIST: fn(data, e->right.sym, e->right.sym->name); if (e->left.expr) { fn(data, NULL, " ^ "); expr_print(e->left.expr, fn, data, E_LIST); } break; case E_RANGE: fn(data, NULL, "["); fn(data, e->left.sym, e->left.sym->name); fn(data, NULL, " "); fn(data, e->right.sym, e->right.sym->name); fn(data, NULL, "]"); break; default: { char buf[32]; sprintf(buf, "", e->type); fn(data, NULL, buf); break; } } if (expr_compare_type(prevtoken, e->type) > 0) fn(data, NULL, ")"); } static void expr_print_file_helper(void *data, struct symbol *sym, const char *str) { xfwrite(str, strlen(str), 1, data); } void expr_fprint(struct expr *e, FILE *out) { expr_print(e, expr_print_file_helper, out, E_NONE); } static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *str) { struct gstr *gs = (struct gstr*)data; const char *sym_str = NULL; if (sym) sym_str = sym_get_string_value(sym); if (gs->max_width) { unsigned extra_length = strlen(str); const char *last_cr = strrchr(gs->s, '\n'); unsigned last_line_length; if (sym_str) extra_length += 4 + strlen(sym_str); if (!last_cr) last_cr = gs->s; last_line_length = strlen(gs->s) - (last_cr - gs->s); if ((last_line_length + extra_length) > gs->max_width) str_append(gs, "\\\n"); } str_append(gs, str); if (sym && sym->type != S_UNKNOWN) str_printf(gs, " [=%s]", sym_str); } void expr_gstr_print(struct expr *e, struct gstr *gs) { expr_print(e, expr_print_gstr_helper, gs, E_NONE); } /* * Transform the top level "||" tokens into newlines and prepend each * line with a minus. This makes expressions much easier to read. * Suitable for reverse dependency expressions. */ static void expr_print_revdep(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, tristate pr_type, const char **title) { if (e->type == E_OR) { expr_print_revdep(e->left.expr, fn, data, pr_type, title); expr_print_revdep(e->right.expr, fn, data, pr_type, title); } else if (expr_calc_value(e) == pr_type) { if (*title) { fn(data, NULL, *title); *title = NULL; } fn(data, NULL, " - "); expr_print(e, fn, data, E_NONE); fn(data, NULL, "\n"); } } void expr_gstr_print_revdep(struct expr *e, struct gstr *gs, tristate pr_type, const char *title) { expr_print_revdep(e, expr_print_gstr_helper, gs, pr_type, &title); } crust-0.5/3rdparty/kconfig/expr.h000066400000000000000000000232211414152222500170330ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2002 Roman Zippel */ #ifndef EXPR_H #define EXPR_H #ifdef __cplusplus extern "C" { #endif #include #include #include "list.h" #ifndef __cplusplus #include #endif struct file { struct file *next; struct file *parent; const char *name; int lineno; }; typedef enum tristate { no, mod, yes } tristate; enum expr_type { E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_LTH, E_LEQ, E_GTH, E_GEQ, E_LIST, E_SYMBOL, E_RANGE }; union expr_data { struct expr *expr; struct symbol *sym; }; struct expr { enum expr_type type; union expr_data left, right; }; #define EXPR_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2)) #define EXPR_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2)) #define EXPR_NOT(dep) (2-(dep)) #define expr_list_for_each_sym(l, e, s) \ for (e = (l); e && (s = e->right.sym); e = e->left.expr) struct expr_value { struct expr *expr; tristate tri; }; struct symbol_value { void *val; tristate tri; }; enum symbol_type { S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING }; /* enum values are used as index to symbol.def[] */ enum { S_DEF_USER, /* main user value */ S_DEF_AUTO, /* values read from auto.conf */ S_DEF_DEF3, /* Reserved for UI usage */ S_DEF_DEF4, /* Reserved for UI usage */ S_DEF_COUNT }; /* * Represents a configuration symbol. * * Choices are represented as a special kind of symbol and have the * SYMBOL_CHOICE bit set in 'flags'. */ struct symbol { /* The next symbol in the same bucket in the symbol hash table */ struct symbol *next; /* The name of the symbol, e.g. "FOO" for 'config FOO' */ char *name; /* S_BOOLEAN, S_TRISTATE, ... */ enum symbol_type type; /* * The calculated value of the symbol. The SYMBOL_VALID bit is set in * 'flags' when this is up to date. Note that this value might differ * from the user value set in e.g. a .config file, due to visibility. */ struct symbol_value curr; /* * Values for the symbol provided from outside. def[S_DEF_USER] holds * the .config value. */ struct symbol_value def[S_DEF_COUNT]; /* * An upper bound on the tristate value the user can set for the symbol * if it is a boolean or tristate. Calculated from prompt dependencies, * which also inherit dependencies from enclosing menus, choices, and * ifs. If 'n', the user value will be ignored. * * Symbols lacking prompts always have visibility 'n'. */ tristate visible; /* SYMBOL_* flags */ int flags; /* List of properties. See prop_type. */ struct property *prop; /* Dependencies from enclosing menus, choices, and ifs */ struct expr_value dir_dep; /* Reverse dependencies through being selected by other symbols */ struct expr_value rev_dep; /* * "Weak" reverse dependencies through being implied by other symbols */ struct expr_value implied; }; #define for_all_symbols(i, sym) for (i = 0; i < SYMBOL_HASHSIZE; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) #define SYMBOL_CONST 0x0001 /* symbol is const */ #define SYMBOL_CHECK 0x0008 /* used during dependency checking */ #define SYMBOL_CHOICE 0x0010 /* start of a choice block (null name) */ #define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */ #define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */ #define SYMBOL_OPTIONAL 0x0100 /* choice is optional - values can be 'n' */ #define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */ #define SYMBOL_CHANGED 0x0400 /* ? */ #define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .config */ #define SYMBOL_NO_WRITE 0x1000 /* Symbol for internal use only; it will not be written */ #define SYMBOL_CHECKED 0x2000 /* used during dependency checking */ #define SYMBOL_WARNED 0x8000 /* warning has been issued */ /* Set when symbol.def[] is used */ #define SYMBOL_DEF 0x10000 /* First bit of SYMBOL_DEF */ #define SYMBOL_DEF_USER 0x10000 /* symbol.def[S_DEF_USER] is valid */ #define SYMBOL_DEF_AUTO 0x20000 /* symbol.def[S_DEF_AUTO] is valid */ #define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */ #define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */ /* choice values need to be set before calculating this symbol value */ #define SYMBOL_NEED_SET_CHOICE_VALUES 0x100000 /* Set symbol to y if allnoconfig; used for symbols that hide others */ #define SYMBOL_ALLNOCONFIG_Y 0x200000 #define SYMBOL_MAXLENGTH 256 #define SYMBOL_HASHSIZE 9973 /* A property represent the config options that can be associated * with a config "symbol". * Sample: * config FOO * default y * prompt "foo prompt" * select BAR * config BAZ * int "BAZ Value" * range 1..255 * * Please, also check parser.y:print_symbol() when modifying the * list of property types! */ enum prop_type { P_UNKNOWN, P_PROMPT, /* prompt "foo prompt" or "BAZ Value" */ P_COMMENT, /* text associated with a comment */ P_MENU, /* prompt associated with a menu or menuconfig symbol */ P_DEFAULT, /* default y */ P_CHOICE, /* choice value */ P_SELECT, /* select BAR */ P_IMPLY, /* imply BAR */ P_RANGE, /* range 7..100 (for a symbol) */ P_SYMBOL, /* where a symbol is defined */ }; struct property { struct property *next; /* next property - null if last */ enum prop_type type; /* type of property */ const char *text; /* the prompt value - P_PROMPT, P_MENU, P_COMMENT */ struct expr_value visible; struct expr *expr; /* the optional conditional part of the property */ struct menu *menu; /* the menu the property are associated with * valid for: P_SELECT, P_RANGE, P_CHOICE, * P_PROMPT, P_DEFAULT, P_MENU, P_COMMENT */ struct file *file; /* what file was this property defined */ int lineno; /* what lineno was this property defined */ }; #define for_all_properties(sym, st, tok) \ for (st = sym->prop; st; st = st->next) \ if (st->type == (tok)) #define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) #define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE) #define for_all_prompts(sym, st) \ for (st = sym->prop; st; st = st->next) \ if (st->text) /* * Represents a node in the menu tree, as seen in e.g. menuconfig (though used * for all front ends). Each symbol, menu, etc. defined in the Kconfig files * gets a node. A symbol defined in multiple locations gets one node at each * location. */ struct menu { /* The next menu node at the same level */ struct menu *next; /* The parent menu node, corresponding to e.g. a menu or choice */ struct menu *parent; /* The first child menu node, for e.g. menus and choices */ struct menu *list; /* * The symbol associated with the menu node. Choices are implemented as * a special kind of symbol. NULL for menus, comments, and ifs. */ struct symbol *sym; /* * The prompt associated with the node. This holds the prompt for a * symbol as well as the text for a menu or comment, along with the * type (P_PROMPT, P_MENU, etc.) */ struct property *prompt; /* * 'visible if' dependencies. If more than one is given, they will be * ANDed together. */ struct expr *visibility; /* * Ordinary dependencies from e.g. 'depends on' and 'if', ANDed * together */ struct expr *dep; /* MENU_* flags */ unsigned int flags; /* Any help text associated with the node */ char *help; /* The location where the menu node appears in the Kconfig files */ struct file *file; int lineno; /* For use by front ends that need to store auxiliary data */ void *data; }; /* * Set on a menu node when the corresponding symbol changes state in some way. * Can be checked by front ends. */ #define MENU_CHANGED 0x0001 #define MENU_ROOT 0x0002 struct jump_key { struct list_head entries; size_t offset; struct menu *target; int index; }; #define JUMP_NB 9 extern struct file *file_list; extern struct file *current_file; struct file *lookup_file(const char *name); extern struct symbol symbol_yes, symbol_no, symbol_mod; extern struct symbol *modules_sym; extern struct symbol *sym_defconfig_list; extern int cdebug; struct expr *expr_alloc_symbol(struct symbol *sym); struct expr *expr_alloc_one(enum expr_type type, struct expr *ce); struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2); struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2); struct expr *expr_alloc_and(struct expr *e1, struct expr *e2); struct expr *expr_alloc_or(struct expr *e1, struct expr *e2); struct expr *expr_copy(const struct expr *org); void expr_free(struct expr *e); void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); int expr_eq(struct expr *e1, struct expr *e2); tristate expr_calc_value(struct expr *e); struct expr *expr_trans_bool(struct expr *e); struct expr *expr_eliminate_dups(struct expr *e); struct expr *expr_transform(struct expr *e); int expr_contains_symbol(struct expr *dep, struct symbol *sym); bool expr_depends_symbol(struct expr *dep, struct symbol *sym); struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym); void expr_fprint(struct expr *e, FILE *out); struct gstr; /* forward */ void expr_gstr_print(struct expr *e, struct gstr *gs); void expr_gstr_print_revdep(struct expr *e, struct gstr *gs, tristate pr_type, const char *title); static inline int expr_is_yes(struct expr *e) { return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes); } static inline int expr_is_no(struct expr *e) { return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no); } #ifdef __cplusplus } #endif #endif /* EXPR_H */ crust-0.5/3rdparty/kconfig/lexer.l000066400000000000000000000227451414152222500172120ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2002 Roman Zippel */ %option nostdinit noyywrap never-interactive full ecs %option 8bit nodefault yylineno %x ASSIGN_VAL HELP STRING %{ #include #include #include #include #include #include #include "lkc.h" #include "parser.tab.h" #define YY_DECL static int yylex1(void) #define START_STRSIZE 16 static struct { struct file *file; int lineno; } current_pos; static int prev_prev_token = T_EOL; static int prev_token = T_EOL; static char *text; static int text_size, text_asize; struct buffer { struct buffer *parent; YY_BUFFER_STATE state; }; static struct buffer *current_buf; static int last_ts, first_ts; static char *expand_token(const char *in, size_t n); static void append_expanded_string(const char *in); static void zconf_endhelp(void); static void zconf_endfile(void); static void new_string(void) { text = xmalloc(START_STRSIZE); text_asize = START_STRSIZE; text_size = 0; *text = 0; } static void append_string(const char *str, int size) { int new_size = text_size + size + 1; if (new_size > text_asize) { new_size += START_STRSIZE - 1; new_size &= -START_STRSIZE; text = xrealloc(text, new_size); text_asize = new_size; } memcpy(text + text_size, str, size); text_size += size; text[text_size] = 0; } static void alloc_string(const char *str, int size) { text = xmalloc(size + 1); memcpy(text, str, size); text[size] = 0; } static void warn_ignored_character(char chr) { fprintf(stderr, "%s:%d:warning: ignoring unsupported character '%c'\n", current_file->name, yylineno, chr); } %} n [A-Za-z0-9_-] %% int str = 0; int ts, i; #.* /* ignore comment */ [ \t]* /* whitespaces */ \\\n /* escaped new line */ \n return T_EOL; "allnoconfig_y" return T_ALLNOCONFIG_Y; "bool" return T_BOOL; "choice" return T_CHOICE; "comment" return T_COMMENT; "config" return T_CONFIG; "def_bool" return T_DEF_BOOL; "def_tristate" return T_DEF_TRISTATE; "default" return T_DEFAULT; "defconfig_list" return T_DEFCONFIG_LIST; "depends" return T_DEPENDS; "endchoice" return T_ENDCHOICE; "endif" return T_ENDIF; "endmenu" return T_ENDMENU; "help" return T_HELP; "hex" return T_HEX; "if" return T_IF; "imply" return T_IMPLY; "int" return T_INT; "mainmenu" return T_MAINMENU; "menu" return T_MENU; "menuconfig" return T_MENUCONFIG; "modules" return T_MODULES; "on" return T_ON; "option" return T_OPTION; "optional" return T_OPTIONAL; "prompt" return T_PROMPT; "range" return T_RANGE; "select" return T_SELECT; "source" return T_SOURCE; "string" return T_STRING; "tristate" return T_TRISTATE; "visible" return T_VISIBLE; "||" return T_OR; "&&" return T_AND; "=" return T_EQUAL; "!=" return T_UNEQUAL; "<" return T_LESS; "<=" return T_LESS_EQUAL; ">" return T_GREATER; ">=" return T_GREATER_EQUAL; "!" return T_NOT; "(" return T_OPEN_PAREN; ")" return T_CLOSE_PAREN; ":=" return T_COLON_EQUAL; "+=" return T_PLUS_EQUAL; \"|\' { str = yytext[0]; new_string(); BEGIN(STRING); } {n}+ { alloc_string(yytext, yyleng); yylval.string = text; return T_WORD; } ({n}|$)+ { /* this token includes at least one '$' */ yylval.string = expand_token(yytext, yyleng); if (strlen(yylval.string)) return T_WORD; free(yylval.string); } . warn_ignored_character(*yytext); { [^[:blank:]\n]+.* { alloc_string(yytext, yyleng); yylval.string = text; return T_ASSIGN_VAL; } \n { BEGIN(INITIAL); return T_EOL; } . } { "$".* append_expanded_string(yytext); [^$'"\\\n]+ { append_string(yytext, yyleng); } \\.? { append_string(yytext + 1, yyleng - 1); } \'|\" { if (str == yytext[0]) { BEGIN(INITIAL); yylval.string = text; return T_WORD_QUOTE; } else append_string(yytext, 1); } \n { fprintf(stderr, "%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); unput('\n'); BEGIN(INITIAL); yylval.string = text; return T_WORD_QUOTE; } <> { BEGIN(INITIAL); yylval.string = text; return T_WORD_QUOTE; } } { [ \t]+ { ts = 0; for (i = 0; i < yyleng; i++) { if (yytext[i] == '\t') ts = (ts & ~7) + 8; else ts++; } last_ts = ts; if (first_ts) { if (ts < first_ts) { zconf_endhelp(); return T_HELPTEXT; } ts -= first_ts; while (ts > 8) { append_string(" ", 8); ts -= 8; } append_string(" ", ts); } } [ \t]*\n/[^ \t\n] { zconf_endhelp(); return T_HELPTEXT; } [ \t]*\n { append_string("\n", 1); } [^ \t\n].* { while (yyleng) { if ((yytext[yyleng-1] != ' ') && (yytext[yyleng-1] != '\t')) break; yyleng--; } append_string(yytext, yyleng); if (!first_ts) first_ts = last_ts; } <> { zconf_endhelp(); return T_HELPTEXT; } } <> { BEGIN(INITIAL); if (prev_token != T_EOL && prev_token != T_HELPTEXT) fprintf(stderr, "%s:%d:warning: no new line at end of file\n", current_file->name, yylineno); if (current_file) { zconf_endfile(); return T_EOL; } fclose(yyin); yyterminate(); } %% /* second stage lexer */ int yylex(void) { int token; repeat: token = yylex1(); if (prev_token == T_EOL || prev_token == T_HELPTEXT) { if (token == T_EOL) { /* Do not pass unneeded T_EOL to the parser. */ goto repeat; } else { /* * For the parser, update file/lineno at the first token * of each statement. Generally, \n is a statement * terminator in Kconfig, but it is not always true * because \n could be escaped by a backslash. */ current_pos.file = current_file; current_pos.lineno = yylineno; } } if (prev_prev_token == T_EOL && prev_token == T_WORD && (token == T_EQUAL || token == T_COLON_EQUAL || token == T_PLUS_EQUAL)) BEGIN(ASSIGN_VAL); prev_prev_token = prev_token; prev_token = token; return token; } static char *expand_token(const char *in, size_t n) { char *out; int c; char c2; const char *rest, *end; new_string(); append_string(in, n); /* get the whole line because we do not know the end of token. */ while ((c = input()) != EOF) { if (c == '\n') { unput(c); break; } c2 = c; append_string(&c2, 1); } rest = text; out = expand_one_token(&rest); /* push back unused characters to the input stream */ end = rest + strlen(rest); while (end > rest) unput(*--end); free(text); return out; } static void append_expanded_string(const char *str) { const char *end; char *res; str++; res = expand_dollar(&str); /* push back unused characters to the input stream */ end = str + strlen(str); while (end > str) unput(*--end); append_string(res, strlen(res)); free(res); } void zconf_starthelp(void) { new_string(); last_ts = first_ts = 0; BEGIN(HELP); } static void zconf_endhelp(void) { yylval.string = text; BEGIN(INITIAL); } /* * Try to open specified file with following names: * ./name * $(srctree)/name * The latter is used when srctree is separate from objtree * when compiling the kernel. * Return NULL if file is not found. */ FILE *zconf_fopen(const char *name) { char *env, fullname[PATH_MAX+1]; FILE *f; f = fopen(name, "r"); if (!f && name != NULL && name[0] != '/') { env = getenv(SRCTREE); if (env) { snprintf(fullname, sizeof(fullname), "%s/%s", env, name); f = fopen(fullname, "r"); } } return f; } void zconf_initscan(const char *name) { yyin = zconf_fopen(name); if (!yyin) { fprintf(stderr, "can't find file %s\n", name); exit(1); } current_buf = xmalloc(sizeof(*current_buf)); memset(current_buf, 0, sizeof(*current_buf)); current_file = file_lookup(name); yylineno = 1; } void zconf_nextfile(const char *name) { char path[PATH_MAX], *slash; if (strlen(current_file->name) >= PATH_MAX) { fprintf(stderr, "can't source %s: path too long\n", name); exit(1); } strcpy(path, current_file->name); slash = strrchr(path, '/'); if (!slash || strlen(name) >= PATH_MAX - (slash - path) - 1) { fprintf(stderr, "can't source %s: path too long\n", name); exit(1); } strcpy(slash + 1, name); struct file *iter; struct file *file = file_lookup(path); struct buffer *buf = xmalloc(sizeof(*buf)); memset(buf, 0, sizeof(*buf)); current_buf->state = YY_CURRENT_BUFFER; yyin = zconf_fopen(file->name); if (!yyin) { fprintf(stderr, "%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), file->name); exit(1); } yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); buf->parent = current_buf; current_buf = buf; current_file->lineno = yylineno; file->parent = current_file; for (iter = current_file; iter; iter = iter->parent) { if (!strcmp(iter->name, file->name)) { fprintf(stderr, "Recursive inclusion detected.\n" "Inclusion path:\n" " current file : %s\n", file->name); iter = file; do { iter = iter->parent; fprintf(stderr, " included from: %s:%d\n", iter->name, iter->lineno - 1); } while (strcmp(iter->name, file->name)); exit(1); } } yylineno = 1; current_file = file; } static void zconf_endfile(void) { struct buffer *parent; current_file = current_file->parent; if (current_file) yylineno = current_file->lineno; parent = current_buf->parent; if (parent) { fclose(yyin); yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(parent->state); } free(current_buf); current_buf = parent; } int zconf_lineno(void) { return current_pos.lineno; } const char *zconf_curname(void) { return current_pos.file ? current_pos.file->name : ""; } crust-0.5/3rdparty/kconfig/list.h000066400000000000000000000072451414152222500170400ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LIST_H #define LIST_H /* * Copied from include/linux/... */ #undef offsetof #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) /** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_head within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_head within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_head within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty(const struct list_head *head) { return head->next == head; } /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add(struct list_head *_new, struct list_head *prev, struct list_head *next) { next->prev = _new; _new->next = next; _new->prev = prev; prev->next = _new; } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *_new, struct list_head *head) { __list_add(_new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head *prev, struct list_head *next) { next->prev = prev; prev->next = next; } #define LIST_POISON1 ((void *) 0x00100100) #define LIST_POISON2 ((void *) 0x00200200) /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty() on entry does not return true after this, the entry is * in an undefined state. */ static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = (struct list_head*)LIST_POISON1; entry->prev = (struct list_head*)LIST_POISON2; } #endif crust-0.5/3rdparty/kconfig/lkc.h000066400000000000000000000104751414152222500166350ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2002 Roman Zippel */ #ifndef LKC_H #define LKC_H #include "expr.h" #ifdef __cplusplus extern "C" { #endif #include "lkc_proto.h" #define SRCTREE "srctree" #ifndef PACKAGE #define PACKAGE "linux" #endif #ifndef CONFIG_ #define CONFIG_ "CONFIG_" #endif static inline const char *CONFIG_prefix(void) { return getenv( "CONFIG_" ) ?: CONFIG_; } #undef CONFIG_ #define CONFIG_ CONFIG_prefix() enum conf_def_mode { def_default, def_yes, def_mod, def_y2m, def_m2y, def_no, def_random }; extern int yylineno; void zconfdump(FILE *out); void zconf_starthelp(void); FILE *zconf_fopen(const char *name); void zconf_initscan(const char *name); void zconf_nextfile(const char *name); int zconf_lineno(void); const char *zconf_curname(void); /* confdata.c */ const char *conf_get_configname(void); void sym_set_change_count(int count); void sym_add_change_count(int count); bool conf_set_all_new_symbols(enum conf_def_mode mode); void conf_rewrite_mod_or_yes(enum conf_def_mode mode); void set_all_choice_values(struct symbol *csym); /* confdata.c and expr.c */ static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out) { assert(len != 0); if (fwrite(str, len, count, out) != count) fprintf(stderr, "Error in writing or end of file.\n"); } /* util.c */ struct file *file_lookup(const char *name); void *xmalloc(size_t size); void *xcalloc(size_t nmemb, size_t size); void *xrealloc(void *p, size_t size); char *xstrdup(const char *s); char *xstrndup(const char *s, size_t n); /* lexer.l */ int yylex(void); struct gstr { size_t len; char *s; /* * when max_width is not zero long lines in string s (if any) get * wrapped not to exceed the max_width value */ int max_width; }; struct gstr str_new(void); void str_free(struct gstr *gs); void str_append(struct gstr *gs, const char *s); void str_printf(struct gstr *gs, const char *fmt, ...); const char *str_get(struct gstr *gs); /* menu.c */ void _menu_init(void); void menu_warn(struct menu *menu, const char *fmt, ...); struct menu *menu_add_menu(void); void menu_end_menu(void); void menu_add_entry(struct symbol *sym); void menu_add_dep(struct expr *dep); void menu_add_visibility(struct expr *dep); struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); void menu_add_option_modules(void); void menu_add_option_defconfig_list(void); void menu_add_option_allnoconfig_y(void); void menu_finalize(struct menu *parent); void menu_set_type(int type); extern struct menu rootmenu; bool menu_is_empty(struct menu *menu); bool menu_is_visible(struct menu *menu); bool menu_has_prompt(struct menu *menu); const char *menu_get_prompt(struct menu *menu); struct menu *menu_get_root_menu(struct menu *menu); struct menu *menu_get_parent_menu(struct menu *menu); bool menu_has_help(struct menu *menu); const char *menu_get_help(struct menu *menu); struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); void menu_get_ext_help(struct menu *menu, struct gstr *help); /* symbol.c */ void sym_clear_all_valid(void); struct symbol *sym_choice_default(struct symbol *sym); struct property *sym_get_range_prop(struct symbol *sym); const char *sym_get_string_default(struct symbol *sym); struct symbol *sym_check_deps(struct symbol *sym); struct symbol *prop_get_symbol(struct property *prop); static inline tristate sym_get_tristate_value(struct symbol *sym) { return sym->curr.tri; } static inline struct symbol *sym_get_choice_value(struct symbol *sym) { return (struct symbol *)sym->curr.val; } static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval) { return sym_set_tristate_value(chval, yes); } static inline bool sym_is_choice(struct symbol *sym) { return sym->flags & SYMBOL_CHOICE ? true : false; } static inline bool sym_is_choice_value(struct symbol *sym) { return sym->flags & SYMBOL_CHOICEVAL ? true : false; } static inline bool sym_is_optional(struct symbol *sym) { return sym->flags & SYMBOL_OPTIONAL ? true : false; } static inline bool sym_has_value(struct symbol *sym) { return sym->flags & SYMBOL_DEF_USER ? true : false; } #ifdef __cplusplus } #endif #endif /* LKC_H */ crust-0.5/3rdparty/kconfig/lkc_proto.h000066400000000000000000000036111414152222500200520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ #include /* confdata.c */ void conf_parse(const char *name); int conf_read(const char *name); int conf_read_simple(const char *name, int); int conf_write_defconfig(const char *name); int conf_write(const char *name); int conf_write_autoconf(int overwrite); bool conf_get_changed(void); void conf_set_changed_callback(void (*fn)(void)); void conf_set_message_callback(void (*fn)(const char *s)); /* symbol.c */ extern struct symbol * symbol_hash[SYMBOL_HASHSIZE]; struct symbol * sym_lookup(const char *name, int flags); struct symbol * sym_find(const char *name); const char * sym_escape_string_value(const char *in); struct symbol ** sym_re_search(const char *pattern); const char * sym_type_name(enum symbol_type type); void sym_calc_value(struct symbol *sym); enum symbol_type sym_get_type(struct symbol *sym); bool sym_tristate_within_range(struct symbol *sym,tristate tri); bool sym_set_tristate_value(struct symbol *sym,tristate tri); tristate sym_toggle_tristate_value(struct symbol *sym); bool sym_string_valid(struct symbol *sym, const char *newval); bool sym_string_within_range(struct symbol *sym, const char *str); bool sym_set_string_value(struct symbol *sym, const char *newval); bool sym_is_changeable(struct symbol *sym); struct property * sym_get_choice_prop(struct symbol *sym); const char * sym_get_string_value(struct symbol *sym); const char * prop_get_type_name(enum prop_type type); /* preprocess.c */ enum variable_flavor { VAR_SIMPLE, VAR_RECURSIVE, VAR_APPEND, }; void env_write_dep(FILE *f, const char *auto_conf_name); void variable_add(const char *name, const char *value, enum variable_flavor flavor); void variable_all_del(void); char *expand_dollar(const char **str); char *expand_one_token(const char **str); /* expr.c */ void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken); crust-0.5/3rdparty/kconfig/menu.c000066400000000000000000000543411414152222500170230ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2002 Roman Zippel */ #include #include #include #include #include "lkc.h" static const char nohelp_text[] = "There is no help available for this option."; struct menu rootmenu; static struct menu **last_entry_ptr; struct file *file_list; struct file *current_file; void menu_warn(struct menu *menu, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } static void prop_warn(struct property *prop, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } void _menu_init(void) { current_entry = current_menu = &rootmenu; last_entry_ptr = &rootmenu.list; } void menu_add_entry(struct symbol *sym) { struct menu *menu; menu = xmalloc(sizeof(*menu)); memset(menu, 0, sizeof(*menu)); menu->sym = sym; menu->parent = current_menu; menu->file = current_file; menu->lineno = zconf_lineno(); *last_entry_ptr = menu; last_entry_ptr = &menu->next; current_entry = menu; if (sym) menu_add_symbol(P_SYMBOL, sym, NULL); } struct menu *menu_add_menu(void) { last_entry_ptr = ¤t_entry->list; current_menu = current_entry; return current_menu; } void menu_end_menu(void) { last_entry_ptr = ¤t_menu->next; current_menu = current_menu->parent; } /* * Rewrites 'm' to 'm' && MODULES, so that it evaluates to 'n' when running * without modules */ static struct expr *rewrite_m(struct expr *e) { if (!e) return e; switch (e->type) { case E_NOT: e->left.expr = rewrite_m(e->left.expr); break; case E_OR: case E_AND: e->left.expr = rewrite_m(e->left.expr); e->right.expr = rewrite_m(e->right.expr); break; case E_SYMBOL: /* change 'm' into 'm' && MODULES */ if (e->left.sym == &symbol_mod) return expr_alloc_and(e, expr_alloc_symbol(modules_sym)); break; default: break; } return e; } void menu_add_dep(struct expr *dep) { current_entry->dep = expr_alloc_and(current_entry->dep, dep); } void menu_set_type(int type) { struct symbol *sym = current_entry->sym; if (sym->type == type) return; if (sym->type == S_UNKNOWN) { sym->type = type; return; } menu_warn(current_entry, "ignoring type redefinition of '%s' from '%s' to '%s'", sym->name ? sym->name : "", sym_type_name(sym->type), sym_type_name(type)); } static struct property *menu_add_prop(enum prop_type type, struct expr *expr, struct expr *dep) { struct property *prop; prop = xmalloc(sizeof(*prop)); memset(prop, 0, sizeof(*prop)); prop->type = type; prop->file = current_file; prop->lineno = zconf_lineno(); prop->menu = current_entry; prop->expr = expr; prop->visible.expr = dep; /* append property to the prop list of symbol */ if (current_entry->sym) { struct property **propp; for (propp = ¤t_entry->sym->prop; *propp; propp = &(*propp)->next) ; *propp = prop; } return prop; } struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep) { struct property *prop = menu_add_prop(type, NULL, dep); if (isspace(*prompt)) { prop_warn(prop, "leading whitespace ignored"); while (isspace(*prompt)) prompt++; } if (current_entry->prompt) prop_warn(prop, "prompt redefined"); /* Apply all upper menus' visibilities to actual prompts. */ if (type == P_PROMPT) { struct menu *menu = current_entry; while ((menu = menu->parent) != NULL) { struct expr *dup_expr; if (!menu->visibility) continue; /* * Do not add a reference to the menu's visibility * expression but use a copy of it. Otherwise the * expression reduction functions will modify * expressions that have multiple references which * can cause unwanted side effects. */ dup_expr = expr_copy(menu->visibility); prop->visible.expr = expr_alloc_and(prop->visible.expr, dup_expr); } } current_entry->prompt = prop; prop->text = prompt; return prop; } void menu_add_visibility(struct expr *expr) { current_entry->visibility = expr_alloc_and(current_entry->visibility, expr); } void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) { menu_add_prop(type, expr, dep); } void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) { menu_add_prop(type, expr_alloc_symbol(sym), dep); } void menu_add_option_modules(void) { if (modules_sym) zconf_error("symbol '%s' redefines option 'modules' already defined by symbol '%s'", current_entry->sym->name, modules_sym->name); modules_sym = current_entry->sym; } void menu_add_option_defconfig_list(void) { if (!sym_defconfig_list) sym_defconfig_list = current_entry->sym; else if (sym_defconfig_list != current_entry->sym) zconf_error("trying to redefine defconfig symbol"); sym_defconfig_list->flags |= SYMBOL_NO_WRITE; } void menu_add_option_allnoconfig_y(void) { current_entry->sym->flags |= SYMBOL_ALLNOCONFIG_Y; } static int menu_validate_number(struct symbol *sym, struct symbol *sym2) { return sym2->type == S_INT || sym2->type == S_HEX || (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name)); } static void sym_check_prop(struct symbol *sym) { struct property *prop; struct symbol *sym2; char *use; for (prop = sym->prop; prop; prop = prop->next) { switch (prop->type) { case P_DEFAULT: if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && prop->expr->type != E_SYMBOL) prop_warn(prop, "default for config symbol '%s'" " must be a single symbol", sym->name); if (prop->expr->type != E_SYMBOL) break; sym2 = prop_get_symbol(prop); if (sym->type == S_HEX || sym->type == S_INT) { if (!menu_validate_number(sym, sym2)) prop_warn(prop, "'%s': number is invalid", sym->name); } if (sym_is_choice(sym)) { struct property *choice_prop = sym_get_choice_prop(sym2); if (!choice_prop || prop_get_symbol(choice_prop) != sym) prop_warn(prop, "choice default symbol '%s' is not contained in the choice", sym2->name); } break; case P_SELECT: case P_IMPLY: use = prop->type == P_SELECT ? "select" : "imply"; sym2 = prop_get_symbol(prop); if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) prop_warn(prop, "config symbol '%s' uses %s, but is " "not bool or tristate", sym->name, use); else if (sym2->type != S_UNKNOWN && sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE) prop_warn(prop, "'%s' has wrong type. '%s' only " "accept arguments of bool and " "tristate type", sym2->name, use); break; case P_RANGE: if (sym->type != S_INT && sym->type != S_HEX) prop_warn(prop, "range is only allowed " "for int or hex symbols"); if (!menu_validate_number(sym, prop->expr->left.sym) || !menu_validate_number(sym, prop->expr->right.sym)) prop_warn(prop, "range is invalid"); break; default: ; } } } void menu_finalize(struct menu *parent) { struct menu *menu, *last_menu; struct symbol *sym; struct property *prop; struct expr *parentdep, *basedep, *dep, *dep2, **ep; sym = parent->sym; if (parent->list) { /* * This menu node has children. We (recursively) process them * and propagate parent dependencies before moving on. */ if (sym && sym_is_choice(sym)) { if (sym->type == S_UNKNOWN) { /* find the first choice value to find out choice type */ current_entry = parent; for (menu = parent->list; menu; menu = menu->next) { if (menu->sym && menu->sym->type != S_UNKNOWN) { menu_set_type(menu->sym->type); break; } } } /* set the type of the remaining choice values */ for (menu = parent->list; menu; menu = menu->next) { current_entry = menu; if (menu->sym && menu->sym->type == S_UNKNOWN) menu_set_type(sym->type); } /* * Use the choice itself as the parent dependency of * the contained items. This turns the mode of the * choice into an upper bound on the visibility of the * choice value symbols. */ parentdep = expr_alloc_symbol(sym); } else { /* Menu node for 'menu', 'if' */ parentdep = parent->dep; } /* For each child menu node... */ for (menu = parent->list; menu; menu = menu->next) { /* * Propagate parent dependencies to the child menu * node, also rewriting and simplifying expressions */ basedep = rewrite_m(menu->dep); basedep = expr_transform(basedep); basedep = expr_alloc_and(expr_copy(parentdep), basedep); basedep = expr_eliminate_dups(basedep); menu->dep = basedep; if (menu->sym) /* * Note: For symbols, all prompts are included * too in the symbol's own property list */ prop = menu->sym->prop; else /* * For non-symbol menu nodes, we just need to * handle the prompt */ prop = menu->prompt; /* For each property... */ for (; prop; prop = prop->next) { if (prop->menu != menu) /* * Two possibilities: * * 1. The property lacks dependencies * and so isn't location-specific, * e.g. an 'option' * * 2. The property belongs to a symbol * defined in multiple locations and * is from some other location. It * will be handled there in that * case. * * Skip the property. */ continue; /* * Propagate parent dependencies to the * property's condition, rewriting and * simplifying expressions at the same time */ dep = rewrite_m(prop->visible.expr); dep = expr_transform(dep); dep = expr_alloc_and(expr_copy(basedep), dep); dep = expr_eliminate_dups(dep); if (menu->sym && menu->sym->type != S_TRISTATE) dep = expr_trans_bool(dep); prop->visible.expr = dep; /* * Handle selects and implies, which modify the * dependencies of the selected/implied symbol */ if (prop->type == P_SELECT) { struct symbol *es = prop_get_symbol(prop); es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); } else if (prop->type == P_IMPLY) { struct symbol *es = prop_get_symbol(prop); es->implied.expr = expr_alloc_or(es->implied.expr, expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); } } } if (sym && sym_is_choice(sym)) expr_free(parentdep); /* * Recursively process children in the same fashion before * moving on */ for (menu = parent->list; menu; menu = menu->next) menu_finalize(menu); } else if (sym) { /* * Automatic submenu creation. If sym is a symbol and A, B, C, * ... are consecutive items (symbols, menus, ifs, etc.) that * all depend on sym, then the following menu structure is * created: * * sym * +-A * +-B * +-C * ... * * This also works recursively, giving the following structure * if A is a symbol and B depends on A: * * sym * +-A * | +-B * +-C * ... */ basedep = parent->prompt ? parent->prompt->visible.expr : NULL; basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); basedep = expr_eliminate_dups(expr_transform(basedep)); /* Examine consecutive elements after sym */ last_menu = NULL; for (menu = parent->next; menu; menu = menu->next) { dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; if (!expr_contains_symbol(dep, sym)) /* No dependency, quit */ break; if (expr_depends_symbol(dep, sym)) /* Absolute dependency, put in submenu */ goto next; /* * Also consider it a dependency on sym if our * dependencies contain sym and are a "superset" of * sym's dependencies, e.g. '(sym || Q) && R' when sym * depends on R. * * Note that 'R' might be from an enclosing menu or if, * making this a more common case than it might seem. */ dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); dep = expr_eliminate_dups(expr_transform(dep)); dep2 = expr_copy(basedep); expr_eliminate_eq(&dep, &dep2); expr_free(dep); if (!expr_is_yes(dep2)) { /* Not superset, quit */ expr_free(dep2); break; } /* Superset, put in submenu */ expr_free(dep2); next: menu_finalize(menu); menu->parent = parent; last_menu = menu; } expr_free(basedep); if (last_menu) { parent->list = parent->next; parent->next = last_menu->next; last_menu->next = NULL; } sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep); } for (menu = parent->list; menu; menu = menu->next) { if (sym && sym_is_choice(sym) && menu->sym && !sym_is_choice_value(menu->sym)) { current_entry = menu; menu->sym->flags |= SYMBOL_CHOICEVAL; if (!menu->prompt) menu_warn(menu, "choice value must have a prompt"); for (prop = menu->sym->prop; prop; prop = prop->next) { if (prop->type == P_DEFAULT) prop_warn(prop, "defaults for choice " "values not supported"); if (prop->menu == menu) continue; if (prop->type == P_PROMPT && prop->menu->parent->sym != sym) prop_warn(prop, "choice value used outside its choice group"); } /* Non-tristate choice values of tristate choices must * depend on the choice being set to Y. The choice * values' dependencies were propagated to their * properties above, so the change here must be re- * propagated. */ if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); menu->dep = expr_alloc_and(basedep, menu->dep); for (prop = menu->sym->prop; prop; prop = prop->next) { if (prop->menu != menu) continue; prop->visible.expr = expr_alloc_and(expr_copy(basedep), prop->visible.expr); } } menu_add_symbol(P_CHOICE, sym, NULL); prop = sym_get_choice_prop(sym); for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) ; *ep = expr_alloc_one(E_LIST, NULL); (*ep)->right.sym = menu->sym; } /* * This code serves two purposes: * * (1) Flattening 'if' blocks, which do not specify a submenu * and only add dependencies. * * (Automatic submenu creation might still create a submenu * from an 'if' before this code runs.) * * (2) "Undoing" any automatic submenus created earlier below * promptless symbols. * * Before: * * A * if ... (or promptless symbol) * +-B * +-C * D * * After: * * A * if ... (or promptless symbol) * B * C * D */ if (menu->list && (!menu->prompt || !menu->prompt->text)) { for (last_menu = menu->list; ; last_menu = last_menu->next) { last_menu->parent = parent; if (!last_menu->next) break; } last_menu->next = menu->next; menu->next = menu->list; menu->list = NULL; } } if (sym && !(sym->flags & SYMBOL_WARNED)) { if (sym->type == S_UNKNOWN) menu_warn(parent, "config symbol defined without type"); if (sym_is_choice(sym) && !parent->prompt) menu_warn(parent, "choice must have a prompt"); /* Check properties connected to this symbol */ sym_check_prop(sym); sym->flags |= SYMBOL_WARNED; } /* * For non-optional choices, add a reverse dependency (corresponding to * a select) of ' && m'. This prevents the user from * setting the choice mode to 'n' when the choice is visible. * * This would also work for non-choice symbols, but only non-optional * choices clear SYMBOL_OPTIONAL as of writing. Choices are implemented * as a type of symbol. */ if (sym && !sym_is_optional(sym) && parent->prompt) { sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, expr_alloc_and(parent->prompt->visible.expr, expr_alloc_symbol(&symbol_mod))); } } bool menu_has_prompt(struct menu *menu) { if (!menu->prompt) return false; return true; } /* * Determine if a menu is empty. * A menu is considered empty if it contains no or only * invisible entries. */ bool menu_is_empty(struct menu *menu) { struct menu *child; for (child = menu->list; child; child = child->next) { if (menu_is_visible(child)) return(false); } return(true); } bool menu_is_visible(struct menu *menu) { struct menu *child; struct symbol *sym; tristate visible; if (!menu->prompt) return false; if (menu->visibility) { if (expr_calc_value(menu->visibility) == no) return false; } sym = menu->sym; if (sym) { sym_calc_value(sym); visible = menu->prompt->visible.tri; } else visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); if (visible != no) return true; if (!sym || sym_get_tristate_value(menu->sym) == no) return false; for (child = menu->list; child; child = child->next) { if (menu_is_visible(child)) { if (sym) sym->flags |= SYMBOL_DEF_USER; return true; } } return false; } const char *menu_get_prompt(struct menu *menu) { if (menu->prompt) return menu->prompt->text; else if (menu->sym) return menu->sym->name; return NULL; } struct menu *menu_get_root_menu(struct menu *menu) { return &rootmenu; } struct menu *menu_get_parent_menu(struct menu *menu) { enum prop_type type; for (; menu != &rootmenu; menu = menu->parent) { type = menu->prompt ? menu->prompt->type : 0; if (type == P_MENU) break; } return menu; } bool menu_has_help(struct menu *menu) { return menu->help != NULL; } const char *menu_get_help(struct menu *menu) { if (menu->help) return menu->help; else return ""; } static void get_def_str(struct gstr *r, struct menu *menu) { str_printf(r, "Defined at %s:%d\n", menu->file->name, menu->lineno); } static void get_dep_str(struct gstr *r, struct expr *expr, const char *prefix) { if (!expr_is_yes(expr)) { str_append(r, prefix); expr_gstr_print(expr, r); str_append(r, "\n"); } } static void get_prompt_str(struct gstr *r, struct property *prop, struct list_head *head) { int i, j; struct menu *submenu[8], *menu, *location = NULL; struct jump_key *jump = NULL; str_printf(r, " Prompt: %s\n", prop->text); get_dep_str(r, prop->menu->dep, " Depends on: "); /* * Most prompts in Linux have visibility that exactly matches their * dependencies. For these, we print only the dependencies to improve * readability. However, prompts with inline "if" expressions and * prompts with a parent that has a "visible if" expression have * differing dependencies and visibility. In these rare cases, we * print both. */ if (!expr_eq(prop->menu->dep, prop->visible.expr)) get_dep_str(r, prop->visible.expr, " Visible if: "); menu = prop->menu->parent; for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) { bool accessible = menu_is_visible(menu); submenu[i++] = menu; if (location == NULL && accessible) location = menu; } if (head && location) { jump = xmalloc(sizeof(struct jump_key)); if (menu_is_visible(prop->menu)) { /* * There is not enough room to put the hint at the * beginning of the "Prompt" line. Put the hint on the * last "Location" line even when it would belong on * the former. */ jump->target = prop->menu; } else jump->target = location; if (list_empty(head)) jump->index = 0; else jump->index = list_entry(head->prev, struct jump_key, entries)->index + 1; list_add_tail(&jump->entries, head); } if (i > 0) { str_printf(r, " Location:\n"); for (j = 4; --i >= 0; j += 2) { menu = submenu[i]; if (jump && menu == location) jump->offset = strlen(r->s); str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu)); if (menu->sym) { str_printf(r, " (%s [=%s])", menu->sym->name ? menu->sym->name : "", sym_get_string_value(menu->sym)); } str_append(r, "\n"); } } } static void get_symbol_props_str(struct gstr *r, struct symbol *sym, enum prop_type tok, const char *prefix) { bool hit = false; struct property *prop; for_all_properties(sym, prop, tok) { if (!hit) { str_append(r, prefix); hit = true; } else str_printf(r, " && "); expr_gstr_print(prop->expr, r); } if (hit) str_append(r, "\n"); } /* * head is optional and may be NULL */ static void get_symbol_str(struct gstr *r, struct symbol *sym, struct list_head *head) { struct property *prop; if (sym && sym->name) { str_printf(r, "Symbol: %s [=%s]\n", sym->name, sym_get_string_value(sym)); str_printf(r, "Type : %s\n", sym_type_name(sym->type)); if (sym->type == S_INT || sym->type == S_HEX) { prop = sym_get_range_prop(sym); if (prop) { str_printf(r, "Range : "); expr_gstr_print(prop->expr, r); str_append(r, "\n"); } } } /* Print the definitions with prompts before the ones without */ for_all_properties(sym, prop, P_SYMBOL) { if (prop->menu->prompt) { get_def_str(r, prop->menu); get_prompt_str(r, prop->menu->prompt, head); } } for_all_properties(sym, prop, P_SYMBOL) { if (!prop->menu->prompt) { get_def_str(r, prop->menu); get_dep_str(r, prop->menu->dep, " Depends on: "); } } get_symbol_props_str(r, sym, P_SELECT, "Selects: "); if (sym->rev_dep.expr) { expr_gstr_print_revdep(sym->rev_dep.expr, r, yes, "Selected by [y]:\n"); expr_gstr_print_revdep(sym->rev_dep.expr, r, mod, "Selected by [m]:\n"); expr_gstr_print_revdep(sym->rev_dep.expr, r, no, "Selected by [n]:\n"); } get_symbol_props_str(r, sym, P_IMPLY, "Implies: "); if (sym->implied.expr) { expr_gstr_print_revdep(sym->implied.expr, r, yes, "Implied by [y]:\n"); expr_gstr_print_revdep(sym->implied.expr, r, mod, "Implied by [m]:\n"); expr_gstr_print_revdep(sym->implied.expr, r, no, "Implied by [n]:\n"); } str_append(r, "\n\n"); } struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head) { struct symbol *sym; struct gstr res = str_new(); int i; for (i = 0; sym_arr && (sym = sym_arr[i]); i++) get_symbol_str(&res, sym, head); if (!i) str_append(&res, "No matches found.\n"); return res; } void menu_get_ext_help(struct menu *menu, struct gstr *help) { struct symbol *sym = menu->sym; const char *help_text = nohelp_text; if (menu_has_help(menu)) { if (sym->name) str_printf(help, "%s%s:\n\n", CONFIG_, sym->name); help_text = menu_get_help(menu); } str_printf(help, "%s\n", help_text); if (sym) get_symbol_str(help, sym, NULL); } crust-0.5/3rdparty/kconfig/nconf-cfg.sh000077500000000000000000000025041414152222500201040ustar00rootroot00000000000000#!/bin/sh # SPDX-License-Identifier: GPL-2.0 PKG="ncursesw menuw panelw" PKG2="ncurses menu panel" if [ -n "$(command -v pkg-config)" ]; then if pkg-config --exists $PKG; then echo cflags=\"$(pkg-config --cflags $PKG)\" echo libs=\"$(pkg-config --libs $PKG)\" exit 0 fi if pkg-config --exists $PKG2; then echo cflags=\"$(pkg-config --cflags $PKG2)\" echo libs=\"$(pkg-config --libs $PKG2)\" exit 0 fi fi # Check the default paths in case pkg-config is not installed. # (Even if it is installed, some distributions such as openSUSE cannot # find ncurses by pkg-config.) if [ -f /usr/include/ncursesw/ncurses.h ]; then echo cflags=\"-D_GNU_SOURCE -I/usr/include/ncursesw\" echo libs=\"-lncursesw -lmenuw -lpanelw\" exit 0 fi if [ -f /usr/include/ncurses/ncurses.h ]; then echo cflags=\"-D_GNU_SOURCE -I/usr/include/ncurses\" echo libs=\"-lncurses -lmenu -lpanel\" exit 0 fi if [ -f /usr/include/ncurses.h ]; then echo cflags=\"-D_GNU_SOURCE\" echo libs=\"-lncurses -lmenu -lpanel\" exit 0 fi echo >&2 "*" echo >&2 "* Unable to find the ncurses package." echo >&2 "* Install ncurses (ncurses-devel or libncurses-dev" echo >&2 "* depending on your distribution)." echo >&2 "*" echo >&2 "* You may also need to install pkg-config to find the" echo >&2 "* ncurses installed in a non-default location." echo >&2 "*" exit 1 crust-0.5/3rdparty/kconfig/nconf.c000066400000000000000000001142301414152222500171540ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2008 Nir Tzachar * * Derived from menuconfig. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include "lkc.h" #include "nconf.h" #include static const char nconf_global_help[] = "Help windows\n" "------------\n" "o Global help: Unless in a data entry window, pressing will give \n" " you the global help window, which you are just reading.\n" "\n" "o A short version of the global help is available by pressing .\n" "\n" "o Local help: To get help related to the current menu entry, use any\n" " of , or if in a data entry window then press .\n" "\n" "\n" "Menu entries\n" "------------\n" "This interface lets you select features and parameters for the kernel\n" "build. Kernel features can either be built-in, modularized, or removed.\n" "Parameters must be entered as text or decimal or hexadecimal numbers.\n" "\n" "Menu entries beginning with following braces represent features that\n" " [ ] can be built in or removed\n" " < > can be built in, modularized or removed\n" " { } can be built in or modularized, are selected by another feature\n" " - - are selected by another feature\n" " XXX cannot be selected. Symbol Info tells you why.\n" "*, M or whitespace inside braces means to build in, build as a module\n" "or to exclude the feature respectively.\n" "\n" "To change any of these features, highlight it with the movement keys\n" "listed below and press to build it in, to make it a module or\n" " to remove it. You may press the key to cycle through the\n" "available options.\n" "\n" "A trailing \"--->\" designates a submenu, a trailing \"----\" an\n" "empty submenu.\n" "\n" "Menu navigation keys\n" "----------------------------------------------------------------------\n" "Linewise up \n" "Linewise down \n" "Pagewise up \n" "Pagewise down \n" "First entry \n" "Last entry \n" "Enter a submenu \n" "Go back to parent menu \n" "Close a help window \n" "Close entry window, apply \n" "Close entry window, forget \n" "Start incremental, case-insensitive search for STRING in menu entries,\n" " no regex support, STRING is displayed in upper left corner\n" " STRING\n" " Remove last character \n" " Jump to next hit \n" " Jump to previous hit \n" "Exit menu search mode \n" "Search for configuration variables with or without leading CONFIG_\n" " RegExpr\n" "Verbose search help \n" "----------------------------------------------------------------------\n" "\n" "Unless in a data entry window, key <1> may be used instead of ,\n" "<2> instead of , etc.\n" "\n" "\n" "Radiolist (Choice list)\n" "-----------------------\n" "Use the movement keys listed above to select the option you wish to set\n" "and press .\n" "\n" "\n" "Data entry\n" "----------\n" "Enter the requested information and press . Hexadecimal values\n" "may be entered without the \"0x\" prefix.\n" "\n" "\n" "Text Box (Help Window)\n" "----------------------\n" "Use movement keys as listed in table above.\n" "\n" "Press any of to exit.\n" "\n" "\n" "Alternate configuration files\n" "-----------------------------\n" "nconfig supports switching between different configurations.\n" "Press to save your current configuration. Press and enter\n" "a file name to load a previously saved configuration.\n" "\n" "\n" "Terminal configuration\n" "----------------------\n" "If you use nconfig in a xterm window, make sure your TERM environment\n" "variable specifies a terminal configuration which supports at least\n" "16 colors. Otherwise nconfig will look rather bad.\n" "\n" "If the \"stty size\" command reports the current terminalsize correctly,\n" "nconfig will adapt to sizes larger than the traditional 80x25 \"standard\"\n" "and display longer menus properly.\n" "\n" "\n" "Single menu mode\n" "----------------\n" "If you prefer to have all of the menu entries listed in a single menu,\n" "rather than the default multimenu hierarchy, run nconfig with\n" "NCONFIG_MODE environment variable set to single_menu. Example:\n" "\n" "make NCONFIG_MODE=single_menu nconfig\n" "\n" " will then unfold the appropriate category, or fold it if it\n" "is already unfolded. Folded menu entries will be designated by a\n" "leading \"++>\" and unfolded entries by a leading \"-->\".\n" "\n" "Note that this mode can eventually be a little more CPU expensive than\n" "the default mode, especially with a larger number of unfolded submenus.\n" "\n", menu_no_f_instructions[] = "Legend: [*] built-in [ ] excluded module < > module capable.\n" "Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n" "\n" "Use the following keys to navigate the menus:\n" "Move up or down with and .\n" "Enter a submenu with or .\n" "Exit a submenu to its parent menu with or .\n" "Pressing includes, excludes, modularizes features.\n" "Pressing cycles through the available options.\n" "To search for menu entries press .\n" " always leaves the current window.\n" "\n" "You do not have function keys support.\n" "Press <1> instead of , <2> instead of , etc.\n" "For verbose global help use key <1>.\n" "For help related to the current menu entry press or .\n", menu_instructions[] = "Legend: [*] built-in [ ] excluded module < > module capable.\n" "Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n" "\n" "Use the following keys to navigate the menus:\n" "Move up or down with or .\n" "Enter a submenu with or .\n" "Exit a submenu to its parent menu with or .\n" "Pressing includes, excludes, modularizes features.\n" "Pressing cycles through the available options.\n" "To search for menu entries press .\n" " always leaves the current window.\n" "\n" "Pressing <1> may be used instead of , <2> instead of , etc.\n" "For verbose global help press .\n" "For help related to the current menu entry press or .\n", radiolist_instructions[] = "Press , , or to navigate a radiolist, select\n" "with .\n" "For help related to the current entry press or .\n" "For global help press .\n", inputbox_instructions_int[] = "Please enter a decimal value.\n" "Fractions will not be accepted.\n" "Press to apply, to cancel.", inputbox_instructions_hex[] = "Please enter a hexadecimal value.\n" "Press to apply, to cancel.", inputbox_instructions_string[] = "Please enter a string value.\n" "Press to apply, to cancel.", setmod_text[] = "This feature depends on another feature which has been configured as a\n" "module. As a result, the current feature will be built as a module too.", load_config_text[] = "Enter the name of the configuration file you wish to load.\n" "Accept the name shown to restore the configuration you last\n" "retrieved. Leave empty to abort.", load_config_help[] = "For various reasons, one may wish to keep several different\n" "configurations available on a single machine.\n" "\n" "If you have saved a previous configuration in a file other than the\n" "default one, entering its name here will allow you to load and modify\n" "that configuration.\n" "\n" "Leave empty to abort.\n", save_config_text[] = "Enter a filename to which this configuration should be saved\n" "as an alternate. Leave empty to abort.", save_config_help[] = "For various reasons, one may wish to keep several different\n" "configurations available on a single machine.\n" "\n" "Entering a file name here will allow you to later retrieve, modify\n" "and use the current configuration as an alternate to whatever\n" "configuration options you have selected at that time.\n" "\n" "Leave empty to abort.\n", search_help[] = "Search for symbols (configuration variable names CONFIG_*) and display\n" "their relations. Regular expressions are supported.\n" "Example: Search for \"^FOO\".\n" "Result:\n" "-----------------------------------------------------------------\n" "Symbol: FOO [ = m]\n" "Prompt: Foo bus is used to drive the bar HW\n" "Defined at drivers/pci/Kconfig:47\n" "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n" "Location:\n" " -> Bus options (PCI, PCMCIA, EISA, ISA)\n" " -> PCI support (PCI [ = y])\n" " -> PCI access mode ( [ = y])\n" "Selects: LIBCRC32\n" "Selected by: BAR\n" "-----------------------------------------------------------------\n" "o The line 'Prompt:' shows the text displayed for this symbol in\n" " the menu hierarchy.\n" "o The 'Defined at' line tells at what file / line number the symbol is\n" " defined.\n" "o The 'Depends on:' line lists symbols that need to be defined for\n" " this symbol to be visible and selectable in the menu.\n" "o The 'Location:' lines tell, where in the menu structure this symbol\n" " is located. A location followed by a [ = y] indicates that this is\n" " a selectable menu item, and the current value is displayed inside\n" " brackets.\n" "o The 'Selects:' line tells, what symbol will be automatically selected\n" " if this symbol is selected (y or m).\n" "o The 'Selected by' line tells what symbol has selected this symbol.\n" "\n" "Only relevant lines are shown.\n" "\n\n" "Search examples:\n" "USB => find all symbols containing USB\n" "^USB => find all symbols starting with USB\n" "USB$ => find all symbols ending with USB\n" "\n"; struct mitem { char str[256]; char tag; void *usrptr; int is_visible; }; #define MAX_MENU_ITEMS 4096 static int show_all_items; static int indent; static struct menu *current_menu; static int child_count; static int single_menu_mode; /* the window in which all information appears */ static WINDOW *main_window; /* the largest size of the menu window */ static int mwin_max_lines; static int mwin_max_cols; /* the window in which we show option buttons */ static MENU *curses_menu; static ITEM *curses_menu_items[MAX_MENU_ITEMS]; static struct mitem k_menu_items[MAX_MENU_ITEMS]; static int items_num; static int global_exit; /* the currently selected button */ static const char *current_instructions = menu_instructions; static char *dialog_input_result; static int dialog_input_result_len; static void conf(struct menu *menu); static void conf_choice(struct menu *menu); static void conf_string(struct menu *menu); static void conf_load(void); static void conf_save(void); static void show_help(struct menu *menu); static int do_exit(void); static void setup_windows(void); static void search_conf(void); typedef void (*function_key_handler_t)(int *key, struct menu *menu); static void handle_f1(int *key, struct menu *current_item); static void handle_f2(int *key, struct menu *current_item); static void handle_f3(int *key, struct menu *current_item); static void handle_f4(int *key, struct menu *current_item); static void handle_f5(int *key, struct menu *current_item); static void handle_f6(int *key, struct menu *current_item); static void handle_f7(int *key, struct menu *current_item); static void handle_f8(int *key, struct menu *current_item); static void handle_f9(int *key, struct menu *current_item); struct function_keys { const char *key_str; const char *func; function_key key; function_key_handler_t handler; }; static const int function_keys_num = 9; static struct function_keys function_keys[] = { { .key_str = "F1", .func = "Help", .key = F_HELP, .handler = handle_f1, }, { .key_str = "F2", .func = "SymInfo", .key = F_SYMBOL, .handler = handle_f2, }, { .key_str = "F3", .func = "Help 2", .key = F_INSTS, .handler = handle_f3, }, { .key_str = "F4", .func = "ShowAll", .key = F_CONF, .handler = handle_f4, }, { .key_str = "F5", .func = "Back", .key = F_BACK, .handler = handle_f5, }, { .key_str = "F6", .func = "Save", .key = F_SAVE, .handler = handle_f6, }, { .key_str = "F7", .func = "Load", .key = F_LOAD, .handler = handle_f7, }, { .key_str = "F8", .func = "SymSearch", .key = F_SEARCH, .handler = handle_f8, }, { .key_str = "F9", .func = "Exit", .key = F_EXIT, .handler = handle_f9, }, }; static void print_function_line(void) { int i; int offset = 1; const int skip = 1; int lines = getmaxy(stdscr); for (i = 0; i < function_keys_num; i++) { (void) wattrset(main_window, attributes[FUNCTION_HIGHLIGHT]); mvwprintw(main_window, lines-3, offset, "%s", function_keys[i].key_str); (void) wattrset(main_window, attributes[FUNCTION_TEXT]); offset += strlen(function_keys[i].key_str); mvwprintw(main_window, lines-3, offset, "%s", function_keys[i].func); offset += strlen(function_keys[i].func) + skip; } (void) wattrset(main_window, attributes[NORMAL]); } /* help */ static void handle_f1(int *key, struct menu *current_item) { show_scroll_win(main_window, "Global help", nconf_global_help); return; } /* symbole help */ static void handle_f2(int *key, struct menu *current_item) { show_help(current_item); return; } /* instructions */ static void handle_f3(int *key, struct menu *current_item) { show_scroll_win(main_window, "Short help", current_instructions); return; } /* config */ static void handle_f4(int *key, struct menu *current_item) { int res = btn_dialog(main_window, "Show all symbols?", 2, " ", ""); if (res == 0) show_all_items = 1; else if (res == 1) show_all_items = 0; return; } /* back */ static void handle_f5(int *key, struct menu *current_item) { *key = KEY_LEFT; return; } /* save */ static void handle_f6(int *key, struct menu *current_item) { conf_save(); return; } /* load */ static void handle_f7(int *key, struct menu *current_item) { conf_load(); return; } /* search */ static void handle_f8(int *key, struct menu *current_item) { search_conf(); return; } /* exit */ static void handle_f9(int *key, struct menu *current_item) { do_exit(); return; } /* return != 0 to indicate the key was handles */ static int process_special_keys(int *key, struct menu *menu) { int i; if (*key == KEY_RESIZE) { setup_windows(); return 1; } for (i = 0; i < function_keys_num; i++) { if (*key == KEY_F(function_keys[i].key) || *key == '0' + function_keys[i].key){ function_keys[i].handler(key, menu); return 1; } } return 0; } static void clean_items(void) { int i; for (i = 0; curses_menu_items[i]; i++) free_item(curses_menu_items[i]); bzero(curses_menu_items, sizeof(curses_menu_items)); bzero(k_menu_items, sizeof(k_menu_items)); items_num = 0; } typedef enum {MATCH_TINKER_PATTERN_UP, MATCH_TINKER_PATTERN_DOWN, FIND_NEXT_MATCH_DOWN, FIND_NEXT_MATCH_UP} match_f; /* return the index of the matched item, or -1 if no such item exists */ static int get_mext_match(const char *match_str, match_f flag) { int match_start = item_index(current_item(curses_menu)); int index; if (flag == FIND_NEXT_MATCH_DOWN) ++match_start; else if (flag == FIND_NEXT_MATCH_UP) --match_start; index = match_start; index = (index + items_num) % items_num; while (true) { char *str = k_menu_items[index].str; if (strcasestr(str, match_str) != NULL) return index; if (flag == FIND_NEXT_MATCH_UP || flag == MATCH_TINKER_PATTERN_UP) --index; else ++index; index = (index + items_num) % items_num; if (index == match_start) return -1; } } /* Make a new item. */ static void item_make(struct menu *menu, char tag, const char *fmt, ...) { va_list ap; if (items_num > MAX_MENU_ITEMS-1) return; bzero(&k_menu_items[items_num], sizeof(k_menu_items[0])); k_menu_items[items_num].tag = tag; k_menu_items[items_num].usrptr = menu; if (menu != NULL) k_menu_items[items_num].is_visible = menu_is_visible(menu); else k_menu_items[items_num].is_visible = 1; va_start(ap, fmt); vsnprintf(k_menu_items[items_num].str, sizeof(k_menu_items[items_num].str), fmt, ap); va_end(ap); if (!k_menu_items[items_num].is_visible) memcpy(k_menu_items[items_num].str, "XXX", 3); curses_menu_items[items_num] = new_item( k_menu_items[items_num].str, k_menu_items[items_num].str); set_item_userptr(curses_menu_items[items_num], &k_menu_items[items_num]); /* if (!k_menu_items[items_num].is_visible) item_opts_off(curses_menu_items[items_num], O_SELECTABLE); */ items_num++; curses_menu_items[items_num] = NULL; } /* very hackish. adds a string to the last item added */ static void item_add_str(const char *fmt, ...) { va_list ap; int index = items_num-1; char new_str[256]; char tmp_str[256]; if (index < 0) return; va_start(ap, fmt); vsnprintf(new_str, sizeof(new_str), fmt, ap); va_end(ap); snprintf(tmp_str, sizeof(tmp_str), "%s%s", k_menu_items[index].str, new_str); strncpy(k_menu_items[index].str, tmp_str, sizeof(k_menu_items[index].str)); free_item(curses_menu_items[index]); curses_menu_items[index] = new_item( k_menu_items[index].str, k_menu_items[index].str); set_item_userptr(curses_menu_items[index], &k_menu_items[index]); } /* get the tag of the currently selected item */ static char item_tag(void) { ITEM *cur; struct mitem *mcur; cur = current_item(curses_menu); if (cur == NULL) return 0; mcur = (struct mitem *) item_userptr(cur); return mcur->tag; } static int curses_item_index(void) { return item_index(current_item(curses_menu)); } static void *item_data(void) { ITEM *cur; struct mitem *mcur; cur = current_item(curses_menu); if (!cur) return NULL; mcur = (struct mitem *) item_userptr(cur); return mcur->usrptr; } static int item_is_tag(char tag) { return item_tag() == tag; } static char filename[PATH_MAX+1]; static char menu_backtitle[PATH_MAX+128]; static const char *set_config_filename(const char *config_filename) { int size; size = snprintf(menu_backtitle, sizeof(menu_backtitle), "%s - %s", config_filename, rootmenu.prompt->text); if (size >= sizeof(menu_backtitle)) menu_backtitle[sizeof(menu_backtitle)-1] = '\0'; size = snprintf(filename, sizeof(filename), "%s", config_filename); if (size >= sizeof(filename)) filename[sizeof(filename)-1] = '\0'; return menu_backtitle; } /* return = 0 means we are successful. * -1 means go on doing what you were doing */ static int do_exit(void) { int res; if (!conf_get_changed()) { global_exit = 1; return 0; } res = btn_dialog(main_window, "Do you wish to save your new configuration?\n" " to cancel and resume nconfig.", 2, " ", ""); if (res == KEY_EXIT) { global_exit = 0; return -1; } /* if we got here, the user really wants to exit */ switch (res) { case 0: res = conf_write(filename); if (res) btn_dialog( main_window, "Error during writing of configuration.\n" "Your configuration changes were NOT saved.", 1, ""); conf_write_autoconf(0); break; default: btn_dialog( main_window, "Your configuration changes were NOT saved.", 1, ""); break; } global_exit = 1; return 0; } static void search_conf(void) { struct symbol **sym_arr; struct gstr res; struct gstr title; char *dialog_input; int dres; title = str_new(); str_printf( &title, "Enter (sub)string or regexp to search for " "(with or without \"%s\")", CONFIG_); again: dres = dialog_inputbox(main_window, "Search Configuration Parameter", str_get(&title), "", &dialog_input_result, &dialog_input_result_len); switch (dres) { case 0: break; case 1: show_scroll_win(main_window, "Search Configuration", search_help); goto again; default: str_free(&title); return; } /* strip the prefix if necessary */ dialog_input = dialog_input_result; if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0) dialog_input += strlen(CONFIG_); sym_arr = sym_re_search(dialog_input); res = get_relations_str(sym_arr, NULL); free(sym_arr); show_scroll_win(main_window, "Search Results", str_get(&res)); str_free(&res); str_free(&title); } static void build_conf(struct menu *menu) { struct symbol *sym; struct property *prop; struct menu *child; int type, tmp, doint = 2; tristate val; char ch; if (!menu || (!show_all_items && !menu_is_visible(menu))) return; sym = menu->sym; prop = menu->prompt; if (!sym) { if (prop && menu != current_menu) { const char *prompt = menu_get_prompt(menu); enum prop_type ptype; ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; switch (ptype) { case P_MENU: child_count++; if (single_menu_mode) { item_make(menu, 'm', "%s%*c%s", menu->data ? "-->" : "++>", indent + 1, ' ', prompt); } else item_make(menu, 'm', " %*c%s %s", indent + 1, ' ', prompt, menu_is_empty(menu) ? "----" : "--->"); if (single_menu_mode && menu->data) goto conf_childs; return; case P_COMMENT: if (prompt) { child_count++; item_make(menu, ':', " %*c*** %s ***", indent + 1, ' ', prompt); } break; default: if (prompt) { child_count++; item_make(menu, ':', "---%*c%s", indent + 1, ' ', prompt); } } } else doint = 0; goto conf_childs; } type = sym_get_type(sym); if (sym_is_choice(sym)) { struct symbol *def_sym = sym_get_choice_value(sym); struct menu *def_menu = NULL; child_count++; for (child = menu->list; child; child = child->next) { if (menu_is_visible(child) && child->sym == def_sym) def_menu = child; } val = sym_get_tristate_value(sym); if (sym_is_changeable(sym)) { switch (type) { case S_BOOLEAN: item_make(menu, 't', "[%c]", val == no ? ' ' : '*'); break; case S_TRISTATE: switch (val) { case yes: ch = '*'; break; case mod: ch = 'M'; break; default: ch = ' '; break; } item_make(menu, 't', "<%c>", ch); break; } } else { item_make(menu, def_menu ? 't' : ':', " "); } item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu)); if (val == yes) { if (def_menu) { item_add_str(" (%s)", menu_get_prompt(def_menu)); item_add_str(" --->"); if (def_menu->list) { indent += 2; build_conf(def_menu); indent -= 2; } } return; } } else { if (menu == current_menu) { item_make(menu, ':', "---%*c%s", indent + 1, ' ', menu_get_prompt(menu)); goto conf_childs; } child_count++; val = sym_get_tristate_value(sym); if (sym_is_choice_value(sym) && val == yes) { item_make(menu, ':', " "); } else { switch (type) { case S_BOOLEAN: if (sym_is_changeable(sym)) item_make(menu, 't', "[%c]", val == no ? ' ' : '*'); else item_make(menu, 't', "-%c-", val == no ? ' ' : '*'); break; case S_TRISTATE: switch (val) { case yes: ch = '*'; break; case mod: ch = 'M'; break; default: ch = ' '; break; } if (sym_is_changeable(sym)) { if (sym->rev_dep.tri == mod) item_make(menu, 't', "{%c}", ch); else item_make(menu, 't', "<%c>", ch); } else item_make(menu, 't', "-%c-", ch); break; default: tmp = 2 + strlen(sym_get_string_value(sym)); item_make(menu, 's', " (%s)", sym_get_string_value(sym)); tmp = indent - tmp + 4; if (tmp < 0) tmp = 0; item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu), (sym_has_value(sym) || !sym_is_changeable(sym)) ? "" : " (NEW)"); goto conf_childs; } } item_add_str("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu), (sym_has_value(sym) || !sym_is_changeable(sym)) ? "" : " (NEW)"); if (menu->prompt && menu->prompt->type == P_MENU) { item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->"); return; } } conf_childs: indent += doint; for (child = menu->list; child; child = child->next) build_conf(child); indent -= doint; } static void reset_menu(void) { unpost_menu(curses_menu); clean_items(); } /* adjust the menu to show this item. * prefer not to scroll the menu if possible*/ static void center_item(int selected_index, int *last_top_row) { int toprow; set_top_row(curses_menu, *last_top_row); toprow = top_row(curses_menu); if (selected_index < toprow || selected_index >= toprow+mwin_max_lines) { toprow = max(selected_index-mwin_max_lines/2, 0); if (toprow >= item_count(curses_menu)-mwin_max_lines) toprow = item_count(curses_menu)-mwin_max_lines; set_top_row(curses_menu, toprow); } set_current_item(curses_menu, curses_menu_items[selected_index]); *last_top_row = toprow; post_menu(curses_menu); refresh_all_windows(main_window); } /* this function assumes reset_menu has been called before */ static void show_menu(const char *prompt, const char *instructions, int selected_index, int *last_top_row) { int maxx, maxy; WINDOW *menu_window; current_instructions = instructions; clear(); (void) wattrset(main_window, attributes[NORMAL]); print_in_middle(stdscr, 1, 0, getmaxx(stdscr), menu_backtitle, attributes[MAIN_HEADING]); (void) wattrset(main_window, attributes[MAIN_MENU_BOX]); box(main_window, 0, 0); (void) wattrset(main_window, attributes[MAIN_MENU_HEADING]); mvwprintw(main_window, 0, 3, " %s ", prompt); (void) wattrset(main_window, attributes[NORMAL]); set_menu_items(curses_menu, curses_menu_items); /* position the menu at the middle of the screen */ scale_menu(curses_menu, &maxy, &maxx); maxx = min(maxx, mwin_max_cols-2); maxy = mwin_max_lines; menu_window = derwin(main_window, maxy, maxx, 2, (mwin_max_cols-maxx)/2); keypad(menu_window, TRUE); set_menu_win(curses_menu, menu_window); set_menu_sub(curses_menu, menu_window); /* must reassert this after changing items, otherwise returns to a * default of 16 */ set_menu_format(curses_menu, maxy, 1); center_item(selected_index, last_top_row); set_menu_format(curses_menu, maxy, 1); print_function_line(); /* Post the menu */ post_menu(curses_menu); refresh_all_windows(main_window); } static void adj_match_dir(match_f *match_direction) { if (*match_direction == FIND_NEXT_MATCH_DOWN) *match_direction = MATCH_TINKER_PATTERN_DOWN; else if (*match_direction == FIND_NEXT_MATCH_UP) *match_direction = MATCH_TINKER_PATTERN_UP; /* else, do no change.. */ } struct match_state { int in_search; match_f match_direction; char pattern[256]; }; /* Return 0 means I have handled the key. In such a case, ans should hold the * item to center, or -1 otherwise. * Else return -1 . */ static int do_match(int key, struct match_state *state, int *ans) { char c = (char) key; int terminate_search = 0; *ans = -1; if (key == '/' || (state->in_search && key == 27)) { move(0, 0); refresh(); clrtoeol(); state->in_search = 1-state->in_search; bzero(state->pattern, sizeof(state->pattern)); state->match_direction = MATCH_TINKER_PATTERN_DOWN; return 0; } else if (!state->in_search) return 1; if (isalnum(c) || isgraph(c) || c == ' ') { state->pattern[strlen(state->pattern)] = c; state->pattern[strlen(state->pattern)] = '\0'; adj_match_dir(&state->match_direction); *ans = get_mext_match(state->pattern, state->match_direction); } else if (key == KEY_DOWN) { state->match_direction = FIND_NEXT_MATCH_DOWN; *ans = get_mext_match(state->pattern, state->match_direction); } else if (key == KEY_UP) { state->match_direction = FIND_NEXT_MATCH_UP; *ans = get_mext_match(state->pattern, state->match_direction); } else if (key == KEY_BACKSPACE || key == 8 || key == 127) { state->pattern[strlen(state->pattern)-1] = '\0'; adj_match_dir(&state->match_direction); } else terminate_search = 1; if (terminate_search) { state->in_search = 0; bzero(state->pattern, sizeof(state->pattern)); move(0, 0); refresh(); clrtoeol(); return -1; } return 0; } static void conf(struct menu *menu) { struct menu *submenu = NULL; const char *prompt = menu_get_prompt(menu); struct symbol *sym; int res; int current_index = 0; int last_top_row = 0; struct match_state match_state = { .in_search = 0, .match_direction = MATCH_TINKER_PATTERN_DOWN, .pattern = "", }; while (!global_exit) { reset_menu(); current_menu = menu; build_conf(menu); if (!child_count) break; show_menu(prompt ? prompt : "Main Menu", menu_instructions, current_index, &last_top_row); keypad((menu_win(curses_menu)), TRUE); while (!global_exit) { if (match_state.in_search) { mvprintw(0, 0, "searching: %s", match_state.pattern); clrtoeol(); } refresh_all_windows(main_window); res = wgetch(menu_win(curses_menu)); if (!res) break; if (do_match(res, &match_state, ¤t_index) == 0) { if (current_index != -1) center_item(current_index, &last_top_row); continue; } if (process_special_keys(&res, (struct menu *) item_data())) break; switch (res) { case KEY_DOWN: menu_driver(curses_menu, REQ_DOWN_ITEM); break; case KEY_UP: menu_driver(curses_menu, REQ_UP_ITEM); break; case KEY_NPAGE: menu_driver(curses_menu, REQ_SCR_DPAGE); break; case KEY_PPAGE: menu_driver(curses_menu, REQ_SCR_UPAGE); break; case KEY_HOME: menu_driver(curses_menu, REQ_FIRST_ITEM); break; case KEY_END: menu_driver(curses_menu, REQ_LAST_ITEM); break; case 'h': case '?': show_help((struct menu *) item_data()); break; } if (res == 10 || res == 27 || res == 32 || res == 'n' || res == 'y' || res == KEY_LEFT || res == KEY_RIGHT || res == 'm') break; refresh_all_windows(main_window); } refresh_all_windows(main_window); /* if ESC or left*/ if (res == 27 || (menu != &rootmenu && res == KEY_LEFT)) break; /* remember location in the menu */ last_top_row = top_row(curses_menu); current_index = curses_item_index(); if (!item_tag()) continue; submenu = (struct menu *) item_data(); if (!submenu || !menu_is_visible(submenu)) continue; sym = submenu->sym; switch (res) { case ' ': if (item_is_tag('t')) sym_toggle_tristate_value(sym); else if (item_is_tag('m')) conf(submenu); break; case KEY_RIGHT: case 10: /* ENTER WAS PRESSED */ switch (item_tag()) { case 'm': if (single_menu_mode) submenu->data = (void *) (long) !submenu->data; else conf(submenu); break; case 't': if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes) conf_choice(submenu); else if (submenu->prompt && submenu->prompt->type == P_MENU) conf(submenu); else if (res == 10) sym_toggle_tristate_value(sym); break; case 's': conf_string(submenu); break; } break; case 'y': if (item_is_tag('t')) { if (sym_set_tristate_value(sym, yes)) break; if (sym_set_tristate_value(sym, mod)) btn_dialog(main_window, setmod_text, 0); } break; case 'n': if (item_is_tag('t')) sym_set_tristate_value(sym, no); break; case 'm': if (item_is_tag('t')) sym_set_tristate_value(sym, mod); break; } } } static void conf_message_callback(const char *s) { btn_dialog(main_window, s, 1, ""); } static void show_help(struct menu *menu) { struct gstr help; if (!menu) return; help = str_new(); menu_get_ext_help(menu, &help); show_scroll_win(main_window, menu_get_prompt(menu), str_get(&help)); str_free(&help); } static void conf_choice(struct menu *menu) { const char *prompt = menu_get_prompt(menu); struct menu *child = NULL; struct symbol *active; int selected_index = 0; int last_top_row = 0; int res, i = 0; struct match_state match_state = { .in_search = 0, .match_direction = MATCH_TINKER_PATTERN_DOWN, .pattern = "", }; active = sym_get_choice_value(menu->sym); /* this is mostly duplicated from the conf() function. */ while (!global_exit) { reset_menu(); for (i = 0, child = menu->list; child; child = child->next) { if (!show_all_items && !menu_is_visible(child)) continue; if (child->sym == sym_get_choice_value(menu->sym)) item_make(child, ':', " %s", menu_get_prompt(child)); else if (child->sym) item_make(child, ':', " %s", menu_get_prompt(child)); else item_make(child, ':', "*** %s ***", menu_get_prompt(child)); if (child->sym == active){ last_top_row = top_row(curses_menu); selected_index = i; } i++; } show_menu(prompt ? prompt : "Choice Menu", radiolist_instructions, selected_index, &last_top_row); while (!global_exit) { if (match_state.in_search) { mvprintw(0, 0, "searching: %s", match_state.pattern); clrtoeol(); } refresh_all_windows(main_window); res = wgetch(menu_win(curses_menu)); if (!res) break; if (do_match(res, &match_state, &selected_index) == 0) { if (selected_index != -1) center_item(selected_index, &last_top_row); continue; } if (process_special_keys( &res, (struct menu *) item_data())) break; switch (res) { case KEY_DOWN: menu_driver(curses_menu, REQ_DOWN_ITEM); break; case KEY_UP: menu_driver(curses_menu, REQ_UP_ITEM); break; case KEY_NPAGE: menu_driver(curses_menu, REQ_SCR_DPAGE); break; case KEY_PPAGE: menu_driver(curses_menu, REQ_SCR_UPAGE); break; case KEY_HOME: menu_driver(curses_menu, REQ_FIRST_ITEM); break; case KEY_END: menu_driver(curses_menu, REQ_LAST_ITEM); break; case 'h': case '?': show_help((struct menu *) item_data()); break; } if (res == 10 || res == 27 || res == ' ' || res == KEY_LEFT){ break; } refresh_all_windows(main_window); } /* if ESC or left */ if (res == 27 || res == KEY_LEFT) break; child = item_data(); if (!child || !menu_is_visible(child) || !child->sym) continue; switch (res) { case ' ': case 10: case KEY_RIGHT: sym_set_tristate_value(child->sym, yes); return; case 'h': case '?': show_help(child); active = child->sym; break; case KEY_EXIT: return; } } } static void conf_string(struct menu *menu) { const char *prompt = menu_get_prompt(menu); while (1) { int res; const char *heading; switch (sym_get_type(menu->sym)) { case S_INT: heading = inputbox_instructions_int; break; case S_HEX: heading = inputbox_instructions_hex; break; case S_STRING: heading = inputbox_instructions_string; break; default: heading = "Internal nconf error!"; } res = dialog_inputbox(main_window, prompt ? prompt : "Main Menu", heading, sym_get_string_value(menu->sym), &dialog_input_result, &dialog_input_result_len); switch (res) { case 0: if (sym_set_string_value(menu->sym, dialog_input_result)) return; btn_dialog(main_window, "You have made an invalid entry.", 0); break; case 1: show_help(menu); break; case KEY_EXIT: return; } } } static void conf_load(void) { while (1) { int res; res = dialog_inputbox(main_window, NULL, load_config_text, filename, &dialog_input_result, &dialog_input_result_len); switch (res) { case 0: if (!dialog_input_result[0]) return; if (!conf_read(dialog_input_result)) { set_config_filename(dialog_input_result); sym_set_change_count(1); return; } btn_dialog(main_window, "File does not exist!", 0); break; case 1: show_scroll_win(main_window, "Load Alternate Configuration", load_config_help); break; case KEY_EXIT: return; } } } static void conf_save(void) { while (1) { int res; res = dialog_inputbox(main_window, NULL, save_config_text, filename, &dialog_input_result, &dialog_input_result_len); switch (res) { case 0: if (!dialog_input_result[0]) return; res = conf_write(dialog_input_result); if (!res) { set_config_filename(dialog_input_result); return; } btn_dialog(main_window, "Can't create file!", 1, ""); break; case 1: show_scroll_win(main_window, "Save Alternate Configuration", save_config_help); break; case KEY_EXIT: return; } } } static void setup_windows(void) { int lines, columns; getmaxyx(stdscr, lines, columns); if (main_window != NULL) delwin(main_window); /* set up the menu and menu window */ main_window = newwin(lines-2, columns-2, 2, 1); keypad(main_window, TRUE); mwin_max_lines = lines-7; mwin_max_cols = columns-6; /* panels order is from bottom to top */ new_panel(main_window); } int main(int ac, char **av) { int lines, columns; char *mode; if (ac > 1 && strcmp(av[1], "-s") == 0) { /* Silence conf_read() until the real callback is set up */ conf_set_message_callback(NULL); av++; } conf_parse(av[1]); conf_read(NULL); mode = getenv("NCONFIG_MODE"); if (mode) { if (!strcasecmp(mode, "single_menu")) single_menu_mode = 1; } /* Initialize curses */ initscr(); /* set color theme */ set_colors(); cbreak(); noecho(); keypad(stdscr, TRUE); curs_set(0); getmaxyx(stdscr, lines, columns); if (columns < 75 || lines < 20) { endwin(); printf("Your terminal should have at " "least 20 lines and 75 columns\n"); return 1; } notimeout(stdscr, FALSE); #if NCURSES_REENTRANT set_escdelay(1); #else ESCDELAY = 1; #endif /* set btns menu */ curses_menu = new_menu(curses_menu_items); menu_opts_off(curses_menu, O_SHOWDESC); menu_opts_on(curses_menu, O_SHOWMATCH); menu_opts_on(curses_menu, O_ONEVALUE); menu_opts_on(curses_menu, O_NONCYCLIC); menu_opts_on(curses_menu, O_IGNORECASE); set_menu_mark(curses_menu, " "); set_menu_fore(curses_menu, attributes[MAIN_MENU_FORE]); set_menu_back(curses_menu, attributes[MAIN_MENU_BACK]); set_menu_grey(curses_menu, attributes[MAIN_MENU_GREY]); set_config_filename(conf_get_configname()); setup_windows(); /* check for KEY_FUNC(1) */ if (has_key(KEY_F(1)) == FALSE) { show_scroll_win(main_window, "Instructions", menu_no_f_instructions); } conf_set_message_callback(conf_message_callback); /* do the work */ while (!global_exit) { conf(&rootmenu); if (!global_exit && do_exit() == 0) break; } /* ok, we are done */ unpost_menu(curses_menu); free_menu(curses_menu); delwin(main_window); clear(); refresh(); endwin(); return 0; } crust-0.5/3rdparty/kconfig/nconf.gui.c000066400000000000000000000354221414152222500177440ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2008 Nir Tzachar * * Derived from menuconfig. */ #include "nconf.h" #include "lkc.h" /* a list of all the different widgets we use */ attributes_t attributes[ATTR_MAX+1] = {0}; /* available colors: COLOR_BLACK 0 COLOR_RED 1 COLOR_GREEN 2 COLOR_YELLOW 3 COLOR_BLUE 4 COLOR_MAGENTA 5 COLOR_CYAN 6 COLOR_WHITE 7 */ static void set_normal_colors(void) { init_pair(NORMAL, -1, -1); init_pair(MAIN_HEADING, COLOR_MAGENTA, -1); /* FORE is for the selected item */ init_pair(MAIN_MENU_FORE, -1, -1); /* BACK for all the rest */ init_pair(MAIN_MENU_BACK, -1, -1); init_pair(MAIN_MENU_GREY, -1, -1); init_pair(MAIN_MENU_HEADING, COLOR_GREEN, -1); init_pair(MAIN_MENU_BOX, COLOR_YELLOW, -1); init_pair(SCROLLWIN_TEXT, -1, -1); init_pair(SCROLLWIN_HEADING, COLOR_GREEN, -1); init_pair(SCROLLWIN_BOX, COLOR_YELLOW, -1); init_pair(DIALOG_TEXT, -1, -1); init_pair(DIALOG_BOX, COLOR_YELLOW, -1); init_pair(DIALOG_MENU_BACK, COLOR_YELLOW, -1); init_pair(DIALOG_MENU_FORE, COLOR_RED, -1); init_pair(INPUT_BOX, COLOR_YELLOW, -1); init_pair(INPUT_HEADING, COLOR_GREEN, -1); init_pair(INPUT_TEXT, -1, -1); init_pair(INPUT_FIELD, -1, -1); init_pair(FUNCTION_HIGHLIGHT, -1, -1); init_pair(FUNCTION_TEXT, COLOR_YELLOW, -1); } /* available attributes: A_NORMAL Normal display (no highlight) A_STANDOUT Best highlighting mode of the terminal. A_UNDERLINE Underlining A_REVERSE Reverse video A_BLINK Blinking A_DIM Half bright A_BOLD Extra bright or bold A_PROTECT Protected mode A_INVIS Invisible or blank mode A_ALTCHARSET Alternate character set A_CHARTEXT Bit-mask to extract a character COLOR_PAIR(n) Color-pair number n */ static void normal_color_theme(void) { /* automatically add color... */ #define mkattr(name, attr) do { \ attributes[name] = attr | COLOR_PAIR(name); } while (0) mkattr(NORMAL, NORMAL); mkattr(MAIN_HEADING, A_BOLD | A_UNDERLINE); mkattr(MAIN_MENU_FORE, A_REVERSE); mkattr(MAIN_MENU_BACK, A_NORMAL); mkattr(MAIN_MENU_GREY, A_NORMAL); mkattr(MAIN_MENU_HEADING, A_BOLD); mkattr(MAIN_MENU_BOX, A_NORMAL); mkattr(SCROLLWIN_TEXT, A_NORMAL); mkattr(SCROLLWIN_HEADING, A_BOLD); mkattr(SCROLLWIN_BOX, A_BOLD); mkattr(DIALOG_TEXT, A_BOLD); mkattr(DIALOG_BOX, A_BOLD); mkattr(DIALOG_MENU_FORE, A_STANDOUT); mkattr(DIALOG_MENU_BACK, A_NORMAL); mkattr(INPUT_BOX, A_NORMAL); mkattr(INPUT_HEADING, A_BOLD); mkattr(INPUT_TEXT, A_NORMAL); mkattr(INPUT_FIELD, A_UNDERLINE); mkattr(FUNCTION_HIGHLIGHT, A_BOLD); mkattr(FUNCTION_TEXT, A_REVERSE); } static void no_colors_theme(void) { /* automatically add highlight, no color */ #define mkattrn(name, attr) { attributes[name] = attr; } mkattrn(NORMAL, NORMAL); mkattrn(MAIN_HEADING, A_BOLD | A_UNDERLINE); mkattrn(MAIN_MENU_FORE, A_STANDOUT); mkattrn(MAIN_MENU_BACK, A_NORMAL); mkattrn(MAIN_MENU_GREY, A_NORMAL); mkattrn(MAIN_MENU_HEADING, A_BOLD); mkattrn(MAIN_MENU_BOX, A_NORMAL); mkattrn(SCROLLWIN_TEXT, A_NORMAL); mkattrn(SCROLLWIN_HEADING, A_BOLD); mkattrn(SCROLLWIN_BOX, A_BOLD); mkattrn(DIALOG_TEXT, A_NORMAL); mkattrn(DIALOG_BOX, A_BOLD); mkattrn(DIALOG_MENU_FORE, A_STANDOUT); mkattrn(DIALOG_MENU_BACK, A_NORMAL); mkattrn(INPUT_BOX, A_BOLD); mkattrn(INPUT_HEADING, A_BOLD); mkattrn(INPUT_TEXT, A_NORMAL); mkattrn(INPUT_FIELD, A_UNDERLINE); mkattrn(FUNCTION_HIGHLIGHT, A_BOLD); mkattrn(FUNCTION_TEXT, A_REVERSE); } void set_colors(void) { start_color(); use_default_colors(); set_normal_colors(); if (has_colors()) { normal_color_theme(); } else { /* give defaults */ no_colors_theme(); } } /* this changes the windows attributes !!! */ void print_in_middle(WINDOW *win, int starty, int startx, int width, const char *string, chtype color) { int length, x, y; float temp; if (win == NULL) win = stdscr; getyx(win, y, x); if (startx != 0) x = startx; if (starty != 0) y = starty; if (width == 0) width = 80; length = strlen(string); temp = (width - length) / 2; x = startx + (int)temp; (void) wattrset(win, color); mvwprintw(win, y, x, "%s", string); refresh(); } int get_line_no(const char *text) { int i; int total = 1; if (!text) return 0; for (i = 0; text[i] != '\0'; i++) if (text[i] == '\n') total++; return total; } const char *get_line(const char *text, int line_no) { int i; int lines = 0; if (!text) return NULL; for (i = 0; text[i] != '\0' && lines < line_no; i++) if (text[i] == '\n') lines++; return text+i; } int get_line_length(const char *line) { int res = 0; while (*line != '\0' && *line != '\n') { line++; res++; } return res; } /* print all lines to the window. */ void fill_window(WINDOW *win, const char *text) { int x, y; int total_lines = get_line_no(text); int i; getmaxyx(win, y, x); /* do not go over end of line */ total_lines = min(total_lines, y); for (i = 0; i < total_lines; i++) { char tmp[x+10]; const char *line = get_line(text, i); int len = get_line_length(line); strncpy(tmp, line, min(len, x)); tmp[len] = '\0'; mvwprintw(win, i, 0, "%s", tmp); } } /* get the message, and buttons. * each button must be a char* * return the selected button * * this dialog is used for 2 different things: * 1) show a text box, no buttons. * 2) show a dialog, with horizontal buttons */ int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...) { va_list ap; char *btn; int btns_width = 0; int msg_lines = 0; int msg_width = 0; int total_width; int win_rows = 0; WINDOW *win; WINDOW *msg_win; WINDOW *menu_win; MENU *menu; ITEM *btns[btn_num+1]; int i, x, y; int res = -1; va_start(ap, btn_num); for (i = 0; i < btn_num; i++) { btn = va_arg(ap, char *); btns[i] = new_item(btn, ""); btns_width += strlen(btn)+1; } va_end(ap); btns[btn_num] = NULL; /* find the widest line of msg: */ msg_lines = get_line_no(msg); for (i = 0; i < msg_lines; i++) { const char *line = get_line(msg, i); int len = get_line_length(line); if (msg_width < len) msg_width = len; } total_width = max(msg_width, btns_width); /* place dialog in middle of screen */ y = (getmaxy(stdscr)-(msg_lines+4))/2; x = (getmaxx(stdscr)-(total_width+4))/2; /* create the windows */ if (btn_num > 0) win_rows = msg_lines+4; else win_rows = msg_lines+2; win = newwin(win_rows, total_width+4, y, x); keypad(win, TRUE); menu_win = derwin(win, 1, btns_width, win_rows-2, 1+(total_width+2-btns_width)/2); menu = new_menu(btns); msg_win = derwin(win, win_rows-2, msg_width, 1, 1+(total_width+2-msg_width)/2); set_menu_fore(menu, attributes[DIALOG_MENU_FORE]); set_menu_back(menu, attributes[DIALOG_MENU_BACK]); (void) wattrset(win, attributes[DIALOG_BOX]); box(win, 0, 0); /* print message */ (void) wattrset(msg_win, attributes[DIALOG_TEXT]); fill_window(msg_win, msg); set_menu_win(menu, win); set_menu_sub(menu, menu_win); set_menu_format(menu, 1, btn_num); menu_opts_off(menu, O_SHOWDESC); menu_opts_off(menu, O_SHOWMATCH); menu_opts_on(menu, O_ONEVALUE); menu_opts_on(menu, O_NONCYCLIC); set_menu_mark(menu, ""); post_menu(menu); touchwin(win); refresh_all_windows(main_window); while ((res = wgetch(win))) { switch (res) { case KEY_LEFT: menu_driver(menu, REQ_LEFT_ITEM); break; case KEY_RIGHT: menu_driver(menu, REQ_RIGHT_ITEM); break; case 10: /* ENTER */ case 27: /* ESCAPE */ case ' ': case KEY_F(F_BACK): case KEY_F(F_EXIT): break; } touchwin(win); refresh_all_windows(main_window); if (res == 10 || res == ' ') { res = item_index(current_item(menu)); break; } else if (res == 27 || res == KEY_F(F_BACK) || res == KEY_F(F_EXIT)) { res = KEY_EXIT; break; } } unpost_menu(menu); free_menu(menu); for (i = 0; i < btn_num; i++) free_item(btns[i]); delwin(win); return res; } int dialog_inputbox(WINDOW *main_window, const char *title, const char *prompt, const char *init, char **resultp, int *result_len) { int prompt_lines = 0; int prompt_width = 0; WINDOW *win; WINDOW *prompt_win; WINDOW *form_win; PANEL *panel; int i, x, y, lines, columns, win_lines, win_cols; int res = -1; int cursor_position = strlen(init); int cursor_form_win; char *result = *resultp; getmaxyx(stdscr, lines, columns); if (strlen(init)+1 > *result_len) { *result_len = strlen(init)+1; *resultp = result = xrealloc(result, *result_len); } /* find the widest line of msg: */ prompt_lines = get_line_no(prompt); for (i = 0; i < prompt_lines; i++) { const char *line = get_line(prompt, i); int len = get_line_length(line); prompt_width = max(prompt_width, len); } if (title) prompt_width = max(prompt_width, strlen(title)); win_lines = min(prompt_lines+6, lines-2); win_cols = min(prompt_width+7, columns-2); prompt_lines = max(win_lines-6, 0); prompt_width = max(win_cols-7, 0); /* place dialog in middle of screen */ y = (lines-win_lines)/2; x = (columns-win_cols)/2; strncpy(result, init, *result_len); /* create the windows */ win = newwin(win_lines, win_cols, y, x); prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2); form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2); keypad(form_win, TRUE); (void) wattrset(form_win, attributes[INPUT_FIELD]); (void) wattrset(win, attributes[INPUT_BOX]); box(win, 0, 0); (void) wattrset(win, attributes[INPUT_HEADING]); if (title) mvwprintw(win, 0, 3, "%s", title); /* print message */ (void) wattrset(prompt_win, attributes[INPUT_TEXT]); fill_window(prompt_win, prompt); mvwprintw(form_win, 0, 0, "%*s", prompt_width, " "); cursor_form_win = min(cursor_position, prompt_width-1); mvwprintw(form_win, 0, 0, "%s", result + cursor_position-cursor_form_win); /* create panels */ panel = new_panel(win); /* show the cursor */ curs_set(1); touchwin(win); refresh_all_windows(main_window); while ((res = wgetch(form_win))) { int len = strlen(result); switch (res) { case 10: /* ENTER */ case 27: /* ESCAPE */ case KEY_F(F_HELP): case KEY_F(F_EXIT): case KEY_F(F_BACK): break; case 8: /* ^H */ case 127: /* ^? */ case KEY_BACKSPACE: if (cursor_position > 0) { memmove(&result[cursor_position-1], &result[cursor_position], len-cursor_position+1); cursor_position--; cursor_form_win--; len--; } break; case KEY_DC: if (cursor_position >= 0 && cursor_position < len) { memmove(&result[cursor_position], &result[cursor_position+1], len-cursor_position+1); len--; } break; case KEY_UP: case KEY_RIGHT: if (cursor_position < len) { cursor_position++; cursor_form_win++; } break; case KEY_DOWN: case KEY_LEFT: if (cursor_position > 0) { cursor_position--; cursor_form_win--; } break; case KEY_HOME: cursor_position = 0; cursor_form_win = 0; break; case KEY_END: cursor_position = len; cursor_form_win = min(cursor_position, prompt_width-1); break; default: if ((isgraph(res) || isspace(res))) { /* one for new char, one for '\0' */ if (len+2 > *result_len) { *result_len = len+2; *resultp = result = realloc(result, *result_len); } /* insert the char at the proper position */ memmove(&result[cursor_position+1], &result[cursor_position], len-cursor_position+1); result[cursor_position] = res; cursor_position++; cursor_form_win++; len++; } else { mvprintw(0, 0, "unknown key: %d\n", res); } break; } if (cursor_form_win < 0) cursor_form_win = 0; else if (cursor_form_win > prompt_width-1) cursor_form_win = prompt_width-1; wmove(form_win, 0, 0); wclrtoeol(form_win); mvwprintw(form_win, 0, 0, "%*s", prompt_width, " "); mvwprintw(form_win, 0, 0, "%s", result + cursor_position-cursor_form_win); wmove(form_win, 0, cursor_form_win); touchwin(win); refresh_all_windows(main_window); if (res == 10) { res = 0; break; } else if (res == 27 || res == KEY_F(F_BACK) || res == KEY_F(F_EXIT)) { res = KEY_EXIT; break; } else if (res == KEY_F(F_HELP)) { res = 1; break; } } /* hide the cursor */ curs_set(0); del_panel(panel); delwin(prompt_win); delwin(form_win); delwin(win); return res; } /* refresh all windows in the correct order */ void refresh_all_windows(WINDOW *main_window) { update_panels(); touchwin(main_window); refresh(); } /* layman's scrollable window... */ void show_scroll_win(WINDOW *main_window, const char *title, const char *text) { int res; int total_lines = get_line_no(text); int x, y, lines, columns; int start_x = 0, start_y = 0; int text_lines = 0, text_cols = 0; int total_cols = 0; int win_cols = 0; int win_lines = 0; int i = 0; WINDOW *win; WINDOW *pad; PANEL *panel; getmaxyx(stdscr, lines, columns); /* find the widest line of msg: */ total_lines = get_line_no(text); for (i = 0; i < total_lines; i++) { const char *line = get_line(text, i); int len = get_line_length(line); total_cols = max(total_cols, len+2); } /* create the pad */ pad = newpad(total_lines+10, total_cols+10); (void) wattrset(pad, attributes[SCROLLWIN_TEXT]); fill_window(pad, text); win_lines = min(total_lines+4, lines-2); win_cols = min(total_cols+2, columns-2); text_lines = max(win_lines-4, 0); text_cols = max(win_cols-2, 0); /* place window in middle of screen */ y = (lines-win_lines)/2; x = (columns-win_cols)/2; win = newwin(win_lines, win_cols, y, x); keypad(win, TRUE); /* show the help in the help window, and show the help panel */ (void) wattrset(win, attributes[SCROLLWIN_BOX]); box(win, 0, 0); (void) wattrset(win, attributes[SCROLLWIN_HEADING]); mvwprintw(win, 0, 3, " %s ", title); panel = new_panel(win); /* handle scrolling */ do { copywin(pad, win, start_y, start_x, 2, 2, text_lines, text_cols, 0); print_in_middle(win, text_lines+2, 0, text_cols, "", attributes[DIALOG_MENU_FORE]); wrefresh(win); res = wgetch(win); switch (res) { case KEY_NPAGE: case ' ': case 'd': start_y += text_lines-2; break; case KEY_PPAGE: case 'u': start_y -= text_lines+2; break; case KEY_HOME: start_y = 0; break; case KEY_END: start_y = total_lines-text_lines; break; case KEY_DOWN: case 'j': start_y++; break; case KEY_UP: case 'k': start_y--; break; case KEY_LEFT: case 'h': start_x--; break; case KEY_RIGHT: case 'l': start_x++; break; } if (res == 10 || res == 27 || res == 'q' || res == KEY_F(F_HELP) || res == KEY_F(F_BACK) || res == KEY_F(F_EXIT)) break; if (start_y < 0) start_y = 0; if (start_y >= total_lines-text_lines) start_y = total_lines-text_lines; if (start_x < 0) start_x = 0; if (start_x >= total_cols-text_cols) start_x = total_cols-text_cols; } while (res); del_panel(panel); delwin(win); refresh_all_windows(main_window); } crust-0.5/3rdparty/kconfig/nconf.h000066400000000000000000000035061414152222500171640ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2008 Nir Tzachar * * Derived from menuconfig. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define max(a, b) ({\ typeof(a) _a = a;\ typeof(b) _b = b;\ _a > _b ? _a : _b; }) #define min(a, b) ({\ typeof(a) _a = a;\ typeof(b) _b = b;\ _a < _b ? _a : _b; }) typedef enum { NORMAL = 1, MAIN_HEADING, MAIN_MENU_BOX, MAIN_MENU_FORE, MAIN_MENU_BACK, MAIN_MENU_GREY, MAIN_MENU_HEADING, SCROLLWIN_TEXT, SCROLLWIN_HEADING, SCROLLWIN_BOX, DIALOG_TEXT, DIALOG_MENU_FORE, DIALOG_MENU_BACK, DIALOG_BOX, INPUT_BOX, INPUT_HEADING, INPUT_TEXT, INPUT_FIELD, FUNCTION_TEXT, FUNCTION_HIGHLIGHT, ATTR_MAX } attributes_t; extern attributes_t attributes[]; typedef enum { F_HELP = 1, F_SYMBOL = 2, F_INSTS = 3, F_CONF = 4, F_BACK = 5, F_SAVE = 6, F_LOAD = 7, F_SEARCH = 8, F_EXIT = 9, } function_key; void set_colors(void); /* this changes the windows attributes !!! */ void print_in_middle(WINDOW *win, int starty, int startx, int width, const char *string, chtype color); int get_line_length(const char *line); int get_line_no(const char *text); const char *get_line(const char *text, int line_no); void fill_window(WINDOW *win, const char *text); int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...); int dialog_inputbox(WINDOW *main_window, const char *title, const char *prompt, const char *init, char **resultp, int *result_len); void refresh_all_windows(WINDOW *main_window); void show_scroll_win(WINDOW *main_window, const char *title, const char *text); crust-0.5/3rdparty/kconfig/parser.y000066400000000000000000000365161414152222500174050ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2002 Roman Zippel */ %{ #include #include #include #include #include #include #include "lkc.h" #define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) #define PRINTD 0x0001 #define DEBUG_PARSE 0x0002 int cdebug = PRINTD; static void yyerror(const char *err); static void zconfprint(const char *err, ...); static void zconf_error(const char *err, ...); static bool zconf_endtoken(const char *tokenname, const char *expected_tokenname); struct symbol *symbol_hash[SYMBOL_HASHSIZE]; static struct menu *current_menu, *current_entry; %} %union { char *string; struct symbol *symbol; struct expr *expr; struct menu *menu; enum symbol_type type; enum variable_flavor flavor; } %token T_HELPTEXT %token T_WORD %token T_WORD_QUOTE %token T_ALLNOCONFIG_Y %token T_BOOL %token T_CHOICE %token T_CLOSE_PAREN %token T_COLON_EQUAL %token T_COMMENT %token T_CONFIG %token T_DEFAULT %token T_DEFCONFIG_LIST %token T_DEF_BOOL %token T_DEF_TRISTATE %token T_DEPENDS %token T_ENDCHOICE %token T_ENDIF %token T_ENDMENU %token T_HELP %token T_HEX %token T_IF %token T_IMPLY %token T_INT %token T_MAINMENU %token T_MENU %token T_MENUCONFIG %token T_MODULES %token T_ON %token T_OPEN_PAREN %token T_OPTION %token T_OPTIONAL %token T_PLUS_EQUAL %token T_PROMPT %token T_RANGE %token T_SELECT %token T_SOURCE %token T_STRING %token T_TRISTATE %token T_VISIBLE %token T_EOL %token T_ASSIGN_VAL %left T_OR %left T_AND %left T_EQUAL T_UNEQUAL %left T_LESS T_LESS_EQUAL T_GREATER T_GREATER_EQUAL %nonassoc T_NOT %type nonconst_symbol %type symbol %type type logic_type default %type expr %type if_expr %type end %type if_entry menu_entry choice_entry %type word_opt assign_val %type assign_op %destructor { fprintf(stderr, "%s:%d: missing end statement for this entry\n", $$->file->name, $$->lineno); if (current_menu == $$) menu_end_menu(); } if_entry menu_entry choice_entry %% input: mainmenu_stmt stmt_list | stmt_list; /* mainmenu entry */ mainmenu_stmt: T_MAINMENU T_WORD_QUOTE T_EOL { menu_add_prompt(P_MENU, $2, NULL); }; stmt_list: /* empty */ | stmt_list assignment_stmt | stmt_list choice_stmt | stmt_list comment_stmt | stmt_list config_stmt | stmt_list if_stmt | stmt_list menu_stmt | stmt_list menuconfig_stmt | stmt_list source_stmt | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); } | stmt_list error T_EOL { zconf_error("invalid statement"); } ; stmt_list_in_choice: /* empty */ | stmt_list_in_choice comment_stmt | stmt_list_in_choice config_stmt | stmt_list_in_choice if_stmt_in_choice | stmt_list_in_choice error T_EOL { zconf_error("invalid statement"); } ; /* config/menuconfig entry */ config_entry_start: T_CONFIG nonconst_symbol T_EOL { $2->flags |= SYMBOL_OPTIONAL; menu_add_entry($2); printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2->name); }; config_stmt: config_entry_start config_option_list { printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); }; menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL { $2->flags |= SYMBOL_OPTIONAL; menu_add_entry($2); printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2->name); }; menuconfig_stmt: menuconfig_entry_start config_option_list { if (current_entry->prompt) current_entry->prompt->type = P_MENU; else zconfprint("warning: menuconfig statement without prompt"); printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); }; config_option_list: /* empty */ | config_option_list config_option | config_option_list depends | config_option_list help ; config_option: type prompt_stmt_opt T_EOL { menu_set_type($1); printd(DEBUG_PARSE, "%s:%d:type(%u)\n", zconf_curname(), zconf_lineno(), $1); }; config_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL { menu_add_prompt(P_PROMPT, $2, $3); printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); }; config_option: default expr if_expr T_EOL { menu_add_expr(P_DEFAULT, $2, $3); if ($1 != S_UNKNOWN) menu_set_type($1); printd(DEBUG_PARSE, "%s:%d:default(%u)\n", zconf_curname(), zconf_lineno(), $1); }; config_option: T_SELECT nonconst_symbol if_expr T_EOL { menu_add_symbol(P_SELECT, $2, $3); printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); }; config_option: T_IMPLY nonconst_symbol if_expr T_EOL { menu_add_symbol(P_IMPLY, $2, $3); printd(DEBUG_PARSE, "%s:%d:imply\n", zconf_curname(), zconf_lineno()); }; config_option: T_RANGE symbol symbol if_expr T_EOL { menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); }; config_option: T_OPTION T_MODULES T_EOL { menu_add_option_modules(); }; config_option: T_OPTION T_DEFCONFIG_LIST T_EOL { menu_add_option_defconfig_list(); }; config_option: T_OPTION T_ALLNOCONFIG_Y T_EOL { menu_add_option_allnoconfig_y(); }; /* choice entry */ choice: T_CHOICE word_opt T_EOL { struct symbol *sym = sym_lookup($2, SYMBOL_CHOICE); sym->flags |= SYMBOL_NO_WRITE; menu_add_entry(sym); menu_add_expr(P_CHOICE, NULL, NULL); free($2); printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); }; choice_entry: choice choice_option_list { $$ = menu_add_menu(); }; choice_end: end { if (zconf_endtoken($1, "choice")) { menu_end_menu(); printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); } }; choice_stmt: choice_entry stmt_list_in_choice choice_end ; choice_option_list: /* empty */ | choice_option_list choice_option | choice_option_list depends | choice_option_list help ; choice_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL { menu_add_prompt(P_PROMPT, $2, $3); printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); }; choice_option: logic_type prompt_stmt_opt T_EOL { menu_set_type($1); printd(DEBUG_PARSE, "%s:%d:type(%u)\n", zconf_curname(), zconf_lineno(), $1); }; choice_option: T_OPTIONAL T_EOL { current_entry->sym->flags |= SYMBOL_OPTIONAL; printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); }; choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL { menu_add_symbol(P_DEFAULT, $2, $3); printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno()); }; type: logic_type | T_INT { $$ = S_INT; } | T_HEX { $$ = S_HEX; } | T_STRING { $$ = S_STRING; } logic_type: T_BOOL { $$ = S_BOOLEAN; } | T_TRISTATE { $$ = S_TRISTATE; } default: T_DEFAULT { $$ = S_UNKNOWN; } | T_DEF_BOOL { $$ = S_BOOLEAN; } | T_DEF_TRISTATE { $$ = S_TRISTATE; } /* if entry */ if_entry: T_IF expr T_EOL { printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); menu_add_entry(NULL); menu_add_dep($2); $$ = menu_add_menu(); }; if_end: end { if (zconf_endtoken($1, "if")) { menu_end_menu(); printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); } }; if_stmt: if_entry stmt_list if_end ; if_stmt_in_choice: if_entry stmt_list_in_choice if_end ; /* menu entry */ menu: T_MENU T_WORD_QUOTE T_EOL { menu_add_entry(NULL); menu_add_prompt(P_MENU, $2, NULL); printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); }; menu_entry: menu menu_option_list { $$ = menu_add_menu(); }; menu_end: end { if (zconf_endtoken($1, "menu")) { menu_end_menu(); printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); } }; menu_stmt: menu_entry stmt_list menu_end ; menu_option_list: /* empty */ | menu_option_list visible | menu_option_list depends ; source_stmt: T_SOURCE T_WORD_QUOTE T_EOL { printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2); zconf_nextfile($2); free($2); }; /* comment entry */ comment: T_COMMENT T_WORD_QUOTE T_EOL { menu_add_entry(NULL); menu_add_prompt(P_COMMENT, $2, NULL); printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); }; comment_stmt: comment comment_option_list ; comment_option_list: /* empty */ | comment_option_list depends ; /* help option */ help_start: T_HELP T_EOL { printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); zconf_starthelp(); }; help: help_start T_HELPTEXT { if (current_entry->help) { free(current_entry->help); zconfprint("warning: '%s' defined with more than one help text -- only the last one will be used", current_entry->sym->name ?: ""); } /* Is the help text empty or all whitespace? */ if ($2[strspn($2, " \f\n\r\t\v")] == '\0') zconfprint("warning: '%s' defined with blank help text", current_entry->sym->name ?: ""); current_entry->help = $2; }; /* depends option */ depends: T_DEPENDS T_ON expr T_EOL { menu_add_dep($3); printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); }; /* visibility option */ visible: T_VISIBLE if_expr T_EOL { menu_add_visibility($2); }; /* prompt statement */ prompt_stmt_opt: /* empty */ | T_WORD_QUOTE if_expr { menu_add_prompt(P_PROMPT, $1, $2); }; end: T_ENDMENU T_EOL { $$ = "menu"; } | T_ENDCHOICE T_EOL { $$ = "choice"; } | T_ENDIF T_EOL { $$ = "if"; } ; if_expr: /* empty */ { $$ = NULL; } | T_IF expr { $$ = $2; } ; expr: symbol { $$ = expr_alloc_symbol($1); } | symbol T_LESS symbol { $$ = expr_alloc_comp(E_LTH, $1, $3); } | symbol T_LESS_EQUAL symbol { $$ = expr_alloc_comp(E_LEQ, $1, $3); } | symbol T_GREATER symbol { $$ = expr_alloc_comp(E_GTH, $1, $3); } | symbol T_GREATER_EQUAL symbol { $$ = expr_alloc_comp(E_GEQ, $1, $3); } | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } ; /* For symbol definitions, selects, etc., where quotes are not accepted */ nonconst_symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); }; symbol: nonconst_symbol | T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); } ; word_opt: /* empty */ { $$ = NULL; } | T_WORD /* assignment statement */ assignment_stmt: T_WORD assign_op assign_val T_EOL { variable_add($1, $3, $2); free($1); free($3); } assign_op: T_EQUAL { $$ = VAR_RECURSIVE; } | T_COLON_EQUAL { $$ = VAR_SIMPLE; } | T_PLUS_EQUAL { $$ = VAR_APPEND; } ; assign_val: /* empty */ { $$ = xstrdup(""); }; | T_ASSIGN_VAL ; %% void conf_parse(const char *name) { struct symbol *sym; int i; zconf_initscan(name); _menu_init(); if (getenv("ZCONF_DEBUG")) yydebug = 1; yyparse(); /* Variables are expanded in the parse phase. We can free them here. */ variable_all_del(); if (yynerrs) exit(1); if (!modules_sym) modules_sym = sym_find( "n" ); if (!menu_has_prompt(&rootmenu)) { current_entry = &rootmenu; menu_add_prompt(P_MENU, "Main menu", NULL); } menu_finalize(&rootmenu); for_all_symbols(i, sym) { if (sym_check_deps(sym)) yynerrs++; } if (yynerrs) exit(1); sym_set_change_count(1); } static bool zconf_endtoken(const char *tokenname, const char *expected_tokenname) { if (strcmp(tokenname, expected_tokenname)) { zconf_error("unexpected '%s' within %s block", tokenname, expected_tokenname); yynerrs++; return false; } if (current_menu->file != current_file) { zconf_error("'%s' in different file than '%s'", tokenname, expected_tokenname); fprintf(stderr, "%s:%d: location of the '%s'\n", current_menu->file->name, current_menu->lineno, expected_tokenname); yynerrs++; return false; } return true; } static void zconfprint(const char *err, ...) { va_list ap; fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); va_start(ap, err); vfprintf(stderr, err, ap); va_end(ap); fprintf(stderr, "\n"); } static void zconf_error(const char *err, ...) { va_list ap; yynerrs++; fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); va_start(ap, err); vfprintf(stderr, err, ap); va_end(ap); fprintf(stderr, "\n"); } static void yyerror(const char *err) { fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); } static void print_quoted_string(FILE *out, const char *str) { const char *p; int len; putc('"', out); while ((p = strchr(str, '"'))) { len = p - str; if (len) fprintf(out, "%.*s", len, str); fputs("\\\"", out); str = p + 1; } fputs(str, out); putc('"', out); } static void print_symbol(FILE *out, struct menu *menu) { struct symbol *sym = menu->sym; struct property *prop; if (sym_is_choice(sym)) fprintf(out, "\nchoice\n"); else fprintf(out, "\nconfig %s\n", sym->name); switch (sym->type) { case S_BOOLEAN: fputs(" bool\n", out); break; case S_TRISTATE: fputs(" tristate\n", out); break; case S_STRING: fputs(" string\n", out); break; case S_INT: fputs(" integer\n", out); break; case S_HEX: fputs(" hex\n", out); break; default: fputs(" ???\n", out); break; } for (prop = sym->prop; prop; prop = prop->next) { if (prop->menu != menu) continue; switch (prop->type) { case P_PROMPT: fputs(" prompt ", out); print_quoted_string(out, prop->text); if (!expr_is_yes(prop->visible.expr)) { fputs(" if ", out); expr_fprint(prop->visible.expr, out); } fputc('\n', out); break; case P_DEFAULT: fputs( " default ", out); expr_fprint(prop->expr, out); if (!expr_is_yes(prop->visible.expr)) { fputs(" if ", out); expr_fprint(prop->visible.expr, out); } fputc('\n', out); break; case P_CHOICE: fputs(" #choice value\n", out); break; case P_SELECT: fputs( " select ", out); expr_fprint(prop->expr, out); fputc('\n', out); break; case P_IMPLY: fputs( " imply ", out); expr_fprint(prop->expr, out); fputc('\n', out); break; case P_RANGE: fputs( " range ", out); expr_fprint(prop->expr, out); fputc('\n', out); break; case P_MENU: fputs( " menu ", out); print_quoted_string(out, prop->text); fputc('\n', out); break; case P_SYMBOL: fputs( " symbol ", out); fprintf(out, "%s\n", prop->menu->sym->name); break; default: fprintf(out, " unknown prop %d!\n", prop->type); break; } } if (menu->help) { int len = strlen(menu->help); while (menu->help[--len] == '\n') menu->help[len] = 0; fprintf(out, " help\n%s\n", menu->help); } } void zconfdump(FILE *out) { struct property *prop; struct symbol *sym; struct menu *menu; menu = rootmenu.list; while (menu) { if ((sym = menu->sym)) print_symbol(out, menu); else if ((prop = menu->prompt)) { switch (prop->type) { case P_COMMENT: fputs("\ncomment ", out); print_quoted_string(out, prop->text); fputs("\n", out); break; case P_MENU: fputs("\nmenu ", out); print_quoted_string(out, prop->text); fputs("\n", out); break; default: ; } if (!expr_is_yes(prop->visible.expr)) { fputs(" depends ", out); expr_fprint(prop->visible.expr, out); fputc('\n', out); } } if (menu->list) menu = menu->list; else if (menu->next) menu = menu->next; else while ((menu = menu->parent)) { if (menu->prompt && menu->prompt->type == P_MENU) fputs("\nendmenu\n", out); if (menu->next) { menu = menu->next; break; } } } } #include "menu.c" crust-0.5/3rdparty/kconfig/preprocess.c000066400000000000000000000257571414152222500202550ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // // Copyright (C) 2018 Masahiro Yamada #include #include #include #include #include #include #include "list.h" #include "lkc.h" #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) static char *expand_string_with_args(const char *in, int argc, char *argv[]); static char *expand_string(const char *in); static void __attribute__((noreturn)) pperror(const char *format, ...) { va_list ap; fprintf(stderr, "%s:%d: ", current_file->name, yylineno); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fprintf(stderr, "\n"); exit(1); } /* * Environment variables */ static LIST_HEAD(env_list); struct env { char *name; char *value; struct list_head node; }; static void env_add(const char *name, const char *value) { struct env *e; e = xmalloc(sizeof(*e)); e->name = xstrdup(name); e->value = xstrdup(value); list_add_tail(&e->node, &env_list); } static void env_del(struct env *e) { list_del(&e->node); free(e->name); free(e->value); free(e); } /* The returned pointer must be freed when done */ static char *env_expand(const char *name) { struct env *e; const char *value; if (!*name) return NULL; list_for_each_entry(e, &env_list, node) { if (!strcmp(name, e->name)) return xstrdup(e->value); } value = getenv(name); if (!value) return NULL; /* * We need to remember all referenced environment variables. * They will be written out to include/config/auto.conf.cmd */ env_add(name, value); return xstrdup(value); } void env_write_dep(FILE *f, const char *autoconfig_name) { struct env *e, *tmp; list_for_each_entry_safe(e, tmp, &env_list, node) { fprintf(f, "ifneq \"$(%s)\" \"%s\"\n", e->name, e->value); fprintf(f, "%s: FORCE\n", autoconfig_name); fprintf(f, "endif\n"); env_del(e); } } /* * Built-in functions */ struct function { const char *name; unsigned int min_args; unsigned int max_args; char *(*func)(int argc, char *argv[]); }; static char *do_error_if(int argc, char *argv[]) { if (!strcmp(argv[0], "y")) pperror("%s", argv[1]); return NULL; } static char *do_filename(int argc, char *argv[]) { return xstrdup(current_file->name); } static char *do_info(int argc, char *argv[]) { printf("%s\n", argv[0]); return xstrdup(""); } static char *do_lineno(int argc, char *argv[]) { char buf[16]; sprintf(buf, "%d", yylineno); return xstrdup(buf); } static char *do_shell(int argc, char *argv[]) { FILE *p; char buf[256]; char *cmd; size_t nread; int i; cmd = argv[0]; p = popen(cmd, "r"); if (!p) { perror(cmd); exit(1); } nread = fread(buf, 1, sizeof(buf), p); if (nread == sizeof(buf)) nread--; /* remove trailing new lines */ while (nread > 0 && buf[nread - 1] == '\n') nread--; buf[nread] = 0; /* replace a new line with a space */ for (i = 0; i < nread; i++) { if (buf[i] == '\n') buf[i] = ' '; } if (pclose(p) == -1) { perror(cmd); exit(1); } return xstrdup(buf); } static char *do_warning_if(int argc, char *argv[]) { if (!strcmp(argv[0], "y")) fprintf(stderr, "%s:%d: %s\n", current_file->name, yylineno, argv[1]); return xstrdup(""); } static const struct function function_table[] = { /* Name MIN MAX Function */ { "error-if", 2, 2, do_error_if }, { "filename", 0, 0, do_filename }, { "info", 1, 1, do_info }, { "lineno", 0, 0, do_lineno }, { "shell", 1, 1, do_shell }, { "warning-if", 2, 2, do_warning_if }, }; #define FUNCTION_MAX_ARGS 16 static char *function_expand(const char *name, int argc, char *argv[]) { const struct function *f; int i; for (i = 0; i < ARRAY_SIZE(function_table); i++) { f = &function_table[i]; if (strcmp(f->name, name)) continue; if (argc < f->min_args) pperror("too few function arguments passed to '%s'", name); if (argc > f->max_args) pperror("too many function arguments passed to '%s'", name); return f->func(argc, argv); } return NULL; } /* * Variables (and user-defined functions) */ static LIST_HEAD(variable_list); struct variable { char *name; char *value; enum variable_flavor flavor; int exp_count; struct list_head node; }; static struct variable *variable_lookup(const char *name) { struct variable *v; list_for_each_entry(v, &variable_list, node) { if (!strcmp(name, v->name)) return v; } return NULL; } static char *variable_expand(const char *name, int argc, char *argv[]) { struct variable *v; char *res; v = variable_lookup(name); if (!v) return NULL; if (argc == 0 && v->exp_count) pperror("Recursive variable '%s' references itself (eventually)", name); if (v->exp_count > 1000) pperror("Too deep recursive expansion"); v->exp_count++; if (v->flavor == VAR_RECURSIVE) res = expand_string_with_args(v->value, argc, argv); else res = xstrdup(v->value); v->exp_count--; return res; } void variable_add(const char *name, const char *value, enum variable_flavor flavor) { struct variable *v; char *new_value; bool append = false; v = variable_lookup(name); if (v) { /* For defined variables, += inherits the existing flavor */ if (flavor == VAR_APPEND) { flavor = v->flavor; append = true; } else { free(v->value); } } else { /* For undefined variables, += assumes the recursive flavor */ if (flavor == VAR_APPEND) flavor = VAR_RECURSIVE; v = xmalloc(sizeof(*v)); v->name = xstrdup(name); v->exp_count = 0; list_add_tail(&v->node, &variable_list); } v->flavor = flavor; if (flavor == VAR_SIMPLE) new_value = expand_string(value); else new_value = xstrdup(value); if (append) { v->value = xrealloc(v->value, strlen(v->value) + strlen(new_value) + 2); strcat(v->value, " "); strcat(v->value, new_value); free(new_value); } else { v->value = new_value; } } static void variable_del(struct variable *v) { list_del(&v->node); free(v->name); free(v->value); free(v); } void variable_all_del(void) { struct variable *v, *tmp; list_for_each_entry_safe(v, tmp, &variable_list, node) variable_del(v); } /* * Evaluate a clause with arguments. argc/argv are arguments from the upper * function call. * * Returned string must be freed when done */ static char *eval_clause(const char *str, size_t len, int argc, char *argv[]) { char *tmp, *name, *res, *endptr, *prev, *p; int new_argc = 0; char *new_argv[FUNCTION_MAX_ARGS]; int nest = 0; int i; unsigned long n; tmp = xstrndup(str, len); /* * If variable name is '1', '2', etc. It is generally an argument * from a user-function call (i.e. local-scope variable). If not * available, then look-up global-scope variables. */ n = strtoul(tmp, &endptr, 10); if (!*endptr && n > 0 && n <= argc) { res = xstrdup(argv[n - 1]); goto free_tmp; } prev = p = tmp; /* * Split into tokens * The function name and arguments are separated by a comma. * For example, if the function call is like this: * $(foo,$(x),$(y)) * * The input string for this helper should be: * foo,$(x),$(y) * * and split into: * new_argv[0] = 'foo' * new_argv[1] = '$(x)' * new_argv[2] = '$(y)' */ while (*p) { if (nest == 0 && *p == ',') { *p = 0; if (new_argc >= FUNCTION_MAX_ARGS) pperror("too many function arguments"); new_argv[new_argc++] = prev; prev = p + 1; } else if (*p == '(') { nest++; } else if (*p == ')') { nest--; } p++; } new_argv[new_argc++] = prev; /* * Shift arguments * new_argv[0] represents a function name or a variable name. Put it * into 'name', then shift the rest of the arguments. This simplifies * 'const' handling. */ name = expand_string_with_args(new_argv[0], argc, argv); new_argc--; for (i = 0; i < new_argc; i++) new_argv[i] = expand_string_with_args(new_argv[i + 1], argc, argv); /* Search for variables */ res = variable_expand(name, new_argc, new_argv); if (res) goto free; /* Look for built-in functions */ res = function_expand(name, new_argc, new_argv); if (res) goto free; /* Last, try environment variable */ if (new_argc == 0) { res = env_expand(name); if (res) goto free; } res = xstrdup(""); free: for (i = 0; i < new_argc; i++) free(new_argv[i]); free(name); free_tmp: free(tmp); return res; } /* * Expand a string that follows '$' * * For example, if the input string is * ($(FOO)$($(BAR)))$(BAZ) * this helper evaluates * $($(FOO)$($(BAR))) * and returns a new string containing the expansion (note that the string is * recursively expanded), also advancing 'str' to point to the next character * after the corresponding closing parenthesis, in this case, *str will be * $(BAR) */ static char *expand_dollar_with_args(const char **str, int argc, char *argv[]) { const char *p = *str; const char *q; int nest = 0; /* * In Kconfig, variable/function references always start with "$(". * Neither single-letter variables as in $A nor curly braces as in ${CC} * are supported. '$' not followed by '(' loses its special meaning. */ if (*p != '(') { *str = p; return xstrdup("$"); } p++; q = p; while (*q) { if (*q == '(') { nest++; } else if (*q == ')') { if (nest-- == 0) break; } q++; } if (!*q) pperror("unterminated reference to '%s': missing ')'", p); /* Advance 'str' to after the expanded initial portion of the string */ *str = q + 1; return eval_clause(p, q - p, argc, argv); } char *expand_dollar(const char **str) { return expand_dollar_with_args(str, 0, NULL); } static char *__expand_string(const char **str, bool (*is_end)(char c), int argc, char *argv[]) { const char *in, *p; char *expansion, *out; size_t in_len, out_len; out = xmalloc(1); *out = 0; out_len = 1; p = in = *str; while (1) { if (*p == '$') { in_len = p - in; p++; expansion = expand_dollar_with_args(&p, argc, argv); out_len += in_len + strlen(expansion); out = xrealloc(out, out_len); strncat(out, in, in_len); strcat(out, expansion); free(expansion); in = p; continue; } if (is_end(*p)) break; p++; } in_len = p - in; out_len += in_len; out = xrealloc(out, out_len); strncat(out, in, in_len); /* Advance 'str' to the end character */ *str = p; return out; } static bool is_end_of_str(char c) { return !c; } /* * Expand variables and functions in the given string. Undefined variables * expand to an empty string. * The returned string must be freed when done. */ static char *expand_string_with_args(const char *in, int argc, char *argv[]) { return __expand_string(&in, is_end_of_str, argc, argv); } static char *expand_string(const char *in) { return expand_string_with_args(in, 0, NULL); } static bool is_end_of_token(char c) { return !(isalnum(c) || c == '_' || c == '-'); } /* * Expand variables in a token. The parsing stops when a token separater * (in most cases, it is a whitespace) is encountered. 'str' is updated to * point to the next character. * * The returned string must be freed when done. */ char *expand_one_token(const char **str) { return __expand_string(str, is_end_of_token, 0, NULL); } crust-0.5/3rdparty/kconfig/symbol.c000066400000000000000000000712121414152222500173600ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2002 Roman Zippel */ #include #include #include #include #include #include "lkc.h" struct symbol symbol_yes = { .name = "y", .curr = { "y", yes }, .flags = SYMBOL_CONST|SYMBOL_VALID, }; struct symbol symbol_mod = { .name = "m", .curr = { "m", mod }, .flags = SYMBOL_CONST|SYMBOL_VALID, }; struct symbol symbol_no = { .name = "n", .curr = { "n", no }, .flags = SYMBOL_CONST|SYMBOL_VALID, }; static struct symbol symbol_empty = { .name = "", .curr = { "", no }, .flags = SYMBOL_VALID, }; struct symbol *sym_defconfig_list; struct symbol *modules_sym; static tristate modules_val; enum symbol_type sym_get_type(struct symbol *sym) { enum symbol_type type = sym->type; if (type == S_TRISTATE) { if (sym_is_choice_value(sym) && sym->visible == yes) type = S_BOOLEAN; else if (modules_val == no) type = S_BOOLEAN; } return type; } const char *sym_type_name(enum symbol_type type) { switch (type) { case S_BOOLEAN: return "bool"; case S_TRISTATE: return "tristate"; case S_INT: return "integer"; case S_HEX: return "hex"; case S_STRING: return "string"; case S_UNKNOWN: return "unknown"; } return "???"; } struct property *sym_get_choice_prop(struct symbol *sym) { struct property *prop; for_all_choices(sym, prop) return prop; return NULL; } static struct property *sym_get_default_prop(struct symbol *sym) { struct property *prop; for_all_defaults(sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); if (prop->visible.tri != no) return prop; } return NULL; } struct property *sym_get_range_prop(struct symbol *sym) { struct property *prop; for_all_properties(sym, prop, P_RANGE) { prop->visible.tri = expr_calc_value(prop->visible.expr); if (prop->visible.tri != no) return prop; } return NULL; } static long long sym_get_range_val(struct symbol *sym, int base) { sym_calc_value(sym); switch (sym->type) { case S_INT: base = 10; break; case S_HEX: base = 16; break; default: break; } return strtoll(sym->curr.val, NULL, base); } static void sym_validate_range(struct symbol *sym) { struct property *prop; int base; long long val, val2; char str[64]; switch (sym->type) { case S_INT: base = 10; break; case S_HEX: base = 16; break; default: return; } prop = sym_get_range_prop(sym); if (!prop) return; val = strtoll(sym->curr.val, NULL, base); val2 = sym_get_range_val(prop->expr->left.sym, base); if (val >= val2) { val2 = sym_get_range_val(prop->expr->right.sym, base); if (val <= val2) return; } if (sym->type == S_INT) sprintf(str, "%lld", val2); else sprintf(str, "0x%llx", val2); sym->curr.val = xstrdup(str); } static void sym_set_changed(struct symbol *sym) { struct property *prop; sym->flags |= SYMBOL_CHANGED; for (prop = sym->prop; prop; prop = prop->next) { if (prop->menu) prop->menu->flags |= MENU_CHANGED; } } static void sym_set_all_changed(void) { struct symbol *sym; int i; for_all_symbols(i, sym) sym_set_changed(sym); } static void sym_calc_visibility(struct symbol *sym) { struct property *prop; struct symbol *choice_sym = NULL; tristate tri; /* any prompt visible? */ tri = no; if (sym_is_choice_value(sym)) choice_sym = prop_get_symbol(sym_get_choice_prop(sym)); for_all_prompts(sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); /* * Tristate choice_values with visibility 'mod' are * not visible if the corresponding choice's value is * 'yes'. */ if (choice_sym && sym->type == S_TRISTATE && prop->visible.tri == mod && choice_sym->curr.tri == yes) prop->visible.tri = no; tri = EXPR_OR(tri, prop->visible.tri); } if (tri == mod && (sym->type != S_TRISTATE || modules_val == no)) tri = yes; if (sym->visible != tri) { sym->visible = tri; sym_set_changed(sym); } if (sym_is_choice_value(sym)) return; /* defaulting to "yes" if no explicit "depends on" are given */ tri = yes; if (sym->dir_dep.expr) tri = expr_calc_value(sym->dir_dep.expr); if (tri == mod && sym_get_type(sym) == S_BOOLEAN) tri = yes; if (sym->dir_dep.tri != tri) { sym->dir_dep.tri = tri; sym_set_changed(sym); } tri = no; if (sym->rev_dep.expr) tri = expr_calc_value(sym->rev_dep.expr); if (tri == mod && sym_get_type(sym) == S_BOOLEAN) tri = yes; if (sym->rev_dep.tri != tri) { sym->rev_dep.tri = tri; sym_set_changed(sym); } tri = no; if (sym->implied.expr) tri = expr_calc_value(sym->implied.expr); if (tri == mod && sym_get_type(sym) == S_BOOLEAN) tri = yes; if (sym->implied.tri != tri) { sym->implied.tri = tri; sym_set_changed(sym); } } /* * Find the default symbol for a choice. * First try the default values for the choice symbol * Next locate the first visible choice value * Return NULL if none was found */ struct symbol *sym_choice_default(struct symbol *sym) { struct symbol *def_sym; struct property *prop; struct expr *e; /* any of the defaults visible? */ for_all_defaults(sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); if (prop->visible.tri == no) continue; def_sym = prop_get_symbol(prop); if (def_sym->visible != no) return def_sym; } /* just get the first visible value */ prop = sym_get_choice_prop(sym); expr_list_for_each_sym(prop->expr, e, def_sym) if (def_sym->visible != no) return def_sym; /* failed to locate any defaults */ return NULL; } static struct symbol *sym_calc_choice(struct symbol *sym) { struct symbol *def_sym; struct property *prop; struct expr *e; int flags; /* first calculate all choice values' visibilities */ flags = sym->flags; prop = sym_get_choice_prop(sym); expr_list_for_each_sym(prop->expr, e, def_sym) { sym_calc_visibility(def_sym); if (def_sym->visible != no) flags &= def_sym->flags; } sym->flags &= flags | ~SYMBOL_DEF_USER; /* is the user choice visible? */ def_sym = sym->def[S_DEF_USER].val; if (def_sym && def_sym->visible != no) return def_sym; def_sym = sym_choice_default(sym); if (def_sym == NULL) /* no choice? reset tristate value */ sym->curr.tri = no; return def_sym; } static void sym_warn_unmet_dep(struct symbol *sym) { struct gstr gs = str_new(); str_printf(&gs, "\nWARNING: unmet direct dependencies detected for %s\n", sym->name); str_printf(&gs, " Depends on [%c]: ", sym->dir_dep.tri == mod ? 'm' : 'n'); expr_gstr_print(sym->dir_dep.expr, &gs); str_printf(&gs, "\n"); expr_gstr_print_revdep(sym->rev_dep.expr, &gs, yes, " Selected by [y]:\n"); expr_gstr_print_revdep(sym->rev_dep.expr, &gs, mod, " Selected by [m]:\n"); fputs(str_get(&gs), stderr); } void sym_calc_value(struct symbol *sym) { struct symbol_value newval, oldval; struct property *prop; struct expr *e; if (!sym) return; if (sym->flags & SYMBOL_VALID) return; if (sym_is_choice_value(sym) && sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) { sym->flags &= ~SYMBOL_NEED_SET_CHOICE_VALUES; prop = sym_get_choice_prop(sym); sym_calc_value(prop_get_symbol(prop)); } sym->flags |= SYMBOL_VALID; oldval = sym->curr; switch (sym->type) { case S_INT: case S_HEX: case S_STRING: newval = symbol_empty.curr; break; case S_BOOLEAN: case S_TRISTATE: newval = symbol_no.curr; break; default: sym->curr.val = sym->name; sym->curr.tri = no; return; } sym->flags &= ~SYMBOL_WRITE; sym_calc_visibility(sym); if (sym->visible != no) sym->flags |= SYMBOL_WRITE; /* set default if recursively called */ sym->curr = newval; switch (sym_get_type(sym)) { case S_BOOLEAN: case S_TRISTATE: if (sym_is_choice_value(sym) && sym->visible == yes) { prop = sym_get_choice_prop(sym); newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no; } else { if (sym->visible != no) { /* if the symbol is visible use the user value * if available, otherwise try the default value */ if (sym_has_value(sym)) { newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri, sym->visible); goto calc_newval; } } if (sym->rev_dep.tri != no) sym->flags |= SYMBOL_WRITE; if (!sym_is_choice(sym)) { prop = sym_get_default_prop(sym); if (prop) { newval.tri = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri); if (newval.tri != no) sym->flags |= SYMBOL_WRITE; } if (sym->implied.tri != no) { sym->flags |= SYMBOL_WRITE; newval.tri = EXPR_OR(newval.tri, sym->implied.tri); newval.tri = EXPR_AND(newval.tri, sym->dir_dep.tri); } } calc_newval: if (sym->dir_dep.tri < sym->rev_dep.tri) sym_warn_unmet_dep(sym); newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri); } if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN) newval.tri = yes; break; case S_STRING: case S_HEX: case S_INT: if (sym->visible != no && sym_has_value(sym)) { newval.val = sym->def[S_DEF_USER].val; break; } prop = sym_get_default_prop(sym); if (prop) { struct symbol *ds = prop_get_symbol(prop); if (ds) { sym->flags |= SYMBOL_WRITE; sym_calc_value(ds); newval.val = ds->curr.val; } } break; default: ; } sym->curr = newval; if (sym_is_choice(sym) && newval.tri == yes) sym->curr.val = sym_calc_choice(sym); sym_validate_range(sym); if (memcmp(&oldval, &sym->curr, sizeof(oldval))) { sym_set_changed(sym); if (modules_sym == sym) { sym_set_all_changed(); modules_val = modules_sym->curr.tri; } } if (sym_is_choice(sym)) { struct symbol *choice_sym; prop = sym_get_choice_prop(sym); expr_list_for_each_sym(prop->expr, e, choice_sym) { if ((sym->flags & SYMBOL_WRITE) && choice_sym->visible != no) choice_sym->flags |= SYMBOL_WRITE; if (sym->flags & SYMBOL_CHANGED) sym_set_changed(choice_sym); } } if (sym->flags & SYMBOL_NO_WRITE) sym->flags &= ~SYMBOL_WRITE; if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) set_all_choice_values(sym); } void sym_clear_all_valid(void) { struct symbol *sym; int i; for_all_symbols(i, sym) sym->flags &= ~SYMBOL_VALID; sym_add_change_count(1); sym_calc_value(modules_sym); } bool sym_tristate_within_range(struct symbol *sym, tristate val) { int type = sym_get_type(sym); if (sym->visible == no) return false; if (type != S_BOOLEAN && type != S_TRISTATE) return false; if (type == S_BOOLEAN && val == mod) return false; if (sym->visible <= sym->rev_dep.tri) return false; if (sym_is_choice_value(sym) && sym->visible == yes) return val == yes; return val >= sym->rev_dep.tri && val <= sym->visible; } bool sym_set_tristate_value(struct symbol *sym, tristate val) { tristate oldval = sym_get_tristate_value(sym); if (oldval != val && !sym_tristate_within_range(sym, val)) return false; if (!(sym->flags & SYMBOL_DEF_USER)) { sym->flags |= SYMBOL_DEF_USER; sym_set_changed(sym); } /* * setting a choice value also resets the new flag of the choice * symbol and all other choice values. */ if (sym_is_choice_value(sym) && val == yes) { struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); struct property *prop; struct expr *e; cs->def[S_DEF_USER].val = sym; cs->flags |= SYMBOL_DEF_USER; prop = sym_get_choice_prop(cs); for (e = prop->expr; e; e = e->left.expr) { if (e->right.sym->visible != no) e->right.sym->flags |= SYMBOL_DEF_USER; } } sym->def[S_DEF_USER].tri = val; if (oldval != val) sym_clear_all_valid(); return true; } tristate sym_toggle_tristate_value(struct symbol *sym) { tristate oldval, newval; oldval = newval = sym_get_tristate_value(sym); do { switch (newval) { case no: newval = mod; break; case mod: newval = yes; break; case yes: newval = no; break; } if (sym_set_tristate_value(sym, newval)) break; } while (oldval != newval); return newval; } bool sym_string_valid(struct symbol *sym, const char *str) { signed char ch; switch (sym->type) { case S_STRING: return true; case S_INT: ch = *str++; if (ch == '-') ch = *str++; if (!isdigit(ch)) return false; if (ch == '0' && *str != 0) return false; while ((ch = *str++)) { if (!isdigit(ch)) return false; } return true; case S_HEX: if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) str += 2; ch = *str++; do { if (!isxdigit(ch)) return false; } while ((ch = *str++)); return true; case S_BOOLEAN: case S_TRISTATE: switch (str[0]) { case 'y': case 'Y': case 'm': case 'M': case 'n': case 'N': return true; } return false; default: return false; } } bool sym_string_within_range(struct symbol *sym, const char *str) { struct property *prop; long long val; switch (sym->type) { case S_STRING: return sym_string_valid(sym, str); case S_INT: if (!sym_string_valid(sym, str)) return false; prop = sym_get_range_prop(sym); if (!prop) return true; val = strtoll(str, NULL, 10); return val >= sym_get_range_val(prop->expr->left.sym, 10) && val <= sym_get_range_val(prop->expr->right.sym, 10); case S_HEX: if (!sym_string_valid(sym, str)) return false; prop = sym_get_range_prop(sym); if (!prop) return true; val = strtoll(str, NULL, 16); return val >= sym_get_range_val(prop->expr->left.sym, 16) && val <= sym_get_range_val(prop->expr->right.sym, 16); case S_BOOLEAN: case S_TRISTATE: switch (str[0]) { case 'y': case 'Y': return sym_tristate_within_range(sym, yes); case 'm': case 'M': return sym_tristate_within_range(sym, mod); case 'n': case 'N': return sym_tristate_within_range(sym, no); } return false; default: return false; } } bool sym_set_string_value(struct symbol *sym, const char *newval) { const char *oldval; char *val; int size; switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: switch (newval[0]) { case 'y': case 'Y': return sym_set_tristate_value(sym, yes); case 'm': case 'M': return sym_set_tristate_value(sym, mod); case 'n': case 'N': return sym_set_tristate_value(sym, no); } return false; default: ; } if (!sym_string_within_range(sym, newval)) return false; if (!(sym->flags & SYMBOL_DEF_USER)) { sym->flags |= SYMBOL_DEF_USER; sym_set_changed(sym); } oldval = sym->def[S_DEF_USER].val; size = strlen(newval) + 1; if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { size += 2; sym->def[S_DEF_USER].val = val = xmalloc(size); *val++ = '0'; *val++ = 'x'; } else if (!oldval || strcmp(oldval, newval)) sym->def[S_DEF_USER].val = val = xmalloc(size); else return true; strcpy(val, newval); free((void *)oldval); sym_clear_all_valid(); return true; } /* * Find the default value associated to a symbol. * For tristate symbol handle the modules=n case * in which case "m" becomes "y". * If the symbol does not have any default then fallback * to the fixed default values. */ const char *sym_get_string_default(struct symbol *sym) { struct property *prop; struct symbol *ds; const char *str; tristate val; sym_calc_visibility(sym); sym_calc_value(modules_sym); val = symbol_no.curr.tri; str = symbol_empty.curr.val; /* If symbol has a default value look it up */ prop = sym_get_default_prop(sym); if (prop != NULL) { switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: /* The visibility may limit the value from yes => mod */ val = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri); break; default: /* * The following fails to handle the situation * where a default value is further limited by * the valid range. */ ds = prop_get_symbol(prop); if (ds != NULL) { sym_calc_value(ds); str = (const char *)ds->curr.val; } } } /* Handle select statements */ val = EXPR_OR(val, sym->rev_dep.tri); /* transpose mod to yes if modules are not enabled */ if (val == mod) if (!sym_is_choice_value(sym) && modules_sym->curr.tri == no) val = yes; /* transpose mod to yes if type is bool */ if (sym->type == S_BOOLEAN && val == mod) val = yes; /* adjust the default value if this symbol is implied by another */ if (val < sym->implied.tri) val = sym->implied.tri; switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: switch (val) { case no: return "n"; case mod: return "m"; case yes: return "y"; } case S_INT: case S_HEX: return str; case S_STRING: return str; case S_UNKNOWN: break; } return ""; } const char *sym_get_string_value(struct symbol *sym) { tristate val; switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: val = sym_get_tristate_value(sym); switch (val) { case no: return "n"; case mod: sym_calc_value(modules_sym); return (modules_sym->curr.tri == no) ? "n" : "m"; case yes: return "y"; } break; default: ; } return (const char *)sym->curr.val; } bool sym_is_changeable(struct symbol *sym) { return sym->visible > sym->rev_dep.tri; } static unsigned strhash(const char *s) { /* fnv32 hash */ unsigned hash = 2166136261U; for (; *s; s++) hash = (hash ^ *s) * 0x01000193; return hash; } struct symbol *sym_lookup(const char *name, int flags) { struct symbol *symbol; char *new_name; int hash; if (name) { if (name[0] && !name[1]) { switch (name[0]) { case 'y': return &symbol_yes; case 'm': return &symbol_mod; case 'n': return &symbol_no; } } hash = strhash(name) % SYMBOL_HASHSIZE; for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { if (symbol->name && !strcmp(symbol->name, name) && (flags ? symbol->flags & flags : !(symbol->flags & (SYMBOL_CONST|SYMBOL_CHOICE)))) return symbol; } new_name = xstrdup(name); } else { new_name = NULL; hash = 0; } symbol = xmalloc(sizeof(*symbol)); memset(symbol, 0, sizeof(*symbol)); symbol->name = new_name; symbol->type = S_UNKNOWN; symbol->flags = flags; symbol->next = symbol_hash[hash]; symbol_hash[hash] = symbol; return symbol; } struct symbol *sym_find(const char *name) { struct symbol *symbol = NULL; int hash = 0; if (!name) return NULL; if (name[0] && !name[1]) { switch (name[0]) { case 'y': return &symbol_yes; case 'm': return &symbol_mod; case 'n': return &symbol_no; } } hash = strhash(name) % SYMBOL_HASHSIZE; for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { if (symbol->name && !strcmp(symbol->name, name) && !(symbol->flags & SYMBOL_CONST)) break; } return symbol; } const char *sym_escape_string_value(const char *in) { const char *p; size_t reslen; char *res; size_t l; reslen = strlen(in) + strlen("\"\"") + 1; p = in; for (;;) { l = strcspn(p, "\"\\"); p += l; if (p[0] == '\0') break; reslen++; p++; } res = xmalloc(reslen); res[0] = '\0'; strcat(res, "\""); p = in; for (;;) { l = strcspn(p, "\"\\"); strncat(res, p, l); p += l; if (p[0] == '\0') break; strcat(res, "\\"); strncat(res, p++, 1); } strcat(res, "\""); return res; } struct sym_match { struct symbol *sym; off_t so, eo; }; /* Compare matched symbols as thus: * - first, symbols that match exactly * - then, alphabetical sort */ static int sym_rel_comp(const void *sym1, const void *sym2) { const struct sym_match *s1 = sym1; const struct sym_match *s2 = sym2; int exact1, exact2; /* Exact match: * - if matched length on symbol s1 is the length of that symbol, * then this symbol should come first; * - if matched length on symbol s2 is the length of that symbol, * then this symbol should come first. * Note: since the search can be a regexp, both symbols may match * exactly; if this is the case, we can't decide which comes first, * and we fallback to sorting alphabetically. */ exact1 = (s1->eo - s1->so) == strlen(s1->sym->name); exact2 = (s2->eo - s2->so) == strlen(s2->sym->name); if (exact1 && !exact2) return -1; if (!exact1 && exact2) return 1; /* As a fallback, sort symbols alphabetically */ return strcmp(s1->sym->name, s2->sym->name); } struct symbol **sym_re_search(const char *pattern) { struct symbol *sym, **sym_arr = NULL; struct sym_match *sym_match_arr = NULL; int i, cnt, size; regex_t re; regmatch_t match[1]; cnt = size = 0; /* Skip if empty */ if (strlen(pattern) == 0) return NULL; if (regcomp(&re, pattern, REG_EXTENDED|REG_ICASE)) return NULL; for_all_symbols(i, sym) { if (sym->flags & SYMBOL_CONST || !sym->name) continue; if (regexec(&re, sym->name, 1, match, 0)) continue; if (cnt >= size) { void *tmp; size += 16; tmp = realloc(sym_match_arr, size * sizeof(struct sym_match)); if (!tmp) goto sym_re_search_free; sym_match_arr = tmp; } sym_calc_value(sym); /* As regexec returned 0, we know we have a match, so * we can use match[0].rm_[se]o without further checks */ sym_match_arr[cnt].so = match[0].rm_so; sym_match_arr[cnt].eo = match[0].rm_eo; sym_match_arr[cnt++].sym = sym; } if (sym_match_arr) { qsort(sym_match_arr, cnt, sizeof(struct sym_match), sym_rel_comp); sym_arr = malloc((cnt+1) * sizeof(struct symbol *)); if (!sym_arr) goto sym_re_search_free; for (i = 0; i < cnt; i++) sym_arr[i] = sym_match_arr[i].sym; sym_arr[cnt] = NULL; } sym_re_search_free: /* sym_match_arr can be NULL if no match, but free(NULL) is OK */ free(sym_match_arr); regfree(&re); return sym_arr; } /* * When we check for recursive dependencies we use a stack to save * current state so we can print out relevant info to user. * The entries are located on the call stack so no need to free memory. * Note insert() remove() must always match to properly clear the stack. */ static struct dep_stack { struct dep_stack *prev, *next; struct symbol *sym; struct property *prop; struct expr **expr; } *check_top; static void dep_stack_insert(struct dep_stack *stack, struct symbol *sym) { memset(stack, 0, sizeof(*stack)); if (check_top) check_top->next = stack; stack->prev = check_top; stack->sym = sym; check_top = stack; } static void dep_stack_remove(void) { check_top = check_top->prev; if (check_top) check_top->next = NULL; } /* * Called when we have detected a recursive dependency. * check_top point to the top of the stact so we use * the ->prev pointer to locate the bottom of the stack. */ static void sym_check_print_recursive(struct symbol *last_sym) { struct dep_stack *stack; struct symbol *sym, *next_sym; struct menu *menu = NULL; struct property *prop; struct dep_stack cv_stack; if (sym_is_choice_value(last_sym)) { dep_stack_insert(&cv_stack, last_sym); last_sym = prop_get_symbol(sym_get_choice_prop(last_sym)); } for (stack = check_top; stack != NULL; stack = stack->prev) if (stack->sym == last_sym) break; if (!stack) { fprintf(stderr, "unexpected recursive dependency error\n"); return; } for (; stack; stack = stack->next) { sym = stack->sym; next_sym = stack->next ? stack->next->sym : last_sym; prop = stack->prop; if (prop == NULL) prop = stack->sym->prop; /* for choice values find the menu entry (used below) */ if (sym_is_choice(sym) || sym_is_choice_value(sym)) { for (prop = sym->prop; prop; prop = prop->next) { menu = prop->menu; if (prop->menu) break; } } if (stack->sym == last_sym) fprintf(stderr, "%s:%d:error: recursive dependency detected!\n", prop->file->name, prop->lineno); if (sym_is_choice(sym)) { fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n", menu->file->name, menu->lineno, sym->name ? sym->name : "", next_sym->name ? next_sym->name : ""); } else if (sym_is_choice_value(sym)) { fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n", menu->file->name, menu->lineno, sym->name ? sym->name : "", next_sym->name ? next_sym->name : ""); } else if (stack->expr == &sym->dir_dep.expr) { fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n", prop->file->name, prop->lineno, sym->name ? sym->name : "", next_sym->name ? next_sym->name : ""); } else if (stack->expr == &sym->rev_dep.expr) { fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n", prop->file->name, prop->lineno, sym->name ? sym->name : "", next_sym->name ? next_sym->name : ""); } else if (stack->expr == &sym->implied.expr) { fprintf(stderr, "%s:%d:\tsymbol %s is implied by %s\n", prop->file->name, prop->lineno, sym->name ? sym->name : "", next_sym->name ? next_sym->name : ""); } else if (stack->expr) { fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n", prop->file->name, prop->lineno, sym->name ? sym->name : "", prop_get_type_name(prop->type), next_sym->name ? next_sym->name : ""); } else { fprintf(stderr, "%s:%d:\tsymbol %s %s is visible depending on %s\n", prop->file->name, prop->lineno, sym->name ? sym->name : "", prop_get_type_name(prop->type), next_sym->name ? next_sym->name : ""); } } fprintf(stderr, "For a resolution refer to Documentation/kbuild/kconfig-language.rst\n" "subsection \"Kconfig recursive dependency limitations\"\n" "\n"); if (check_top == &cv_stack) dep_stack_remove(); } static struct symbol *sym_check_expr_deps(struct expr *e) { struct symbol *sym; if (!e) return NULL; switch (e->type) { case E_OR: case E_AND: sym = sym_check_expr_deps(e->left.expr); if (sym) return sym; return sym_check_expr_deps(e->right.expr); case E_NOT: return sym_check_expr_deps(e->left.expr); case E_EQUAL: case E_GEQ: case E_GTH: case E_LEQ: case E_LTH: case E_UNEQUAL: sym = sym_check_deps(e->left.sym); if (sym) return sym; return sym_check_deps(e->right.sym); case E_SYMBOL: return sym_check_deps(e->left.sym); default: break; } fprintf(stderr, "Oops! How to check %d?\n", e->type); return NULL; } /* return NULL when dependencies are OK */ static struct symbol *sym_check_sym_deps(struct symbol *sym) { struct symbol *sym2; struct property *prop; struct dep_stack stack; dep_stack_insert(&stack, sym); stack.expr = &sym->dir_dep.expr; sym2 = sym_check_expr_deps(sym->dir_dep.expr); if (sym2) goto out; stack.expr = &sym->rev_dep.expr; sym2 = sym_check_expr_deps(sym->rev_dep.expr); if (sym2) goto out; stack.expr = &sym->implied.expr; sym2 = sym_check_expr_deps(sym->implied.expr); if (sym2) goto out; stack.expr = NULL; for (prop = sym->prop; prop; prop = prop->next) { if (prop->type == P_CHOICE || prop->type == P_SELECT || prop->type == P_IMPLY) continue; stack.prop = prop; sym2 = sym_check_expr_deps(prop->visible.expr); if (sym2) break; if (prop->type != P_DEFAULT || sym_is_choice(sym)) continue; stack.expr = &prop->expr; sym2 = sym_check_expr_deps(prop->expr); if (sym2) break; stack.expr = NULL; } out: dep_stack_remove(); return sym2; } static struct symbol *sym_check_choice_deps(struct symbol *choice) { struct symbol *sym, *sym2; struct property *prop; struct expr *e; struct dep_stack stack; dep_stack_insert(&stack, choice); prop = sym_get_choice_prop(choice); expr_list_for_each_sym(prop->expr, e, sym) sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); choice->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); sym2 = sym_check_sym_deps(choice); choice->flags &= ~SYMBOL_CHECK; if (sym2) goto out; expr_list_for_each_sym(prop->expr, e, sym) { sym2 = sym_check_sym_deps(sym); if (sym2) break; } out: expr_list_for_each_sym(prop->expr, e, sym) sym->flags &= ~SYMBOL_CHECK; if (sym2 && sym_is_choice_value(sym2) && prop_get_symbol(sym_get_choice_prop(sym2)) == choice) sym2 = choice; dep_stack_remove(); return sym2; } struct symbol *sym_check_deps(struct symbol *sym) { struct symbol *sym2; struct property *prop; if (sym->flags & SYMBOL_CHECK) { sym_check_print_recursive(sym); return sym; } if (sym->flags & SYMBOL_CHECKED) return NULL; if (sym_is_choice_value(sym)) { struct dep_stack stack; /* for choice groups start the check with main choice symbol */ dep_stack_insert(&stack, sym); prop = sym_get_choice_prop(sym); sym2 = sym_check_deps(prop_get_symbol(prop)); dep_stack_remove(); } else if (sym_is_choice(sym)) { sym2 = sym_check_choice_deps(sym); } else { sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); sym2 = sym_check_sym_deps(sym); sym->flags &= ~SYMBOL_CHECK; } return sym2; } struct symbol *prop_get_symbol(struct property *prop) { if (prop->expr && (prop->expr->type == E_SYMBOL || prop->expr->type == E_LIST)) return prop->expr->left.sym; return NULL; } const char *prop_get_type_name(enum prop_type type) { switch (type) { case P_PROMPT: return "prompt"; case P_COMMENT: return "comment"; case P_MENU: return "menu"; case P_DEFAULT: return "default"; case P_CHOICE: return "choice"; case P_SELECT: return "select"; case P_IMPLY: return "imply"; case P_RANGE: return "range"; case P_SYMBOL: return "symbol"; case P_UNKNOWN: break; } return "unknown"; } crust-0.5/3rdparty/kconfig/util.c000066400000000000000000000042421414152222500170270ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2002-2005 Roman Zippel * Copyright (C) 2002-2005 Sam Ravnborg */ #include #include #include #include "lkc.h" /* file already present in list? If not add it */ struct file *file_lookup(const char *name) { struct file *file; for (file = file_list; file; file = file->next) { if (!strcmp(name, file->name)) { return file; } } file = xmalloc(sizeof(*file)); memset(file, 0, sizeof(*file)); file->name = xstrdup(name); file->next = file_list; file_list = file; return file; } /* Allocate initial growable string */ struct gstr str_new(void) { struct gstr gs; gs.s = xmalloc(sizeof(char) * 64); gs.len = 64; gs.max_width = 0; strcpy(gs.s, "\0"); return gs; } /* Free storage for growable string */ void str_free(struct gstr *gs) { if (gs->s) free(gs->s); gs->s = NULL; gs->len = 0; } /* Append to growable string */ void str_append(struct gstr *gs, const char *s) { size_t l; if (s) { l = strlen(gs->s) + strlen(s) + 1; if (l > gs->len) { gs->s = xrealloc(gs->s, l); gs->len = l; } strcat(gs->s, s); } } /* Append printf formatted string to growable string */ void str_printf(struct gstr *gs, const char *fmt, ...) { va_list ap; char s[10000]; /* big enough... */ va_start(ap, fmt); vsnprintf(s, sizeof(s), fmt, ap); str_append(gs, s); va_end(ap); } /* Retrieve value of growable string */ const char *str_get(struct gstr *gs) { return gs->s; } void *xmalloc(size_t size) { void *p = malloc(size); if (p) return p; fprintf(stderr, "Out of memory.\n"); exit(1); } void *xcalloc(size_t nmemb, size_t size) { void *p = calloc(nmemb, size); if (p) return p; fprintf(stderr, "Out of memory.\n"); exit(1); } void *xrealloc(void *p, size_t size) { p = realloc(p, size); if (p) return p; fprintf(stderr, "Out of memory.\n"); exit(1); } char *xstrdup(const char *s) { char *p; p = strdup(s); if (p) return p; fprintf(stderr, "Out of memory.\n"); exit(1); } char *xstrndup(const char *s, size_t n) { char *p; p = strndup(s, n); if (p) return p; fprintf(stderr, "Out of memory.\n"); exit(1); } crust-0.5/CONTRIBUTING.md000066400000000000000000000171371414152222500147560ustar00rootroot00000000000000# Contribution guidelines Thank you for expressing interest in contributing to the development and/or maintenance of the Crust firmware. This document contains guidelines and procedures for contributing to the project. ## Code of conduct By contributing to Crust firmware, you are entering into an agreement to uphold the project's [Code of Conduct][conduct]. Please review that document. ## Issue tracking Bugs and features are tracked through the GitHub [issues page][issues]. When filing issues, please follow the provided issue template. ### Labels Labels are used to indicate the lifecycle of all Crust firmware issues. Labels are prefixed with letters corresponding to the chart below. | Prefix | Description |------------------|----------------------------------------------------------- | Area (A) | Indicates the area of the project impacted. | Difficulty (D) | Indicates the expected difficulty of an issue. | Resolution (R) | Indicates the resolution of an issue. | Status (S) | Indicates the status of an issue. | Type (T) | Indicates the type of an issue. Additionally, the labels "good first issue" and "help wanted" may be applied to an issue or pull request. An issue labeled with "good first issue" is an issue that is recommended for new contributors. An issue labeled with "help wanted" is an issue that project maintainers request community help resolving. ## Submitting a patch The Crust firmware project accepts patches through the use of GitHub pull requests. It is recommended that pull requests correspond to an issue on the Crust firmware [issues page][issues]. Pull requests submitted _without_ a corresponding issue will require more review to understand the intent behind the code change. ### Commits All contributions to this project must be released under the open-source license used by the project. To help ensure this, all commits you make must be accompanied by your real name and a `Signed-off-by` tag asserting authorship and permission to contribute, per the [Developer Certificate of Origin][dco]. This tag can be added automatically by git using `git commit -s`. Each change submitted in a pull request that addresses a different scope of the project should be contained within its own commit. For example, a pull request adding support for a new hardware component could include the following four commits: 1. The first commit should add the headers and source files for the generic driver class, if one is being added. (e.g. `drivers//.c` and `include/drivers/.h`). 2. The second commit should add the driver and its header (e.g. `drivers//.c` and `include/drivers//.h`). 3. The third commit may use the device from common code (e.g. in `common/system.c` or `common/scpi_cmds.c`). 4. The fourth commit would enable the device in Kconfig on the appropriate boards and platforms. Changes addressing feedback from project maintainers should be squashed into existing commits rather than being included at the end of a pull request as a new commit. ### Continuous integration testing All pull requests are built and tested with Travis CI upon submission. To ensure that your changes will pass the CI build, the following process should be used: - Verify the source code style by running `make check-format`. (This requires installing [Uncrustify][uncrustify]. See below.) - Build the firmware using several `defconfig`s for various platforms. ## Architecture This firmware is designed to be flexible yet extremely lightweight. It borrows heavily from ideas in both Linux and ATF for its layout and driver model. The code is divided into directories based on major function: - `configs`: These files contain configuration for each board supported by this firmware. They determine which subdirectory of `platform` is used and which devices are enabled. - `common`: Files in this directory contain the main logic of the firmware, which involves power state sequencing and handling SCPI commands. - `drivers`: This directory contains a subdirectory for each class of drivers. - `drivers/`: These directories contain all of the drivers of a class, as well as generic dispatch code (in `drivers//.c`). - `include`: This directory contains headers for code in the rest of the firmware, as well as the definitions for a freestanding C implementation. - `lib`: This directory contains standalone utility code. - `platform`: This directory contains a subdirectory for each supported platform, which refers to a family of SoCs with a similar programming interface. - `platform//include`: This directory contains headers with platform-specific macro definitions, such as memory addresses of devices and register layouts that change between platforms. - `scripts`: These files are used to assist in building and linking the firmware, but contain no code themselves. - `tools`: Each file here is a self-contained program intended to be run on the host machine (in Linux userspace on the ARM cores) to gather information, manipulate hardware, load firmware, or for other reasons. ## Formatting Crust firmware uses Uncrustify to format all source code files. Running `make format` will format all project files according to a predefined style guide. The following are _general_ formatting guidelines: - Lines should be no longer than 79 characters plus the trailing newline character. - Use tabs for indentation and spaces for alignment for all C source code and header files. - Trim all trailing whitespace. - Where multiple words must be combined into one, use hyphens in strings, filenames, and documentation; however, use underscores in function, variable names and header guards. ### Writing headers - Header files act as APIs and are reserved for functions that will be exposed to the rest of the project. Macros, `struct` definitions, and function declarations that are only utilized by a driver or module belong in the corresponding implementation file, rather than the header file. - Public macros, function prototypes, and declarations should be documented according to [Doxygen JavaDoc-style comment blocks][doxygen]. - Functions defined in assembly should also be declared in a header, unless they are not intended to be called directly (e.g. `__udivsi3`). - Ideally, the header should be named the same as the driver or source file, except that it should be placed inside the include hierarchy. - The header guard should be the same as the path you would need to put in your `#include` directive, but with the following changes: - All letters should be uppercase. - All hyphens, slashes, and periods should be replaced by underscores. ### Assembly Assembly files are preprocessed, so they should use the file extension `.S` (and this is the only extension the build system looks for). They should not have the same path as a C file (excluding the extension), as the build system cannot disambiguate the two. Assembly should be formatted as follows: - Local control-flow labels should be numbered, not named. - Local data labels should be named descriptively. - Global data and functions should use the `data` and `func`/`endfunc` macros from `macros.S`. - Indent the instruction by one tab. - Indent the first operand with one tab. - Separate following operands with a single space. - Use `#` to start comments. - Align comments at 40 columns with tabs. [conduct]: docs/code_of_conduct.md [dco]: https://developercertificate.org [doxygen]: https://www.stack.nl/~dimitri/doxygen/manual/docblocks.html [issues]: https://github.com/crust-firmware/crust/issues [uncrustify]: https://github.com/uncrustify/uncrustify crust-0.5/Doxyfile000066400000000000000000000121151414152222500142220ustar00rootroot00000000000000# Doxyfile 1.8.16 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "crust" PROJECT_BRIEF = "SCP firmware for sunxi SoCs" OUTPUT_DIRECTORY = @OBJ@/docs BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ALWAYS_DETAILED_SEC = NO FULL_PATH_NAMES = YES JAVADOC_AUTOBRIEF = YES JAVADOC_BANNER = YES TAB_SIZE = 8 OPTIMIZE_OUTPUT_FOR_C = YES MARKDOWN_SUPPORT = YES AUTOLINK_SUPPORT = YES INLINE_SIMPLE_STRUCTS = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = YES SORT_GROUP_NAMES = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES MAX_INITIALIZER_LINES = 0 SHOW_USED_FILES = NO SHOW_FILES = YES SHOW_NAMESPACES = NO #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES WARN_AS_ERROR = NO #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = @SRC@/common \ @SRC@/drivers \ @SRC@/include \ @SRC@/lib \ @SRC@/platform \ @DOCS@ INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.c \ *.h \ *.md RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = YES EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = USE_MDFILE_AS_MAINPAGE = README.md #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_TIMESTAMP = NO HTML_DYNAMIC_MENUS = NO HTML_DYNAMIC_SECTIONS = YES SEARCHENGINE = YES SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # Configuration options related to the other outputs #--------------------------------------------------------------------------- GENERATE_LATEX = NO GENERATE_RTF = NO GENERATE_MAN = NO GENERATE_XML = NO GENERATE_DOCBOOK = NO GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES INCLUDE_PATH = @SRC@/include/drivers PREDEFINED = ATTRIBUTE(...)= UNUSED= WEAK= SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- HAVE_DOT = YES DOT_FONTNAME = sans-serif INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = YES DIRECTORY_GRAPH = NO DOT_IMAGE_FORMAT = svg INTERACTIVE_SVG = NO DOT_GRAPH_MAX_NODES = 100 DOT_TRANSPARENT = YES GENERATE_LEGEND = YES DOT_CLEANUP = YES crust-0.5/Kconfig000066400000000000000000000003741414152222500140230ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # mainmenu "Crust Firmware Configuration" source "arch/Kconfig" source "platform/Kconfig" source "drivers/Kconfig" source "common/Kconfig" crust-0.5/LICENSE.md000066400000000000000000000543571414152222500141360ustar00rootroot00000000000000# Crust Firmware Licensing The Crust firmware itself is dual-licensed as BSD-3-clause or GPL-2.0-only. You may use and redistribute it under either set of license terms. It uses some third-party BSD-1-Clause and MIT-licensed headers; those licenses are compatible with either main license. The Kconfig configuration system is licensed as GPL-2.0-only. However, it is a build time dependency only, and all GPL-2.0-only files are restricted to the `3rdparty/kconfig` directory. ## BSD 1-clause License Copyright © 2014 OpenRISC Project Maintainers. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following condition is met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --- ## BSD 3-clause License Copyright © 2013-2017, ARM Limited and Contributors. All rights reserved. Copyright © 2017-2021 The Crust Firmware Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --- ## GNU General Public License Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. ### Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. ### Terms and Conditions for Copying, Distribution and Modification 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. ### NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ### End of Terms and Conditions ### How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free software, and you are welcome to redistribute it under certain conditions; type 'show c' for details. The hypothetical commands 'show w' and 'show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than 'show w' and 'show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program 'Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the [GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl.html) instead of this License. --- ## MIT License Copyright © 2005-2014 Rich Felker, et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. crust-0.5/Makefile000066400000000000000000000120461414152222500141570ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # SRC = . OBJ = build TGT = $(OBJ)/scp BUILDAR = ar BUILDCC = cc HOST_COMPILE ?= aarch64-linux-musl- HOSTAR = $(HOST_COMPILE)gcc-ar HOSTCC = $(HOST_COMPILE)gcc AR = $(CROSS_COMPILE)gcc-ar CC = $(CROSS_COMPILE)gcc OBJCOPY = $(CROSS_COMPILE)objcopy LEX = lex YACC = bison COMMON_CFLAGS = -Os -pipe -std=c11 \ -fdata-sections \ -ffunction-sections \ -fno-builtin \ -fno-common \ -fvar-tracking-assignments \ -g$(if $(CONFIG_DEBUG_INFO),gdb,0) \ -Wall -Wextra -Wformat=2 -Wpedantic -Wshadow \ -Werror=implicit-function-declaration \ -Werror=implicit-int \ -Werror=pointer-arith \ -Werror=pointer-sign \ -Werror=strict-prototypes \ -Werror=undef \ -Werror=vla \ -Wno-missing-field-initializers COMMON_CPPFLAGS = -I$(OBJ)/include \ -I$(SRC)/platform/$(CONFIG_PLATFORM)/include \ -I$(SRC)/arch/$(CONFIG_ARCH)/include \ -I$(SRC)/include/common \ -I$(SRC)/include/lib HEADERS = $(OBJ)/include/config.h \ $(SRC)/lib/compiler.h \ $(SRC)/lib/kconfig.h BUILDCFLAGS = $(COMMON_CFLAGS) BUILDCPPFLAGS = $(COMMON_CPPFLAGS) \ -D_XOPEN_SOURCE=700 BUILDLDFLAGS = BUILDLDLIBS = HOSTCFLAGS = $(COMMON_CFLAGS) \ -Werror=implicit-fallthrough=5 HOSTCPPFLAGS = $(COMMON_CPPFLAGS) \ -D_XOPEN_SOURCE=700 HOSTLDFLAGS = HOSTLDLIBS = AFLAGS = -Wa,--fatal-warnings CFLAGS = $(COMMON_CFLAGS) \ -ffreestanding \ -flto \ -fno-asynchronous-unwind-tables \ -fno-pie \ -fomit-frame-pointer \ -funsigned-char \ -Werror=implicit-fallthrough=5 CPPFLAGS = $(COMMON_CPPFLAGS) \ -I$(SRC)/include/drivers \ -I$(SRC)/include/stdlib \ $(foreach header,$(HEADERS),-include $(notdir $(header))) \ -nostdinc \ -Werror=missing-include-dirs LDFLAGS = -nostdlib \ -no-pie \ -static \ -Wl,-O1 \ -Wl,--build-id=none \ -Wl,--fatal-warnings \ -Wl,--gc-sections \ -Wl,--no-dynamic-linker \ -Wl,--no-undefined ############################################################################### .DEFAULT_GOAL := all GOALS := $(if $(MAKECMDGOALS),$(MAKECMDGOALS),$(.DEFAULT_GOAL)) MAKEFLAGS += -Rr DOCS = $(wildcard $(SRC)/*.md $(SRC)/docs/*.md) OBJ_DEPS = $(OBJ)/include/version.h export KCONFIG_AUTOCONFIG := $(OBJ)/include/config/auto.conf export KCONFIG_AUTOHEADER := $(OBJ)/include/config.h ifneq ($(filter-out %clean %clobber %config %format,$(GOALS)),) include $(OBJ)/include/config/auto.conf include $(OBJ)/include/config/auto.conf.cmd endif include $(SRC)/scripts/Makefile.format include $(SRC)/scripts/Makefile.kbuild $(call descend,3rdparty arch common drivers lib tools) ############################################################################### M := @$(if $(filter-out 0,$(V)),:,exec printf ' %-7s %s\n') Q := $(if $(filter-out 0,$(V)),,@)exec all: scp tools $(test-all) check: $(test-all:%=%.test) clean: $(Q) rm -fr $(TGT) clobber: $(Q) rm -fr $(OBJ) distclean: $(Q) rm -fr $(OBJ) ..config* .config* html: $(OBJ)/docs scp: $(TGT)/scp.bin tools: $(tools-all) .config:; %/.: $(Q) mkdir -p $* %.d:; $(OBJ)/docs: $(OBJ)/Doxyfile $(DOCS) $(obj-all) $(M) DOXYGEN $@ $(Q) doxygen $< $(Q) touch $@ $(OBJ)/Doxyfile: $(SRC)/Doxyfile $(Q) sed 's|@DOCS@|$(DOCS)|g;s|@OBJ@|$(OBJ)|g;s|@SRC@|$(SRC)|g' $< > $@ $(OBJ)/%.test: $(SRC)/scripts/test.sh $(OBJ)/% $(M) TEST $@ $(Q) $^ $@ $(OBJ)/%: $(OBJ)/%.o $(M) HOSTLD $@ $(Q) $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ $(HOSTLDLIBS) $(test-all): $(OBJ)/lib.a $(tools-all): $(OBJ)/lib.a $(OBJ)/lib.a: $(M) HOSTAR $@ $(Q) $(HOSTAR) Drcs $@ $^ $(OBJ)/%.o: $(OBJ)/%.c $(M) HOSTCC $@ $(Q) $(HOSTCC) $(HOSTCPPFLAGS) $(HOSTCFLAGS) -MMD -c -o $@ $< $(OBJ)/%.o: $(SRC)/%.c $(M) HOSTCC $@ $(Q) $(HOSTCC) $(HOSTCPPFLAGS) $(HOSTCFLAGS) -MMD -c -o $@ $< $(OBJ)/%.lex.c: $(SRC)/%.l $(M) LEX $@ $(Q) $(LEX) -o $@ $< $(OBJ)/%.tab.c $(OBJ)/%.tab.h: $(SRC)/%.y $(M) YACC $@ $(Q) $(YACC) -d -t -o $@ $< $(OBJ)/include/config.h: $(OBJ)/include/config/auto.conf; $(OBJ)/include/config/auto.conf: $(OBJ)/3rdparty/kconfig/conf .config $(M) GEN $@ $(Q) $< --syncconfig $(SRC)/Kconfig $(OBJ)/include/config/auto.conf.cmd: $(OBJ)/include/config/auto.conf; $(OBJ)/include/version.h: $(SRC)/scripts/version.sh FORCE | $(OBJ)/include/. $(Q) $< $(realpath $(SRC)) $@ $(TGT)/%.bin: $(TGT)/%.elf $(M) OBJCOPY $@ $(Q) $(OBJCOPY) -O binary -S --reverse-bytes 4 $< $@ $(TGT)/%.elf $(TGT)/%.map: $(obj-all) $(TGT)/lib.a $(M) LD $@ $(Q) $(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$(TGT)/$*.map -o $@ -T $^ $(TGT)/lib.a: $(M) AR $@ $(Q) $(AR) Drcs $@ $^ $(TGT)/%.o: $(SRC)/%.c $(M) CC $@ $(Q) $(CC) $(CPPFLAGS) $(CFLAGS) $(AFLAGS) -MMD -c -o $@ $< $(TGT)/%.o: $(SRC)/%.S $(M) AS $@ $(Q) $(CC) $(CPPFLAGS) $(AFLAGS) -MMD -c -o $@ $< $(TGT)/%.ld.o: $(SRC)/%.ld.S $(M) CPP $@ $(Q) $(CC) $(CPPFLAGS) -E -MMD -MT $@ -P -o $@ $< $(SRC)/Makefile:; $(SRC)/%.h:; .PHONY: FORCE all check clean clobber distclean doc scp tools .SECONDARY: .SUFFIXES: crust-0.5/README.md000066400000000000000000000155411414152222500140010ustar00rootroot00000000000000# Crust: Libre SCP firmware for Allwinner sunxi SoCs [![CI status](https://travis-ci.com/crust-firmware/crust.svg?branch=master)][t] [t]: https://travis-ci.com/crust-firmware/crust ## What is it? The crust is the lowest-level component of a delicious fruit pie. Similarly, Crust is the lowest-level firmware component that runs on `$FRUIT` (Banana, Orange, Lichee) Pi single-board computers and other Allwinner-based devices, such as the Pine64 Pinebook and PinePhone. Crust improves battery life and thermal performance by implementing a deep sleep state. During deep sleep, the CPU cores, the DRAM controller, and most onboard peripherals are powered down, reducing power consumption by 80% or more compared to an idle device. On boards without a PMIC, Crust is also responsible for orderly power-off and power-on of the device. For this to work, Crust runs outside the main CPU and DRAM, on a dedicated always-on microprocessor called a System Control Processor (SCP). Crust is designed to run on a specific SCP implementation, Allwinner's [AR100][]. Note that Crust only provides the mechanism for deep sleep. It does not dictate any system sleep policy. Specifically, Crust does _not_ decide when to go to sleep; the Linux kernel or userspace does that. And with one exception (listening for IR remote control key presses), Crust does not decide when to wake the system up, either; the hardware, as programmed by Linux, does that. Crust is designed to be a mostly-invisible implementation detail of the Linux power management interface. See [Crust's ABI documentation][abi] for a detailed description of how Crust interacts with Linux and other firmware components at runtime. Interested users and contributors are encouraged to join `#linux-sunxi` on OFTC to discuss the firmware and its integration with other software. [abi]: docs/abi.md [AR100]: https://linux-sunxi.org/AR100 ## Supported devices Crust supports any board with a SoC listed in the table below. There is no board-specific code needed for basic functionality. Boards that are tested and known to work have a `defconfig` file in the repository. For everything else, use the defconfig for a similar board, or run `make config` or `make nconfig` to choose the appropriate options (there aren't many). | SoC | Support level | SCPI | CPU cores | CPU subsystem | DRAM | PMIC | |-------|-------------------|------|-----------|---------------|------|------| | A64 | Production/stable | Yes | Yes | Yes | Yes | Yes | | A83T | Known to compile | Yes | No | No | No | No | | H3 | Working beta | Yes | Yes | Yes | Yes | N/A | | H5 | Production/stable | Yes | Yes | Yes | Yes | N/A | | H6 | Production/stable | Yes | Yes | Yes | Yes | Yes | ## Prerequisites Crust supports mainline Linux only. It completely replaces Allwinner's bespoke, proprietary firmware with a libre solution that supports [standard protocols][scpi] and is developed entirely in the open with community input. Effort is underway to upstream all changes to third-party projects; however, some patches are currently still needed. - ARM Trusted Firmware-A: upstream support for Crust was merged in commit [`c335ad480d41`][atf-c335ad480d41], and is present in all releases starting with [v2.3][atf-v2.3]. Optional patches for improved support are available in the `crust` branch of [the crust-firmware fork][crust-atf]. - Linux: while Linux does not directly communicate with Crust, it requires some small patches to cleanly share the clock controller and PMIC bus controller hardware with Crust. They are available in the `crust-minimal` branch of [the crust-firmware fork][crust-linux]. Those patches, plus additional optional changes for reduced power consumption (helpful even if you are not using Crust), are available in the `crust` branch. - U-Boot: upstream support for loading Crust into SRAM was merged in commit [`18261b855223`][u-boot-18261b855223], and is present in all releases starting with [v2021.01-rc1][u-boot-v2021.01-rc1]. It is also possible to load Crust by padding your ATF binary to 48KiB (64KiB for H6) and then concatenating Crust onto the end. Note: The default PMIC bus configuration for most H6 boards is not compatible with versions of Linux before commit [`531fdbeedeb8`][531fdbeedeb8]. To use Crust on those boards with older versions of Linux, you must explicitly select `CONFIG_I2C_PINS_PL0_PL1`, or you may use the Crust v0.4 release. [atf-c335ad480d41]: https://github.com/ARM-Software/ARM-Trusted-Firmware/commits/c335ad480d41 [atf-v2.3]: https://github.com/ARM-software/arm-trusted-firmware/releases/tag/v2.3 [crust-atf]: https://github.com/crust-firmware/arm-trusted-firmware [crust-linux]: https://github.com/crust-firmware/linux [scpi]: http://infocenter.arm.com/help/topic/com.arm.doc.dui0922-/index.html [u-boot-18261b855223]: https://github.com/u-boot/u-boot/commit/18261b855223 [u-boot-v2021.01-rc1]: https://github.com/u-boot/u-boot/releases/tag/v2021.01-rc1 [531fdbeedeb8]: https://git.kernel.org/torvalds/c/531fdbeedeb8 ## Building the firmware An easy way to get all the pieces in the right places, with the right patches, is to use the `Makefile` in the [crust-firmware meta repository][crust-meta]. See the README file there for further instructions. Alternatively, you can build each firmware component individually. See the [README.sunxi64][sunxi64] file in the U-Boot source tree for more details. Installation of the combined U-Boot+ATF+Crust binary works the same as for U-Boot without Crust. Building Crust requires a cross-compiler targeting the `or1k` architecture (OpenRISC 1000, *not* RISC-V), which is officially supported in upstream GCC starting with GCC 9.1.0. Prebuilt toolchains are available from [musl.cc][], [bootlin][], and possibly your Linux distribution's package archive. If your cross toolchain has a different tuple (the toolchain's `libc` is not relevant when compiling freestanding firmware programs), or if it is not in your `PATH`, export `CROSS_COMPILE` or edit the top of the `Makefile` to provide the appropriate prefix or full path. Run `make` to build the firmware and related tools, or `make scp` to build just the firmware, which will be placed at `build/scp/scp.bin`. Adding `V=1` to the command line will run a verbose build, showing you the commands as they run. Set `SRC`, `OBJ`, or `TGT` as necessary if you want to do an out-of-tree build. [bootlin]: https://toolchains.bootlin.com/ [crust-meta]: https://github.com/crust-firmware/meta [musl.cc]: http://musl.cc/or1k-linux-musl-cross.tgz [sunxi64]: https://github.com/u-boot/u-boot/raw/master/board/sunxi/README.sunxi64 ## Contributing The success of the crust firmware project is made possible by community support. For more information regarding community contributions, please reference the crust firmware [contribution guidelines][cg]. [cg]: CONTRIBUTING.md crust-0.5/arch/000077500000000000000000000000001414152222500134315ustar00rootroot00000000000000crust-0.5/arch/Kconfig000066400000000000000000000003061414152222500147330ustar00rootroot00000000000000# # Copyright © 2020-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # config ARCH string default "or1k" if ARCH_OR1K config ARCH_OR1K bool default y crust-0.5/arch/Makefile000066400000000000000000000002231414152222500150660ustar00rootroot00000000000000# # Copyright © 2020-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-$(CONFIG_ARCH_OR1K) += or1k/ crust-0.5/arch/or1k/000077500000000000000000000000001414152222500143055ustar00rootroot00000000000000crust-0.5/arch/or1k/Makefile000066400000000000000000000005721414152222500157510ustar00rootroot00000000000000# # Copyright © 2020-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # CROSS_COMPILE ?= or1k-linux-musl- CFLAGS += -ffixed-r2 \ -msfimm -mshftimm -msoft-div -msoft-mul # The first object is used as the linker script. obj-y += scp.ld.o obj-y += counter.o obj-y += exception.o obj-y += math.o obj-y += runtime.o obj-y += start.o crust-0.5/arch/or1k/counter.c000066400000000000000000000005301414152222500161260ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include void counter_init(void) { mtspr(SPR_TICK_TTMR_ADDR, SPR_TICK_TTMR_MODE_CONTINUE << SPR_TICK_TTMR_MODE_LSB); } uint32_t counter_read(void) { return mfspr(SPR_TICK_TTCR_ADDR); } crust-0.5/arch/or1k/exception.c000066400000000000000000000006321414152222500164500ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include void report_exception(uint32_t exception) { uint32_t pc; if (!exception) return; pc = mfspr(SPR_SYS_EPCR_ADDR(0)); record_exception(exception, pc); error("Exception %u at %p!", exception, (void *)pc); } crust-0.5/arch/or1k/include/000077500000000000000000000000001414152222500157305ustar00rootroot00000000000000crust-0.5/arch/or1k/include/asm/000077500000000000000000000000001414152222500165105ustar00rootroot00000000000000crust-0.5/arch/or1k/include/asm/exception.h000066400000000000000000000014671414152222500206670ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef ASM_EXCEPTION_H #define ASM_EXCEPTION_H #define RESET_EXCEPTION 0x01 #define BUS_ERROR 0x02 #define DATA_PAGE_FAULT 0x03 #define INSTRUCTION_PAGE_FAULT 0x04 #define TICK_TIMER_EXCEPTION 0x05 #define ALIGNMENT_EXCEPTION 0x06 #define ILLEGAL_INSTRUCTION 0x07 #define EXTERNAL_INTERRUPT 0x08 #define DATA_TLB_MISS 0x09 #define INSTRUCTION_TLB_MISS 0x0a #define RANGE_EXCEPTION 0x0b #define SYSTEM_CALL 0x0c #define FLOATING_POINT_EXCEPTION 0x0d #define TRAP_EXCEPTION 0x0e #define EXCEPTION_VECTOR_ADDRESS(n) (0x100 * (n)) #endif /* ASM_EXCEPTION_H */ crust-0.5/arch/or1k/include/asm/spr.h000066400000000000000000003302421414152222500174710ustar00rootroot00000000000000/* * Copyright © 2014 OpenRISC Project Maintainers. All rights reserved. * SPDX-License-Identifier: BSD-1-Clause */ #ifndef ASM_SPR_H #define ASM_SPR_H #include #define SPR_GROUP_BITS 5 #define SPR_GROUP_LSB 11 #define SPR_GROUP_MSB 15 #define SPR_INDEX_BITS 11 #define SPR_INDEX_LSB 0 #define SPR_INDEX_MSB 10 /****************/ /* System Group */ /****************/ #define SPR_SYS_GROUP 0x00 /* Version Register */ #define SPR_SYS_VR_INDEX U(0x000) #define SPR_SYS_VR_ADDR U(0x0000) /* Revision */ #define SPR_SYS_VR_REV_LSB 0 #define SPR_SYS_VR_REV_MSB 5 #define SPR_SYS_VR_REV_BITS 6 #define SPR_SYS_VR_REV_MASK U(0x0000003f) #define SPR_SYS_VR_REV_GET(x) (((x) >> 0) & U(0x0000003f)) #define SPR_SYS_VR_REV_SET(x, y) (((x) & U(0xffffffc0)) | \ ((y) << 0)) /* Updated Version Registers Present */ #define SPR_SYS_VR_UVRP_OFFSET 6 #define SPR_SYS_VR_UVRP_MASK 0x00000040 #define SPR_SYS_VR_UVRP_GET(x) (((x) >> 6) & 0x1) #define SPR_SYS_VR_UVRP_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* Configuration Template */ #define SPR_SYS_VR_CFG_LSB 16 #define SPR_SYS_VR_CFG_MSB 23 #define SPR_SYS_VR_CFG_BITS 8 #define SPR_SYS_VR_CFG_MASK U(0x00ff0000) #define SPR_SYS_VR_CFG_GET(x) (((x) >> 16) & U(0x000000ff)) #define SPR_SYS_VR_CFG_SET(x, y) (((x) & U(0xff00ffff)) | \ ((y) << 16)) /* Version */ #define SPR_SYS_VR_VER_LSB 24 #define SPR_SYS_VR_VER_MSB 31 #define SPR_SYS_VR_VER_BITS 8 #define SPR_SYS_VR_VER_MASK U(0xff000000) #define SPR_SYS_VR_VER_GET(x) (((x) >> 24) & U(0x000000ff)) #define SPR_SYS_VR_VER_SET(x, y) (((x) & U(0x00ffffff)) | \ ((y) << 24)) /* Unit Present Register */ #define SPR_SYS_UPR_INDEX U(0x001) #define SPR_SYS_UPR_ADDR U(0x0001) /* UPR Present */ #define SPR_SYS_UPR_UP_OFFSET 0 #define SPR_SYS_UPR_UP_MASK 0x00000001 #define SPR_SYS_UPR_UP_GET(x) (((x) >> 0) & 0x1) #define SPR_SYS_UPR_UP_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Data Cache Present */ #define SPR_SYS_UPR_DCP_OFFSET 1 #define SPR_SYS_UPR_DCP_MASK 0x00000002 #define SPR_SYS_UPR_DCP_GET(x) (((x) >> 1) & 0x1) #define SPR_SYS_UPR_DCP_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Instruction Cache Present */ #define SPR_SYS_UPR_ICP_OFFSET 2 #define SPR_SYS_UPR_ICP_MASK 0x00000004 #define SPR_SYS_UPR_ICP_GET(x) (((x) >> 2) & 0x1) #define SPR_SYS_UPR_ICP_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* Data MMU Present */ #define SPR_SYS_UPR_DMP_OFFSET 3 #define SPR_SYS_UPR_DMP_MASK 0x00000008 #define SPR_SYS_UPR_DMP_GET(x) (((x) >> 3) & 0x1) #define SPR_SYS_UPR_DMP_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Instruction MMU Present */ #define SPR_SYS_UPR_IMP_OFFSET 4 #define SPR_SYS_UPR_IMP_MASK 0x00000010 #define SPR_SYS_UPR_IMP_GET(x) (((x) >> 4) & 0x1) #define SPR_SYS_UPR_IMP_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* MAC Present */ #define SPR_SYS_UPR_MP_OFFSET 5 #define SPR_SYS_UPR_MP_MASK 0x00000020 #define SPR_SYS_UPR_MP_GET(x) (((x) >> 5) & 0x1) #define SPR_SYS_UPR_MP_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Debug Unit Present */ #define SPR_SYS_UPR_DUP_OFFSET 6 #define SPR_SYS_UPR_DUP_MASK 0x00000040 #define SPR_SYS_UPR_DUP_GET(x) (((x) >> 6) & 0x1) #define SPR_SYS_UPR_DUP_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* Performance Counters Unit Present */ #define SPR_SYS_UPR_PCUP_OFFSET 7 #define SPR_SYS_UPR_PCUP_MASK 0x00000080 #define SPR_SYS_UPR_PCUP_GET(x) (((x) >> 7) & 0x1) #define SPR_SYS_UPR_PCUP_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* Power Management Present */ #define SPR_SYS_UPR_PICP_OFFSET 8 #define SPR_SYS_UPR_PICP_MASK 0x00000100 #define SPR_SYS_UPR_PICP_GET(x) (((x) >> 8) & 0x1) #define SPR_SYS_UPR_PICP_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* Programmable Interrupt Controller Present */ #define SPR_SYS_UPR_PMP_OFFSET 9 #define SPR_SYS_UPR_PMP_MASK 0x00000200 #define SPR_SYS_UPR_PMP_GET(x) (((x) >> 9) & 0x1) #define SPR_SYS_UPR_PMP_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* Tick Timer Present */ #define SPR_SYS_UPR_TTP_OFFSET 10 #define SPR_SYS_UPR_TTP_MASK 0x00000400 #define SPR_SYS_UPR_TTP_GET(x) (((x) >> 10) & 0x1) #define SPR_SYS_UPR_TTP_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* Custom Units Present */ #define SPR_SYS_UPR_CUP_LSB 24 #define SPR_SYS_UPR_CUP_MSB 31 #define SPR_SYS_UPR_CUP_BITS 8 #define SPR_SYS_UPR_CUP_MASK U(0xff000000) #define SPR_SYS_UPR_CUP_GET(x) (((x) >> 24) & U(0x000000ff)) #define SPR_SYS_UPR_CUP_SET(x, y) (((x) & U(0x00ffffff)) | \ ((y) << 24)) /* CPU Configuration Register */ #define SPR_SYS_CPUCFGR_INDEX U(0x002) #define SPR_SYS_CPUCFGR_ADDR U(0x0002) /* Number of Shadow GPR Files */ #define SPR_SYS_CPUCFGR_NSGF_LSB 0 #define SPR_SYS_CPUCFGR_NSGF_MSB 3 #define SPR_SYS_CPUCFGR_NSGF_BITS 4 #define SPR_SYS_CPUCFGR_NSGF_MASK U(0x0000000f) #define SPR_SYS_CPUCFGR_NSGF_GET(x) (((x) >> 0) & U(0x0000000f)) #define SPR_SYS_CPUCFGR_NSGF_SET(x, y) (((x) & U(0xfffffff0)) | \ ((y) << 0)) /* Custom GPR File */ #define SPR_SYS_CPUCFGR_CGF_OFFSET 4 #define SPR_SYS_CPUCFGR_CGF_MASK 0x00000010 #define SPR_SYS_CPUCFGR_CGF_GET(x) (((x) >> 4) & 0x1) #define SPR_SYS_CPUCFGR_CGF_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* ORBIS32 Supported */ #define SPR_SYS_CPUCFGR_OB32S_OFFSET 5 #define SPR_SYS_CPUCFGR_OB32S_MASK 0x00000020 #define SPR_SYS_CPUCFGR_OB32S_GET(x) (((x) >> 5) & 0x1) #define SPR_SYS_CPUCFGR_OB32S_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* ORBIS64 Supported */ #define SPR_SYS_CPUCFGR_OB64S_OFFSET 6 #define SPR_SYS_CPUCFGR_OB64S_MASK 0x00000040 #define SPR_SYS_CPUCFGR_OB64S_GET(x) (((x) >> 6) & 0x1) #define SPR_SYS_CPUCFGR_OB64S_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* ORFPX32 Supported */ #define SPR_SYS_CPUCFGR_OF32S_OFFSET 7 #define SPR_SYS_CPUCFGR_OF32S_MASK 0x00000080 #define SPR_SYS_CPUCFGR_OF32S_GET(x) (((x) >> 7) & 0x1) #define SPR_SYS_CPUCFGR_OF32S_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* ORFPX64 Supported */ #define SPR_SYS_CPUCFGR_OF64S_OFFSET 8 #define SPR_SYS_CPUCFGR_OF64S_MASK 0x00000100 #define SPR_SYS_CPUCFGR_OF64S_GET(x) (((x) >> 8) & 0x1) #define SPR_SYS_CPUCFGR_OF64S_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* ORVDX64 Supported */ #define SPR_SYS_CPUCFGR_OV64S_OFFSET 9 #define SPR_SYS_CPUCFGR_OV64S_MASK 0x00000200 #define SPR_SYS_CPUCFGR_OV64S_GET(x) (((x) >> 9) & 0x1) #define SPR_SYS_CPUCFGR_OV64S_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* No Delay-Slot */ #define SPR_SYS_CPUCFGR_ND_OFFSET 10 #define SPR_SYS_CPUCFGR_ND_MASK 0x00000400 #define SPR_SYS_CPUCFGR_ND_GET(x) (((x) >> 10) & 0x1) #define SPR_SYS_CPUCFGR_ND_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* Architecture Version Register Present */ #define SPR_SYS_CPUCFGR_AVRP_OFFSET 11 #define SPR_SYS_CPUCFGR_AVRP_MASK 0x00000800 #define SPR_SYS_CPUCFGR_AVRP_GET(x) (((x) >> 11) & 0x1) #define SPR_SYS_CPUCFGR_AVRP_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* Exception Vector Base Address Register Present */ #define SPR_SYS_CPUCFGR_EVBARP_OFFSET 12 #define SPR_SYS_CPUCFGR_EVBARP_MASK 0x00001000 #define SPR_SYS_CPUCFGR_EVBARP_GET(x) (((x) >> 12) & 0x1) #define SPR_SYS_CPUCFGR_EVBARP_SET(x, y) (((x) & U(0xffffefff)) | \ ((!!(y)) << 12)) /* Implementation-Specific Registers (ISR0-7) Present */ #define SPR_SYS_CPUCFGR_ISRP_OFFSET 13 #define SPR_SYS_CPUCFGR_ISRP_MASK 0x00002000 #define SPR_SYS_CPUCFGR_ISRP_GET(x) (((x) >> 13) & 0x1) #define SPR_SYS_CPUCFGR_ISRP_SET(x, y) (((x) & U(0xffffdfff)) | \ ((!!(y)) << 13)) /* Arithmetic Exception Control/Status Registers Present */ #define SPR_SYS_CPUCFGR_AECSRP_OFFSET 14 #define SPR_SYS_CPUCFGR_AECSRP_MASK 0x00004000 #define SPR_SYS_CPUCFGR_AECSRP_GET(x) (((x) >> 14) & 0x1) #define SPR_SYS_CPUCFGR_AECSRP_SET(x, y) (((x) & U(0xffffbfff)) | \ ((!!(y)) << 14)) /* Data MMU Configuration Register */ #define SPR_SYS_DMMUCFGR_INDEX U(0x003) #define SPR_SYS_DMMUCFGR_ADDR U(0x0003) /* Number of TLB Ways */ #define SPR_SYS_DMMUCFGR_NTW_LSB 0 #define SPR_SYS_DMMUCFGR_NTW_MSB 1 #define SPR_SYS_DMMUCFGR_NTW_BITS 2 #define SPR_SYS_DMMUCFGR_NTW_MASK U(0x00000003) #define SPR_SYS_DMMUCFGR_NTW_GET(x) (((x) >> 0) & U(0x00000003)) #define SPR_SYS_DMMUCFGR_NTW_SET(x, y) (((x) & U(0xfffffffc)) | \ ((y) << 0)) /* Number of TLB Sets */ #define SPR_SYS_DMMUCFGR_NTS_LSB 2 #define SPR_SYS_DMMUCFGR_NTS_MSB 4 #define SPR_SYS_DMMUCFGR_NTS_BITS 3 #define SPR_SYS_DMMUCFGR_NTS_MASK U(0x0000001c) #define SPR_SYS_DMMUCFGR_NTS_GET(x) (((x) >> 2) & U(0x00000007)) #define SPR_SYS_DMMUCFGR_NTS_SET(x, y) (((x) & U(0xffffffe3)) | \ ((y) << 2)) /* Number of ATB Entries */ #define SPR_SYS_DMMUCFGR_NAE_LSB 5 #define SPR_SYS_DMMUCFGR_NAE_MSB 7 #define SPR_SYS_DMMUCFGR_NAE_BITS 3 #define SPR_SYS_DMMUCFGR_NAE_MASK U(0x000000e0) #define SPR_SYS_DMMUCFGR_NAE_GET(x) (((x) >> 5) & U(0x00000007)) #define SPR_SYS_DMMUCFGR_NAE_SET(x, y) (((x) & U(0xffffff1f)) | \ ((y) << 5)) /* Control Register Implemented */ #define SPR_SYS_DMMUCFGR_CRI_OFFSET 8 #define SPR_SYS_DMMUCFGR_CRI_MASK 0x00000100 #define SPR_SYS_DMMUCFGR_CRI_GET(x) (((x) >> 8) & 0x1) #define SPR_SYS_DMMUCFGR_CRI_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* Protection Register Implemented */ #define SPR_SYS_DMMUCFGR_PRI_OFFSET 9 #define SPR_SYS_DMMUCFGR_PRI_MASK 0x00000200 #define SPR_SYS_DMMUCFGR_PRI_GET(x) (((x) >> 9) & 0x1) #define SPR_SYS_DMMUCFGR_PRI_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* TLB Entry Invalidate Register Implemented */ #define SPR_SYS_DMMUCFGR_TEIRI_OFFSET 10 #define SPR_SYS_DMMUCFGR_TEIRI_MASK 0x00000400 #define SPR_SYS_DMMUCFGR_TEIRI_GET(x) (((x) >> 10) & 0x1) #define SPR_SYS_DMMUCFGR_TEIRI_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* Hardware TLB Reload */ #define SPR_SYS_DMMUCFGR_HTR_OFFSET 11 #define SPR_SYS_DMMUCFGR_HTR_MASK 0x00000800 #define SPR_SYS_DMMUCFGR_HTR_GET(x) (((x) >> 11) & 0x1) #define SPR_SYS_DMMUCFGR_HTR_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* DTLB reloaded in software */ #define SPR_SYS_DMMUCFGR_HTR_SW 0 /* DTLB reloaded in hardware */ #define SPR_SYS_DMMUCFGR_HTR_HW 1 /* Instruction MMU Configuration Register */ #define SPR_SYS_IMMUCFGR_INDEX U(0x004) #define SPR_SYS_IMMUCFGR_ADDR U(0x0004) /* Number of TLB Ways */ #define SPR_SYS_IMMUCFGR_NTW_LSB 0 #define SPR_SYS_IMMUCFGR_NTW_MSB 1 #define SPR_SYS_IMMUCFGR_NTW_BITS 2 #define SPR_SYS_IMMUCFGR_NTW_MASK U(0x00000003) #define SPR_SYS_IMMUCFGR_NTW_GET(x) (((x) >> 0) & U(0x00000003)) #define SPR_SYS_IMMUCFGR_NTW_SET(x, y) (((x) & U(0xfffffffc)) | \ ((y) << 0)) /* Number of TLB Sets */ #define SPR_SYS_IMMUCFGR_NTS_LSB 2 #define SPR_SYS_IMMUCFGR_NTS_MSB 4 #define SPR_SYS_IMMUCFGR_NTS_BITS 3 #define SPR_SYS_IMMUCFGR_NTS_MASK U(0x0000001c) #define SPR_SYS_IMMUCFGR_NTS_GET(x) (((x) >> 2) & U(0x00000007)) #define SPR_SYS_IMMUCFGR_NTS_SET(x, y) (((x) & U(0xffffffe3)) | \ ((y) << 2)) /* Number of ATB Entries */ #define SPR_SYS_IMMUCFGR_NAE_LSB 5 #define SPR_SYS_IMMUCFGR_NAE_MSB 7 #define SPR_SYS_IMMUCFGR_NAE_BITS 3 #define SPR_SYS_IMMUCFGR_NAE_MASK U(0x000000e0) #define SPR_SYS_IMMUCFGR_NAE_GET(x) (((x) >> 5) & U(0x00000007)) #define SPR_SYS_IMMUCFGR_NAE_SET(x, y) (((x) & U(0xffffff1f)) | \ ((y) << 5)) /* Control Register Implemented */ #define SPR_SYS_IMMUCFGR_CRI_OFFSET 8 #define SPR_SYS_IMMUCFGR_CRI_MASK 0x00000100 #define SPR_SYS_IMMUCFGR_CRI_GET(x) (((x) >> 8) & 0x1) #define SPR_SYS_IMMUCFGR_CRI_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* Protection Register Implemented */ #define SPR_SYS_IMMUCFGR_PRI_OFFSET 9 #define SPR_SYS_IMMUCFGR_PRI_MASK 0x00000200 #define SPR_SYS_IMMUCFGR_PRI_GET(x) (((x) >> 9) & 0x1) #define SPR_SYS_IMMUCFGR_PRI_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* TLB Entry Invalidate Register Implemented */ #define SPR_SYS_IMMUCFGR_TEIRI_OFFSET 10 #define SPR_SYS_IMMUCFGR_TEIRI_MASK 0x00000400 #define SPR_SYS_IMMUCFGR_TEIRI_GET(x) (((x) >> 10) & 0x1) #define SPR_SYS_IMMUCFGR_TEIRI_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* Hardware TLB Reload */ #define SPR_SYS_IMMUCFGR_HTR_OFFSET 11 #define SPR_SYS_IMMUCFGR_HTR_MASK 0x00000800 #define SPR_SYS_IMMUCFGR_HTR_GET(x) (((x) >> 11) & 0x1) #define SPR_SYS_IMMUCFGR_HTR_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* DTLB reloaded in software */ #define SPR_SYS_IMMUCFGR_HTR_SW 0 /* DTLB reloaded in hardware */ #define SPR_SYS_IMMUCFGR_HTR_HW 1 /* Data Cache Configuration Register */ #define SPR_SYS_DCCFGR_INDEX U(0x005) #define SPR_SYS_DCCFGR_ADDR U(0x0005) /* Number of Cache Ways */ #define SPR_SYS_DCCFGR_NCW_LSB 0 #define SPR_SYS_DCCFGR_NCW_MSB 2 #define SPR_SYS_DCCFGR_NCW_BITS 3 #define SPR_SYS_DCCFGR_NCW_MASK U(0x00000007) #define SPR_SYS_DCCFGR_NCW_GET(x) (((x) >> 0) & U(0x00000007)) #define SPR_SYS_DCCFGR_NCW_SET(x, y) (((x) & U(0xfffffff8)) | \ ((y) << 0)) /* Number of Cache Sets */ #define SPR_SYS_DCCFGR_NCS_LSB 3 #define SPR_SYS_DCCFGR_NCS_MSB 6 #define SPR_SYS_DCCFGR_NCS_BITS 4 #define SPR_SYS_DCCFGR_NCS_MASK U(0x00000078) #define SPR_SYS_DCCFGR_NCS_GET(x) (((x) >> 3) & U(0x0000000f)) #define SPR_SYS_DCCFGR_NCS_SET(x, y) (((x) & U(0xffffff87)) | \ ((y) << 3)) /* Cache Block Size */ #define SPR_SYS_DCCFGR_CBS_OFFSET 7 #define SPR_SYS_DCCFGR_CBS_MASK 0x00000080 #define SPR_SYS_DCCFGR_CBS_GET(x) (((x) >> 7) & 0x1) #define SPR_SYS_DCCFGR_CBS_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* 16 Bytes */ #define SPR_SYS_DCCFGR_CBS_16 0 /* 32 Bytes */ #define SPR_SYS_DCCFGR_CBS_32 1 /* Cache Write Strategy */ #define SPR_SYS_DCCFGR_CWS_OFFSET 8 #define SPR_SYS_DCCFGR_CWS_MASK 0x00000100 #define SPR_SYS_DCCFGR_CWS_GET(x) (((x) >> 8) & 0x1) #define SPR_SYS_DCCFGR_CWS_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* Write Through */ #define SPR_SYS_DCCFGR_CWS_WT 0 /* Write Back */ #define SPR_SYS_DCCFGR_CWS_WB 1 /* Cache Control Register Implemented */ #define SPR_SYS_DCCFGR_CCRI_OFFSET 9 #define SPR_SYS_DCCFGR_CCRI_MASK 0x00000200 #define SPR_SYS_DCCFGR_CCRI_GET(x) (((x) >> 9) & 0x1) #define SPR_SYS_DCCFGR_CCRI_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* Cache Block Invalidate Register Implemented */ #define SPR_SYS_DCCFGR_CBIRI_OFFSET 10 #define SPR_SYS_DCCFGR_CBIRI_MASK 0x00000400 #define SPR_SYS_DCCFGR_CBIRI_GET(x) (((x) >> 10) & 0x1) #define SPR_SYS_DCCFGR_CBIRI_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* Cache Block Prefetch Register Implemented */ #define SPR_SYS_DCCFGR_CBPRI_OFFSET 11 #define SPR_SYS_DCCFGR_CBPRI_MASK 0x00000800 #define SPR_SYS_DCCFGR_CBPRI_GET(x) (((x) >> 11) & 0x1) #define SPR_SYS_DCCFGR_CBPRI_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* Cache Block Lock Register Implemented */ #define SPR_SYS_DCCFGR_CBLRI_OFFSET 12 #define SPR_SYS_DCCFGR_CBLRI_MASK 0x00001000 #define SPR_SYS_DCCFGR_CBLRI_GET(x) (((x) >> 12) & 0x1) #define SPR_SYS_DCCFGR_CBLRI_SET(x, y) (((x) & U(0xffffefff)) | \ ((!!(y)) << 12)) /* Cache Block Flush Register Implemented */ #define SPR_SYS_DCCFGR_CBFRI_OFFSET 13 #define SPR_SYS_DCCFGR_CBFRI_MASK 0x00002000 #define SPR_SYS_DCCFGR_CBFRI_GET(x) (((x) >> 13) & 0x1) #define SPR_SYS_DCCFGR_CBFRI_SET(x, y) (((x) & U(0xffffdfff)) | \ ((!!(y)) << 13)) /* Cache Block Write-back Register Implemented */ #define SPR_SYS_DCCFGR_CBWBRI_OFFSET 14 #define SPR_SYS_DCCFGR_CBWBRI_MASK 0x00004000 #define SPR_SYS_DCCFGR_CBWBRI_GET(x) (((x) >> 14) & 0x1) #define SPR_SYS_DCCFGR_CBWBRI_SET(x, y) (((x) & U(0xffffbfff)) | \ ((!!(y)) << 14)) /* Instruction Cache Configuration Register */ #define SPR_SYS_ICCFGR_INDEX U(0x006) #define SPR_SYS_ICCFGR_ADDR U(0x0006) /* Number of Cache Ways */ #define SPR_SYS_ICCFGR_NCW_LSB 0 #define SPR_SYS_ICCFGR_NCW_MSB 2 #define SPR_SYS_ICCFGR_NCW_BITS 3 #define SPR_SYS_ICCFGR_NCW_MASK U(0x00000007) #define SPR_SYS_ICCFGR_NCW_GET(x) (((x) >> 0) & U(0x00000007)) #define SPR_SYS_ICCFGR_NCW_SET(x, y) (((x) & U(0xfffffff8)) | \ ((y) << 0)) /* Number of Cache Sets */ #define SPR_SYS_ICCFGR_NCS_LSB 3 #define SPR_SYS_ICCFGR_NCS_MSB 6 #define SPR_SYS_ICCFGR_NCS_BITS 4 #define SPR_SYS_ICCFGR_NCS_MASK U(0x00000078) #define SPR_SYS_ICCFGR_NCS_GET(x) (((x) >> 3) & U(0x0000000f)) #define SPR_SYS_ICCFGR_NCS_SET(x, y) (((x) & U(0xffffff87)) | \ ((y) << 3)) /* Cache Block Size */ #define SPR_SYS_ICCFGR_CBS_OFFSET 7 #define SPR_SYS_ICCFGR_CBS_MASK 0x00000080 #define SPR_SYS_ICCFGR_CBS_GET(x) (((x) >> 7) & 0x1) #define SPR_SYS_ICCFGR_CBS_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* 16 Bytes */ #define SPR_SYS_ICCFGR_CBS_16 0 /* 32 Bytes */ #define SPR_SYS_ICCFGR_CBS_32 1 /* Cache Control Register Implemented */ #define SPR_SYS_ICCFGR_CCRI_OFFSET 9 #define SPR_SYS_ICCFGR_CCRI_MASK 0x00000200 #define SPR_SYS_ICCFGR_CCRI_GET(x) (((x) >> 9) & 0x1) #define SPR_SYS_ICCFGR_CCRI_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* Cache Block Invalidate Register Implemented */ #define SPR_SYS_ICCFGR_CBIRI_OFFSET 10 #define SPR_SYS_ICCFGR_CBIRI_MASK 0x00000400 #define SPR_SYS_ICCFGR_CBIRI_GET(x) (((x) >> 10) & 0x1) #define SPR_SYS_ICCFGR_CBIRI_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* Cache Block Prefetch Register Implemented */ #define SPR_SYS_ICCFGR_CBPRI_OFFSET 11 #define SPR_SYS_ICCFGR_CBPRI_MASK 0x00000800 #define SPR_SYS_ICCFGR_CBPRI_GET(x) (((x) >> 11) & 0x1) #define SPR_SYS_ICCFGR_CBPRI_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* Cache Block Lock Register Implemented */ #define SPR_SYS_ICCFGR_CBLRI_OFFSET 12 #define SPR_SYS_ICCFGR_CBLRI_MASK 0x00001000 #define SPR_SYS_ICCFGR_CBLRI_GET(x) (((x) >> 12) & 0x1) #define SPR_SYS_ICCFGR_CBLRI_SET(x, y) (((x) & U(0xffffefff)) | \ ((!!(y)) << 12)) /* Debug Configuration Register */ #define SPR_SYS_DCFGR_INDEX U(0x007) #define SPR_SYS_DCFGR_ADDR U(0x0007) /* Number of Debug Pairs */ #define SPR_SYS_DCFGR_NDP_LSB 0 #define SPR_SYS_DCFGR_NDP_MSB 2 #define SPR_SYS_DCFGR_NDP_BITS 3 #define SPR_SYS_DCFGR_NDP_MASK U(0x00000007) #define SPR_SYS_DCFGR_NDP_GET(x) (((x) >> 0) & U(0x00000007)) #define SPR_SYS_DCFGR_NDP_SET(x, y) (((x) & U(0xfffffff8)) | \ ((y) << 0)) /* Watchpoint Counters Implemented */ #define SPR_SYS_DCFGR_WPCI_OFFSET 3 #define SPR_SYS_DCFGR_WPCI_MASK 0x00000008 #define SPR_SYS_DCFGR_WPCI_GET(x) (((x) >> 3) & 0x1) #define SPR_SYS_DCFGR_WPCI_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Performance Counters Configuration */ #define SPR_SYS_PCCFGR_INDEX U(0x008) #define SPR_SYS_PCCFGR_ADDR U(0x0008) /* Number of Performance Counters */ #define SPR_SYS_PCCFGR_NPC_LSB 0 #define SPR_SYS_PCCFGR_NPC_MSB 2 #define SPR_SYS_PCCFGR_NPC_BITS 3 #define SPR_SYS_PCCFGR_NPC_MASK U(0x00000007) #define SPR_SYS_PCCFGR_NPC_GET(x) (((x) >> 0) & U(0x00000007)) #define SPR_SYS_PCCFGR_NPC_SET(x, y) (((x) & U(0xfffffff8)) | \ ((y) << 0)) /* Version Register 2 */ #define SPR_SYS_VR2_INDEX U(0x009) #define SPR_SYS_VR2_ADDR U(0x0009) /* Version */ #define SPR_SYS_VR2_VER_LSB 0 #define SPR_SYS_VR2_VER_MSB 23 #define SPR_SYS_VR2_VER_BITS 24 #define SPR_SYS_VR2_VER_MASK U(0x00ffffff) #define SPR_SYS_VR2_VER_GET(x) (((x) >> 0) & U(0x00ffffff)) #define SPR_SYS_VR2_VER_SET(x, y) (((x) & U(0xff000000)) | \ ((y) << 0)) /* CPU Identification Number */ #define SPR_SYS_VR2_CPUID_LSB 24 #define SPR_SYS_VR2_CPUID_MSB 31 #define SPR_SYS_VR2_CPUID_BITS 8 #define SPR_SYS_VR2_CPUID_MASK U(0xff000000) #define SPR_SYS_VR2_CPUID_GET(x) (((x) >> 24) & U(0x000000ff)) #define SPR_SYS_VR2_CPUID_SET(x, y) (((x) & U(0x00ffffff)) | \ ((y) << 24)) /* Architecture Version Register */ #define SPR_SYS_AVR_INDEX U(0x00a) #define SPR_SYS_AVR_ADDR U(0x000a) /* Major Architecture Version Number */ #define SPR_SYS_AVR_REV_LSB 8 #define SPR_SYS_AVR_REV_MSB 15 #define SPR_SYS_AVR_REV_BITS 8 #define SPR_SYS_AVR_REV_MASK U(0x0000ff00) #define SPR_SYS_AVR_REV_GET(x) (((x) >> 8) & U(0x000000ff)) #define SPR_SYS_AVR_REV_SET(x, y) (((x) & U(0xffff00ff)) | \ ((y) << 8)) /* Minor Architecture Version Number */ #define SPR_SYS_AVR_MIN_LSB 16 #define SPR_SYS_AVR_MIN_MSB 23 #define SPR_SYS_AVR_MIN_BITS 8 #define SPR_SYS_AVR_MIN_MASK U(0x00ff0000) #define SPR_SYS_AVR_MIN_GET(x) (((x) >> 16) & U(0x000000ff)) #define SPR_SYS_AVR_MIN_SET(x, y) (((x) & U(0xff00ffff)) | \ ((y) << 16)) /* Architecture Revision Number */ #define SPR_SYS_AVR_MAJ_LSB 24 #define SPR_SYS_AVR_MAJ_MSB 31 #define SPR_SYS_AVR_MAJ_BITS 8 #define SPR_SYS_AVR_MAJ_MASK U(0xff000000) #define SPR_SYS_AVR_MAJ_GET(x) (((x) >> 24) & U(0x000000ff)) #define SPR_SYS_AVR_MAJ_SET(x, y) (((x) & U(0x00ffffff)) | \ ((y) << 24)) /* Exception Vector Base Address Register */ #define SPR_SYS_EVBAR_INDEX U(0x00b) #define SPR_SYS_EVBAR_ADDR U(0x000b) /* Exception Vector Base Address */ #define SPR_SYS_EVBAR_EVBA_LSB 13 #define SPR_SYS_EVBAR_EVBA_MSB 31 #define SPR_SYS_EVBAR_EVBA_BITS 19 #define SPR_SYS_EVBAR_EVBA_MASK U(0xffffe000) #define SPR_SYS_EVBAR_EVBA_GET(x) (((x) >> 13) & U(0x0007ffff)) #define SPR_SYS_EVBAR_EVBA_SET(x, y) (((x) & U(0x00001fff)) | \ ((y) << 13)) /* Arithmetic Exception Control Register */ #define SPR_SYS_AECR_INDEX U(0x00c) #define SPR_SYS_AECR_ADDR U(0x000c) /* Carry on Add Exception Enabled */ #define SPR_SYS_AECR_CYADDE_OFFSET 0 #define SPR_SYS_AECR_CYADDE_MASK 0x00000001 #define SPR_SYS_AECR_CYADDE_GET(x) (((x) >> 0) & 0x1) #define SPR_SYS_AECR_CYADDE_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Overflow on Add Exception Enabled */ #define SPR_SYS_AECR_OVADDE_OFFSET 1 #define SPR_SYS_AECR_OVADDE_MASK 0x00000002 #define SPR_SYS_AECR_OVADDE_GET(x) (((x) >> 1) & 0x1) #define SPR_SYS_AECR_OVADDE_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Carry on Multiply Exception Enabled */ #define SPR_SYS_AECR_CYMULE_OFFSET 2 #define SPR_SYS_AECR_CYMULE_MASK 0x00000004 #define SPR_SYS_AECR_CYMULE_GET(x) (((x) >> 2) & 0x1) #define SPR_SYS_AECR_CYMULE_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* Overflow on Multiply Exception Enabled */ #define SPR_SYS_AECR_OVMULE_OFFSET 3 #define SPR_SYS_AECR_OVMULE_MASK 0x00000008 #define SPR_SYS_AECR_OVMULE_GET(x) (((x) >> 3) & 0x1) #define SPR_SYS_AECR_OVMULE_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Divide by Zero Exception Enabled */ #define SPR_SYS_AECR_DBZE_OFFSET 4 #define SPR_SYS_AECR_DBZE_MASK 0x00000010 #define SPR_SYS_AECR_DBZE_GET(x) (((x) >> 4) & 0x1) #define SPR_SYS_AECR_DBZE_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Carry on MAC Addition Exception Enabled */ #define SPR_SYS_AECR_CYMACADDE_OFFSET 5 #define SPR_SYS_AECR_CYMACADDE_MASK 0x00000020 #define SPR_SYS_AECR_CYMACADDE_GET(x) (((x) >> 5) & 0x1) #define SPR_SYS_AECR_CYMACADDE_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Overflow on MAC Addition Exception Enabled */ #define SPR_SYS_AECR_OVMACADDE_OFFSET 6 #define SPR_SYS_AECR_OVMACADDE_MASK 0x00000040 #define SPR_SYS_AECR_OVMACADDE_GET(x) (((x) >> 6) & 0x1) #define SPR_SYS_AECR_OVMACADDE_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* Arithmetic Exception Status Register */ #define SPR_SYS_AESR_INDEX U(0x00d) #define SPR_SYS_AESR_ADDR U(0x000d) /* Carry on Add Exception */ #define SPR_SYS_AESR_CYADDE_OFFSET 0 #define SPR_SYS_AESR_CYADDE_MASK 0x00000001 #define SPR_SYS_AESR_CYADDE_GET(x) (((x) >> 0) & 0x1) #define SPR_SYS_AESR_CYADDE_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Overflow on Add Exception */ #define SPR_SYS_AESR_OVADDE_OFFSET 1 #define SPR_SYS_AESR_OVADDE_MASK 0x00000002 #define SPR_SYS_AESR_OVADDE_GET(x) (((x) >> 1) & 0x1) #define SPR_SYS_AESR_OVADDE_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Carry on Multiply Exception */ #define SPR_SYS_AESR_CYMULE_OFFSET 2 #define SPR_SYS_AESR_CYMULE_MASK 0x00000004 #define SPR_SYS_AESR_CYMULE_GET(x) (((x) >> 2) & 0x1) #define SPR_SYS_AESR_CYMULE_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* Overflow on Multiply Exception */ #define SPR_SYS_AESR_OVMULE_OFFSET 3 #define SPR_SYS_AESR_OVMULE_MASK 0x00000008 #define SPR_SYS_AESR_OVMULE_GET(x) (((x) >> 3) & 0x1) #define SPR_SYS_AESR_OVMULE_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Divide by Zero Exception */ #define SPR_SYS_AESR_DBZE_OFFSET 4 #define SPR_SYS_AESR_DBZE_MASK 0x00000010 #define SPR_SYS_AESR_DBZE_GET(x) (((x) >> 4) & 0x1) #define SPR_SYS_AESR_DBZE_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Carry on MAC Addition Exception */ #define SPR_SYS_AESR_CYMACADDE_OFFSET 5 #define SPR_SYS_AESR_CYMACADDE_MASK 0x00000020 #define SPR_SYS_AESR_CYMACADDE_GET(x) (((x) >> 5) & 0x1) #define SPR_SYS_AESR_CYMACADDE_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Overflow on MAC Addition Exception */ #define SPR_SYS_AESR_OVMACADDE_OFFSET 6 #define SPR_SYS_AESR_OVMACADDE_MASK 0x00000040 #define SPR_SYS_AESR_OVMACADDE_GET(x) (((x) >> 6) & 0x1) #define SPR_SYS_AESR_OVMACADDE_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* Next Program Counter */ #define SPR_SYS_NPC_INDEX U(0x010) #define SPR_SYS_NPC_ADDR U(0x0010) /* Supervision Register */ #define SPR_SYS_SR_INDEX U(0x011) #define SPR_SYS_SR_ADDR U(0x0011) /* Supervisor Mode */ #define SPR_SYS_SR_SM_OFFSET 0 #define SPR_SYS_SR_SM_MASK 0x00000001 #define SPR_SYS_SR_SM_GET(x) (((x) >> 0) & 0x1) #define SPR_SYS_SR_SM_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Tick Timer Exception Enabled */ #define SPR_SYS_SR_TEE_OFFSET 1 #define SPR_SYS_SR_TEE_MASK 0x00000002 #define SPR_SYS_SR_TEE_GET(x) (((x) >> 1) & 0x1) #define SPR_SYS_SR_TEE_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Interrupt Exception Enabled */ #define SPR_SYS_SR_IEE_OFFSET 2 #define SPR_SYS_SR_IEE_MASK 0x00000004 #define SPR_SYS_SR_IEE_GET(x) (((x) >> 2) & 0x1) #define SPR_SYS_SR_IEE_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* Data Cache Enabled */ #define SPR_SYS_SR_DCE_OFFSET 3 #define SPR_SYS_SR_DCE_MASK 0x00000008 #define SPR_SYS_SR_DCE_GET(x) (((x) >> 3) & 0x1) #define SPR_SYS_SR_DCE_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Instruction Cache Enabled */ #define SPR_SYS_SR_ICE_OFFSET 4 #define SPR_SYS_SR_ICE_MASK 0x00000010 #define SPR_SYS_SR_ICE_GET(x) (((x) >> 4) & 0x1) #define SPR_SYS_SR_ICE_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Data MMU Enabled */ #define SPR_SYS_SR_DME_OFFSET 5 #define SPR_SYS_SR_DME_MASK 0x00000020 #define SPR_SYS_SR_DME_GET(x) (((x) >> 5) & 0x1) #define SPR_SYS_SR_DME_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Instruction MMU Enabled */ #define SPR_SYS_SR_IME_OFFSET 6 #define SPR_SYS_SR_IME_MASK 0x00000040 #define SPR_SYS_SR_IME_GET(x) (((x) >> 6) & 0x1) #define SPR_SYS_SR_IME_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* Little Endian Enabled */ #define SPR_SYS_SR_LEE_OFFSET 7 #define SPR_SYS_SR_LEE_MASK 0x00000080 #define SPR_SYS_SR_LEE_GET(x) (((x) >> 7) & 0x1) #define SPR_SYS_SR_LEE_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* CID Enable */ #define SPR_SYS_SR_CE_OFFSET 8 #define SPR_SYS_SR_CE_MASK 0x00000100 #define SPR_SYS_SR_CE_GET(x) (((x) >> 8) & 0x1) #define SPR_SYS_SR_CE_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* Flag */ #define SPR_SYS_SR_F_OFFSET 9 #define SPR_SYS_SR_F_MASK 0x00000200 #define SPR_SYS_SR_F_GET(x) (((x) >> 9) & 0x1) #define SPR_SYS_SR_F_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* Carry */ #define SPR_SYS_SR_CY_OFFSET 10 #define SPR_SYS_SR_CY_MASK 0x00000400 #define SPR_SYS_SR_CY_GET(x) (((x) >> 10) & 0x1) #define SPR_SYS_SR_CY_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* Overflow */ #define SPR_SYS_SR_OV_OFFSET 11 #define SPR_SYS_SR_OV_MASK 0x00000800 #define SPR_SYS_SR_OV_GET(x) (((x) >> 11) & 0x1) #define SPR_SYS_SR_OV_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* Overflow Exception Enabled */ #define SPR_SYS_SR_OVE_OFFSET 12 #define SPR_SYS_SR_OVE_MASK 0x00001000 #define SPR_SYS_SR_OVE_GET(x) (((x) >> 12) & 0x1) #define SPR_SYS_SR_OVE_SET(x, y) (((x) & U(0xffffefff)) | \ ((!!(y)) << 12)) /* Delay-slot Exception */ #define SPR_SYS_SR_DSX_OFFSET 13 #define SPR_SYS_SR_DSX_MASK 0x00002000 #define SPR_SYS_SR_DSX_GET(x) (((x) >> 13) & 0x1) #define SPR_SYS_SR_DSX_SET(x, y) (((x) & U(0xffffdfff)) | \ ((!!(y)) << 13)) /* Exception Prefix High */ #define SPR_SYS_SR_EPH_OFFSET 14 #define SPR_SYS_SR_EPH_MASK 0x00004000 #define SPR_SYS_SR_EPH_GET(x) (((x) >> 14) & 0x1) #define SPR_SYS_SR_EPH_SET(x, y) (((x) & U(0xffffbfff)) | \ ((!!(y)) << 14)) /* Fixed One */ #define SPR_SYS_SR_FO_OFFSET 15 #define SPR_SYS_SR_FO_MASK 0x00008000 #define SPR_SYS_SR_FO_GET(x) (((x) >> 15) & 0x1) #define SPR_SYS_SR_FO_SET(x, y) (((x) & U(0xffff7fff)) | \ ((!!(y)) << 15)) /* SPR User Mode Read Access */ #define SPR_SYS_SR_SUMRA_OFFSET 16 #define SPR_SYS_SR_SUMRA_MASK 0x00010000 #define SPR_SYS_SR_SUMRA_GET(x) (((x) >> 16) & 0x1) #define SPR_SYS_SR_SUMRA_SET(x, y) (((x) & U(0xfffeffff)) | \ ((!!(y)) << 16)) /* Context ID */ #define SPR_SYS_SR_CID_LSB 28 #define SPR_SYS_SR_CID_MSB 31 #define SPR_SYS_SR_CID_BITS 4 #define SPR_SYS_SR_CID_MASK U(0xf0000000) #define SPR_SYS_SR_CID_GET(x) (((x) >> 28) & U(0x0000000f)) #define SPR_SYS_SR_CID_SET(x, y) (((x) & U(0x0fffffff)) | \ ((y) << 28)) /* Previous Program Counter */ #define SPR_SYS_PPC_INDEX U(0x012) #define SPR_SYS_PPC_ADDR U(0x0012) /* Floating Point Control Status Register */ #define SPR_SYS_FPCSR_INDEX U(0x014) #define SPR_SYS_FPCSR_ADDR U(0x0014) /* Floating Point Exception Enabled */ #define SPR_SYS_FPCSR_FPEE_OFFSET 0 #define SPR_SYS_FPCSR_FPEE_MASK 0x00000001 #define SPR_SYS_FPCSR_FPEE_GET(x) (((x) >> 0) & 0x1) #define SPR_SYS_FPCSR_FPEE_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Rounding Mode */ #define SPR_SYS_FPCSR_RM_LSB 1 #define SPR_SYS_FPCSR_RM_MSB 2 #define SPR_SYS_FPCSR_RM_BITS 2 #define SPR_SYS_FPCSR_RM_MASK U(0x00000006) #define SPR_SYS_FPCSR_RM_GET(x) (((x) >> 1) & U(0x00000003)) #define SPR_SYS_FPCSR_RM_SET(x, y) (((x) & U(0xfffffff9)) | \ ((y) << 1)) /* Round to nearest */ #define SPR_SYS_FPCSR_RM_NEAREST 0 /* Round to zero */ #define SPR_SYS_FPCSR_RM_ZERO 1 /* Round to infinity+ */ #define SPR_SYS_FPCSR_RM_INFPLUS 2 /* Round to infinity- */ #define SPR_SYS_FPCSR_RM_INFMINUS 3 /* Overflow Flag */ #define SPR_SYS_FPCSR_OVF_OFFSET 3 #define SPR_SYS_FPCSR_OVF_MASK 0x00000008 #define SPR_SYS_FPCSR_OVF_GET(x) (((x) >> 3) & 0x1) #define SPR_SYS_FPCSR_OVF_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Underflow Flag */ #define SPR_SYS_FPCSR_UNF_OFFSET 4 #define SPR_SYS_FPCSR_UNF_MASK 0x00000010 #define SPR_SYS_FPCSR_UNF_GET(x) (((x) >> 4) & 0x1) #define SPR_SYS_FPCSR_UNF_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* SNAN Flag */ #define SPR_SYS_FPCSR_SNF_OFFSET 5 #define SPR_SYS_FPCSR_SNF_MASK 0x00000020 #define SPR_SYS_FPCSR_SNF_GET(x) (((x) >> 5) & 0x1) #define SPR_SYS_FPCSR_SNF_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* QNAN Flag */ #define SPR_SYS_FPCSR_QNF_OFFSET 6 #define SPR_SYS_FPCSR_QNF_MASK 0x00000040 #define SPR_SYS_FPCSR_QNF_GET(x) (((x) >> 6) & 0x1) #define SPR_SYS_FPCSR_QNF_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* Zero Flag */ #define SPR_SYS_FPCSR_ZF_OFFSET 7 #define SPR_SYS_FPCSR_ZF_MASK 0x00000080 #define SPR_SYS_FPCSR_ZF_GET(x) (((x) >> 7) & 0x1) #define SPR_SYS_FPCSR_ZF_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* Inexact Flag */ #define SPR_SYS_FPCSR_IXF_OFFSET 8 #define SPR_SYS_FPCSR_IXF_MASK 0x00000100 #define SPR_SYS_FPCSR_IXF_GET(x) (((x) >> 8) & 0x1) #define SPR_SYS_FPCSR_IXF_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* Invalid Flag */ #define SPR_SYS_FPCSR_IVF_OFFSET 9 #define SPR_SYS_FPCSR_IVF_MASK 0x00000200 #define SPR_SYS_FPCSR_IVF_GET(x) (((x) >> 9) & 0x1) #define SPR_SYS_FPCSR_IVF_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* Infinity Flag */ #define SPR_SYS_FPCSR_INF_OFFSET 10 #define SPR_SYS_FPCSR_INF_MASK 0x00000400 #define SPR_SYS_FPCSR_INF_GET(x) (((x) >> 10) & 0x1) #define SPR_SYS_FPCSR_INF_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* Divide by Zero Flag */ #define SPR_SYS_FPCSR_DZF_OFFSET 11 #define SPR_SYS_FPCSR_DZF_MASK 0x00000800 #define SPR_SYS_FPCSR_DZF_GET(x) (((x) >> 11) & 0x1) #define SPR_SYS_FPCSR_DZF_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* Implementation-specific Registers */ #define SPR_SYS_ISR_BASE U(0x015) #define SPR_SYS_ISR_COUNT U(0x008) #define SPR_SYS_ISR_STEP U(0x001) #define SPR_SYS_ISR_INDEX(N) (SPR_SYS_ISR_BASE + \ (SPR_SYS_ISR_STEP * (N))) #define SPR_SYS_ISR_ADDR(N) ((SPR_SYS_GROUP << SPR_GROUP_LSB) | \ SPR_SYS_ISR_INDEX(N)) /* Exception PC Registers */ #define SPR_SYS_EPCR_BASE U(0x020) #define SPR_SYS_EPCR_COUNT U(0x010) #define SPR_SYS_EPCR_STEP U(0x001) #define SPR_SYS_EPCR_INDEX(N) (SPR_SYS_EPCR_BASE + \ (SPR_SYS_EPCR_STEP * (N))) #define SPR_SYS_EPCR_ADDR(N) ((SPR_SYS_GROUP << SPR_GROUP_LSB) | \ SPR_SYS_EPCR_INDEX(N)) /* Exception Effective Address Registers */ #define SPR_SYS_EEAR_BASE U(0x030) #define SPR_SYS_EEAR_COUNT U(0x010) #define SPR_SYS_EEAR_STEP U(0x001) #define SPR_SYS_EEAR_INDEX(N) (SPR_SYS_EEAR_BASE + \ (SPR_SYS_EEAR_STEP * (N))) #define SPR_SYS_EEAR_ADDR(N) ((SPR_SYS_GROUP << SPR_GROUP_LSB) | \ SPR_SYS_EEAR_INDEX(N)) /* Exception Supervision Registers */ #define SPR_SYS_ESR_BASE U(0x040) #define SPR_SYS_ESR_COUNT U(0x010) #define SPR_SYS_ESR_STEP U(0x001) #define SPR_SYS_ESR_INDEX(N) (SPR_SYS_ESR_BASE + \ (SPR_SYS_ESR_STEP * (N))) #define SPR_SYS_ESR_ADDR(N) ((SPR_SYS_GROUP << SPR_GROUP_LSB) | \ SPR_SYS_ESR_INDEX(N)) /* Supervisor Mode */ #define SPR_SYS_ESR_SM_OFFSET 0 #define SPR_SYS_ESR_SM_MASK 0x00000001 #define SPR_SYS_ESR_SM_GET(x) (((x) >> 0) & 0x1) #define SPR_SYS_ESR_SM_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Tick Timer Exception Enabled */ #define SPR_SYS_ESR_TEE_OFFSET 1 #define SPR_SYS_ESR_TEE_MASK 0x00000002 #define SPR_SYS_ESR_TEE_GET(x) (((x) >> 1) & 0x1) #define SPR_SYS_ESR_TEE_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Interrupt Exception Enabled */ #define SPR_SYS_ESR_IEE_OFFSET 2 #define SPR_SYS_ESR_IEE_MASK 0x00000004 #define SPR_SYS_ESR_IEE_GET(x) (((x) >> 2) & 0x1) #define SPR_SYS_ESR_IEE_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* Data Cache Enabled */ #define SPR_SYS_ESR_DCE_OFFSET 3 #define SPR_SYS_ESR_DCE_MASK 0x00000008 #define SPR_SYS_ESR_DCE_GET(x) (((x) >> 3) & 0x1) #define SPR_SYS_ESR_DCE_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Instruction Cache Enabled */ #define SPR_SYS_ESR_ICE_OFFSET 4 #define SPR_SYS_ESR_ICE_MASK 0x00000010 #define SPR_SYS_ESR_ICE_GET(x) (((x) >> 4) & 0x1) #define SPR_SYS_ESR_ICE_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Data MMU Enabled */ #define SPR_SYS_ESR_DME_OFFSET 5 #define SPR_SYS_ESR_DME_MASK 0x00000020 #define SPR_SYS_ESR_DME_GET(x) (((x) >> 5) & 0x1) #define SPR_SYS_ESR_DME_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Instruction MMU Enabled */ #define SPR_SYS_ESR_IME_OFFSET 6 #define SPR_SYS_ESR_IME_MASK 0x00000040 #define SPR_SYS_ESR_IME_GET(x) (((x) >> 6) & 0x1) #define SPR_SYS_ESR_IME_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* Little Endian Enabled */ #define SPR_SYS_ESR_LEE_OFFSET 7 #define SPR_SYS_ESR_LEE_MASK 0x00000080 #define SPR_SYS_ESR_LEE_GET(x) (((x) >> 7) & 0x1) #define SPR_SYS_ESR_LEE_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* CID Enable */ #define SPR_SYS_ESR_CE_OFFSET 8 #define SPR_SYS_ESR_CE_MASK 0x00000100 #define SPR_SYS_ESR_CE_GET(x) (((x) >> 8) & 0x1) #define SPR_SYS_ESR_CE_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* Flag */ #define SPR_SYS_ESR_F_OFFSET 9 #define SPR_SYS_ESR_F_MASK 0x00000200 #define SPR_SYS_ESR_F_GET(x) (((x) >> 9) & 0x1) #define SPR_SYS_ESR_F_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* Carry */ #define SPR_SYS_ESR_CY_OFFSET 10 #define SPR_SYS_ESR_CY_MASK 0x00000400 #define SPR_SYS_ESR_CY_GET(x) (((x) >> 10) & 0x1) #define SPR_SYS_ESR_CY_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* Overflow */ #define SPR_SYS_ESR_OV_OFFSET 11 #define SPR_SYS_ESR_OV_MASK 0x00000800 #define SPR_SYS_ESR_OV_GET(x) (((x) >> 11) & 0x1) #define SPR_SYS_ESR_OV_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* Overflow Exception Enabled */ #define SPR_SYS_ESR_OVE_OFFSET 12 #define SPR_SYS_ESR_OVE_MASK 0x00001000 #define SPR_SYS_ESR_OVE_GET(x) (((x) >> 12) & 0x1) #define SPR_SYS_ESR_OVE_SET(x, y) (((x) & U(0xffffefff)) | \ ((!!(y)) << 12)) /* Delay-slot Exception */ #define SPR_SYS_ESR_DSX_OFFSET 13 #define SPR_SYS_ESR_DSX_MASK 0x00002000 #define SPR_SYS_ESR_DSX_GET(x) (((x) >> 13) & 0x1) #define SPR_SYS_ESR_DSX_SET(x, y) (((x) & U(0xffffdfff)) | \ ((!!(y)) << 13)) /* Exception Prefix High */ #define SPR_SYS_ESR_EPH_OFFSET 14 #define SPR_SYS_ESR_EPH_MASK 0x00004000 #define SPR_SYS_ESR_EPH_GET(x) (((x) >> 14) & 0x1) #define SPR_SYS_ESR_EPH_SET(x, y) (((x) & U(0xffffbfff)) | \ ((!!(y)) << 14)) /* Fixed One */ #define SPR_SYS_ESR_FO_OFFSET 15 #define SPR_SYS_ESR_FO_MASK 0x00008000 #define SPR_SYS_ESR_FO_GET(x) (((x) >> 15) & 0x1) #define SPR_SYS_ESR_FO_SET(x, y) (((x) & U(0xffff7fff)) | \ ((!!(y)) << 15)) /* SPR User Mode Read Access */ #define SPR_SYS_ESR_SUMRA_OFFSET 16 #define SPR_SYS_ESR_SUMRA_MASK 0x00010000 #define SPR_SYS_ESR_SUMRA_GET(x) (((x) >> 16) & 0x1) #define SPR_SYS_ESR_SUMRA_SET(x, y) (((x) & U(0xfffeffff)) | \ ((!!(y)) << 16)) /* Context ID */ #define SPR_SYS_ESR_CID_LSB 28 #define SPR_SYS_ESR_CID_MSB 31 #define SPR_SYS_ESR_CID_BITS 4 #define SPR_SYS_ESR_CID_MASK U(0xf0000000) #define SPR_SYS_ESR_CID_GET(x) (((x) >> 28) & U(0x0000000f)) #define SPR_SYS_ESR_CID_SET(x, y) (((x) & U(0x0fffffff)) | \ ((y) << 28)) /* Core identifier (multicore) */ #define SPR_SYS_COREID_INDEX U(0x080) #define SPR_SYS_COREID_ADDR U(0x0080) /* Number of cores (multicore) */ #define SPR_SYS_NUMCORES_INDEX U(0x081) #define SPR_SYS_NUMCORES_ADDR U(0x0081) /* General Purpose Registers */ #define SPR_SYS_GPR_BASE U(0x400) #define SPR_SYS_GPR_COUNT U(0x100) #define SPR_SYS_GPR_STEP U(0x001) #define SPR_SYS_GPR_INDEX(N) (SPR_SYS_GPR_BASE + \ (SPR_SYS_GPR_STEP * (N))) #define SPR_SYS_GPR_ADDR(N) ((SPR_SYS_GROUP << SPR_GROUP_LSB) | \ SPR_SYS_GPR_INDEX(N)) /******************/ /* Data MMU Group */ /******************/ #define SPR_DMMU_GROUP 0x01 /* Instruction MMU Control Register */ #define SPR_DMMU_DMMUCR_INDEX U(0x000) #define SPR_DMMU_DMMUCR_ADDR U(0x0800) /* DTLB Flush */ #define SPR_DMMU_DMMUCR_DTF_OFFSET 0 #define SPR_DMMU_DMMUCR_DTF_MASK 0x00000001 #define SPR_DMMU_DMMUCR_DTF_GET(x) (((x) >> 0) & 0x1) #define SPR_DMMU_DMMUCR_DTF_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Page Table Base Pointer */ #define SPR_DMMU_DMMUCR_PTBP_LSB 10 #define SPR_DMMU_DMMUCR_PTBP_MSB 31 #define SPR_DMMU_DMMUCR_PTBP_BITS 22 #define SPR_DMMU_DMMUCR_PTBP_MASK U(0xfffffc00) #define SPR_DMMU_DMMUCR_PTBP_GET(x) (((x) >> 10) & U(0x003fffff)) #define SPR_DMMU_DMMUCR_PTBP_SET(x, y) (((x) & U(0x000003ff)) | \ ((y) << 10)) /* Data MMU Protection Register */ #define SPR_DMMU_DMMUPR_INDEX U(0x001) #define SPR_DMMU_DMMUPR_ADDR U(0x0801) /* Supervisor Read Enable 1 */ #define SPR_DMMU_DMMUPR_SRE1_OFFSET 0 #define SPR_DMMU_DMMUPR_SRE1_MASK 0x00000001 #define SPR_DMMU_DMMUPR_SRE1_GET(x) (((x) >> 0) & 0x1) #define SPR_DMMU_DMMUPR_SRE1_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Supervisor Write Enable 1 */ #define SPR_DMMU_DMMUPR_SWE1_OFFSET 1 #define SPR_DMMU_DMMUPR_SWE1_MASK 0x00000002 #define SPR_DMMU_DMMUPR_SWE1_GET(x) (((x) >> 1) & 0x1) #define SPR_DMMU_DMMUPR_SWE1_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* User Read Enable 1 */ #define SPR_DMMU_DMMUPR_URE1_OFFSET 2 #define SPR_DMMU_DMMUPR_URE1_MASK 0x00000004 #define SPR_DMMU_DMMUPR_URE1_GET(x) (((x) >> 2) & 0x1) #define SPR_DMMU_DMMUPR_URE1_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* User Write Enable 1 */ #define SPR_DMMU_DMMUPR_UWE1_OFFSET 3 #define SPR_DMMU_DMMUPR_UWE1_MASK 0x00000008 #define SPR_DMMU_DMMUPR_UWE1_GET(x) (((x) >> 3) & 0x1) #define SPR_DMMU_DMMUPR_UWE1_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Supervisor Read Enable 2 */ #define SPR_DMMU_DMMUPR_SRE2_OFFSET 4 #define SPR_DMMU_DMMUPR_SRE2_MASK 0x00000010 #define SPR_DMMU_DMMUPR_SRE2_GET(x) (((x) >> 4) & 0x1) #define SPR_DMMU_DMMUPR_SRE2_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Supervisor Write Enable 2 */ #define SPR_DMMU_DMMUPR_SWE2_OFFSET 5 #define SPR_DMMU_DMMUPR_SWE2_MASK 0x00000020 #define SPR_DMMU_DMMUPR_SWE2_GET(x) (((x) >> 5) & 0x1) #define SPR_DMMU_DMMUPR_SWE2_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* User Read Enable 2 */ #define SPR_DMMU_DMMUPR_URE2_OFFSET 6 #define SPR_DMMU_DMMUPR_URE2_MASK 0x00000040 #define SPR_DMMU_DMMUPR_URE2_GET(x) (((x) >> 6) & 0x1) #define SPR_DMMU_DMMUPR_URE2_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* User Write Enable 2 */ #define SPR_DMMU_DMMUPR_UWE2_OFFSET 7 #define SPR_DMMU_DMMUPR_UWE2_MASK 0x00000080 #define SPR_DMMU_DMMUPR_UWE2_GET(x) (((x) >> 7) & 0x1) #define SPR_DMMU_DMMUPR_UWE2_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* Supervisor Read Enable 3 */ #define SPR_DMMU_DMMUPR_SRE3_OFFSET 8 #define SPR_DMMU_DMMUPR_SRE3_MASK 0x00000100 #define SPR_DMMU_DMMUPR_SRE3_GET(x) (((x) >> 8) & 0x1) #define SPR_DMMU_DMMUPR_SRE3_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* Supervisor Write Enable 3 */ #define SPR_DMMU_DMMUPR_SWE3_OFFSET 9 #define SPR_DMMU_DMMUPR_SWE3_MASK 0x00000200 #define SPR_DMMU_DMMUPR_SWE3_GET(x) (((x) >> 9) & 0x1) #define SPR_DMMU_DMMUPR_SWE3_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* User Read Enable 3 */ #define SPR_DMMU_DMMUPR_URE3_OFFSET 10 #define SPR_DMMU_DMMUPR_URE3_MASK 0x00000400 #define SPR_DMMU_DMMUPR_URE3_GET(x) (((x) >> 10) & 0x1) #define SPR_DMMU_DMMUPR_URE3_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* User Write Enable 3 */ #define SPR_DMMU_DMMUPR_UWE3_OFFSET 11 #define SPR_DMMU_DMMUPR_UWE3_MASK 0x00000800 #define SPR_DMMU_DMMUPR_UWE3_GET(x) (((x) >> 11) & 0x1) #define SPR_DMMU_DMMUPR_UWE3_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* Supervisor Read Enable 4 */ #define SPR_DMMU_DMMUPR_SRE4_OFFSET 12 #define SPR_DMMU_DMMUPR_SRE4_MASK 0x00001000 #define SPR_DMMU_DMMUPR_SRE4_GET(x) (((x) >> 12) & 0x1) #define SPR_DMMU_DMMUPR_SRE4_SET(x, y) (((x) & U(0xffffefff)) | \ ((!!(y)) << 12)) /* Supervisor Write Enable 4 */ #define SPR_DMMU_DMMUPR_SWE4_OFFSET 13 #define SPR_DMMU_DMMUPR_SWE4_MASK 0x00002000 #define SPR_DMMU_DMMUPR_SWE4_GET(x) (((x) >> 13) & 0x1) #define SPR_DMMU_DMMUPR_SWE4_SET(x, y) (((x) & U(0xffffdfff)) | \ ((!!(y)) << 13)) /* User Read Enable 4 */ #define SPR_DMMU_DMMUPR_URE4_OFFSET 14 #define SPR_DMMU_DMMUPR_URE4_MASK 0x00004000 #define SPR_DMMU_DMMUPR_URE4_GET(x) (((x) >> 14) & 0x1) #define SPR_DMMU_DMMUPR_URE4_SET(x, y) (((x) & U(0xffffbfff)) | \ ((!!(y)) << 14)) /* User Write Enable 4 */ #define SPR_DMMU_DMMUPR_UWE4_OFFSET 15 #define SPR_DMMU_DMMUPR_UWE4_MASK 0x00008000 #define SPR_DMMU_DMMUPR_UWE4_GET(x) (((x) >> 15) & 0x1) #define SPR_DMMU_DMMUPR_UWE4_SET(x, y) (((x) & U(0xffff7fff)) | \ ((!!(y)) << 15)) /* Supervisor Read Enable 5 */ #define SPR_DMMU_DMMUPR_SRE5_OFFSET 16 #define SPR_DMMU_DMMUPR_SRE5_MASK 0x00010000 #define SPR_DMMU_DMMUPR_SRE5_GET(x) (((x) >> 16) & 0x1) #define SPR_DMMU_DMMUPR_SRE5_SET(x, y) (((x) & U(0xfffeffff)) | \ ((!!(y)) << 16)) /* Supervisor Write Enable 5 */ #define SPR_DMMU_DMMUPR_SWE5_OFFSET 17 #define SPR_DMMU_DMMUPR_SWE5_MASK 0x00020000 #define SPR_DMMU_DMMUPR_SWE5_GET(x) (((x) >> 17) & 0x1) #define SPR_DMMU_DMMUPR_SWE5_SET(x, y) (((x) & U(0xfffdffff)) | \ ((!!(y)) << 17)) /* User Read Enable 5 */ #define SPR_DMMU_DMMUPR_URE5_OFFSET 18 #define SPR_DMMU_DMMUPR_URE5_MASK 0x00040000 #define SPR_DMMU_DMMUPR_URE5_GET(x) (((x) >> 18) & 0x1) #define SPR_DMMU_DMMUPR_URE5_SET(x, y) (((x) & U(0xfffbffff)) | \ ((!!(y)) << 18)) /* User Write Enable 5 */ #define SPR_DMMU_DMMUPR_UWE5_OFFSET 19 #define SPR_DMMU_DMMUPR_UWE5_MASK 0x00080000 #define SPR_DMMU_DMMUPR_UWE5_GET(x) (((x) >> 19) & 0x1) #define SPR_DMMU_DMMUPR_UWE5_SET(x, y) (((x) & U(0xfff7ffff)) | \ ((!!(y)) << 19)) /* Supervisor Read Enable 6 */ #define SPR_DMMU_DMMUPR_SRE6_OFFSET 20 #define SPR_DMMU_DMMUPR_SRE6_MASK 0x00100000 #define SPR_DMMU_DMMUPR_SRE6_GET(x) (((x) >> 20) & 0x1) #define SPR_DMMU_DMMUPR_SRE6_SET(x, y) (((x) & U(0xffefffff)) | \ ((!!(y)) << 20)) /* Supervisor Write Enable 6 */ #define SPR_DMMU_DMMUPR_SWE6_OFFSET 21 #define SPR_DMMU_DMMUPR_SWE6_MASK 0x00200000 #define SPR_DMMU_DMMUPR_SWE6_GET(x) (((x) >> 21) & 0x1) #define SPR_DMMU_DMMUPR_SWE6_SET(x, y) (((x) & U(0xffdfffff)) | \ ((!!(y)) << 21)) /* User Read Enable 6 */ #define SPR_DMMU_DMMUPR_URE6_OFFSET 22 #define SPR_DMMU_DMMUPR_URE6_MASK 0x00400000 #define SPR_DMMU_DMMUPR_URE6_GET(x) (((x) >> 22) & 0x1) #define SPR_DMMU_DMMUPR_URE6_SET(x, y) (((x) & U(0xffbfffff)) | \ ((!!(y)) << 22)) /* User Write Enable 6 */ #define SPR_DMMU_DMMUPR_UWE6_OFFSET 23 #define SPR_DMMU_DMMUPR_UWE6_MASK 0x00800000 #define SPR_DMMU_DMMUPR_UWE6_GET(x) (((x) >> 23) & 0x1) #define SPR_DMMU_DMMUPR_UWE6_SET(x, y) (((x) & U(0xff7fffff)) | \ ((!!(y)) << 23)) /* Supervisor Read Enable 7 */ #define SPR_DMMU_DMMUPR_SRE7_OFFSET 24 #define SPR_DMMU_DMMUPR_SRE7_MASK 0x01000000 #define SPR_DMMU_DMMUPR_SRE7_GET(x) (((x) >> 24) & 0x1) #define SPR_DMMU_DMMUPR_SRE7_SET(x, y) (((x) & U(0xfeffffff)) | \ ((!!(y)) << 24)) /* Supervisor Write Enable 7 */ #define SPR_DMMU_DMMUPR_SWE7_OFFSET 25 #define SPR_DMMU_DMMUPR_SWE7_MASK 0x02000000 #define SPR_DMMU_DMMUPR_SWE7_GET(x) (((x) >> 25) & 0x1) #define SPR_DMMU_DMMUPR_SWE7_SET(x, y) (((x) & U(0xfdffffff)) | \ ((!!(y)) << 25)) /* User Read Enable 7 */ #define SPR_DMMU_DMMUPR_URE7_OFFSET 26 #define SPR_DMMU_DMMUPR_URE7_MASK 0x04000000 #define SPR_DMMU_DMMUPR_URE7_GET(x) (((x) >> 26) & 0x1) #define SPR_DMMU_DMMUPR_URE7_SET(x, y) (((x) & U(0xfbffffff)) | \ ((!!(y)) << 26)) /* User Write Enable 7 */ #define SPR_DMMU_DMMUPR_UWE7_OFFSET 27 #define SPR_DMMU_DMMUPR_UWE7_MASK 0x08000000 #define SPR_DMMU_DMMUPR_UWE7_GET(x) (((x) >> 27) & 0x1) #define SPR_DMMU_DMMUPR_UWE7_SET(x, y) (((x) & U(0xf7ffffff)) | \ ((!!(y)) << 27)) /* Data TLB Entry Invalidate Register */ #define SPR_DMMU_DTLBEIR_INDEX U(0x002) #define SPR_DMMU_DTLBEIR_ADDR U(0x0802) /* Data ATB Match Registers */ #define SPR_DMMU_DATBMR_BASE U(0x004) #define SPR_DMMU_DATBMR_COUNT U(0x004) #define SPR_DMMU_DATBMR_STEP U(0x001) #define SPR_DMMU_DATBMR_INDEX(N) (SPR_DMMU_DATBMR_BASE + \ (SPR_DMMU_DATBMR_STEP * (N))) #define SPR_DMMU_DATBMR_ADDR(N) ((SPR_DMMU_GROUP << SPR_GROUP_LSB) | \ SPR_DMMU_DATBMR_INDEX(N)) /* Valid */ #define SPR_DMMU_DATBMR_V_OFFSET 0 #define SPR_DMMU_DATBMR_V_MASK 0x00000001 #define SPR_DMMU_DATBMR_V_GET(x) (((x) >> 0) & 0x1) #define SPR_DMMU_DATBMR_V_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Context ID */ #define SPR_DMMU_DATBMR_CID_LSB 1 #define SPR_DMMU_DATBMR_CID_MSB 4 #define SPR_DMMU_DATBMR_CID_BITS 4 #define SPR_DMMU_DATBMR_CID_MASK U(0x0000001e) #define SPR_DMMU_DATBMR_CID_GET(x) (((x) >> 1) & U(0x0000000f)) #define SPR_DMMU_DATBMR_CID_SET(x, y) (((x) & U(0xffffffe1)) | \ ((y) << 1)) /* Page Size */ #define SPR_DMMU_DATBMR_PS_OFFSET 5 #define SPR_DMMU_DATBMR_PS_MASK 0x00000020 #define SPR_DMMU_DATBMR_PS_GET(x) (((x) >> 5) & 0x1) #define SPR_DMMU_DATBMR_PS_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Virtual Page Number */ #define SPR_DMMU_DATBMR_VPN_LSB 10 #define SPR_DMMU_DATBMR_VPN_MSB 31 #define SPR_DMMU_DATBMR_VPN_BITS 22 #define SPR_DMMU_DATBMR_VPN_MASK U(0xfffffc00) #define SPR_DMMU_DATBMR_VPN_GET(x) (((x) >> 10) & U(0x003fffff)) #define SPR_DMMU_DATBMR_VPN_SET(x, y) (((x) & U(0x000003ff)) | \ ((y) << 10)) /* Data ATB Translate Registers */ #define SPR_DMMU_DATBTR_BASE U(0x008) #define SPR_DMMU_DATBTR_COUNT U(0x004) #define SPR_DMMU_DATBTR_STEP U(0x001) #define SPR_DMMU_DATBTR_INDEX(N) (SPR_DMMU_DATBTR_BASE + \ (SPR_DMMU_DATBTR_STEP * (N))) #define SPR_DMMU_DATBTR_ADDR(N) ((SPR_DMMU_GROUP << SPR_GROUP_LSB) | \ SPR_DMMU_DATBTR_INDEX(N)) /* Cache Coherency */ #define SPR_DMMU_DATBTR_CC_OFFSET 0 #define SPR_DMMU_DATBTR_CC_MASK 0x00000001 #define SPR_DMMU_DATBTR_CC_GET(x) (((x) >> 0) & 0x1) #define SPR_DMMU_DATBTR_CC_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Cache Inhibit */ #define SPR_DMMU_DATBTR_CI_OFFSET 1 #define SPR_DMMU_DATBTR_CI_MASK 0x00000002 #define SPR_DMMU_DATBTR_CI_GET(x) (((x) >> 1) & 0x1) #define SPR_DMMU_DATBTR_CI_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Write-back Cache */ #define SPR_DMMU_DATBTR_WBC_OFFSET 2 #define SPR_DMMU_DATBTR_WBC_MASK 0x00000004 #define SPR_DMMU_DATBTR_WBC_GET(x) (((x) >> 2) & 0x1) #define SPR_DMMU_DATBTR_WBC_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* Weakly-ordered Memory */ #define SPR_DMMU_DATBTR_WOM_OFFSET 3 #define SPR_DMMU_DATBTR_WOM_MASK 0x00000008 #define SPR_DMMU_DATBTR_WOM_GET(x) (((x) >> 3) & 0x1) #define SPR_DMMU_DATBTR_WOM_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Accessed */ #define SPR_DMMU_DATBTR_A_OFFSET 4 #define SPR_DMMU_DATBTR_A_MASK 0x00000010 #define SPR_DMMU_DATBTR_A_GET(x) (((x) >> 4) & 0x1) #define SPR_DMMU_DATBTR_A_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Dirty */ #define SPR_DMMU_DATBTR_D_OFFSET 5 #define SPR_DMMU_DATBTR_D_MASK 0x00000020 #define SPR_DMMU_DATBTR_D_GET(x) (((x) >> 5) & 0x1) #define SPR_DMMU_DATBTR_D_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Supervisor Read Enable */ #define SPR_DMMU_DATBTR_SRE_OFFSET 6 #define SPR_DMMU_DATBTR_SRE_MASK 0x00000040 #define SPR_DMMU_DATBTR_SRE_GET(x) (((x) >> 6) & 0x1) #define SPR_DMMU_DATBTR_SRE_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* Supervisor Write Enable */ #define SPR_DMMU_DATBTR_SWE_OFFSET 7 #define SPR_DMMU_DATBTR_SWE_MASK 0x00000080 #define SPR_DMMU_DATBTR_SWE_GET(x) (((x) >> 7) & 0x1) #define SPR_DMMU_DATBTR_SWE_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* User Read Enable */ #define SPR_DMMU_DATBTR_URE_OFFSET 8 #define SPR_DMMU_DATBTR_URE_MASK 0x00000100 #define SPR_DMMU_DATBTR_URE_GET(x) (((x) >> 8) & 0x1) #define SPR_DMMU_DATBTR_URE_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* User Write Enable */ #define SPR_DMMU_DATBTR_UWE_OFFSET 9 #define SPR_DMMU_DATBTR_UWE_MASK 0x00000200 #define SPR_DMMU_DATBTR_UWE_GET(x) (((x) >> 9) & 0x1) #define SPR_DMMU_DATBTR_UWE_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* Physical Page Number */ #define SPR_DMMU_DATBTR_PPN_LSB 10 #define SPR_DMMU_DATBTR_PPN_MSB 31 #define SPR_DMMU_DATBTR_PPN_BITS 22 #define SPR_DMMU_DATBTR_PPN_MASK U(0xfffffc00) #define SPR_DMMU_DATBTR_PPN_GET(x) (((x) >> 10) & U(0x003fffff)) #define SPR_DMMU_DATBTR_PPN_SET(x, y) (((x) & U(0x000003ff)) | \ ((y) << 10)) /* Data TLB */ #define SPR_DMMU_DTLBW_BASE U(0x200) #define SPR_DMMU_DTLBW_COUNT U(0x004) #define SPR_DMMU_DTLBW_STEP U(0x100) #define SPR_DMMU_DTLBW_SUBBASE(N0) (SPR_DMMU_DTLBW_BASE + \ (SPR_DMMU_DTLBW_STEP * (N0))) /* Data TLB Match Registers */ #define SPR_DMMU_DTLBW_MR_BASE U(0x000) #define SPR_DMMU_DTLBW_MR_COUNT U(0x080) #define SPR_DMMU_DTLBW_MR_STEP U(0x001) #define SPR_DMMU_DTLBW_MR_INDEX(N0, N1) (SPR_DMMU_DTLBW_SUBBASE(N0) + \ SPR_DMMU_DTLBW_MR_BASE + \ (SPR_DMMU_DTLBW_MR_STEP * (N1))) #define SPR_DMMU_DTLBW_MR_ADDR(N0, N1) ((SPR_DMMU_GROUP << SPR_GROUP_LSB) | \ SPR_DMMU_DTLBW_MR_INDEX(N0, N1)) /* Valid */ #define SPR_DMMU_DTLBW_MR_V_OFFSET 0 #define SPR_DMMU_DTLBW_MR_V_MASK 0x00000001 #define SPR_DMMU_DTLBW_MR_V_GET(x) (((x) >> 0) & 0x1) #define SPR_DMMU_DTLBW_MR_V_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Page Level 1 */ #define SPR_DMMU_DTLBW_MR_PL1_OFFSET 1 #define SPR_DMMU_DTLBW_MR_PL1_MASK 0x00000002 #define SPR_DMMU_DTLBW_MR_PL1_GET(x) (((x) >> 1) & 0x1) #define SPR_DMMU_DTLBW_MR_PL1_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Context ID */ #define SPR_DMMU_DTLBW_MR_CID_LSB 2 #define SPR_DMMU_DTLBW_MR_CID_MSB 5 #define SPR_DMMU_DTLBW_MR_CID_BITS 4 #define SPR_DMMU_DTLBW_MR_CID_MASK U(0x0000003c) #define SPR_DMMU_DTLBW_MR_CID_GET(x) (((x) >> 2) & U(0x0000000f)) #define SPR_DMMU_DTLBW_MR_CID_SET(x, y) (((x) & U(0xffffffc3)) | \ ((y) << 2)) /* Least Recently Used */ #define SPR_DMMU_DTLBW_MR_LRU_LSB 6 #define SPR_DMMU_DTLBW_MR_LRU_MSB 7 #define SPR_DMMU_DTLBW_MR_LRU_BITS 2 #define SPR_DMMU_DTLBW_MR_LRU_MASK U(0x000000c0) #define SPR_DMMU_DTLBW_MR_LRU_GET(x) (((x) >> 6) & U(0x00000003)) #define SPR_DMMU_DTLBW_MR_LRU_SET(x, y) (((x) & U(0xffffff3f)) | \ ((y) << 6)) /* Virtual Page Number */ #define SPR_DMMU_DTLBW_MR_VPN_LSB 13 #define SPR_DMMU_DTLBW_MR_VPN_MSB 31 #define SPR_DMMU_DTLBW_MR_VPN_BITS 19 #define SPR_DMMU_DTLBW_MR_VPN_MASK U(0xffffe000) #define SPR_DMMU_DTLBW_MR_VPN_GET(x) (((x) >> 13) & U(0x0007ffff)) #define SPR_DMMU_DTLBW_MR_VPN_SET(x, y) (((x) & U(0x00001fff)) | \ ((y) << 13)) /* Data TLB Translate Registers */ #define SPR_DMMU_DTLBW_TR_BASE U(0x080) #define SPR_DMMU_DTLBW_TR_COUNT U(0x080) #define SPR_DMMU_DTLBW_TR_STEP U(0x001) #define SPR_DMMU_DTLBW_TR_INDEX(N0, N1) (SPR_DMMU_DTLBW_SUBBASE(N0) + \ SPR_DMMU_DTLBW_TR_BASE + \ (SPR_DMMU_DTLBW_TR_STEP * (N1))) #define SPR_DMMU_DTLBW_TR_ADDR(N0, N1) ((SPR_DMMU_GROUP << SPR_GROUP_LSB) | \ SPR_DMMU_DTLBW_TR_INDEX(N0, N1)) /* Cache Coherency */ #define SPR_DMMU_DTLBW_TR_CC_OFFSET 0 #define SPR_DMMU_DTLBW_TR_CC_MASK 0x00000001 #define SPR_DMMU_DTLBW_TR_CC_GET(x) (((x) >> 0) & 0x1) #define SPR_DMMU_DTLBW_TR_CC_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Cache Inhibit */ #define SPR_DMMU_DTLBW_TR_CI_OFFSET 1 #define SPR_DMMU_DTLBW_TR_CI_MASK 0x00000002 #define SPR_DMMU_DTLBW_TR_CI_GET(x) (((x) >> 1) & 0x1) #define SPR_DMMU_DTLBW_TR_CI_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Write-back Cache */ #define SPR_DMMU_DTLBW_TR_WBC_OFFSET 2 #define SPR_DMMU_DTLBW_TR_WBC_MASK 0x00000004 #define SPR_DMMU_DTLBW_TR_WBC_GET(x) (((x) >> 2) & 0x1) #define SPR_DMMU_DTLBW_TR_WBC_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* Weakly-ordered Memory */ #define SPR_DMMU_DTLBW_TR_WOM_OFFSET 3 #define SPR_DMMU_DTLBW_TR_WOM_MASK 0x00000008 #define SPR_DMMU_DTLBW_TR_WOM_GET(x) (((x) >> 3) & 0x1) #define SPR_DMMU_DTLBW_TR_WOM_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Accessed */ #define SPR_DMMU_DTLBW_TR_A_OFFSET 4 #define SPR_DMMU_DTLBW_TR_A_MASK 0x00000010 #define SPR_DMMU_DTLBW_TR_A_GET(x) (((x) >> 4) & 0x1) #define SPR_DMMU_DTLBW_TR_A_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Dirty */ #define SPR_DMMU_DTLBW_TR_D_OFFSET 5 #define SPR_DMMU_DTLBW_TR_D_MASK 0x00000020 #define SPR_DMMU_DTLBW_TR_D_GET(x) (((x) >> 5) & 0x1) #define SPR_DMMU_DTLBW_TR_D_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* User Read Enable */ #define SPR_DMMU_DTLBW_TR_URE_OFFSET 6 #define SPR_DMMU_DTLBW_TR_URE_MASK 0x00000040 #define SPR_DMMU_DTLBW_TR_URE_GET(x) (((x) >> 6) & 0x1) #define SPR_DMMU_DTLBW_TR_URE_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* User Write Enable */ #define SPR_DMMU_DTLBW_TR_UWE_OFFSET 7 #define SPR_DMMU_DTLBW_TR_UWE_MASK 0x00000080 #define SPR_DMMU_DTLBW_TR_UWE_GET(x) (((x) >> 7) & 0x1) #define SPR_DMMU_DTLBW_TR_UWE_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* Supervisor Read Enable */ #define SPR_DMMU_DTLBW_TR_SRE_OFFSET 8 #define SPR_DMMU_DTLBW_TR_SRE_MASK 0x00000100 #define SPR_DMMU_DTLBW_TR_SRE_GET(x) (((x) >> 8) & 0x1) #define SPR_DMMU_DTLBW_TR_SRE_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* Supervisor Write Enable */ #define SPR_DMMU_DTLBW_TR_SWE_OFFSET 9 #define SPR_DMMU_DTLBW_TR_SWE_MASK 0x00000200 #define SPR_DMMU_DTLBW_TR_SWE_GET(x) (((x) >> 9) & 0x1) #define SPR_DMMU_DTLBW_TR_SWE_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* Physical Page Number */ #define SPR_DMMU_DTLBW_TR_PPN_LSB 13 #define SPR_DMMU_DTLBW_TR_PPN_MSB 31 #define SPR_DMMU_DTLBW_TR_PPN_BITS 19 #define SPR_DMMU_DTLBW_TR_PPN_MASK U(0xffffe000) #define SPR_DMMU_DTLBW_TR_PPN_GET(x) (((x) >> 13) & U(0x0007ffff)) #define SPR_DMMU_DTLBW_TR_PPN_SET(x, y) (((x) & U(0x00001fff)) | \ ((y) << 13)) /*************************/ /* Instruction MMU Group */ /*************************/ #define SPR_IMMU_GROUP 0x02 /* Instruction MMU Control Register */ #define SPR_IMMU_IMMUCR_INDEX U(0x000) #define SPR_IMMU_IMMUCR_ADDR U(0x1000) /* ITLB Flush */ #define SPR_IMMU_IMMUCR_ITF_OFFSET 0 #define SPR_IMMU_IMMUCR_ITF_MASK 0x00000001 #define SPR_IMMU_IMMUCR_ITF_GET(x) (((x) >> 0) & 0x1) #define SPR_IMMU_IMMUCR_ITF_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Page Table Base Pointer */ #define SPR_IMMU_IMMUCR_PTBP_LSB 10 #define SPR_IMMU_IMMUCR_PTBP_MSB 31 #define SPR_IMMU_IMMUCR_PTBP_BITS 22 #define SPR_IMMU_IMMUCR_PTBP_MASK U(0xfffffc00) #define SPR_IMMU_IMMUCR_PTBP_GET(x) (((x) >> 10) & U(0x003fffff)) #define SPR_IMMU_IMMUCR_PTBP_SET(x, y) (((x) & U(0x000003ff)) | \ ((y) << 10)) /* Instruction MMU Protection Register */ #define SPR_IMMU_IMMUPR_INDEX U(0x001) #define SPR_IMMU_IMMUPR_ADDR U(0x1001) /* Supervisor Execute Enable 1 */ #define SPR_IMMU_IMMUPR_SXE1_OFFSET 0 #define SPR_IMMU_IMMUPR_SXE1_MASK 0x00000001 #define SPR_IMMU_IMMUPR_SXE1_GET(x) (((x) >> 0) & 0x1) #define SPR_IMMU_IMMUPR_SXE1_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* User Execute Enable 1 */ #define SPR_IMMU_IMMUPR_UXE1_OFFSET 1 #define SPR_IMMU_IMMUPR_UXE1_MASK 0x00000002 #define SPR_IMMU_IMMUPR_UXE1_GET(x) (((x) >> 1) & 0x1) #define SPR_IMMU_IMMUPR_UXE1_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Supervisor Execute Enable 2 */ #define SPR_IMMU_IMMUPR_SXE2_OFFSET 2 #define SPR_IMMU_IMMUPR_SXE2_MASK 0x00000004 #define SPR_IMMU_IMMUPR_SXE2_GET(x) (((x) >> 2) & 0x1) #define SPR_IMMU_IMMUPR_SXE2_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* User Execute Enable 2 */ #define SPR_IMMU_IMMUPR_UXE2_OFFSET 3 #define SPR_IMMU_IMMUPR_UXE2_MASK 0x00000008 #define SPR_IMMU_IMMUPR_UXE2_GET(x) (((x) >> 3) & 0x1) #define SPR_IMMU_IMMUPR_UXE2_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Supervisor Execute Enable 3 */ #define SPR_IMMU_IMMUPR_SXE3_OFFSET 4 #define SPR_IMMU_IMMUPR_SXE3_MASK 0x00000010 #define SPR_IMMU_IMMUPR_SXE3_GET(x) (((x) >> 4) & 0x1) #define SPR_IMMU_IMMUPR_SXE3_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* User Execute Enable 3 */ #define SPR_IMMU_IMMUPR_UXE3_OFFSET 5 #define SPR_IMMU_IMMUPR_UXE3_MASK 0x00000020 #define SPR_IMMU_IMMUPR_UXE3_GET(x) (((x) >> 5) & 0x1) #define SPR_IMMU_IMMUPR_UXE3_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Supervisor Execute Enable 4 */ #define SPR_IMMU_IMMUPR_SXE4_OFFSET 6 #define SPR_IMMU_IMMUPR_SXE4_MASK 0x00000040 #define SPR_IMMU_IMMUPR_SXE4_GET(x) (((x) >> 6) & 0x1) #define SPR_IMMU_IMMUPR_SXE4_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* User Execute Enable 4 */ #define SPR_IMMU_IMMUPR_UXE4_OFFSET 7 #define SPR_IMMU_IMMUPR_UXE4_MASK 0x00000080 #define SPR_IMMU_IMMUPR_UXE4_GET(x) (((x) >> 7) & 0x1) #define SPR_IMMU_IMMUPR_UXE4_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* Supervisor Execute Enable 5 */ #define SPR_IMMU_IMMUPR_SXE5_OFFSET 8 #define SPR_IMMU_IMMUPR_SXE5_MASK 0x00000100 #define SPR_IMMU_IMMUPR_SXE5_GET(x) (((x) >> 8) & 0x1) #define SPR_IMMU_IMMUPR_SXE5_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* User Execute Enable 5 */ #define SPR_IMMU_IMMUPR_UXE5_OFFSET 9 #define SPR_IMMU_IMMUPR_UXE5_MASK 0x00000200 #define SPR_IMMU_IMMUPR_UXE5_GET(x) (((x) >> 9) & 0x1) #define SPR_IMMU_IMMUPR_UXE5_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* Supervisor Execute Enable 6 */ #define SPR_IMMU_IMMUPR_SXE6_OFFSET 10 #define SPR_IMMU_IMMUPR_SXE6_MASK 0x00000400 #define SPR_IMMU_IMMUPR_SXE6_GET(x) (((x) >> 10) & 0x1) #define SPR_IMMU_IMMUPR_SXE6_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* User Execute Enable 6 */ #define SPR_IMMU_IMMUPR_UXE6_OFFSET 11 #define SPR_IMMU_IMMUPR_UXE6_MASK 0x00000800 #define SPR_IMMU_IMMUPR_UXE6_GET(x) (((x) >> 11) & 0x1) #define SPR_IMMU_IMMUPR_UXE6_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* Supervisor Execute Enable 7 */ #define SPR_IMMU_IMMUPR_SXE7_OFFSET 12 #define SPR_IMMU_IMMUPR_SXE7_MASK 0x00001000 #define SPR_IMMU_IMMUPR_SXE7_GET(x) (((x) >> 12) & 0x1) #define SPR_IMMU_IMMUPR_SXE7_SET(x, y) (((x) & U(0xffffefff)) | \ ((!!(y)) << 12)) /* User Execute Enable 7 */ #define SPR_IMMU_IMMUPR_UXE7_OFFSET 13 #define SPR_IMMU_IMMUPR_UXE7_MASK 0x00002000 #define SPR_IMMU_IMMUPR_UXE7_GET(x) (((x) >> 13) & 0x1) #define SPR_IMMU_IMMUPR_UXE7_SET(x, y) (((x) & U(0xffffdfff)) | \ ((!!(y)) << 13)) /* Instruction TLB Entry Invalidate Register */ #define SPR_IMMU_ITLBEIR_INDEX U(0x002) #define SPR_IMMU_ITLBEIR_ADDR U(0x1002) /* Instruction ATB Match Registers */ #define SPR_IMMU_IATBMR_BASE U(0x004) #define SPR_IMMU_IATBMR_COUNT U(0x004) #define SPR_IMMU_IATBMR_STEP U(0x001) #define SPR_IMMU_IATBMR_INDEX(N) (SPR_IMMU_IATBMR_BASE + \ (SPR_IMMU_IATBMR_STEP * (N))) #define SPR_IMMU_IATBMR_ADDR(N) ((SPR_IMMU_GROUP << SPR_GROUP_LSB) | \ SPR_IMMU_IATBMR_INDEX(N)) /* Valid */ #define SPR_IMMU_IATBMR_V_OFFSET 0 #define SPR_IMMU_IATBMR_V_MASK 0x00000001 #define SPR_IMMU_IATBMR_V_GET(x) (((x) >> 0) & 0x1) #define SPR_IMMU_IATBMR_V_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Context ID */ #define SPR_IMMU_IATBMR_CID_LSB 1 #define SPR_IMMU_IATBMR_CID_MSB 4 #define SPR_IMMU_IATBMR_CID_BITS 4 #define SPR_IMMU_IATBMR_CID_MASK U(0x0000001e) #define SPR_IMMU_IATBMR_CID_GET(x) (((x) >> 1) & U(0x0000000f)) #define SPR_IMMU_IATBMR_CID_SET(x, y) (((x) & U(0xffffffe1)) | \ ((y) << 1)) /* Page Size */ #define SPR_IMMU_IATBMR_PS_OFFSET 5 #define SPR_IMMU_IATBMR_PS_MASK 0x00000020 #define SPR_IMMU_IATBMR_PS_GET(x) (((x) >> 5) & 0x1) #define SPR_IMMU_IATBMR_PS_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Virtual Page Number */ #define SPR_IMMU_IATBMR_VPN_LSB 10 #define SPR_IMMU_IATBMR_VPN_MSB 31 #define SPR_IMMU_IATBMR_VPN_BITS 22 #define SPR_IMMU_IATBMR_VPN_MASK U(0xfffffc00) #define SPR_IMMU_IATBMR_VPN_GET(x) (((x) >> 10) & U(0x003fffff)) #define SPR_IMMU_IATBMR_VPN_SET(x, y) (((x) & U(0x000003ff)) | \ ((y) << 10)) /* Instruction ATB Translate Registers */ #define SPR_IMMU_IATBTR_BASE U(0x008) #define SPR_IMMU_IATBTR_COUNT U(0x004) #define SPR_IMMU_IATBTR_STEP U(0x001) #define SPR_IMMU_IATBTR_INDEX(N) (SPR_IMMU_IATBTR_BASE + \ (SPR_IMMU_IATBTR_STEP * (N))) #define SPR_IMMU_IATBTR_ADDR(N) ((SPR_IMMU_GROUP << SPR_GROUP_LSB) | \ SPR_IMMU_IATBTR_INDEX(N)) /* Cache Coherency */ #define SPR_IMMU_IATBTR_CC_OFFSET 0 #define SPR_IMMU_IATBTR_CC_MASK 0x00000001 #define SPR_IMMU_IATBTR_CC_GET(x) (((x) >> 0) & 0x1) #define SPR_IMMU_IATBTR_CC_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Cache Inhibit */ #define SPR_IMMU_IATBTR_CI_OFFSET 1 #define SPR_IMMU_IATBTR_CI_MASK 0x00000002 #define SPR_IMMU_IATBTR_CI_GET(x) (((x) >> 1) & 0x1) #define SPR_IMMU_IATBTR_CI_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Write-back Cache */ #define SPR_IMMU_IATBTR_WBC_OFFSET 2 #define SPR_IMMU_IATBTR_WBC_MASK 0x00000004 #define SPR_IMMU_IATBTR_WBC_GET(x) (((x) >> 2) & 0x1) #define SPR_IMMU_IATBTR_WBC_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* Weakly-ordered Memory */ #define SPR_IMMU_IATBTR_WOM_OFFSET 3 #define SPR_IMMU_IATBTR_WOM_MASK 0x00000008 #define SPR_IMMU_IATBTR_WOM_GET(x) (((x) >> 3) & 0x1) #define SPR_IMMU_IATBTR_WOM_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Accessed */ #define SPR_IMMU_IATBTR_A_OFFSET 4 #define SPR_IMMU_IATBTR_A_MASK 0x00000010 #define SPR_IMMU_IATBTR_A_GET(x) (((x) >> 4) & 0x1) #define SPR_IMMU_IATBTR_A_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Dirty */ #define SPR_IMMU_IATBTR_D_OFFSET 5 #define SPR_IMMU_IATBTR_D_MASK 0x00000020 #define SPR_IMMU_IATBTR_D_GET(x) (((x) >> 5) & 0x1) #define SPR_IMMU_IATBTR_D_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Supervisor Execute Enable */ #define SPR_IMMU_IATBTR_SRE_OFFSET 6 #define SPR_IMMU_IATBTR_SRE_MASK 0x00000040 #define SPR_IMMU_IATBTR_SRE_GET(x) (((x) >> 6) & 0x1) #define SPR_IMMU_IATBTR_SRE_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* User Execute Enable */ #define SPR_IMMU_IATBTR_URE_OFFSET 7 #define SPR_IMMU_IATBTR_URE_MASK 0x00000080 #define SPR_IMMU_IATBTR_URE_GET(x) (((x) >> 7) & 0x1) #define SPR_IMMU_IATBTR_URE_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* Physical Page Number */ #define SPR_IMMU_IATBTR_PPN_LSB 10 #define SPR_IMMU_IATBTR_PPN_MSB 31 #define SPR_IMMU_IATBTR_PPN_BITS 22 #define SPR_IMMU_IATBTR_PPN_MASK U(0xfffffc00) #define SPR_IMMU_IATBTR_PPN_GET(x) (((x) >> 10) & U(0x003fffff)) #define SPR_IMMU_IATBTR_PPN_SET(x, y) (((x) & U(0x000003ff)) | \ ((y) << 10)) /* Instruction TLB */ #define SPR_IMMU_ITLBW_BASE U(0x200) #define SPR_IMMU_ITLBW_COUNT U(0x004) #define SPR_IMMU_ITLBW_STEP U(0x100) #define SPR_IMMU_ITLBW_SUBBASE(N0) (SPR_IMMU_ITLBW_BASE + \ (SPR_IMMU_ITLBW_STEP * (N0))) /* Instruction TLB Match Registers */ #define SPR_IMMU_ITLBW_MR_BASE U(0x000) #define SPR_IMMU_ITLBW_MR_COUNT U(0x080) #define SPR_IMMU_ITLBW_MR_STEP U(0x001) #define SPR_IMMU_ITLBW_MR_INDEX(N0, N1) (SPR_IMMU_ITLBW_SUBBASE(N0) + \ SPR_IMMU_ITLBW_MR_BASE + \ (SPR_IMMU_ITLBW_MR_STEP * (N1))) #define SPR_IMMU_ITLBW_MR_ADDR(N0, N1) ((SPR_IMMU_GROUP << SPR_GROUP_LSB) | \ SPR_IMMU_ITLBW_MR_INDEX(N0, N1)) /* Valid */ #define SPR_IMMU_ITLBW_MR_V_OFFSET 0 #define SPR_IMMU_ITLBW_MR_V_MASK 0x00000001 #define SPR_IMMU_ITLBW_MR_V_GET(x) (((x) >> 0) & 0x1) #define SPR_IMMU_ITLBW_MR_V_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Page Level 1 */ #define SPR_IMMU_ITLBW_MR_PL1_OFFSET 1 #define SPR_IMMU_ITLBW_MR_PL1_MASK 0x00000002 #define SPR_IMMU_ITLBW_MR_PL1_GET(x) (((x) >> 1) & 0x1) #define SPR_IMMU_ITLBW_MR_PL1_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Context ID */ #define SPR_IMMU_ITLBW_MR_CID_LSB 2 #define SPR_IMMU_ITLBW_MR_CID_MSB 5 #define SPR_IMMU_ITLBW_MR_CID_BITS 4 #define SPR_IMMU_ITLBW_MR_CID_MASK U(0x0000003c) #define SPR_IMMU_ITLBW_MR_CID_GET(x) (((x) >> 2) & U(0x0000000f)) #define SPR_IMMU_ITLBW_MR_CID_SET(x, y) (((x) & U(0xffffffc3)) | \ ((y) << 2)) /* Least Recently Used */ #define SPR_IMMU_ITLBW_MR_LRU_LSB 6 #define SPR_IMMU_ITLBW_MR_LRU_MSB 7 #define SPR_IMMU_ITLBW_MR_LRU_BITS 2 #define SPR_IMMU_ITLBW_MR_LRU_MASK U(0x000000c0) #define SPR_IMMU_ITLBW_MR_LRU_GET(x) (((x) >> 6) & U(0x00000003)) #define SPR_IMMU_ITLBW_MR_LRU_SET(x, y) (((x) & U(0xffffff3f)) | \ ((y) << 6)) /* Virtual Page Number */ #define SPR_IMMU_ITLBW_MR_VPN_LSB 13 #define SPR_IMMU_ITLBW_MR_VPN_MSB 31 #define SPR_IMMU_ITLBW_MR_VPN_BITS 19 #define SPR_IMMU_ITLBW_MR_VPN_MASK U(0xffffe000) #define SPR_IMMU_ITLBW_MR_VPN_GET(x) (((x) >> 13) & U(0x0007ffff)) #define SPR_IMMU_ITLBW_MR_VPN_SET(x, y) (((x) & U(0x00001fff)) | \ ((y) << 13)) /* Instruction TLB Translate Registers */ #define SPR_IMMU_ITLBW_TR_BASE U(0x080) #define SPR_IMMU_ITLBW_TR_COUNT U(0x080) #define SPR_IMMU_ITLBW_TR_STEP U(0x001) #define SPR_IMMU_ITLBW_TR_INDEX(N0, N1) (SPR_IMMU_ITLBW_SUBBASE(N0) + \ SPR_IMMU_ITLBW_TR_BASE + \ (SPR_IMMU_ITLBW_TR_STEP * (N1))) #define SPR_IMMU_ITLBW_TR_ADDR(N0, N1) ((SPR_IMMU_GROUP << SPR_GROUP_LSB) | \ SPR_IMMU_ITLBW_TR_INDEX(N0, N1)) /* Cache Coherency */ #define SPR_IMMU_ITLBW_TR_CC_OFFSET 0 #define SPR_IMMU_ITLBW_TR_CC_MASK 0x00000001 #define SPR_IMMU_ITLBW_TR_CC_GET(x) (((x) >> 0) & 0x1) #define SPR_IMMU_ITLBW_TR_CC_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Cache Inhibit */ #define SPR_IMMU_ITLBW_TR_CI_OFFSET 1 #define SPR_IMMU_ITLBW_TR_CI_MASK 0x00000002 #define SPR_IMMU_ITLBW_TR_CI_GET(x) (((x) >> 1) & 0x1) #define SPR_IMMU_ITLBW_TR_CI_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Write-back Cache */ #define SPR_IMMU_ITLBW_TR_WBC_OFFSET 2 #define SPR_IMMU_ITLBW_TR_WBC_MASK 0x00000004 #define SPR_IMMU_ITLBW_TR_WBC_GET(x) (((x) >> 2) & 0x1) #define SPR_IMMU_ITLBW_TR_WBC_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* Weakly-ordered Memory */ #define SPR_IMMU_ITLBW_TR_WOM_OFFSET 3 #define SPR_IMMU_ITLBW_TR_WOM_MASK 0x00000008 #define SPR_IMMU_ITLBW_TR_WOM_GET(x) (((x) >> 3) & 0x1) #define SPR_IMMU_ITLBW_TR_WOM_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Accessed */ #define SPR_IMMU_ITLBW_TR_A_OFFSET 4 #define SPR_IMMU_ITLBW_TR_A_MASK 0x00000010 #define SPR_IMMU_ITLBW_TR_A_GET(x) (((x) >> 4) & 0x1) #define SPR_IMMU_ITLBW_TR_A_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Dirty */ #define SPR_IMMU_ITLBW_TR_D_OFFSET 5 #define SPR_IMMU_ITLBW_TR_D_MASK 0x00000020 #define SPR_IMMU_ITLBW_TR_D_GET(x) (((x) >> 5) & 0x1) #define SPR_IMMU_ITLBW_TR_D_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* User Execute Enable */ #define SPR_IMMU_ITLBW_TR_UXE_OFFSET 6 #define SPR_IMMU_ITLBW_TR_UXE_MASK 0x00000040 #define SPR_IMMU_ITLBW_TR_UXE_GET(x) (((x) >> 6) & 0x1) #define SPR_IMMU_ITLBW_TR_UXE_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* Supervisor Execute Enable */ #define SPR_IMMU_ITLBW_TR_SXE_OFFSET 7 #define SPR_IMMU_ITLBW_TR_SXE_MASK 0x00000080 #define SPR_IMMU_ITLBW_TR_SXE_GET(x) (((x) >> 7) & 0x1) #define SPR_IMMU_ITLBW_TR_SXE_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* Physical Page Number */ #define SPR_IMMU_ITLBW_TR_PPN_LSB 13 #define SPR_IMMU_ITLBW_TR_PPN_MSB 31 #define SPR_IMMU_ITLBW_TR_PPN_BITS 19 #define SPR_IMMU_ITLBW_TR_PPN_MASK U(0xffffe000) #define SPR_IMMU_ITLBW_TR_PPN_GET(x) (((x) >> 13) & U(0x0007ffff)) #define SPR_IMMU_ITLBW_TR_PPN_SET(x, y) (((x) & U(0x00001fff)) | \ ((y) << 13)) /********************/ /* Data Cache Group */ /********************/ #define SPR_DCACHE_GROUP 0x03 /* Data Cache Control Register */ #define SPR_DCACHE_DCCR_INDEX U(0x000) #define SPR_DCACHE_DCCR_ADDR U(0x1800) /* Enable Ways */ #define SPR_DCACHE_DCCR_EW_LSB 0 #define SPR_DCACHE_DCCR_EW_MSB 7 #define SPR_DCACHE_DCCR_EW_BITS 8 #define SPR_DCACHE_DCCR_EW_MASK U(0x000000ff) #define SPR_DCACHE_DCCR_EW_GET(x) (((x) >> 0) & U(0x000000ff)) #define SPR_DCACHE_DCCR_EW_SET(x, y) (((x) & U(0xffffff00)) | \ ((y) << 0)) /* Data Cache Block Prefetch Register */ #define SPR_DCACHE_DCBPR_INDEX U(0x001) #define SPR_DCACHE_DCBPR_ADDR U(0x1801) /* Data Cache Block Flush Register */ #define SPR_DCACHE_DCBFR_INDEX U(0x002) #define SPR_DCACHE_DCBFR_ADDR U(0x1802) /* Data Cache Block Invalidate Register */ #define SPR_DCACHE_DCBIR_INDEX U(0x003) #define SPR_DCACHE_DCBIR_ADDR U(0x1803) /* Data Cache Block Write-back Register */ #define SPR_DCACHE_DCBWR_INDEX U(0x004) #define SPR_DCACHE_DCBWR_ADDR U(0x1804) /* Data Cache Block Lock Register */ #define SPR_DCACHE_DCBLR_INDEX U(0x005) #define SPR_DCACHE_DCBLR_ADDR U(0x1805) /***************************/ /* Instruction Cache Group */ /***************************/ #define SPR_ICACHE_GROUP 0x04 /* Instruction Cache Control Register */ #define SPR_ICACHE_ICCR_INDEX U(0x000) #define SPR_ICACHE_ICCR_ADDR U(0x2000) /* Enable Ways */ #define SPR_ICACHE_ICCR_EW_LSB 0 #define SPR_ICACHE_ICCR_EW_MSB 7 #define SPR_ICACHE_ICCR_EW_BITS 8 #define SPR_ICACHE_ICCR_EW_MASK U(0x000000ff) #define SPR_ICACHE_ICCR_EW_GET(x) (((x) >> 0) & U(0x000000ff)) #define SPR_ICACHE_ICCR_EW_SET(x, y) (((x) & U(0xffffff00)) | \ ((y) << 0)) /* Instruction Cache Block Prefetch Register */ #define SPR_ICACHE_ICBPR_INDEX U(0x001) #define SPR_ICACHE_ICBPR_ADDR U(0x2001) /* Instruction Cache Block Invalidate Register */ #define SPR_ICACHE_ICBIR_INDEX U(0x002) #define SPR_ICACHE_ICBIR_ADDR U(0x2002) /* Instruction Cache Block Lock Register */ #define SPR_ICACHE_ICBLR_INDEX U(0x003) #define SPR_ICACHE_ICBLR_ADDR U(0x2003) /*********************************/ /* Multiply and Accumulate Group */ /*********************************/ #define SPR_MAC_GROUP 0x05 /* MAC Result Low Word */ #define SPR_MAC_MACLO_INDEX U(0x001) #define SPR_MAC_MACLO_ADDR U(0x2801) /* MAC Result High Word */ #define SPR_MAC_MACHI_INDEX U(0x002) #define SPR_MAC_MACHI_ADDR U(0x2802) /***************/ /* Debug Group */ /***************/ #define SPR_DEBUG_GROUP 0x06 /* Debug Value Registers */ #define SPR_DEBUG_DVR_BASE U(0x000) #define SPR_DEBUG_DVR_COUNT U(0x008) #define SPR_DEBUG_DVR_STEP U(0x001) #define SPR_DEBUG_DVR_INDEX(N) (SPR_DEBUG_DVR_BASE + \ (SPR_DEBUG_DVR_STEP * (N))) #define SPR_DEBUG_DVR_ADDR(N) ((SPR_DEBUG_GROUP << SPR_GROUP_LSB) | \ SPR_DEBUG_DVR_INDEX(N)) /* Debug Control Registers */ #define SPR_DEBUG_DCR_BASE U(0x008) #define SPR_DEBUG_DCR_COUNT U(0x008) #define SPR_DEBUG_DCR_STEP U(0x001) #define SPR_DEBUG_DCR_INDEX(N) (SPR_DEBUG_DCR_BASE + \ (SPR_DEBUG_DCR_STEP * (N))) #define SPR_DEBUG_DCR_ADDR(N) ((SPR_DEBUG_GROUP << SPR_GROUP_LSB) | \ SPR_DEBUG_DCR_INDEX(N)) /* DVR/DCR Present */ #define SPR_DEBUG_DCR_DP_OFFSET 0 #define SPR_DEBUG_DCR_DP_MASK 0x00000001 #define SPR_DEBUG_DCR_DP_GET(x) (((x) >> 0) & 0x1) #define SPR_DEBUG_DCR_DP_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Compare Condition */ #define SPR_DEBUG_DCR_CC_LSB 1 #define SPR_DEBUG_DCR_CC_MSB 3 #define SPR_DEBUG_DCR_CC_BITS 3 #define SPR_DEBUG_DCR_CC_MASK U(0x0000000e) #define SPR_DEBUG_DCR_CC_GET(x) (((x) >> 1) & U(0x00000007)) #define SPR_DEBUG_DCR_CC_SET(x, y) (((x) & U(0xfffffff1)) | \ ((y) << 1)) /* Masked */ #define SPR_DEBUG_DCR_CC_MASKED 0 /* Equal */ #define SPR_DEBUG_DCR_CC_EQ 1 /* Less than */ #define SPR_DEBUG_DCR_CC_LT 2 /* Less than or equal */ #define SPR_DEBUG_DCR_CC_LTE 3 /* Greater than */ #define SPR_DEBUG_DCR_CC_GT 4 /* Greater than or equal */ #define SPR_DEBUG_DCR_CC_GTE 5 /* Not equal */ #define SPR_DEBUG_DCR_CC_NEQ 6 /* Signed Comparison */ #define SPR_DEBUG_DCR_SC_OFFSET 4 #define SPR_DEBUG_DCR_SC_MASK 0x00000010 #define SPR_DEBUG_DCR_SC_GET(x) (((x) >> 4) & 0x1) #define SPR_DEBUG_DCR_SC_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Compare To */ #define SPR_DEBUG_DCR_CT_LSB 5 #define SPR_DEBUG_DCR_CT_MSB 7 #define SPR_DEBUG_DCR_CT_BITS 3 #define SPR_DEBUG_DCR_CT_MASK U(0x000000e0) #define SPR_DEBUG_DCR_CT_GET(x) (((x) >> 5) & U(0x00000007)) #define SPR_DEBUG_DCR_CT_SET(x, y) (((x) & U(0xffffff1f)) | \ ((y) << 5)) /* Comparison disabled */ #define SPR_DEBUG_DCR_CT_DISABLED 0 /* Instruction fetch EA */ #define SPR_DEBUG_DCR_CT_FEA 1 /* Load EA */ #define SPR_DEBUG_DCR_CT_LEA 2 /* Store EA */ #define SPR_DEBUG_DCR_CT_SEA 3 /* Load data */ #define SPR_DEBUG_DCR_CT_LD 4 /* Store data */ #define SPR_DEBUG_DCR_CT_SD 5 /* Load/store EA */ #define SPR_DEBUG_DCR_CT_LSEA 6 /* Load/store data */ #define SPR_DEBUG_DCR_CT_LSD 7 /* Debug Mode Register 1 */ #define SPR_DEBUG_DMR1_INDEX U(0x010) #define SPR_DEBUG_DMR1_ADDR U(0x3010) /* Chain Watchpoint 0 */ #define SPR_DEBUG_DMR1_CW0_LSB 0 #define SPR_DEBUG_DMR1_CW0_MSB 1 #define SPR_DEBUG_DMR1_CW0_BITS 2 #define SPR_DEBUG_DMR1_CW0_MASK U(0x00000003) #define SPR_DEBUG_DMR1_CW0_GET(x) (((x) >> 0) & U(0x00000003)) #define SPR_DEBUG_DMR1_CW0_SET(x, y) (((x) & U(0xfffffffc)) | \ ((y) << 0)) /* Chain Watchpoint 1 */ #define SPR_DEBUG_DMR1_CW1_LSB 2 #define SPR_DEBUG_DMR1_CW1_MSB 3 #define SPR_DEBUG_DMR1_CW1_BITS 2 #define SPR_DEBUG_DMR1_CW1_MASK U(0x0000000c) #define SPR_DEBUG_DMR1_CW1_GET(x) (((x) >> 2) & U(0x00000003)) #define SPR_DEBUG_DMR1_CW1_SET(x, y) (((x) & U(0xfffffff3)) | \ ((y) << 2)) /* Chain Watchpoint 2 */ #define SPR_DEBUG_DMR1_CW2_LSB 4 #define SPR_DEBUG_DMR1_CW2_MSB 5 #define SPR_DEBUG_DMR1_CW2_BITS 2 #define SPR_DEBUG_DMR1_CW2_MASK U(0x00000030) #define SPR_DEBUG_DMR1_CW2_GET(x) (((x) >> 4) & U(0x00000003)) #define SPR_DEBUG_DMR1_CW2_SET(x, y) (((x) & U(0xffffffcf)) | \ ((y) << 4)) /* Chain Watchpoint 3 */ #define SPR_DEBUG_DMR1_CW3_LSB 6 #define SPR_DEBUG_DMR1_CW3_MSB 7 #define SPR_DEBUG_DMR1_CW3_BITS 2 #define SPR_DEBUG_DMR1_CW3_MASK U(0x000000c0) #define SPR_DEBUG_DMR1_CW3_GET(x) (((x) >> 6) & U(0x00000003)) #define SPR_DEBUG_DMR1_CW3_SET(x, y) (((x) & U(0xffffff3f)) | \ ((y) << 6)) /* Chain Watchpoint 4 */ #define SPR_DEBUG_DMR1_CW4_LSB 9 #define SPR_DEBUG_DMR1_CW4_MSB 9 #define SPR_DEBUG_DMR1_CW4_BITS 1 #define SPR_DEBUG_DMR1_CW4_MASK U(0x00000200) #define SPR_DEBUG_DMR1_CW4_GET(x) (((x) >> 9) & U(0x00000001)) #define SPR_DEBUG_DMR1_CW4_SET(x, y) (((x) & U(0xfffffdff)) | \ ((y) << 9)) /* Chain Watchpoint 5 */ #define SPR_DEBUG_DMR1_CW5_LSB 10 #define SPR_DEBUG_DMR1_CW5_MSB 11 #define SPR_DEBUG_DMR1_CW5_BITS 2 #define SPR_DEBUG_DMR1_CW5_MASK U(0x00000c00) #define SPR_DEBUG_DMR1_CW5_GET(x) (((x) >> 10) & U(0x00000003)) #define SPR_DEBUG_DMR1_CW5_SET(x, y) (((x) & U(0xfffff3ff)) | \ ((y) << 10)) /* Chain Watchpoint 6 */ #define SPR_DEBUG_DMR1_CW6_LSB 12 #define SPR_DEBUG_DMR1_CW6_MSB 13 #define SPR_DEBUG_DMR1_CW6_BITS 2 #define SPR_DEBUG_DMR1_CW6_MASK U(0x00003000) #define SPR_DEBUG_DMR1_CW6_GET(x) (((x) >> 12) & U(0x00000003)) #define SPR_DEBUG_DMR1_CW6_SET(x, y) (((x) & U(0xffffcfff)) | \ ((y) << 12)) /* Chain Watchpoint 7 */ #define SPR_DEBUG_DMR1_CW7_LSB 14 #define SPR_DEBUG_DMR1_CW7_MSB 15 #define SPR_DEBUG_DMR1_CW7_BITS 2 #define SPR_DEBUG_DMR1_CW7_MASK U(0x0000c000) #define SPR_DEBUG_DMR1_CW7_GET(x) (((x) >> 14) & U(0x00000003)) #define SPR_DEBUG_DMR1_CW7_SET(x, y) (((x) & U(0xffff3fff)) | \ ((y) << 14)) /* Chain Watchpoint 8 */ #define SPR_DEBUG_DMR1_CW8_LSB 16 #define SPR_DEBUG_DMR1_CW8_MSB 17 #define SPR_DEBUG_DMR1_CW8_BITS 2 #define SPR_DEBUG_DMR1_CW8_MASK U(0x00030000) #define SPR_DEBUG_DMR1_CW8_GET(x) (((x) >> 16) & U(0x00000003)) #define SPR_DEBUG_DMR1_CW8_SET(x, y) (((x) & U(0xfffcffff)) | \ ((y) << 16)) /* Chain Watchpoint 9 */ #define SPR_DEBUG_DMR1_CW9_LSB 18 #define SPR_DEBUG_DMR1_CW9_MSB 19 #define SPR_DEBUG_DMR1_CW9_BITS 2 #define SPR_DEBUG_DMR1_CW9_MASK U(0x000c0000) #define SPR_DEBUG_DMR1_CW9_GET(x) (((x) >> 18) & U(0x00000003)) #define SPR_DEBUG_DMR1_CW9_SET(x, y) (((x) & U(0xfff3ffff)) | \ ((y) << 18)) /* Single-step Trace */ #define SPR_DEBUG_DMR1_ST_OFFSET 22 #define SPR_DEBUG_DMR1_ST_MASK 0x00400000 #define SPR_DEBUG_DMR1_ST_GET(x) (((x) >> 22) & 0x1) #define SPR_DEBUG_DMR1_ST_SET(x, y) (((x) & U(0xffbfffff)) | \ ((!!(y)) << 22)) /* Branch Trace */ #define SPR_DEBUG_DMR1_BT_OFFSET 23 #define SPR_DEBUG_DMR1_BT_MASK 0x00800000 #define SPR_DEBUG_DMR1_BT_GET(x) (((x) >> 23) & 0x1) #define SPR_DEBUG_DMR1_BT_SET(x, y) (((x) & U(0xff7fffff)) | \ ((!!(y)) << 23)) /* Debug Mode Register 2 */ #define SPR_DEBUG_DMR2_INDEX U(0x011) #define SPR_DEBUG_DMR2_ADDR U(0x3011) /* Watchpoint Counter Enable 0 */ #define SPR_DEBUG_DMR2_WCE0_OFFSET 0 #define SPR_DEBUG_DMR2_WCE0_MASK 0x00000001 #define SPR_DEBUG_DMR2_WCE0_GET(x) (((x) >> 0) & 0x1) #define SPR_DEBUG_DMR2_WCE0_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Watchpoint Counter Enable 1 */ #define SPR_DEBUG_DMR2_WCE1_OFFSET 1 #define SPR_DEBUG_DMR2_WCE1_MASK 0x00000002 #define SPR_DEBUG_DMR2_WCE1_GET(x) (((x) >> 1) & 0x1) #define SPR_DEBUG_DMR2_WCE1_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Assign Watchpoints to Counter */ #define SPR_DEBUG_DMR2_AWTC_LSB 2 #define SPR_DEBUG_DMR2_AWTC_MSB 11 #define SPR_DEBUG_DMR2_AWTC_BITS 10 #define SPR_DEBUG_DMR2_AWTC_MASK U(0x00000ffc) #define SPR_DEBUG_DMR2_AWTC_GET(x) (((x) >> 2) & U(0x000003ff)) #define SPR_DEBUG_DMR2_AWTC_SET(x, y) (((x) & U(0xfffff003)) | \ ((y) << 2)) /* Watchpoints Generating Breakpoint */ #define SPR_DEBUG_DMR2_WGB_LSB 12 #define SPR_DEBUG_DMR2_WGB_MSB 21 #define SPR_DEBUG_DMR2_WGB_BITS 10 #define SPR_DEBUG_DMR2_WGB_MASK U(0x003ff000) #define SPR_DEBUG_DMR2_WGB_GET(x) (((x) >> 12) & U(0x000003ff)) #define SPR_DEBUG_DMR2_WGB_SET(x, y) (((x) & U(0xffc00fff)) | \ ((y) << 12)) /* Watchpoints Breakpoint Status */ #define SPR_DEBUG_DMR2_WBS_LSB 22 #define SPR_DEBUG_DMR2_WBS_MSB 31 #define SPR_DEBUG_DMR2_WBS_BITS 10 #define SPR_DEBUG_DMR2_WBS_MASK U(0xffc00000) #define SPR_DEBUG_DMR2_WBS_GET(x) (((x) >> 22) & U(0x000003ff)) #define SPR_DEBUG_DMR2_WBS_SET(x, y) (((x) & U(0x003fffff)) | \ ((y) << 22)) /* Debug Watchpoint Counter Registers */ #define SPR_DEBUG_DCWR_BASE U(0x012) #define SPR_DEBUG_DCWR_COUNT U(0x002) #define SPR_DEBUG_DCWR_STEP U(0x001) #define SPR_DEBUG_DCWR_INDEX(N) (SPR_DEBUG_DCWR_BASE + \ (SPR_DEBUG_DCWR_STEP * (N))) #define SPR_DEBUG_DCWR_ADDR(N) ((SPR_DEBUG_GROUP << SPR_GROUP_LSB) | \ SPR_DEBUG_DCWR_INDEX(N)) /* Debug Stop Register */ #define SPR_DEBUG_DSR_INDEX U(0x014) #define SPR_DEBUG_DSR_ADDR U(0x3014) /* Reset Exception */ #define SPR_DEBUG_DSR_RSTE_OFFSET 0 #define SPR_DEBUG_DSR_RSTE_MASK 0x00000001 #define SPR_DEBUG_DSR_RSTE_GET(x) (((x) >> 0) & 0x1) #define SPR_DEBUG_DSR_RSTE_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Bus Error Exception */ #define SPR_DEBUG_DSR_BUSEE_OFFSET 1 #define SPR_DEBUG_DSR_BUSEE_MASK 0x00000002 #define SPR_DEBUG_DSR_BUSEE_GET(x) (((x) >> 1) & 0x1) #define SPR_DEBUG_DSR_BUSEE_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Data Page Fault Exception */ #define SPR_DEBUG_DSR_DPFE_OFFSET 2 #define SPR_DEBUG_DSR_DPFE_MASK 0x00000004 #define SPR_DEBUG_DSR_DPFE_GET(x) (((x) >> 2) & 0x1) #define SPR_DEBUG_DSR_DPFE_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* Instruction Page Fault Exception */ #define SPR_DEBUG_DSR_IPFE_OFFSET 3 #define SPR_DEBUG_DSR_IPFE_MASK 0x00000008 #define SPR_DEBUG_DSR_IPFE_GET(x) (((x) >> 3) & 0x1) #define SPR_DEBUG_DSR_IPFE_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Tick Timer Exception */ #define SPR_DEBUG_DSR_TTE_OFFSET 4 #define SPR_DEBUG_DSR_TTE_MASK 0x00000010 #define SPR_DEBUG_DSR_TTE_GET(x) (((x) >> 4) & 0x1) #define SPR_DEBUG_DSR_TTE_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Alignment Exception */ #define SPR_DEBUG_DSR_AE_OFFSET 5 #define SPR_DEBUG_DSR_AE_MASK 0x00000020 #define SPR_DEBUG_DSR_AE_GET(x) (((x) >> 5) & 0x1) #define SPR_DEBUG_DSR_AE_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Illegal Instruction Exception */ #define SPR_DEBUG_DSR_IIE_OFFSET 6 #define SPR_DEBUG_DSR_IIE_MASK 0x00000040 #define SPR_DEBUG_DSR_IIE_GET(x) (((x) >> 6) & 0x1) #define SPR_DEBUG_DSR_IIE_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* Interrupt Exception */ #define SPR_DEBUG_DSR_INTE_OFFSET 7 #define SPR_DEBUG_DSR_INTE_MASK 0x00000080 #define SPR_DEBUG_DSR_INTE_GET(x) (((x) >> 7) & 0x1) #define SPR_DEBUG_DSR_INTE_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* DTLB Miss Exception */ #define SPR_DEBUG_DSR_DME_OFFSET 8 #define SPR_DEBUG_DSR_DME_MASK 0x00000100 #define SPR_DEBUG_DSR_DME_GET(x) (((x) >> 8) & 0x1) #define SPR_DEBUG_DSR_DME_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* ITLB Miss Exception */ #define SPR_DEBUG_DSR_IME_OFFSET 9 #define SPR_DEBUG_DSR_IME_MASK 0x00000200 #define SPR_DEBUG_DSR_IME_GET(x) (((x) >> 9) & 0x1) #define SPR_DEBUG_DSR_IME_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* Range Exception */ #define SPR_DEBUG_DSR_RE_OFFSET 10 #define SPR_DEBUG_DSR_RE_MASK 0x00000400 #define SPR_DEBUG_DSR_RE_GET(x) (((x) >> 10) & 0x1) #define SPR_DEBUG_DSR_RE_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* System Call Exception */ #define SPR_DEBUG_DSR_SCE_OFFSET 11 #define SPR_DEBUG_DSR_SCE_MASK 0x00000800 #define SPR_DEBUG_DSR_SCE_GET(x) (((x) >> 11) & 0x1) #define SPR_DEBUG_DSR_SCE_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* Floating Point Exception */ #define SPR_DEBUG_DSR_FPE_OFFSET 12 #define SPR_DEBUG_DSR_FPE_MASK 0x00001000 #define SPR_DEBUG_DSR_FPE_GET(x) (((x) >> 12) & 0x1) #define SPR_DEBUG_DSR_FPE_SET(x, y) (((x) & U(0xffffefff)) | \ ((!!(y)) << 12)) /* Trap Exception */ #define SPR_DEBUG_DSR_TE_OFFSET 13 #define SPR_DEBUG_DSR_TE_MASK 0x00002000 #define SPR_DEBUG_DSR_TE_GET(x) (((x) >> 13) & 0x1) #define SPR_DEBUG_DSR_TE_SET(x, y) (((x) & U(0xffffdfff)) | \ ((!!(y)) << 13)) /* Debug Reason Register */ #define SPR_DEBUG_DRR_INDEX U(0x015) #define SPR_DEBUG_DRR_ADDR U(0x3015) /* Reset Exception */ #define SPR_DEBUG_DRR_RSTE_OFFSET 0 #define SPR_DEBUG_DRR_RSTE_MASK 0x00000001 #define SPR_DEBUG_DRR_RSTE_GET(x) (((x) >> 0) & 0x1) #define SPR_DEBUG_DRR_RSTE_SET(x, y) (((x) & U(0xfffffffe)) | \ ((!!(y)) << 0)) /* Bus Error Exception */ #define SPR_DEBUG_DRR_BUSEE_OFFSET 1 #define SPR_DEBUG_DRR_BUSEE_MASK 0x00000002 #define SPR_DEBUG_DRR_BUSEE_GET(x) (((x) >> 1) & 0x1) #define SPR_DEBUG_DRR_BUSEE_SET(x, y) (((x) & U(0xfffffffd)) | \ ((!!(y)) << 1)) /* Data Page Fault Exception */ #define SPR_DEBUG_DRR_DPFE_OFFSET 2 #define SPR_DEBUG_DRR_DPFE_MASK 0x00000004 #define SPR_DEBUG_DRR_DPFE_GET(x) (((x) >> 2) & 0x1) #define SPR_DEBUG_DRR_DPFE_SET(x, y) (((x) & U(0xfffffffb)) | \ ((!!(y)) << 2)) /* Instruction Page Fault Exception */ #define SPR_DEBUG_DRR_IPFE_OFFSET 3 #define SPR_DEBUG_DRR_IPFE_MASK 0x00000008 #define SPR_DEBUG_DRR_IPFE_GET(x) (((x) >> 3) & 0x1) #define SPR_DEBUG_DRR_IPFE_SET(x, y) (((x) & U(0xfffffff7)) | \ ((!!(y)) << 3)) /* Tick Timer Exception */ #define SPR_DEBUG_DRR_TTE_OFFSET 4 #define SPR_DEBUG_DRR_TTE_MASK 0x00000010 #define SPR_DEBUG_DRR_TTE_GET(x) (((x) >> 4) & 0x1) #define SPR_DEBUG_DRR_TTE_SET(x, y) (((x) & U(0xffffffef)) | \ ((!!(y)) << 4)) /* Alignment Exception */ #define SPR_DEBUG_DRR_AE_OFFSET 5 #define SPR_DEBUG_DRR_AE_MASK 0x00000020 #define SPR_DEBUG_DRR_AE_GET(x) (((x) >> 5) & 0x1) #define SPR_DEBUG_DRR_AE_SET(x, y) (((x) & U(0xffffffdf)) | \ ((!!(y)) << 5)) /* Illegal Instruction Exception */ #define SPR_DEBUG_DRR_IIE_OFFSET 6 #define SPR_DEBUG_DRR_IIE_MASK 0x00000040 #define SPR_DEBUG_DRR_IIE_GET(x) (((x) >> 6) & 0x1) #define SPR_DEBUG_DRR_IIE_SET(x, y) (((x) & U(0xffffffbf)) | \ ((!!(y)) << 6)) /* Interrupt Exception */ #define SPR_DEBUG_DRR_INTE_OFFSET 7 #define SPR_DEBUG_DRR_INTE_MASK 0x00000080 #define SPR_DEBUG_DRR_INTE_GET(x) (((x) >> 7) & 0x1) #define SPR_DEBUG_DRR_INTE_SET(x, y) (((x) & U(0xffffff7f)) | \ ((!!(y)) << 7)) /* DTLB Miss Exception */ #define SPR_DEBUG_DRR_DME_OFFSET 8 #define SPR_DEBUG_DRR_DME_MASK 0x00000100 #define SPR_DEBUG_DRR_DME_GET(x) (((x) >> 8) & 0x1) #define SPR_DEBUG_DRR_DME_SET(x, y) (((x) & U(0xfffffeff)) | \ ((!!(y)) << 8)) /* ITLB Miss Exception */ #define SPR_DEBUG_DRR_IME_OFFSET 9 #define SPR_DEBUG_DRR_IME_MASK 0x00000200 #define SPR_DEBUG_DRR_IME_GET(x) (((x) >> 9) & 0x1) #define SPR_DEBUG_DRR_IME_SET(x, y) (((x) & U(0xfffffdff)) | \ ((!!(y)) << 9)) /* Range Exception */ #define SPR_DEBUG_DRR_RE_OFFSET 10 #define SPR_DEBUG_DRR_RE_MASK 0x00000400 #define SPR_DEBUG_DRR_RE_GET(x) (((x) >> 10) & 0x1) #define SPR_DEBUG_DRR_RE_SET(x, y) (((x) & U(0xfffffbff)) | \ ((!!(y)) << 10)) /* System Call Exception */ #define SPR_DEBUG_DRR_SCE_OFFSET 11 #define SPR_DEBUG_DRR_SCE_MASK 0x00000800 #define SPR_DEBUG_DRR_SCE_GET(x) (((x) >> 11) & 0x1) #define SPR_DEBUG_DRR_SCE_SET(x, y) (((x) & U(0xfffff7ff)) | \ ((!!(y)) << 11)) /* Floating Point Exception */ #define SPR_DEBUG_DRR_FPE_OFFSET 12 #define SPR_DEBUG_DRR_FPE_MASK 0x00001000 #define SPR_DEBUG_DRR_FPE_GET(x) (((x) >> 12) & 0x1) #define SPR_DEBUG_DRR_FPE_SET(x, y) (((x) & U(0xffffefff)) | \ ((!!(y)) << 12)) /* Trap Exception */ #define SPR_DEBUG_DRR_TE_OFFSET 13 #define SPR_DEBUG_DRR_TE_MASK 0x00002000 #define SPR_DEBUG_DRR_TE_GET(x) (((x) >> 13) & 0x1) #define SPR_DEBUG_DRR_TE_SET(x, y) (((x) & U(0xffffdfff)) | \ ((!!(y)) << 13)) /******************************/ /* Performance Counters Group */ /******************************/ #define SPR_PERF_GROUP 0x07 /* Performance Counters Count Registers */ #define SPR_PERF_PCCR_BASE U(0x000) #define SPR_PERF_PCCR_COUNT U(0x008) #define SPR_PERF_PCCR_STEP U(0x001) #define SPR_PERF_PCCR_INDEX(N) (SPR_PERF_PCCR_BASE + \ (SPR_PERF_PCCR_STEP * (N))) #define SPR_PERF_PCCR_ADDR(N) ((SPR_PERF_GROUP << SPR_GROUP_LSB) | \ SPR_PERF_PCCR_INDEX(N)) /* Performance Counters Mode Registers */ #define SPR_PERF_PCMR_BASE U(0x008) #define SPR_PERF_PCMR_COUNT U(0x008) #define SPR_PERF_PCMR_STEP U(0x001) #define SPR_PERF_PCMR_INDEX(N) (SPR_PERF_PCMR_BASE + \ (SPR_PERF_PCMR_STEP * (N))) #define SPR_PERF_PCMR_ADDR(N) ((SPR_PERF_GROUP << SPR_GROUP_LSB) | \ SPR_PERF_PCMR_INDEX(N)) /**************************/ /* Power Management Group */ /**************************/ #define SPR_POWER_GROUP 0x08 /* Power Management Register */ #define SPR_POWER_PMR_INDEX U(0x000) #define SPR_POWER_PMR_ADDR U(0x4000) /*******************************************/ /* Programmable Interrupt Controller Group */ /*******************************************/ #define SPR_PIC_GROUP 0x09 /* PIC Mask Register */ #define SPR_PIC_PICMR_INDEX U(0x000) #define SPR_PIC_PICMR_ADDR U(0x4800) /* PIC Status Register */ #define SPR_PIC_PICSR_INDEX U(0x002) #define SPR_PIC_PICSR_ADDR U(0x4802) /********************/ /* Tick Timer Group */ /********************/ #define SPR_TICK_GROUP 0x0a /* Tick Timer Mode Register */ #define SPR_TICK_TTMR_INDEX U(0x000) #define SPR_TICK_TTMR_ADDR U(0x5000) /* Time Period */ #define SPR_TICK_TTMR_TP_LSB 0 #define SPR_TICK_TTMR_TP_MSB 27 #define SPR_TICK_TTMR_TP_BITS 28 #define SPR_TICK_TTMR_TP_MASK U(0x0fffffff) #define SPR_TICK_TTMR_TP_GET(x) (((x) >> 0) & U(0x0fffffff)) #define SPR_TICK_TTMR_TP_SET(x, y) (((x) & U(0xf0000000)) | \ ((y) << 0)) /* Interrupt Pending */ #define SPR_TICK_TTMR_IP_OFFSET 28 #define SPR_TICK_TTMR_IP_MASK 0x10000000 #define SPR_TICK_TTMR_IP_GET(x) (((x) >> 28) & 0x1) #define SPR_TICK_TTMR_IP_SET(x, y) (((x) & U(0xefffffff)) | \ ((!!(y)) << 28)) /* Interrupt Enable */ #define SPR_TICK_TTMR_IE_OFFSET 29 #define SPR_TICK_TTMR_IE_MASK 0x20000000 #define SPR_TICK_TTMR_IE_GET(x) (((x) >> 29) & 0x1) #define SPR_TICK_TTMR_IE_SET(x, y) (((x) & U(0xdfffffff)) | \ ((!!(y)) << 29)) /* Mode */ #define SPR_TICK_TTMR_MODE_LSB 30 #define SPR_TICK_TTMR_MODE_MSB 31 #define SPR_TICK_TTMR_MODE_BITS 2 #define SPR_TICK_TTMR_MODE_MASK U(0xc0000000) #define SPR_TICK_TTMR_MODE_GET(x) (((x) >> 30) & U(0x00000003)) #define SPR_TICK_TTMR_MODE_SET(x, y) (((x) & U(0x3fffffff)) | \ ((y) << 30)) /* Disabled */ #define SPR_TICK_TTMR_MODE_DISABLE 0 /* Restart counting when TTMR[TP]==TTCR */ #define SPR_TICK_TTMR_MODE_RESTART 1 /* Stop counting when TTMR[TP]==TTCR */ #define SPR_TICK_TTMR_MODE_STOP 2 /* Continue counting when TTMR[TP]==TTCR */ #define SPR_TICK_TTMR_MODE_CONTINUE 3 /* Tick Timer Count Register */ #define SPR_TICK_TTCR_INDEX U(0x001) #define SPR_TICK_TTCR_ADDR U(0x5001) /*****************************/ /* Floating Point Unit Group */ /*****************************/ #define SPR_FPU_GROUP 0x0b #endif /* ASM_SPR_H */ crust-0.5/arch/or1k/include/counter.h000066400000000000000000000010421414152222500175550ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COUNTER_H #define COUNTER_H #include /** * Initialize the system counter. * * This function must be called before delays or timeouts can be used. */ void counter_init(void); /** * Read the system counter. * * This counter (the OpenRISC 1000 architectural tick timer) is a 32-bit up * counter running at the same frequency as the CPU clock. */ uint32_t counter_read(void); #endif /* COUNTER_H */ crust-0.5/arch/or1k/include/exception.h000066400000000000000000000006361414152222500201040ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef EXCEPTION_H #define EXCEPTION_H #include /** * Report the exception that caused the firmware to restart, if applicable. * * @param exception Exception information provided by startup assembly code. */ void report_exception(uint32_t exception); #endif /* EXCEPTION_H */ crust-0.5/arch/or1k/include/spr.h000066400000000000000000000007441414152222500167120ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef SPR_H #define SPR_H #include #include static inline uint32_t mfspr(uint16_t addr) { uint32_t value; asm volatile ("l.mfspr %0, r0, %1" : "=r" (value) : "K" (addr)); return value; } static inline void mtspr(uint16_t addr, uint32_t value) { asm volatile ("l.mtspr r0, %1, %0" : : "K" (addr), "r" (value)); } #endif /* SPR_H */ crust-0.5/arch/or1k/include/trap.h000066400000000000000000000004021414152222500170430ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef TRAP_H #define TRAP_H static inline noreturn void trap(void) { asm volatile ("l.trap 0"); unreachable(); } #endif /* TRAP_H */ crust-0.5/arch/or1k/math.S000066400000000000000000000007331414152222500153650ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include func udivmod l.sw -8(r1), r18 l.ori r18, r3, 0 # Save pointer to dividend l.sw -4(r1), r9 l.lwz r3, 0(r3) # Load dividend l.jal __udivsi3 l.addi r1, r1, -8 l.sw 0(r18), r11 # Replace dividend with quotient l.addi r1, r1, 8 l.lwz r9, -4(r1) l.lwz r18, -8(r1) l.jr r9 l.ori r11, r12, 0 # Return remainder endfunc udivmod crust-0.5/arch/or1k/runtime.S000066400000000000000000000065641414152222500161270ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include func __divsi3 l.sflts r3, r0 l.sw -8(r1), r18 l.bnf 1f l.ori r18, r0, 0 l.sub r3, r0, r3 # Negate x if it is negative l.addi r18, r18, 1 # Increment the flag if x is negative 1: l.sflts r4, r0 l.bnf 2f l.sw -4(r1), r9 l.sub r4, r0, r4 # Negate y if it is negative l.addi r18, r18, -1 # Decrement the flag if y is negative 2: l.jal __udivsi3 l.addi r1, r1, -8 l.sfne r18, r0 l.bnf 3f l.addi r1, r1, 8 l.sub r11, r0, r11 # Negate q if the flag is nonzero 3: l.lwz r9, -4(r1) l.jr r9 l.lwz r18, -8(r1) endfunc __divsi3 /* * Of the three ORBIS32 32-bit multiplication instructions (l.mul, l.muli, and * l.mulu), only l.mul works. By passing "-msoft-mul" to the compiler, and * delegating to this function, we can force all multiplication to use l.mul. */ func __mulsi3 l.jr r9 l.mul r11, r3, r4 endfunc __mulsi3 /* * Derived from the "best method for counting bits in a 32-bit integer" at * https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel. * * Signed multiplication is used because l.mulu is broken in hardware. This is * safe because the previous bit masking ensures neither operand is negative. */ func __popcountsi2 l.movhi r5, 0x5555 # Statement 1: l.ori r5, r5, 0x5555 # r5 = 0x55555555 l.srli r4, r3, 1 # r4 = v >> 1 l.and r4, r4, r5 # r4 = (v >> 1) & 0x55555555 l.sub r3, r3, r4 # v = v - ((v >> 1) & 0x55555555) l.movhi r5, 0x3333 # Statement 2: l.ori r5, r5, 0x3333 # r5 = 0x33333333 l.srli r4, r3, 2 # r4 = v >> 2 l.and r4, r4, r5 # r4 = (v >> 2) & 0x33333333 l.and r3, r3, r5 # v = v & 0x33333333 l.add r3, r3, r4 # v += ((v >> 2) & 0x33333333) l.movhi r5, 0x0f0f # Statement 3: l.ori r5, r5, 0x0f0f # r5 = 0x0f0f0f0f l.srli r4, r3, 4 # r4 = v >> 4 l.add r4, r3, r4 # r4 = v + (v >> 4) l.and r4, r4, r5 # r4 = v + (v >> 4) & 0x0f0f0f0f l.movhi r5, 0x0101 l.ori r5, r5, 0x0101 # r5 = 0x01010101 l.mul r11, r4, r5 # c = r4 * 0x01010101 l.jr r9 l.srli r11, r11, 24 # return c >> 24 endfunc __popcountsi2 /* * Optimized implementation of the "shift divisor method" algorithm from * T. Rodeheffer. Software Integer Division. Microsoft Research, 2008. * * In addition to returning the quotient in r11, this function also returns * the remainder in r12. __umodsi3 simply copies the remainder into r11. */ func __udivsi3 # u32 __udivsi3(u32 x, u32 y) { l.sfeqi r4, 1 # if (y == 1) l.bf 5f # goto identity; l.ori r12, r3, 0 # u32 r = x; l.ori r5, r4, 0 # u32 y0 = y; l.addi r11, r0, 0 # u32 q = 0; l.sfltu r3, r4 # if (x >= y) { l.bf 2f l.sub r3, r3, r4 # x = x−y; 1: l.sfltu r3, r4 # while (x >= y) { l.bf 2f l.sub r3, r3, r4 # x = x−y; l.add r4, r4, r4 # y *= 2; l.j 1b # } 2: l.sfltu r12, r4 # } for (;;) { l.bf 3f # if (r >= y) { l.sfeq r4, r5 # [if (y == y0)] l.sub r12, r12, r4 # r = r−y; l.addi r11, r11, 1 # q = q + 1; 3: l.bf 4f # } if (y == y0) break; l.srli r4, r4, 1 # y >>= 1; l.j 2b # } l.add r11, r11, r11 # q *= 2; 4: l.jr r9 # return q; l.nop 5: l.ori r11, r3, 0 # identity: l.jr r9 # return x; l.ori r12, r0, 0 # r = 0; endfunc __udivsi3 # } func __umodsi3 l.sw -4(r1), r9 l.jal __udivsi3 l.addi r1, r1, -4 l.addi r1, r1, 4 l.lwz r9, -4(r1) l.jr r9 l.ori r11, r12, 0 endfunc __umodsi3 crust-0.5/arch/or1k/scp.ld.S000066400000000000000000000040221414152222500156120ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #if CONFIG(COMPILE_TEST) #undef SRAM_A2_SIZE #define SRAM_A2_SIZE 0xffffffff #endif OUTPUT_ARCH(or1k) OUTPUT_FORMAT(elf32-or1k) ENTRY (start) MEMORY { SRAM_A2 (rwx): ORIGIN = SRAM_A2_BASE, LENGTH = SRAM_A2_SIZE } SECTIONS { ASSERT(FIRMWARE_BASE >= SRAM_A2_BASE, "Firmware must be placed in SRAM A2") ASSERT(FIRMWARE_LIMIT <= SRAM_A2_LIMIT, "Firmware must fit inside SRAM A2") /* * If addresses within the firmware differ only in their lower 16 bits, a * single l.movhi instruction can be shared by multiple symbol references. */ ASSERT(FIRMWARE_BASE >> 16 == (FIRMWARE_LIMIT - 1) >> 16, "Firmware must not cross a 64k address boundary") . = FIRMWARE_BASE; .text . : ALIGN(4) { KEEP(*(.text.start)) *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.text*))) . = ALIGN(4); } >SRAM_A2 .rodata . : ALIGN(4) { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) . = ALIGN(4); } >SRAM_A2 .data . : ALIGN(4) { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.data*))) . = ALIGN(4); } >SRAM_A2 /* * With few exceptions, global data should be constant or initialized during * boot. Before changing this value, verify the firmware can recover from a * crash even after the new data is modified. */ ASSERT(SIZEOF(.data) == MAX_CLUSTERS * MAX_CORES_PER_CLUSTER + 0x08, "Changes to .data persist after an exception!") .bss . : ALIGN(4) { __bss_start = .; *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) . = ALIGN(4); __bss_end = .; } >SRAM_A2 __stack_start = .; . += STACK_SIZE; __stack_end = .; #if !CONFIG(COMPILE_TEST) ASSERT(. <= FIRMWARE_LIMIT, "Firmware overflows allocated memory area") ASSERT(. <= SCPI_MEM_BASE, "Firmware overflows into SCPI shared memory") #endif __scpi_mem = SCPI_MEM_BASE; /DISCARD/ : { *(.comment*) *(.eh_frame_hdr*) *(.iplt*) *(.note*) *(.rela*) } } crust-0.5/arch/or1k/start.S000066400000000000000000000022621414152222500155700ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include func start # Save the exception vector address l.mfspr r2, r0, SPR_SYS_PPC_ADDR # Invalidate the instruction cache l.addi r3, r0, 0 l.addi r4, r0, 4096 # Cache lines (256) * block size (16) 1: l.mtspr r0, r3, SPR_ICACHE_ICBIR_ADDR l.sfltu r3, r4 l.bf 1b l.addi r3, r3, 16 # Cache block size # Flush the CPU pipeline l.psync # Enable the instruction cache l.mfspr r3, r0, SPR_SYS_SR_ADDR l.ori r3, r3, SPR_SYS_SR_ICE_MASK l.mtspr r0, r3, SPR_SYS_SR_ADDR # One cache block of nops l.nop l.nop l.nop l.nop # Clear .bss l.movhi r10, hi(start) # High word is common to all symbols l.ori r3, r10, lo(__bss_start) l.ori r4, r10, lo(__bss_end) 1: l.sw 0(r3), r0 l.sfltu r3, r4 l.bf 1b l.addi r3, r3, 4 # Prepare function arguments l.sfltui r2, 0x4000 # Did PC come from an exception vector? l.bnf 1f l.movhi r3, 0 # Set to zero if not an exception l.srli r3, r2, 8 # Else compute the exception number # Jump to the C entry point 1: l.j system_state_machine l.ori r1, r10, lo(__stack_end) endfunc start crust-0.5/common/000077500000000000000000000000001414152222500140045ustar00rootroot00000000000000crust-0.5/common/Kconfig000066400000000000000000000015261414152222500153130ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # menu "Firmware features" config PMIC_SHUTDOWN bool "Use PMIC for full hardware shutdown" depends on PMIC default y help Command the PMIC to turn off all outputs during the shutdown procedure. This reduces power usage to almost zero, but also stops the firmware. In this state, only a power button connected directly to the PMIC can turn the system back on. If this option is disabled, the shutdown procedure will be implemented in software only (similar to the suspend procedure), so the firmware can turn the system back on. Say Y if your board is powered by a battery, or N if you need some other method of turning on the system, such as an IR remote control or a GPIO input. endmenu source "debug/Kconfig" crust-0.5/common/Makefile000066400000000000000000000004641414152222500154500ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += debug/ obj-y += debug.o obj-y += delay.o obj-y += device.o obj-y += regulator_list.o obj-y += scpi.o obj-y += scpi_cmds.o obj-y += simple_device.o obj-y += system.o obj-y += timeout.o crust-0.5/common/debug.c000066400000000000000000000060601414152222500152400ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #define BYTES_PER_ROW 16 #define BYTES_PER_WORD sizeof(uint32_t) static char *prefixes[LOG_LEVELS] = { "SCP/ERR: ", "SCP/WRN: ", "SCP/INF: ", "SCP/DBG: ", }; static void print_number(uint32_t num, int base, int width, bool zero); static void print_signed(int32_t num, int base, int width, bool zero); void hexdump(uintptr_t addr, uint32_t bytes) { uintptr_t start; /* Always start at a multiple of BYTES_PER_ROW. */ addr &= ~(BYTES_PER_ROW - 1); for (start = addr; addr - start < bytes; addr += BYTES_PER_ROW) { uint32_t *words = (uint32_t *)addr; /* This assumes BYTES_PER_ROW is 16, which it will always be. * It's more of an informational constant, not a variable. */ log("%08x: %08x %08x %08x %08x ", addr, words[0], words[1], words[2], words[3]); /* The ARISC processor's data lines are swapped in hardware for * compatibility with the little-endian ARM CPUs. To examine * individual bytes, we must reverse each group of 4 bytes. */ for (int i = 0; i < BYTES_PER_ROW; ++i) { char c = ((char *)addr)[i ^ 3]; serial_putc(isprint(c) ? c : '.'); } serial_putc('\n'); } } void log(const char *fmt, ...) { bool zero; char c; int width; uintptr_t arg, level; va_list args; assert(fmt); if (!serial_ready()) return; level = *fmt - 1; if (level < LOG_LEVELS) { serial_puts(prefixes[level]); ++fmt; } va_start(args, fmt); while ((c = *fmt++)) { if (c != '%') { serial_putc(c); continue; } if (*fmt == '%') { ++fmt; serial_putc(c); continue; } arg = va_arg(args, uintptr_t); width = 0; zero = false; conversion: switch ((c = *fmt++)) { case 'c': serial_putc(arg); break; case 'd': case 'i': print_signed(arg, 10, width, zero); break; case 'p': /* "%p" behaves like "0x%08x". */ serial_puts("0x"); print_number(arg, 16, 2 * sizeof(arg), true); break; case 'x': print_number(arg, 16, width, zero); break; case 's': assert(arg); serial_puts((const char *)arg); break; case 'u': print_number(arg, 10, width, zero); break; default: assert(c); if (c == '0' && width == 0) zero = true; else if (isdigit(c)) width = 10 * width + (c - '0'); goto conversion; } } va_end(args); if (level < LOG_LEVELS) serial_putc('\n'); } static void print_number(uint32_t num, int base, int width, bool zero) { static const char chars[16] = "0123456789abcdef"; char digits[3 * sizeof(num)]; int i = 0; do { digits[i++] = chars[udivmod(&num, base)]; } while (num); while (width-- > i) serial_putc(zero ? '0' : ' '); while (i--) serial_putc(digits[i]); } static void print_signed(int32_t num, int base, int width, bool zero) { if (num < 0) { serial_putc('-'); print_number(-num, base, width ? width - 1 : width, zero); } else { print_number(num, base, width, zero); } } crust-0.5/common/debug/000077500000000000000000000000001414152222500150725ustar00rootroot00000000000000crust-0.5/common/debug/Kconfig000066400000000000000000000077371414152222500164130ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # menu "Debugging options" config ASSERT bool "Enable runtime assertion checking" default y help Enable assert() logic to validate assumptions and find bugs at runtime. If in doubt, say Y. config ASSERT_VERBOSE bool "Verbose logging of assertion failures" depends on ASSERT help When an assertion fails, print detailed information about it to the console. This includes the expression that was checked, as well as the file and line number of the call to assert(). The address of the failed assertion is printed with or without this option. Since all of the other details can be derived from the address and debug info (see below), enabling this option is only necessary when debug info is unavailable. Note that this option significantly increases the size of the final firmware binary. If in doubt, say N. config COMPILE_TEST bool "Allow compiling a firmware that does not run" help Some combinations of options may not make sense or may not produce a functional firmware, for example if the firmware becomes too large, or if it contains multiple drivers for the same piece of hardware. This is mostly useful for development and automated testing. Users should say N. config DEBUG_INFO bool "Compile the firmware with debug info" default y help When compiling the firmware, generate debug information for use with gdb. These are stripped out of the final firmware binary, so this option does not affect the loaded firmware size. If in doubt, say Y. config DEBUG_LOG bool "Print additional debug-level log messages" help This enables the debug() logging macro to print verbose informational messages that may aid in debugging. config DEBUG_MONITOR bool "Provide an interactive debug monitor while off/asleep" help While the system is off or asleep, accept simple commands on the serial port. Parsing is extremely basic, and line editing is not supported. Available commands are: m
[value] -- read/write memory w -- trigger wakeup With a PMIC present, additional commands are supported: p
[value] -- read/write PMIC registers config DEBUG_MONITOR_PMIC bool depends on DEBUG_MONITOR default MFD_AXP20X config DEBUG_PRINT_BATTERY bool "Print battery consumption periodically while off/asleep" depends on MFD_AXP803 help While the system is off or asleep and the battery is discharging, print the battery voltage, discharge current, and calculated power usage every 30 seconds. config DEBUG_PRINT_LATENCY bool "Print average latency after each state transition" help After each system state transition, calculate the average latency of the main loop, in AR100 clock cycles. The latency will be printed after the firmware has performed 10000 iterations in that state. config DEBUG_PRINT_SPRS bool "Print the contents of Special Purpose Registers at boot" help The OpenRISC 1000 architecture defines various Special Purpose Registers, or SPRs, that describe the particular CPU implementation. Enable this option to print a subset of these SPRs in a human-readable format during boot. config DEBUG_RECORD_STEPS bool "Record steps of the suspend/resume process in the RTC" depends on !PLATFORM_A83T # No RTC help Record the start of various steps of the suspend and resume processes in one of the RTC's general purpose registers. This provides an approximation of what the firmware was doing at the time of a hang. The last step will remain available in the RTC until a clean shutdown or reboot, or until power is removed. config DEBUG_VERIFY_DRAM bool "Verify DRAM contents after controller resume" help After reinitializing the DRAM controller during system resume, verify that DRAM contents were preserved via a simple checksum. The algorithm is very simplistic, and only a small portion of DRAM is checked, so there is no guarantee that corruption will be detected. endmenu crust-0.5/common/debug/Makefile000066400000000000000000000006041414152222500165320ustar00rootroot00000000000000# # Copyright © 2020-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-$(CONFIG_DEBUG_MONITOR) += monitor.o obj-$(CONFIG_DEBUG_PRINT_BATTERY) += battery.o obj-$(CONFIG_DEBUG_PRINT_LATENCY) += latency.o obj-$(CONFIG_DEBUG_PRINT_SPRS) += sprs.o obj-$(CONFIG_DEBUG_RECORD_STEPS) += steps.o obj-$(CONFIG_DEBUG_VERIFY_DRAM) += dram.o crust-0.5/common/debug/battery.c000066400000000000000000000023551414152222500167150ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #define MEASUREMENT_INTERVAL (30 * USEC_PER_SEC) /* 30s */ static uint32_t timeout; void debug_print_battery(void) { const struct regmap *map = &axp20x.map; uint32_t current, voltage; uint8_t hi, lo, val; if (!timeout_expired(timeout)) return; if (regmap_user_probe(map)) return; /* Battery present? */ if (regmap_read(map, 0x01, &val) || !(val & BIT(5))) goto err_put_mfd; /* Battery discharging? */ if (regmap_read(map, 0x00, &val) || (val & BIT(2))) goto err_put_mfd; if (regmap_read(map, 0x78, &hi)) goto err_put_mfd; if (regmap_read(map, 0x79, &lo)) goto err_put_mfd; voltage = udiv_round(((hi << 4) | (lo & 0xf)) * 1100, 1000); if (regmap_read(map, 0x7c, &hi)) goto err_put_mfd; if (regmap_read(map, 0x7d, &lo)) goto err_put_mfd; current = (hi << 4) | (lo & 0xf); info("Using %u mW (%u mA @ %u mV)", udiv_round(current * voltage, 1000), current, voltage); err_put_mfd: regmap_user_release(map); timeout = timeout_set(MEASUREMENT_INTERVAL); } crust-0.5/common/debug/dram.c000066400000000000000000000015511414152222500161630ustar00rootroot00000000000000/* * Copyright © 2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #define DRAM_BASE 0x40000000 #define MIN_OFFSET sizeof(uint32_t) #define MAX_OFFSET BIT(26) /* 64 MiB */ static uint32_t saved_checksum; static uint32_t dram_calc_checksum(void) { uint32_t checksum = 0; for (uint32_t offset = MIN_OFFSET; offset < MAX_OFFSET; offset <<= 1) { checksum += mmio_read_32(DRAM_BASE + 1 * offset); checksum += mmio_read_32(DRAM_BASE + 3 * offset); checksum += 1; checksum *= ~offset; } return checksum; } void dram_save_checksum(void) { saved_checksum = dram_calc_checksum(); } void dram_verify_checksum(void) { record_step(STEP_RESUME_DRAM_CHECKSUM); if (dram_calc_checksum() != saved_checksum) panic("DRAM checksum mismatch!"); } crust-0.5/common/debug/latency.c000066400000000000000000000012241414152222500166740ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #define ITERATIONS 10000 static uint32_t cycles; static uint32_t iterations; static uint8_t last_state; void debug_print_latency(uint8_t current_state) { if (current_state != last_state) { cycles = counter_read(); iterations = 0; last_state = current_state; } else if (iterations < ITERATIONS && ++iterations == ITERATIONS) { info("State %u: %u cycles/iteration", current_state, udiv_round(counter_read() - cycles, ITERATIONS)); } } crust-0.5/common/debug/monitor.c000066400000000000000000000044401414152222500167270ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #define MAX_LENGTH 23 /* "m 0x???????? 0x????????" */ static char buffer[MAX_LENGTH + 1]; static uint8_t length; static bool parse_hex(const char **str, uint32_t *num) { uint32_t n = 0; const char *s = *str; bool ret = false; /* Skip spaces. */ while (*s == ' ') ++s; /* At least one space must precede the number. */ if (s == *str) return false; /* Consume as many hex digits as found. */ for (;;) { uint32_t c = *s | 0x20; uint32_t digit; if (c - '0' < 10) digit = c - '0'; else if (c - 'a' < 6) digit = c - ('a' - 10); else if (c == 'x') digit = 0; else break; ++s; n = n << 4 | digit; ret = true; } *str = s; *num = n; return ret; } static void run_command(const char *cmd) { uint32_t addr, len; switch (*cmd++) { case 'd': /* Hex dump: "d xxxxxxxx xxxxxxxx", arguments in bare hex. */ if (parse_hex(&cmd, &addr) && parse_hex(&cmd, &len)) hexdump(addr, len); return; case 'm': /* MMIO: "m xxxxxxxx" or "m xxxxxxxx xxxxxxxx", bare hex. */ if (parse_hex(&cmd, &addr)) { uint32_t val; if (parse_hex(&cmd, &val)) mmio_write_32(addr, val); log("%08x: %08x\n", addr, mmio_read_32(addr)); } return; #if CONFIG(DEBUG_MONITOR_PMIC) case 'p': /* PMIC: "p xx" or "p xx xx", bare hex. */ if (parse_hex(&cmd, &addr)) { const struct regmap *map = &axp20x.map; uint32_t val32; uint8_t val8; if (regmap_user_probe(map)) return; if (parse_hex(&cmd, &val32)) regmap_write(map, addr, val32); regmap_read(map, addr, &val8); log("%02x: %02x\n", addr, val8); regmap_user_release(map); } return; #endif case 'w': /* Wake: "w". */ system_wake(); return; } } void debug_monitor(void) { unsigned char c; if (!serial_ready()) return; if (!(c = serial_getc())) return; if (c < ' ' || length == MAX_LENGTH) { serial_putc('\n'); if (c == '\r') { buffer[length] = 0; run_command(buffer); } serial_puts("> "); length = 0; } else { serial_putc(c); buffer[length++] = c; } } crust-0.5/common/debug/sprs.c000066400000000000000000000103211414152222500162220ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #define present(val) ((val) ? "present" : "not present") #define yesno(val) ((val) ? "yes" : "no") void debug_print_sprs(void) { uint32_t val; val = mfspr(SPR_SYS_VR_ADDR); info("Version Register: 0x%08x", val); info("\tRevision: %u", SPR_SYS_VR_REV_GET(val)); info("\tUpdated Version Registers: %s", present(SPR_SYS_VR_UVRP_GET(val))); info("\tConfiguration Template: 0x%x", SPR_SYS_VR_CFG_GET(val)); info("\tVersion: 0x%x", SPR_SYS_VR_VER_GET(val)); val = mfspr(SPR_SYS_UPR_ADDR); info("Unit Present Register: 0x%08x", val); info("\tUPR: %s", present(SPR_SYS_UPR_UP_GET(val))); info("\tData Cache: %s", present(SPR_SYS_UPR_DCP_GET(val))); info("\tInstruction Cache: %s", present(SPR_SYS_UPR_ICP_GET(val))); info("\tData MMU: %s", present(SPR_SYS_UPR_DMP_GET(val))); info("\tInstruction MMU: %s", present(SPR_SYS_UPR_IMP_GET(val))); info("\tMAC: %s", present(SPR_SYS_UPR_MP_GET(val))); info("\tDebug Unit: %s", present(SPR_SYS_UPR_DUP_GET(val))); info("\tPerformance Counters Unit: %s", present(SPR_SYS_UPR_PCUP_GET(val))); info("\tPower Management: %s", present(SPR_SYS_UPR_PICP_GET(val))); info("\tProgrammable Interrupt Controller: %s", present(SPR_SYS_UPR_PMP_GET(val))); info("\tTick Timer: %s", present(SPR_SYS_UPR_TTP_GET(val))); info("\tCustom Units (mask): 0x%02x", SPR_SYS_UPR_CUP_GET(val)); val = mfspr(SPR_SYS_CPUCFGR_ADDR); info("CPU Configuration Register: 0x%08x", val); info("\tNumber of Shadow GPR Files: %d", SPR_SYS_CPUCFGR_NSGF_GET(val)); info("\tCustom GPR File: %s", yesno(SPR_SYS_CPUCFGR_CGF_GET(val))); info("\tORBIS32 Supported: %s", yesno(SPR_SYS_CPUCFGR_OB32S_GET(val))); info("\tORBIS64 Supported: %s", yesno(SPR_SYS_CPUCFGR_OB64S_GET(val))); info("\tORFPX32 Supported: %s", yesno(SPR_SYS_CPUCFGR_OF32S_GET(val))); info("\tORFPX64 Supported: %s", yesno(SPR_SYS_CPUCFGR_OF64S_GET(val))); info("\tORVDX64 Supported: %s", yesno(SPR_SYS_CPUCFGR_OV64S_GET(val))); info("\tDelay Slot: %s", yesno(!SPR_SYS_CPUCFGR_ND_GET(val))); info("\tArchitecture Version Register: %s", present(SPR_SYS_CPUCFGR_AVRP_GET(val))); info("\tException Vector Base Address Register: %s", present(SPR_SYS_CPUCFGR_EVBARP_GET(val))); info("\tImplementation-Specific Registers (ISR0-7): %s", present(SPR_SYS_CPUCFGR_ISRP_GET(val))); info("\tArithmetic Exception Control/Status Registers: %s", present(SPR_SYS_CPUCFGR_AECSRP_GET(val))); val = mfspr(SPR_SYS_DCCFGR_ADDR); info("Data Cache Configuration Register: 0x%08x", val); info("\tNumber of Cache Ways: %d", BIT(SPR_SYS_DCCFGR_NCW_GET(val))); info("\tNumber of Cache Sets: %d", BIT(SPR_SYS_DCCFGR_NCS_GET(val))); info("\tCache Block Size: %d", BIT(4 + SPR_SYS_DCCFGR_CBS_GET(val))); info("\tCache Write Strategy: %s", SPR_SYS_DCCFGR_CWS_GET(val) ? "WT" : "WB"); info("\tCache Control Register Implemented: %s", yesno(SPR_SYS_DCCFGR_CCRI_GET(val))); info("\tCache Block Invalidate Register Implemented: %s", yesno(SPR_SYS_DCCFGR_CBIRI_GET(val))); info("\tCache Block Prefetch Register Implemented: %s", yesno(SPR_SYS_DCCFGR_CBPRI_GET(val))); info("\tCache Block Lock Register Implemented: %s", yesno(SPR_SYS_DCCFGR_CBLRI_GET(val))); info("\tCache Block Flush Register Implemented: %s", yesno(SPR_SYS_DCCFGR_CBFRI_GET(val))); info("\tCache Block Write-back Register Implemented: %s", yesno(SPR_SYS_DCCFGR_CBWBRI_GET(val))); val = mfspr(SPR_SYS_ICCFGR_ADDR); info("Instruction Cache Configuration Register: 0x%08x", val); info("\tNumber of Cache Ways: %d", BIT(SPR_SYS_ICCFGR_NCW_GET(val))); info("\tNumber of Cache Sets: %d", BIT(SPR_SYS_ICCFGR_NCS_GET(val))); info("\tCache Block Size: %d", BIT(4 + SPR_SYS_ICCFGR_CBS_GET(val))); info("\tCache Control Register Implemented: %s", yesno(SPR_SYS_ICCFGR_CCRI_GET(val))); info("\tCache Block Invalidate Register Implemented: %s", yesno(SPR_SYS_ICCFGR_CBIRI_GET(val))); info("\tCache Block Prefetch Register Implemented: %s", yesno(SPR_SYS_ICCFGR_CBPRI_GET(val))); info("\tCache Block Lock Register Implemented: %s", yesno(SPR_SYS_ICCFGR_CBLRI_GET(val))); } crust-0.5/common/debug/steps.c000066400000000000000000000012531414152222500163750ustar00rootroot00000000000000/* * Copyright © 2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #define RTC_GP_DATA_REG(n) (DEV_RTC + 0x0100 + 0x4 * (n)) #define LAST_EXCEPTION_REG RTC_GP_DATA_REG(2) #define LAST_STEP_REG RTC_GP_DATA_REG(3) void record_exception(uint32_t exception, uint32_t pc) { mmio_write_32(LAST_EXCEPTION_REG, exception << 24 | pc); } void record_step(uint32_t step) { mmio_write_32(LAST_STEP_REG, step); } void report_last_step(void) { uint32_t step = mmio_read_32(LAST_STEP_REG); if (step != STEP_NONE) error("Step %04x failed!", step); } crust-0.5/common/delay.c000066400000000000000000000004771414152222500152560ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include void udelay(uint32_t useconds) { uint32_t timeout = timeout_set(useconds); while (!timeout_expired(timeout)) { /* Do nothing. */ } } crust-0.5/common/device.c000066400000000000000000000020441414152222500154070ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include bool device_active(const struct device *dev) { return dev && dev->state->refcount; } int device_get(const struct device *dev) { int err; if (!dev) return ENODEV; if (!dev->state->refcount) { if ((err = dev->drv->probe(dev))) { warn("%s: Probe failed: %d", dev->name, err); return err; } debug("%s: Probed", dev->name); } /* Increment the refcount only after successful initialization. */ ++dev->state->refcount; return SUCCESS; } const struct device * device_get_or_null(const struct device *dev) { return device_get(dev) == SUCCESS ? dev : NULL; } void device_put(const struct device *dev) { if (!dev || --dev->state->refcount) return; debug("%s: Releasing", dev->name); dev->drv->release(dev); } int dummy_probe(const struct device *dev UNUSED) { return SUCCESS; } void dummy_release(const struct device *dev UNUSED) { } crust-0.5/common/regulator_list.c000066400000000000000000000031471414152222500172140ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include const struct regulator_handle cpu_supply = { #if CONFIG(REGULATOR_AXP803) .dev = &axp803_regulator.dev, .id = AXP803_REGL_DCDC2, #elif CONFIG(REGULATOR_AXP805) .dev = &axp805_regulator.dev, .id = AXP805_REGL_DCDCA, #elif CONFIG(REGULATOR_SY8106A) .dev = &sy8106a.dev, #elif CONFIG(REGULATOR_GPIO_CPU) .dev = &gpio_cpu_regulator.dev, #else .dev = NULL, #endif }; const struct regulator_handle dram_supply = { #if CONFIG(REGULATOR_AXP803) .dev = &axp803_regulator.dev, .id = AXP803_REGL_DCDC5, #elif CONFIG(REGULATOR_AXP805) .dev = &axp805_regulator.dev, .id = AXP805_REGL_DCDCE, #elif CONFIG(REGULATOR_GPIO_DRAM) .dev = &gpio_dram_regulator.dev, #else .dev = NULL, #endif }; const struct regulator_handle vcc_pll_supply = { #if CONFIG(REGULATOR_AXP803) .dev = &axp803_regulator.dev, .id = AXP803_REGL_ALDO3, #elif CONFIG(REGULATOR_AXP805) .dev = &axp805_regulator.dev, .id = AXP805_REGL_BLDO1, #elif CONFIG(REGULATOR_GPIO_VCC_PLL) .dev = &gpio_vcc_pll_regulator.dev, #else .dev = NULL, #endif }; const struct regulator_handle vdd_sys_supply = { #if CONFIG(REGULATOR_AXP803) .dev = &axp803_regulator.dev, .id = AXP803_REGL_DCDC6, #elif CONFIG(REGULATOR_AXP805) .dev = &axp805_regulator.dev, .id = AXP805_REGL_DCDCD, #elif CONFIG(REGULATOR_GPIO_VDD_SYS) .dev = &gpio_vdd_sys_regulator.dev, #else .dev = NULL, #endif }; crust-0.5/common/scpi.c000066400000000000000000000075111414152222500151120ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #define SCPI_MEM_AREA(n) (__scpi_mem[SCPI_CLIENTS - n - 1]) #define SCPI_TX_TIMEOUT (10 * USEC_PER_MSEC) /* 10ms */ #define RX_CHAN(client) (2 * (client)) #define TX_CHAN(client) (2 * (client) + 1) struct scpi_state { uint32_t timeout; bool tx_full; }; /** The shared memory area, with an address defined in the linker script. */ extern struct scpi_mem __scpi_mem[SCPI_CLIENTS]; /** The current state of each client. */ static struct scpi_state scpi_state[SCPI_CLIENTS]; /** * Send an SCPI message to the client, recording the timeout for when the * client must acknowledge the message. */ static void scpi_send_message(const struct device *mailbox, uint8_t client, struct scpi_state *state) { int err; /* Ensure the outgoing message is fully written at this point. */ barrier(); /* Ensure the timeout is updated before triggering transmission. */ state->timeout = timeout_set(SCPI_TX_TIMEOUT); state->tx_full = true; barrier(); /* Notify the client that the message has been sent. */ if ((err = msgbox_send(mailbox, TX_CHAN(client), SCPI_VIRTUAL_CHANNEL))) warn("SCPI%u: Send error: %d", client, err); } void scpi_create_message(const struct device *mailbox, uint8_t client, uint8_t command) { struct scpi_mem *mem = &SCPI_MEM_AREA(client); struct scpi_state *state = &scpi_state[client]; if (state->tx_full) return; /* Write the message header. */ mem->tx_msg.command = command; mem->tx_msg.sender = SCPI_SENDER_SCP; mem->tx_msg.size = 0; mem->tx_msg.status = SCPI_OK; /* Send the message. */ scpi_send_message(mailbox, client, state); } /** * Attempt as much forward progress as possible for a client, by checking * for client ACKs and then responding to incoming messages. * * Dispatch handling of messages to the appropriate function for that specific * command. The functions for doing so are defined in a separate file to * separate the API functionality from communication/state management code. */ static void scpi_poll_one_client(const struct device *mailbox, uint8_t client) { struct scpi_state *state = &scpi_state[client]; uint8_t rx_chan = RX_CHAN(client); uint8_t tx_chan = TX_CHAN(client); /* Flush any outgoing messages. The TX buffer becomes free when a * previously-sent message is acknowledged or when it times out. */ if (state->tx_full) { if (msgbox_last_tx_done(mailbox, tx_chan) || timeout_expired(state->timeout)) state->tx_full = false; } /* Once the TX buffer is free, we can process new messages, reading * from the RX buffer and generating responses in the TX buffer. */ if (!state->tx_full) { bool reply_needed = false; uint32_t msg; /* Try to grab a new message. All errors are handled by * retrying on the next iteration through the main loop. */ if (msgbox_receive(mailbox, rx_chan, &msg) == SUCCESS) { /* Only process messages sent with the correct * protocol, which SCPI calls a "virtual channel". */ if (msg == SCPI_VIRTUAL_CHANNEL) { struct scpi_mem *mem = &SCPI_MEM_AREA(client); /* The handler relays if a reply is needed. */ reply_needed = scpi_handle_cmd(client, mem); } /* Acknowledging the message allows the client to reuse * the RX buffer, so the handler must run first. */ msgbox_ack_rx(mailbox, rx_chan); } /* If the TX buffer now contains a reply, send it. */ if (reply_needed) scpi_send_message(mailbox, client, state); } } void scpi_poll(const struct device *mailbox) { for (uint8_t client = 0; client < SCPI_CLIENTS; ++client) scpi_poll_one_client(mailbox, client); } crust-0.5/common/scpi_cmds.c000066400000000000000000000155031414152222500161200ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include enum { /** Do not send a reply to this command. */ FLAG_NO_REPLY = BIT(0), /** Reject this command from the non-secure message box channel. */ FLAG_SECURE_ONLY = BIT(1), }; struct scpi_cmd { /** Handler that can process a message and create a dynamic reply. */ int (*handler)(uint32_t *rx_payload, uint32_t *tx_payload, uint16_t *tx_size); /** Expected size of received payload. */ uint8_t rx_size; /** Any combination of flags from above, if applicable. */ uint8_t flags; }; /* * Handler for SCPI_CMD_SCP_READY: Response to SCP ready. */ static int scpi_cmd_scp_ready_handler(uint32_t *rx_payload UNUSED, uint32_t *tx_payload UNUSED, uint16_t *tx_size UNUSED) { return SCPI_OK; } /* * Handler for SCPI_CMD_GET_SCP_CAP: Get SCP capability. */ #define SCP_FIRMWARE_VERSION(x, y, z) \ ((((x) & 0xff) << 24) | (((y) & 0xff) << 16) | ((z) & 0xffff)) #define SCPI_PAYLOAD_LIMITS(x, y) (((x) & 0x01ff) << 16 | ((y) & 0x01ff)) #define SCPI_PROTOCOL_VERSION(x, y) (((x) & 0xffff) << 16 | ((y) & 0xffff)) static int scpi_cmd_get_scp_cap_handler(uint32_t *rx_payload UNUSED, uint32_t *tx_payload, uint16_t *tx_size) { /* SCPI protocol version. */ tx_payload[0] = SCPI_PROTOCOL_VERSION(1, 2); /* Payload size limits. */ tx_payload[1] = SCPI_PAYLOAD_LIMITS(SCPI_PAYLOAD_SIZE, SCPI_PAYLOAD_SIZE); /* Firmware version. */ tx_payload[2] = SCP_FIRMWARE_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); /* Commands enabled 0. */ tx_payload[3] = BIT(SCPI_CMD_SCP_READY) | BIT(SCPI_CMD_GET_SCP_CAP) | BIT(SCPI_CMD_SET_CSS_POWER) | BIT(SCPI_CMD_GET_CSS_POWER) | BIT(SCPI_CMD_SET_SYS_POWER); /* Commands enabled 1. */ tx_payload[4] = 0; /* Commands enabled 2. */ tx_payload[5] = 0; /* Commands enabled 3. */ tx_payload[6] = 0; *tx_size = 7 * sizeof(*tx_payload); return SCPI_OK; } /* * Handler for SCPI_CMD_SET_CSS_POWER: Set CSS power state. * * This sets the power state of a single core, its parent cluster, and the CSS. */ static int scpi_cmd_set_css_power_handler(uint32_t *rx_payload, uint32_t *tx_payload UNUSED, uint16_t *tx_size UNUSED) { uint32_t descriptor = rx_payload[0]; uint32_t core = bitfield_get(descriptor, 0x00, 4); uint32_t cluster = bitfield_get(descriptor, 0x04, 4); uint32_t core_state = bitfield_get(descriptor, 0x08, 4); uint32_t cluster_state = bitfield_get(descriptor, 0x0c, 4); uint32_t css_state = bitfield_get(descriptor, 0x10, 4); int err; err = css_set_power_state(cluster, core, core_state, cluster_state, css_state); if (err) return err; return SCPI_OK; } /* * Handler for SCPI_CMD_GET_CSS_POWER: Get CSS power state. * * This gets the power states of all clusters and all cores they contain. */ #define CLUSTER_ID(x) ((x) & GENMASK(3, 0)) #define CLUSTER_POWER_STATE(x) (((x) & GENMASK(3, 0)) << 4) #define CORE_POWER_STATES(x) ((x) << 8) static int scpi_cmd_get_css_power_handler(uint32_t *rx_payload UNUSED, uint32_t *tx_payload, uint16_t *tx_size) { uint32_t clusters = css_get_cluster_count(); uint16_t descriptor; int err; /* Each cluster has its own power state descriptor. */ for (uint32_t i = 0; i < clusters; ++i) { uint32_t state, online_cores; if ((err = css_get_power_state(i, &state, &online_cores))) return err; descriptor = CLUSTER_ID(i) | CLUSTER_POWER_STATE(state) | CORE_POWER_STATES(online_cores); /* Work around the hardware byte swapping, since this is an * array of elements each aligned to less than 4 bytes. */ ((uint16_t *)tx_payload)[i ^ 1] = descriptor; } *tx_size = clusters * sizeof(descriptor); return SCPI_OK; } /* * Handler for SCPI_CMD_SET_SYS_POWER: Set system power state. */ static int scpi_cmd_set_sys_power_handler(uint32_t *rx_payload, uint32_t *tx_payload UNUSED, uint16_t *tx_size UNUSED) { uint8_t state = rx_payload[0]; if (state == SCPI_SYSTEM_SHUTDOWN) system_shutdown(); else if (state == SCPI_SYSTEM_REBOOT) system_reboot(); else if (state == SCPI_SYSTEM_RESET) system_reset(); else return SCPI_E_PARAM; /* Clear the current step during an orderly state transition. */ record_step(STEP_NONE); return SCPI_OK; } /* * The list of supported SCPI commands. */ static const struct scpi_cmd scpi_cmds[] = { [SCPI_CMD_SCP_READY] = { .handler = scpi_cmd_scp_ready_handler, .flags = FLAG_NO_REPLY | FLAG_SECURE_ONLY, }, [SCPI_CMD_GET_SCP_CAP] = { .handler = scpi_cmd_get_scp_cap_handler, }, [SCPI_CMD_SET_CSS_POWER] = { .handler = scpi_cmd_set_css_power_handler, .rx_size = sizeof(uint32_t), .flags = FLAG_NO_REPLY | FLAG_SECURE_ONLY, }, [SCPI_CMD_GET_CSS_POWER] = { .handler = scpi_cmd_get_css_power_handler, }, [SCPI_CMD_SET_SYS_POWER] = { .handler = scpi_cmd_set_sys_power_handler, .rx_size = sizeof(uint8_t), .flags = FLAG_SECURE_ONLY, }, }; /* * Generic SCPI command handling function. */ bool scpi_handle_cmd(uint8_t client, struct scpi_mem *mem) { struct scpi_msg *rx_msg = &mem->rx_msg; struct scpi_msg *tx_msg = &mem->tx_msg; const struct scpi_cmd *cmd; /* Initialize the response (defaults for unsupported commands). */ tx_msg->command = rx_msg->command; tx_msg->sender = rx_msg->sender; tx_msg->size = 0; tx_msg->status = SCPI_E_SUPPORT; /* Avoid reading past the end of the array; reply with the error. */ if (rx_msg->command >= ARRAY_SIZE(scpi_cmds)) return true; cmd = &scpi_cmds[rx_msg->command]; /* Update the command status and payload based on the message. */ if ((cmd->flags & FLAG_SECURE_ONLY) && client != SCPI_CLIENT_EL3) { /* Prevent Linux from sending commands that bypass PSCI. */ tx_msg->status = SCPI_E_ACCESS; } else if (rx_msg->size != cmd->rx_size) { /* Check that the request payload matches the expected size. */ tx_msg->status = SCPI_E_SIZE; } else if (cmd->handler) { /* Run the handler for this command to make a response. */ tx_msg->status = cmd->handler(rx_msg->payload, tx_msg->payload, &tx_msg->size); } else { debug("SCPI%u: Bad command: %u", client, rx_msg->command); } /* Report back if a reply should be sent. */ return !(cmd->flags & FLAG_NO_REPLY); } crust-0.5/common/simple_device.c000066400000000000000000000020131414152222500167540ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include int simple_device_probe(const struct device *dev) { const struct simple_device *self = to_simple_device(dev); int err; if (self->pins) { if ((err = gpio_get(&self->pins[0]))) return err; if ((err = gpio_get(&self->pins[1]))) goto err_put_gpio0; } clock_get(&self->clock); return SUCCESS; err_put_gpio0: gpio_put(&self->pins[0]); return err; } void simple_device_release(const struct device *dev) { const struct simple_device *self = to_simple_device(dev); clock_put(&self->clock); if (self->pins) { gpio_put(&self->pins[1]); gpio_put(&self->pins[0]); } } void simple_device_sync(const struct simple_device *sdev) { uint8_t refcount = sdev->dev.state->refcount; if (clock_get_state(&sdev->clock) == CLOCK_STATE_ENABLED) { if (refcount == 0) device_get(&sdev->dev); } else { if (refcount == 1) device_put(&sdev->dev); } } crust-0.5/common/system.c000066400000000000000000000236301414152222500155000ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NEXT_STATE (system_state + 2) /** * The enumeration of possible system states. * * The first three states are ordered to meet two constraints: * 1. BOOT must be nonzero so system_state is stored in .data and * reinitialized only after a SoC reset (not after a firmware restart). * 2. REBOOT must be AWAKE+2 for the system_wake() transition. * * The remaining values are divided into two parallel sequences. The states are * ordered such that adding two to the state variable advances to the next * step in the sequence. Paired states have similar functions and share code. */ enum { SS_AWAKE = 0x0, /**< System is awake; rich OS is running. */ SS_BOOT = 0x1, /**< First firmware boot after SoC reset. */ SS_REBOOT = 0x2, /**< Attempting board-level (PMIC) reboot. */ SS_SHUTDOWN = 0x3, /**< Transition from awake to off. */ SS_SUSPEND = 0x4, /**< Transition from awake to asleep. */ SS_OFF = 0x5, /**< System is off; RAM contents are lost. */ SS_ASLEEP = 0x6, /**< System is asleep; RAM contents are kept. */ SS_PRE_RESET = 0x7, /**< Common part of reset/resume transition. */ SS_PRE_RESUME = 0x8, /**< Common part of reset/resume transition. */ SS_RESET = 0x9, /**< Transition from off to boot via SoC reset. */ SS_RESUME = 0xa, /**< Transition from asleep to awake. */ }; /* This variable is persisted across exception restarts. */ static uint8_t system_state = SS_BOOT; static uint8_t select_suspend_depth(uint8_t current_state) { static const struct clock_handle osc24m = { &r_ccu.dev, CLK_OSC24M }; /* Bail if the DRAM controller or peripherals need running clocks. */ if (!CONFIG(HAVE_DRAM_SUSPEND) || clock_active(&osc24m)) return SD_NONE; /* Wakeup sources needing AVCC are only supported while asleep. */ if (current_state != SS_SHUTDOWN && irq_needs_avcc()) return SD_OSC24M; if (current_state != SS_SHUTDOWN || irq_needs_vdd_sys()) return SD_AVCC; return SD_VDD_SYS; } noreturn void system_state_machine(uint32_t exception) { const struct device *cec, *cir, *mailbox, *pmic, *watchdog; uint8_t initial_state = system_state; uint8_t suspend_depth; if (initial_state > SS_BOOT) { /* * If the firmware started in any state other than BOOT or * AWAKE, assume the system is off. It could be transitioning * or asleep, but resetting the board after an IRQ is safer * than attempting to resume in an unpredictable environment. */ system_state = SS_OFF; /* Clear out inactive references. */ cec = NULL; cir = NULL; watchdog = NULL; mailbox = NULL; } else { /* Otherwise, perform BOOT actions and switch to AWAKE. */ system_state = SS_AWAKE; /* First, enable watchdog protection. */ watchdog = device_get_or_null(&r_twd.dev); /* Perform one-time device driver initialization. */ counter_init(); r_ccu_init(); ccu_init(); css_init(); dram_init(); /* Acquire runtime-only devices. */ mailbox = device_get_or_null(&msgbox.dev); } /* * Initialize the serial port. Unless a preinitialized port (UART0) is * selected, errors occurring before this function call will not be * logged, and exceptions (such as those caused by assertion failures) * could result in a silent infinite loop. * * Serial communication needs accurate clock frequencies. Specifically, * OSC16M must be calibrated before initializing R_UART, and R_UART * will only work after an exception restart while off/asleep if the * calibrated OSC16M frequency is retained across the restart. */ serial_init(); /* Log startup messages. */ info("Crust " VERSION_STRING); report_exception(exception); report_last_step(); debug_print_sprs(); /* * If the firmware started in the initial state, assume the secure * monitor is waiting for an SCP_READY message. Otherwise, assume * nothing is listening, and skip sending the SCP_READY message to * avoid filling up the mailbox. */ if (mailbox && initial_state == SS_BOOT) { scpi_create_message(mailbox, SCPI_CLIENT_EL3, SCPI_CMD_SCP_READY); } for (;;) { debug_print_latency(system_state); switch (system_state) { case SS_AWAKE: /* Poll runtime devices. */ css_poll(); if (watchdog) watchdog_restart(watchdog); /* Poll runtime services. */ if (mailbox) scpi_poll(mailbox); break; case SS_SHUTDOWN: case SS_SUSPEND: debug("Suspending..."); /* Synchronize device state with Linux. */ record_step(STEP_SUSPEND_DEVICES); simple_device_sync(&pio); simple_device_sync(&r_pio); /* Release runtime-only devices. */ device_put(mailbox), mailbox = NULL; /* Acquire wakeup sources. */ cec = cec_get(); cir = cir_get(); /* Configure the SoC for minimal power consumption. */ record_step(STEP_SUSPEND_DRAM); dram_save_checksum(); dram_suspend(); record_step(STEP_SUSPEND_CCU); ccu_suspend(); /* * Disable watchdog protection. Once devices outside * the SoC (oscillators and regulators) are disabled, * the watchdog cannot successfully reset the SoC. */ device_put(watchdog), watchdog = NULL; /* Gate the rest of the SoC before removing power. */ record_step(STEP_SUSPEND_PRCM); suspend_depth = select_suspend_depth(system_state); r_ccu_suspend(suspend_depth); /* Perform PMIC-specific actions. */ record_step(STEP_SUSPEND_PMIC); if ((pmic = pmic_get())) { if (system_state == SS_SHUTDOWN && CONFIG(PMIC_SHUTDOWN)) pmic_shutdown(pmic); else pmic_suspend(pmic); } /* Turn off all unnecessary power domains. */ record_step(STEP_SUSPEND_REGULATORS); regulator_disable(&cpu_supply); if (system_state == SS_SHUTDOWN) { regulator_disable(&dram_supply); if (suspend_depth >= SD_OSC24M) regulator_disable(&vcc_pll_supply); if (suspend_depth >= SD_VDD_SYS) regulator_disable(&vdd_sys_supply); } /* * The regulator provider is often part of the same * device as the PMIC. Reduce churn by doing both PMIC * and regulator actions before releasing the PMIC. */ device_put(pmic); record_step(STEP_SUSPEND_COMPLETE); debug("Suspend to %d complete!", suspend_depth); /* The system is now off or asleep. */ system_state = NEXT_STATE; break; case SS_OFF: case SS_ASLEEP: debug_monitor(); debug_print_battery(); /* Poll wakeup sources. Reset or resume on wakeup. */ if ((cec && cec_poll(cec)) || (cir && cir_poll(cir)) || irq_poll()) system_state = NEXT_STATE; break; case SS_PRE_RESET: case SS_PRE_RESUME: /* * Perform PMIC-specific resume actions. * The PMIC is expected to restore regulator state. * If it fails, manually turn the regulators back on. */ record_step(STEP_RESUME_PMIC); if (!(pmic = pmic_get()) || pmic_resume(pmic)) { record_step(STEP_RESUME_REGULATORS); regulator_enable(&vdd_sys_supply); regulator_enable(&vcc_pll_supply); regulator_enable(&dram_supply); regulator_enable(&cpu_supply); } device_put(pmic); /* Give regulator outputs time to rise. */ udelay(5000); /* Restore SoC-internal power domains. */ record_step(STEP_RESUME_PRCM); r_ccu_resume(); /* Enable watchdog protection. */ watchdog = device_get_or_null(&r_twd.dev); /* The system is now ready to reset or resume. */ system_state = NEXT_STATE; break; case SS_RESUME: debug("Resuming..."); /* Configure the SoC for full functionality. */ record_step(STEP_RESUME_CCU); ccu_resume(); record_step(STEP_RESUME_DRAM); dram_resume(); dram_verify_checksum(); /* Release wakeup sources. */ record_step(STEP_RESUME_DEVICES); device_put(cir), cir = NULL; device_put(cec), cec = NULL; /* Acquire runtime-only devices. */ mailbox = device_get_or_null(&msgbox.dev); /* Resume execution on the CSS. */ css_resume(); record_step(STEP_RESUME_COMPLETE); debug("Resume complete!"); /* The system is now awake. */ system_state = SS_AWAKE; break; case SS_REBOOT: /* Attempt to reset the board using the PMIC. */ if ((pmic = pmic_get())) { pmic_reset(pmic); device_put(pmic); } /* Continue through to resetting the SoC. */ fallthrough; case SS_RESET: /* Attempt to reset the SoC using the watchdog. */ if (watchdog) watchdog_set_timeout(watchdog, 1); /* Continue making reboot/reset attempts. */ break; default: unreachable(); } } } void system_reboot(void) { /* This transition skips PRE_RESET, so the system must be awake. */ assert(system_state == SS_AWAKE); system_state = SS_REBOOT; } void system_reset(void) { /* This transition skips PRE_RESET, so the system must be awake. */ assert(system_state == SS_AWAKE); system_state = SS_RESET; } void system_shutdown(void) { /* This transition only makes sense when the system is awake. */ assert(system_state == SS_AWAKE); system_state = SS_SHUTDOWN; } void system_suspend(void) { /* This transition only makes sense when the system is awake. */ assert(system_state == SS_AWAKE); system_state = SS_SUSPEND; } void system_wake(void) { /* * Initiate a state transition sequence, starting from * any of the three stable states: * 1. AWAKE => REBOOT [=> RESET] => BOOT => AWAKE * 2. OFF => PRE_RESET => RESET => BOOT => AWAKE * 3. ASLEEP => PRE_RESUME => RESUME => AWAKE */ system_state = NEXT_STATE; } crust-0.5/common/timeout.c000066400000000000000000000012001414152222500156270ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include bool timeout_expired(uint32_t timeout) { uint32_t now = counter_read(); /* If the timeout wrapped, wait until the counter also wraps. */ return (now ^ timeout) >> 31 == 0 && now >= timeout; } uint32_t timeout_set(uint32_t useconds) { uint32_t cycles = CPUCLK_MHz * useconds; uint32_t now = counter_read(); /* Ensure the MSB is zero for the wraparound check above. */ assert(cycles >> 31 == 0); return now + cycles; } crust-0.5/configs/000077500000000000000000000000001414152222500141445ustar00rootroot00000000000000crust-0.5/configs/bananapi_m2_plus_h3_defconfig000066400000000000000000000000771414152222500217030ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_CIR=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/beelink_gs1_defconfig000066400000000000000000000002011414152222500202470ustar00rootroot00000000000000CONFIG_PLATFORM_H6=y CONFIG_CIR=y CONFIG_CIR_USE_OSC24M=y CONFIG_CIR_PROTO_NEC=y CONFIG_CIR_WAKE_CODE=0x8051 CONFIG_MFD_AXP20X=y crust-0.5/configs/beelink_x2_defconfig000066400000000000000000000001621414152222500201140ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_CIR=y CONFIG_CIR_PROTO_NEC=y CONFIG_CIR_WAKE_CODE=0x8051 CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/defconfig000066400000000000000000000000001414152222500160010ustar00rootroot00000000000000crust-0.5/configs/libretech_all_h3_cc_h3_defconfig000066400000000000000000000000771414152222500223210ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_CIR=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/libretech_all_h3_cc_h5_defconfig000066400000000000000000000000721414152222500223160ustar00rootroot00000000000000CONFIG_SOC_H5=y CONFIG_CIR=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/nanopi_a64_defconfig000066400000000000000000000000411414152222500200240ustar00rootroot00000000000000CONFIG_CIR=y CONFIG_MFD_AXP20X=y crust-0.5/configs/nanopi_m1_defconfig000066400000000000000000000001271414152222500177540ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_CIR=y CONFIG_CIR_USE_OSC24M=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/orangepi_2_defconfig000066400000000000000000000000771414152222500201240ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_CIR=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/orangepi_3_defconfig000066400000000000000000000000661414152222500201230ustar00rootroot00000000000000CONFIG_PLATFORM_H6=y CONFIG_CIR=y CONFIG_MFD_AXP20X=y crust-0.5/configs/orangepi_lite_defconfig000066400000000000000000000000621414152222500207120ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/orangepi_one_defconfig000066400000000000000000000000621414152222500205360ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/orangepi_pc2_defconfig000066400000000000000000000001551414152222500204440ustar00rootroot00000000000000CONFIG_SOC_H5=y CONFIG_CIR=y CONFIG_CIR_USE_OSC24M=y CONFIG_REGULATOR_GPIO_DRAM=y CONFIG_REGULATOR_SY8106A=y crust-0.5/configs/orangepi_pc_defconfig000066400000000000000000000001271414152222500203610ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_CIR=y CONFIG_CIR_USE_OSC24M=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/orangepi_pc_plus_defconfig000066400000000000000000000001271414152222500214240ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_CIR=y CONFIG_CIR_USE_OSC24M=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/orangepi_plus2e_defconfig000066400000000000000000000001271414152222500211710ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_CIR=y CONFIG_CIR_USE_OSC24M=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/orangepi_plus_defconfig000066400000000000000000000001271414152222500207420ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_CIR=y CONFIG_CIR_USE_OSC24M=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/orangepi_win_defconfig000066400000000000000000000000411414152222500205470ustar00rootroot00000000000000CONFIG_CIR=y CONFIG_MFD_AXP20X=y crust-0.5/configs/orangepi_zero_defconfig000066400000000000000000000000621414152222500207340ustar00rootroot00000000000000CONFIG_PLATFORM_H3=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/orangepi_zero_plus_defconfig000066400000000000000000000000551414152222500220010ustar00rootroot00000000000000CONFIG_SOC_H5=y CONFIG_REGULATOR_GPIO_DRAM=y crust-0.5/configs/pine64-lts_defconfig000066400000000000000000000000241414152222500177740ustar00rootroot00000000000000CONFIG_MFD_AXP20X=y crust-0.5/configs/pine64_plus_defconfig000066400000000000000000000000241414152222500202370ustar00rootroot00000000000000CONFIG_MFD_AXP20X=y crust-0.5/configs/pine_h64_defconfig000066400000000000000000000001201414152222500175000ustar00rootroot00000000000000CONFIG_PLATFORM_H6=y CONFIG_CIR=y CONFIG_I2C_PINS_PL0_PL1=y CONFIG_MFD_AXP20X=y crust-0.5/configs/pinebook_defconfig000066400000000000000000000000571414152222500177030ustar00rootroot00000000000000CONFIG_MFD_AXP20X=y # CONFIG_SERIAL is not set crust-0.5/configs/pinephone_defconfig000066400000000000000000000000571414152222500200620ustar00rootroot00000000000000CONFIG_MFD_AXP20X=y # CONFIG_SERIAL is not set crust-0.5/configs/pinetab_defconfig000066400000000000000000000000571414152222500175170ustar00rootroot00000000000000CONFIG_MFD_AXP20X=y # CONFIG_SERIAL is not set crust-0.5/configs/tanix_tx6_defconfig000066400000000000000000000001601414152222500200140ustar00rootroot00000000000000CONFIG_PLATFORM_H6=y CONFIG_CIR=y CONFIG_CIR_PROTO_NECX=y CONFIG_CIR_USE_OSC24M=y CONFIG_CIR_WAKE_CODE=0x40404d crust-0.5/configs/tbs_a711_defconfig000066400000000000000000000000271414152222500174130ustar00rootroot00000000000000CONFIG_PLATFORM_A83T=y crust-0.5/docs/000077500000000000000000000000001414152222500134445ustar00rootroot00000000000000crust-0.5/docs/abi.md000066400000000000000000000232031414152222500145210ustar00rootroot00000000000000# Crust Firmware ABI Crust, and any firmware that wishes to be compatible with it, interacts with other third-party software and firmware components at several ABI boundaries. Some of those boundaries were arbitrarily chosen, and are now hardcoded in various places throughout the ecosystem. This document attempts to enumerate those boundaries and provide the rationale for choosing them. ## Loading the firmware ### Location in SRAM Crust is loaded into SRAM A2, because that is the SRAM region in the same power domain as the AR100. It is also the SRAM region containing the AR100 exception vector area. On platforms that use ATF (A64/H5/H6), Crust is allocated the last 16KiB of SRAM A2. 16KiB was chosen as the smallest size that an SCP firmware could feasibly be made. It is simultaneously the largest amount of space that could be shaved off of ATF without breaking debug builds (it was previously using all of SRAM A2). Crust was placed at the end of SRAM A2 to avoid changing ATF's load address. This allowed SCP firmware support to be added to ATF over the course of several months, without requiring any breaking changes. On platforms that do not use ATF (32-bit platforms), Crust reserves the right to use the entirety of SRAM A2, if needed. Crust's load address is hardcoded: - In Crust, as `FIRMWARE_BASE` in `platform/*/include/platform/memory.h` - In ATF, as `SUNXI_SCP_BASE` in `plat/allwinner/common/include/platform_def.h` - In U-Boot, as `SCP_ADDR` in `board/sunxi/mksunxi_fit_atf.sh` ### Firmware contents Other than the first 32-bit word, no assumptions should be made about the contents of the Crust firmware binary. The first 32-bit word (the entry point) is stable, because it is the only way to use a single entry point for all exception vectors. Other firmware can assume that if the first four bytes at Crust's load address are `12 00 40 b4`, then Crust has been loaded into SRAM. Conversely, if the bytes are any other value, it is safe to assume that Crust has not been loaded. This magic value is hardcoded: - In Crust, as the first instruction in `common/start.S` - In ATF, as `SCP_FIRMWARE_MAGIC` in `plat/allwinner/common/sunxi_pm.c` ### Loading process Crust is not loaded adjacent to the AR100 exception vector area (which is 4KiB before the beginning of "usable" SRAM A2). Therefore, the exception vectors cannot be stored inside the firmware binary, and they must be programmed separately. Since the opcode for the `l.j` (unconditional jump) instruction is 0, this is easy. The value to write is `(entry - vector) >> 2`. This process is performed: - In Crust, as the loop labeled "Writing exception vectors" in `tools/load.c` - In ATF, inside `plat_setup_psci_ops()` in `plat/allwinner/common/sunxi_pm.c` ## Communicating via SCPI SCPI uses two hardware interfaces: a mailbox and a shared memory area. Crust provides two SCPI communication channels: one for ATF (secure") and one for Linux ("non-secure"). Crust reserves the right to add a third communication channel in the future for a TEE in Secure EL1. Therefore, it must be assumed to use three pairs mailbox channels and three shared memory segments. ### Mailbox SCP firmware is responsible for enabling the mailbox clock and reset. Other mailbox drivers SHOULD enable them if they are not already enabled, but MUST NOT disable them once they are enabled. SCP firmware is responsible for configuring the mailbox channel directions. Other mailbox drivers MUST NOT modify the channel direction registers. Crust has reserved the first six mailbox channels for use with SCPI. While they MAY also be used for other protocols, SCPI client drivers are unlikely to handle this cleanly. Channel allocation is as follows: | Channel | Direction | Use | |---------|-----------|----------------------------| | 0 | AP → SCP | SCPI (Secure EL3) | | 1 | SCP → AP | SCPI (Secure EL3) | | 2 | AP → SCP | SCPI (Non-secure EL1/EL2) | | 3 | SCP → AP | SCPI (Non-secure EL1/EL2) | | 4 | AP → SCP | SCPI (Secure EL1) (future) | | 5 | SCP → AP | SCPI (Secure EL1) (future) | | 6 | AP → SCP | Unallocated | | 7 | SCP → AP | Unallocated | This allocation is defined: - In Crust, as `RX_CHAN`/`TX_CHAN` in `common/scpi.c` - In ATF, as `RX_CHAN`/`TX_CHAN` in `drivers/allwinner/sunxi_msgbox.c` - In Linux, in the `mboxes` property of the `scpi` node in the device tree SCPI mailbox notifications use the standard virtual channel number 1. This is defined: - In Crust, as `SCPI_VIRTUAL_CHANNEL` in `include/lib/scpi_protocol.h` - In ATF, as `SCPI_MHU_SLOT_ID` in `drivers/arm/css/scpi/css_scpi.c` ### Shared memory Crust supports SCPI with a message size of 256 bytes. This is hardcoded: - In Crust, as `SCPI_MESSAGE_SIZE` in `include/lib/scpi_protocol.h` - In ATF, as `SCPI_SHARED_MEM_AP_TO_SCP` in `drivers/arm/css/scpi/css_scpi.c` Thus, each shared memory segment is 512 bytes long. Shared memory segments grow down from the top of SRAM A2, in the same order as mailbox channels grow up. | Offset | Direction | Use | |--------|-----------|----------------------------| | -0x100 | AP → SCP | SCPI (Secure EL3) | | -0x200 | SCP → AP | SCPI (Secure EL3) | | -0x300 | AP → SCP | SCPI (Non-secure EL1/EL2) | | -0x400 | SCP → AP | SCPI (Non-secure EL1/EL2) | | -0x500 | AP → SCP | SCPI (Secure EL1) (future) | | -0x600 | SCP → AP | SCPI (Secure EL1) (future) | These offsets are defined: - In Crust, as `SCPI_MEM_AREA` in `common/scpi.c` - In ATF, as `PLAT_CSS_SCP_COM_SHARED_MEM_BASE` in `plat/allwinner/common/include/platform_def.h` - In Linux, as the `reg` property of the `scp-shmem` node in the device tree Note that because SRAM A2 is "secure-only", Linux may not be able to access it when the secure mode fuse is blown. In that case, the non-secure shared memory will have to be moved to DRAM or SRAM A1. ### Timeouts Testing shows that Crust normally responds to SCPI commands within 1ms. Acknowledgment of responses is polled in ATF and interrupt-driven in Linux. In both cases, it should be at least as fast. Thus the current timeouts are very conservative. The only timeout that is sometimes too short is the ATF polling timeout, when adding many lines of serial debugging to the Crust boot process. Timeouts are defined: - In Crust, as `SCPI_TX_TIMEOUT` in `common/scpi.c` (10ms) - In ATF, as `MHU_TIMEOUT_ITERS` in `drivers/allwinner/sunxi_msgbox.c` (1s) - In Linux, as `MAX_RX_TIMEOUT` in `drivers/firmware/arm_scpi.c` (30ms) ### Concurrent use Crust supports concurrent SCPI commands by all clients, but only one command at a time per client. No further commands from a client will be processed until the previous response is acknowledged. Currently, neither ATF nor Linux attempts to send multiple concurrent commands on a single channel. ### Supported commands Crust attempts to follow the SCPI specification in advertising the list of supported commands, and implementing them according to spec. ### Sending "SCP Ready" Since the mailbox hardware in sunxi SoCs is a FIFO, not the doorbell that SCPI was designed for, it is important to avoid sending messages that will be ignored. To this end, Crust only sends the "SCP Ready" message when it believes the system is first booting. Crust assumes the system is booting iff CPU 0 is the only running CPU. This can be wrong, for example if Crust crashed and restarted while Linux was booted and all other CPUs were offlined to save power. It may be prudent to introduce an explicit handshake with ATF to signal first boot. ### Power states Crust uses the same CPU/CSS power states as defined in the SCPI specification appendix: | State | Meaning | |-------|-----------| | 0 | On | | 1 | Retention | | 2 | RESERVED | | 3 | Off | These are also defined: - In Crust, as `SCPI_CSS_*` in `include/lib/scpi_protocol.h` - In ATF, as `scpi_power_state_t` in `include/drivers/arm/css/css_scpi.h` System power states are defined by the SCPI specification. ## Hardware ownership - Crust owns the shared part and its private half of `HWSPINLOCK` and `MSGBOX`. ATF and Linux MUST NOT modify any part but the ARM private half. - Crust owns `R_CPUCFG`, `R_TIMER`, and `R_TWD`. Linux MUST NOT use them. ATF MUST NOT use them except for using `R_CPUCFG` to turn on the AR100. - Crust owns all of `R_PRCM` except the clock/reset registers. ATF and Linux MUST NOT use any registers except those controlling clocks/resets. - Crust MAY modify `R_RSB` and `R_TWI`, but only during boot or suspend. Linux and ATF MUST reset these devices after resume before attempting to use them. - Crust MAY modify `RTC`, but only during boot or suspend, except for the general purpose registers, which may be modified at any time. Use of general purpose registers by ATF, Linux, or Crust MUST be documented. - Crust MAY modify `CCU` and `R_PRCM`, but only during boot or suspend, and it MUST restore the original configuration before Linux resumes, except for: - The following bus clocks/resets, which Crust owns: `HWSPINLOCK`, `MSGBOX`, `DRAM`, `R_TWD` - And the following bus clocks/resets, which Crust MAY leave disabled after resume: `R_RSB`, `R_TWI` - Crust MAY modify `PIO`, `R_CIR_RX`, `R_PIO`, `R_INTC`, and `R_UART`, but only during boot or suspend, and it MUST restore the original configuration before Linux resumes. ### RTC Crust uses the following RTC general purpose registers: | N | Use | |---|-----------------------------------| | 0 | N/A (used by p-boot/u-boot) | | 1 | N/A (used by p-boot/u-boot) | | 2 | Last exception encountered | | 3 | Last suspend/resume step executed | crust-0.5/docs/code_of_conduct.md000066400000000000000000000041331414152222500171040ustar00rootroot00000000000000# Code of Conduct This code of conduct outlines expectations for participation in the Crust Firmware community, which is committed to providing a welcoming and inspiring community for all. People violating this code of conduct may be banned from the community. Our open source community strives to: * Be friendly and patient: Remember you might not be communicating in someone else's primary spoken or programming language, and others may not have your level of understanding. * Be welcoming: Our community welcomes and supports people of all types and backgrounds. This includes, but is not limited to members of any race, ethnicity, culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. * Be respectful: We are a world-wide community of professionals, and we conduct ourselves professionally. Disagreement is no excuse for poor behavior and poor manners. Disrespectful and unacceptable behavior includes, but is not limited to: * Violent threats or language. * Discriminatory or derogatory jokes and language. * Posting gratuitous, sexually explicit or violent material. * Posting, or threatening to post, people's personally identifying information ("doxing"). * Insults, especially those using discriminatory terms or slurs. * Behavior that could be perceived as unwanted sexual attention. * Advocating for or encouraging any of the above behaviors. * Understand disagreements: Disagreements, both social and technical, are useful learning opportunities. Seek to understand the other viewpoints and resolve differences constructively. This code is not exhaustive or complete. It serves to capture our common understanding of a productive, collaborative environment. We expect the code to be followed in spirit as much as in the letter. This document was derived from the [Adélie Linux Code of Conduct][alcoc], which is licensed as CC BY-NC-SA 4.0. [alcoc]: http://www.adelielinux.org/conduct.html crust-0.5/docs/issue_template.md000066400000000000000000000013031414152222500170060ustar00rootroot00000000000000 ## Type of issue crust-0.5/docs/pull_request_template.md000066400000000000000000000006521414152222500204100ustar00rootroot00000000000000 ## Purpose ## Considerations for reviewers Closes # crust-0.5/drivers/000077500000000000000000000000001414152222500141725ustar00rootroot00000000000000crust-0.5/drivers/Kconfig000066400000000000000000000005111414152222500154720ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # menu "Device drivers" source "cec/Kconfig" source "cir/Kconfig" source "clock/Kconfig" source "regmap/Kconfig" source "mfd/Kconfig" source "pmic/Kconfig" source "regulator/Kconfig" source "serial/Kconfig" endmenu crust-0.5/drivers/Makefile000066400000000000000000000005661414152222500156410ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-$(CONFIG_CEC) += cec/ obj-$(CONFIG_CIR) += cir/ obj-y += clock/ obj-y += css/ obj-y += dram/ obj-y += gpio/ obj-y += irq/ obj-y += mfd/ obj-y += msgbox/ obj-y += pmic/ obj-y += regmap/ obj-y += regulator/ obj-$(CONFIG_SERIAL) += serial/ obj-y += watchdog/ crust-0.5/drivers/cec/000077500000000000000000000000001414152222500147245ustar00rootroot00000000000000crust-0.5/drivers/cec/Kconfig000066400000000000000000000004641414152222500162330ustar00rootroot00000000000000# # Copyright © 2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # config CEC bool "HDMI CEC" help Listen for messages from TV or other devices on HDMI CEC bus. This can be used as a wakeup source. Note: Clocks and resets must be pre-initialized by rich OS. crust-0.5/drivers/cec/Makefile000066400000000000000000000002241414152222500163620ustar00rootroot00000000000000# # Copyright © 2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += cec.o obj-y += dw-hdmi-cec.o crust-0.5/drivers/cec/cec.c000066400000000000000000000005601414152222500156230ustar00rootroot00000000000000/* * Copyright © 2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include const struct device * cec_get(void) { return device_get_or_null(&hdmi_cec.dev); } uint32_t cec_poll(const struct device *dev) { if (!dev) return 0; return dw_hdmi_cec_poll(dev); } crust-0.5/drivers/cec/dw-hdmi-cec.c000066400000000000000000000112711414152222500171530ustar00rootroot00000000000000/* * Copyright © 2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #define CEC_STAT 0x0106 #define CEC_MUTE 0x0186 #define IH_MUTE 0x01ff #define CEC_CTRL 0x7d00 #define CEC_MASK 0x7d02 #define CEC_POL 0x7d03 #define CEC_ADDRL 0x7d05 #define CEC_ADDRH 0x7d06 #define CEC_TX_CNT 0x7d07 #define CEC_RX_CNT 0x7d08 #define CEC_TX_DATA 0x7d10 #define CEC_RX_DATA 0x7d20 #define CEC_LOCK 0x7d30 #define CEC_WKUPCTRL 0x7d31 #define IRQ_WAKEUP BIT(6) #define IRQ_ALL 0xff #define IH_MUTE_ALL 0x03 #define CEC_CTRL_STANDBY BIT(4) #define CEC_LOCK_RELEASE 0x00 /* Set Stream Path */ #define CEC_WKUP_MSG_86 BIT(7) /* Active Source */ #define CEC_WKUP_MSG_82 BIT(6) /* System Audio Mode Request */ #define CEC_WKUP_MSG_70 BIT(5) /* User Control Pressed */ #define CEC_WKUP_MSG_44 BIT(4) /* Deck Control */ #define CEC_WKUP_MSG_42 BIT(3) /* Play */ #define CEC_WKUP_MSG_41 BIT(2) /* Text View On */ #define CEC_WKUP_MSG_0d BIT(1) /* Image View On */ #define CEC_WKUP_MSG_04 BIT(0) #define CEC_WKUP_MSG_ALL (CEC_WKUP_MSG_86 | CEC_WKUP_MSG_82 | \ CEC_WKUP_MSG_70 | CEC_WKUP_MSG_44 | \ CEC_WKUP_MSG_42 | CEC_WKUP_MSG_41 | \ CEC_WKUP_MSG_0d | CEC_WKUP_MSG_04) struct dw_hdmi_cec_state { struct device_state ds; uint8_t stash[4]; }; static inline const struct dw_hdmi_cec * to_dw_hdmi_cec(const struct device *dev) { return container_of(dev, const struct dw_hdmi_cec, dev); } static inline struct dw_hdmi_cec_state * dw_hdmi_cec_state_for(const struct device *dev) { return container_of(dev->state, struct dw_hdmi_cec_state, ds); } uint32_t dw_hdmi_cec_poll(const struct device *dev) { const struct dw_hdmi_cec *self = to_dw_hdmi_cec(dev); uint8_t stat; stat = mmio_read_8(self->regs + CEC_STAT); mmio_write_8(self->regs + CEC_STAT, stat); return stat & IRQ_WAKEUP; } static int dw_hdmi_cec_probe(const struct device *dev) { const struct dw_hdmi_cec *self = to_dw_hdmi_cec(dev); struct dw_hdmi_cec_state *state = dw_hdmi_cec_state_for(dev); clock_get(&self->bus_clock); state->stash[0] = mmio_read_8(self->regs + CEC_MUTE); state->stash[1] = mmio_read_8(self->regs + CEC_MASK); state->stash[2] = mmio_read_8(self->regs + CEC_POL); state->stash[3] = mmio_read_8(self->regs + IH_MUTE); /* Mute all interrupts */ mmio_write_8(self->regs + IH_MUTE, IH_MUTE_ALL); /* Configure CEC wake up sources */ mmio_write_8(self->regs + CEC_WKUPCTRL, CEC_WKUP_MSG_ALL); /* Allow only wakeup interrupt on posedge */ mmio_write_8(self->regs + CEC_POL, IRQ_WAKEUP); mmio_write_8(self->regs + CEC_MUTE, (uint8_t) ~IRQ_WAKEUP); mmio_write_8(self->regs + CEC_MASK, (uint8_t) ~IRQ_WAKEUP); /* Clear any pending interrupt */ mmio_write_8(self->regs + CEC_STAT, IRQ_ALL); /* Release CEC message in RX buffer (if any) */ mmio_write_8(self->regs + CEC_LOCK, CEC_LOCK_RELEASE); /* * Put CEC controller in automatic mode. It will NACK all messages * except those, which are allowed in CEC_WKUPCTRL register. */ mmio_write_8(self->regs + CEC_CTRL, CEC_CTRL_STANDBY); /* Unmute interrupts */ mmio_write_8(self->regs + IH_MUTE, 0x00); return SUCCESS; } static void dw_hdmi_cec_release(const struct device *dev) { const struct dw_hdmi_cec *self = to_dw_hdmi_cec(dev); struct dw_hdmi_cec_state *state = dw_hdmi_cec_state_for(dev); /* Mute all interrupts */ mmio_write_8(self->regs + IH_MUTE, IH_MUTE_ALL); /* Clear wake up sources */ mmio_write_8(self->regs + CEC_WKUPCTRL, 0x00); /* Put controller out of automatic mode */ mmio_write_8(self->regs + CEC_CTRL, 0x00); /* Clear any pending interrupt */ mmio_write_8(self->regs + CEC_STAT, IRQ_ALL); /* Release CEC message in RX buffer (if any) */ mmio_write_8(self->regs + CEC_LOCK, CEC_LOCK_RELEASE); /* restore registers */ mmio_write_8(self->regs + CEC_POL, state->stash[2]); mmio_write_8(self->regs + CEC_MASK, state->stash[1]); mmio_write_8(self->regs + CEC_MUTE, state->stash[0]); /* Restoring IH_MUTE must be last, since it enables interrutps */ mmio_write_8(self->regs + IH_MUTE, state->stash[3]); clock_put(&self->bus_clock); } static const struct driver dw_hdmi_cec_driver = { .probe = dw_hdmi_cec_probe, .release = dw_hdmi_cec_release, }; const struct dw_hdmi_cec hdmi_cec = { .dev = { .name = "DW HDMI CEC", .drv = &dw_hdmi_cec_driver, .state = &(struct dw_hdmi_cec_state) { { 0 } }.ds, }, .bus_clock = { .dev = &r_ccu.dev, .id = CLK_OSC24M }, .regs = DEV_HDMI, }; crust-0.5/drivers/cir/000077500000000000000000000000001414152222500147475ustar00rootroot00000000000000crust-0.5/drivers/cir/Kconfig000066400000000000000000000031151414152222500162520ustar00rootroot00000000000000# # Copyright © 2020-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # config CIR bool "CIR (infrared) receiver" help Listen for input from an infrared remote controller or keyboard. This can be used as a wakeup source. if CIR choice bool "IR protocol" default CIR_PROTO_RC6 config CIR_PROTO_NEC bool "NEC" help Select this if your remote speaks NEC. config CIR_PROTO_NECX bool "Extended NEC" help Select this if your remote speaks extended NEC. config CIR_PROTO_RC6 bool "RC6" help Select this for standard RC6 MCE remotes. endchoice config CIR_WAKE_CODE hex "Scan code for wakeup" range 0x1 0xffffffff default 0x800f040c help Choose the scan code that will wake the system when detected. The default value will work with an RC6 MCE remote controller. config CIR_CLK_RATE int default 125000 if CIR_USE_OSC24M # 24 MHz divided by 192 default 32768 help Provide the sample frequency used for protocol decoding. config CIR_USE_OSC24M bool "Use OSC24M as parent clock" help Sometimes boards come without external 32768 Hz oscillator and thus RTC use the internal RC oscilator as a source for generating 32768 Hz clock. However, such clock is not precise enough for IR signal decoding purposes. Set this option to Y if your board doesn't have external 32768 Hz oscillator. Note that this increases power consumption. config R_CIR_RX_PIN int default 9 if PLATFORM_H6 default 11 if PLATFORM_A64 || PLATFORM_H3 default 12 if PLATFORM_A83T help Provide the Port L pin number used by the CIR receiver. endif crust-0.5/drivers/cir/Makefile000066400000000000000000000004121414152222500164040ustar00rootroot00000000000000# # Copyright © 2020-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += cir.o obj-$(CONFIG_CIR_PROTO_NEC) += nec.o obj-$(CONFIG_CIR_PROTO_NECX) += nec.o obj-$(CONFIG_CIR_PROTO_RC6) += rc6.o obj-y += sunxi-cir.o crust-0.5/drivers/cir/cir.c000066400000000000000000000005611414152222500156720ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include const struct device * cir_get(void) { return device_get_or_null(&r_cir_rx.dev); } uint32_t cir_poll(const struct device *dev) { return sunxi_cir_poll(dev) == CONFIG_CIR_WAKE_CODE; } crust-0.5/drivers/cir/cir.h000066400000000000000000000022031414152222500156720ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef CIR_PRIVATE_H #define CIR_PRIVATE_H #include /** * Possible values for pulse type. */ enum { CIR_SPACE, CIR_MARK, }; /** * Context for CIR pulse decoding. */ struct cir_dec_ctx { uint32_t buffer; /*<< Buffer to store a partial scancode. */ uint32_t counter; /*<< Cumulative pulse width counter. */ uint8_t bits; /*<< Number of bits decoded. */ uint8_t state; /*<< Internal decoder state. */ uint8_t pulse; /*<< Current pulse type (mark or space). */ int8_t width; /*<< Current pulse width in clock cycles. */ }; /** * Decode a CIR pulse sequence. * * The pulse flag and width must be valid each time this function is called. * Each call will decode a single pulse, so decoding a complete scancode * requires many calls. All but the last in the sequence will return zero. * * If an error occurs, decoding restarts, but the error is not reported. * * @return A successfully decoded scancode, or zero. */ uint32_t cir_decode(struct cir_dec_ctx *ctx); #endif /* CIR_PRIVATE_H */ crust-0.5/drivers/cir/nec.c000066400000000000000000000054151414152222500156650ustar00rootroot00000000000000/* * Copyright © 2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include "cir.h" #define EQ_MARGIN(val, time, margin) \ (((time) - (margin)) < (val) && (val) < ((time) + (margin))) #define NUM_DATA_BITS 32 /* NEC time unit is ~562.5 us, or ~1777 units/s. */ #define NEC_UNIT_RATE 1777 /* Convert specified number of time units to number of clock cycles. */ #define NEC_UNITS_TO_CLKS(num) \ UDIV_ROUND((num) * CONFIG_CIR_CLK_RATE, NEC_UNIT_RATE) #define NEC_LEAD_M NEC_UNITS_TO_CLKS(16) #define NEC_LEAD_S NEC_UNITS_TO_CLKS(8) #define NEC_DATA_M NEC_UNITS_TO_CLKS(1) #define NEC_DATA_S_0 NEC_UNITS_TO_CLKS(1) #define NEC_DATA_S_1 NEC_UNITS_TO_CLKS(3) #define NEC_HALF_MARGIN (NEC_UNITS_TO_CLKS(1) / 2) #define NEC_SINGLE_MARGIN NEC_UNITS_TO_CLKS(1) #define NEC_DOUBLE_MARGIN NEC_UNITS_TO_CLKS(2) enum { NEC_IDLE, NEC_HEAD_S, NEC_PULSE, NEC_DATA, NEC_STATES }; static const uint8_t nec_pulse_states[NEC_STATES] = { [NEC_IDLE] = CIR_MARK, [NEC_HEAD_S] = CIR_SPACE, [NEC_PULSE] = CIR_MARK, [NEC_DATA] = CIR_SPACE, }; uint32_t cir_decode(struct cir_dec_ctx *ctx) { uint32_t counter = ctx->counter; uint32_t ret = 0; /* Consume samples until the pulse state changes. */ if (nec_pulse_states[ctx->state] == ctx->pulse) { ctx->counter += ctx->width; ctx->width = 0; return 0; } /* Then reinitialize the cumulative counter for the next state. */ ctx->counter = 0; switch (ctx->state) { case NEC_IDLE: if (EQ_MARGIN(counter, NEC_LEAD_M, NEC_DOUBLE_MARGIN)) ctx->state = NEC_HEAD_S; else ctx->width = 0; break; case NEC_HEAD_S: if (EQ_MARGIN(counter, NEC_LEAD_S, NEC_SINGLE_MARGIN)) { ctx->bits = 0; ctx->buffer = 0; ctx->state = NEC_PULSE; } else { ctx->state = NEC_IDLE; } break; case NEC_PULSE: ctx->state = NEC_IDLE; if (!EQ_MARGIN(counter, NEC_DATA_M, NEC_HALF_MARGIN)) break; if (ctx->bits == NUM_DATA_BITS) { /* Would be nice to check if inverted values match. */ if (CONFIG(CIR_PROTO_NECX)) { ret = ((ctx->buffer << 16) & GENMASK(23, 16)) | (ctx->buffer & GENMASK(15, 8)) | ((ctx->buffer >> 16) & GENMASK(7, 0)); } else { ret = ((ctx->buffer << 8) & GENMASK(15, 8)) | ((ctx->buffer >> 16) & GENMASK(7, 0)); } debug("NEC code %06x", ret); } else { ctx->state = NEC_DATA; } break; case NEC_DATA: /* NEC is LSB first. */ ctx->buffer >>= 1; ctx->bits++; ctx->state = NEC_PULSE; if (EQ_MARGIN(counter, NEC_DATA_S_1, NEC_HALF_MARGIN)) ctx->buffer |= BIT(31); else if (!EQ_MARGIN(counter, NEC_DATA_S_0, NEC_HALF_MARGIN)) ctx->state = NEC_IDLE; break; default: unreachable(); } return ret; } crust-0.5/drivers/cir/rc6.c000066400000000000000000000064751414152222500156210ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include "cir.h" #define NUM_HEADER_BITS 4 #define NUM_MODE_0_DATA_BITS 16 #define NUM_MODE_6A_DATA_BITS 32 #define RC6_MAN_MASK GENMASK(31, 16) #define RC6_MAN_MCE 0x800f0000 #define RC6_MODE(header) ((header) & 7) /* RC6 time unit is 16 periods @ 36 kHz, ~444 us. */ #define RC6_CARRIER_FREQ 36000 #define RC6_UNIT_RATE (RC6_CARRIER_FREQ / 16) /* Convert specified number of time units to number of clock cycles. */ #define RC6_UNITS_TO_CLKS(num) \ UDIV_ROUND((num) * CONFIG_CIR_CLK_RATE, RC6_UNIT_RATE) enum { RC6_IDLE, RC6_LEADER_S, RC6_HEADER_P, RC6_HEADER_N, RC6_TRAILER_P, RC6_TRAILER_N, RC6_DATA_P, RC6_DATA_N, RC6_STATES }; static const int16_t rc6_durations[RC6_STATES] = { [RC6_IDLE] = RC6_UNITS_TO_CLKS(6), [RC6_LEADER_S] = RC6_UNITS_TO_CLKS(2), [RC6_HEADER_P] = RC6_UNITS_TO_CLKS(1), [RC6_HEADER_N] = RC6_UNITS_TO_CLKS(1), [RC6_TRAILER_P] = RC6_UNITS_TO_CLKS(2), [RC6_TRAILER_N] = RC6_UNITS_TO_CLKS(2), [RC6_DATA_P] = RC6_UNITS_TO_CLKS(1), [RC6_DATA_N] = RC6_UNITS_TO_CLKS(1), }; uint32_t cir_decode(struct cir_dec_ctx *ctx) { int32_t duration = rc6_durations[ctx->state]; int32_t epsilon = ctx->state == RC6_IDLE ? RC6_UNITS_TO_CLKS(1) : RC6_UNITS_TO_CLKS(1) / 2; /* Subtract the expected pulse with from the sample width. */ ctx->width -= duration; /* * If the duration of this pulse is larger than the remaining time in * the current sample, an error has occurred. Either noise introduced * a short pulse, or the decoder got out of sync. Restart the decoder. */ if (ctx->width < -epsilon) { ctx->state = RC6_IDLE; return 0; } /* Discard the remainder of the sample if less than epsilon remains. */ if (ctx->width <= epsilon) ctx->width = 0; switch (ctx->state) { case RC6_IDLE: if (!ctx->pulse) break; /* Found a leader mark. Initialize the context. */ ctx->bits = NUM_HEADER_BITS; ctx->buffer = 0; ctx->state = RC6_LEADER_S; break; case RC6_LEADER_S: /* Expect a space. */ ctx->state = ctx->pulse ? RC6_IDLE : RC6_HEADER_P; break; case RC6_HEADER_P: case RC6_DATA_P: ctx->bits--; ctx->buffer = ctx->buffer << 1 | ctx->pulse; ctx->state++; break; case RC6_HEADER_N: /* This pulse must negate the previous pulse. */ if (ctx->pulse == (ctx->buffer & 1)) { ctx->state = RC6_IDLE; } else if (ctx->bits == 0) { /* Reinitialize the buffer for decoding data. */ ctx->bits = RC6_MODE(ctx->buffer) == 6 ? NUM_MODE_6A_DATA_BITS : NUM_MODE_0_DATA_BITS; ctx->buffer = 0; ctx->state = RC6_TRAILER_P; } else { ctx->state = RC6_HEADER_P; } break; case RC6_TRAILER_P: case RC6_TRAILER_N: ctx->state++; break; case RC6_DATA_N: ctx->state = RC6_IDLE; /* This pulse must negate the previous pulse. */ if (ctx->pulse == (ctx->buffer & 1)) break; if (ctx->bits == 0) { uint32_t code = ctx->buffer; /* Remove MCE toggle bit. */ if ((code & RC6_MAN_MASK) == RC6_MAN_MCE) code &= ~BIT(15); debug("RC6 code %08x", code); return code; } else { ctx->state = RC6_DATA_P; } break; default: unreachable(); } return 0; } crust-0.5/drivers/cir/sunxi-cir.c000066400000000000000000000057771414152222500170540ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include "cir.h" #define CIR_RXCTL 0x00 #define CIR_RXPCFG 0x10 #define CIR_RXFIFO 0x20 #define CIR_RXINT 0x2c #define CIR_RXSTA 0x30 #define CIR_RXCFG 0x34 struct sunxi_cir_state { struct device_state ds; struct cir_dec_ctx dec_ctx; uint32_t clk_stash; }; static inline const struct sunxi_cir * to_sunxi_cir(const struct device *dev) { return container_of(dev, const struct sunxi_cir, dev); } static inline struct sunxi_cir_state * sunxi_cir_state_for(const struct device *dev) { return container_of(dev->state, struct sunxi_cir_state, ds); } uint32_t sunxi_cir_poll(const struct device *dev) { const struct sunxi_cir *self = to_sunxi_cir(dev); struct sunxi_cir_state *state = sunxi_cir_state_for(dev); struct cir_dec_ctx *dec_ctx = &state->dec_ctx; /* Feed the decoder data as needed and as it becomes available. */ if (dec_ctx->width <= 0) { /* If no data is available, do not call the decoder. */ if (!(mmio_read_32(self->regs + CIR_RXSTA) >> 8)) return 0; uint32_t sample = mmio_read_32(self->regs + CIR_RXFIFO); dec_ctx->pulse = sample >> 7; dec_ctx->width = sample & GENMASK(6, 0); } return cir_decode(dec_ctx); } static int sunxi_cir_probe(const struct device *dev) { const struct sunxi_cir *self = to_sunxi_cir(dev); struct sunxi_cir_state *state = sunxi_cir_state_for(dev); int err; if ((err = gpio_get(&self->pin))) return err; /* Set module clock parent and divider. */ state->clk_stash = mmio_read_32(R_CIR_RX_CLK_REG); mmio_write_32(R_CIR_RX_CLK_REG, CONFIG(CIR_USE_OSC24M) ? 0x01000002 : 0x0); clock_get(&self->bus_clock); clock_get(&self->mod_clock); /* Configure thresholds and sample clock. */ mmio_write_32(self->regs + CIR_RXCFG, CONFIG(CIR_USE_OSC24M) ? 0x00001404 : 0x010f0310); /* Enable CIR module. */ mmio_write_32(self->regs + CIR_RXCTL, 0x33); return SUCCESS; } static void sunxi_cir_release(const struct device *dev) { const struct sunxi_cir *self = to_sunxi_cir(dev); struct sunxi_cir_state *state = sunxi_cir_state_for(dev); clock_put(&self->mod_clock); clock_put(&self->bus_clock); mmio_write_32(R_CIR_RX_CLK_REG, state->clk_stash); gpio_put(&self->pin); } static const struct driver sunxi_cir_driver = { .probe = sunxi_cir_probe, .release = sunxi_cir_release, }; const struct sunxi_cir r_cir_rx = { .dev = { .name = "r_cir_rx", .drv = &sunxi_cir_driver, .state = &(struct sunxi_cir_state) { { 0 } }.ds, }, .bus_clock = { .dev = &r_ccu.dev, .id = CLK_BUS_R_CIR }, .mod_clock = { .dev = &r_ccu.dev, .id = CLK_R_CIR }, .pin = { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, CONFIG_R_CIR_RX_PIN), .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, .regs = DEV_R_CIR_RX, }; crust-0.5/drivers/clock/000077500000000000000000000000001414152222500152655ustar00rootroot00000000000000crust-0.5/drivers/clock/Kconfig000066400000000000000000000010141414152222500165640ustar00rootroot00000000000000# # Copyright © 2020 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # choice bool "OSC24M clock source" default OSC24M_SRC_DCXO if HAVE_DCXO default OSC24M_SRC_X24M config OSC24M_SRC_DCXO bool "DCXO" depends on HAVE_DCXO help Select this option if your board has the 24 MHz crystal connected to the DCXO pads on the SoC. config OSC24M_SRC_X24M bool "X24M" help Select this option if your board has the 24 MHz crystal connected to the X24M pads on the SoC. endchoice crust-0.5/drivers/clock/Makefile000066400000000000000000000010701414152222500167230ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += clock.o obj-y += ccu.o obj-y += ccu_helpers.o obj-y += r_ccu_common.o obj-$(CONFIG_PLATFORM_A64) += sun50i-a64-ccu.o obj-$(CONFIG_PLATFORM_A64) += sun8i-r-ccu.o obj-$(CONFIG_PLATFORM_A83T) += sun8i-a83t-ccu.o obj-$(CONFIG_PLATFORM_A83T) += sun8i-r-ccu.o obj-$(CONFIG_PLATFORM_H3) += sun8i-h3-ccu.o obj-$(CONFIG_PLATFORM_H3) += sun8i-r-ccu.o obj-$(CONFIG_PLATFORM_H6) += sun50i-h6-ccu.o obj-$(CONFIG_PLATFORM_H6) += sun50i-h6-r-ccu.o crust-0.5/drivers/clock/ccu.c000066400000000000000000000056001414152222500162040ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include "ccu.h" static inline const struct ccu * to_ccu(const struct device *dev) { return container_of(dev, const struct ccu, dev); } const struct clock_handle * ccu_get_null_parent(const struct ccu *self UNUSED, const struct ccu_clock *clk UNUSED) { return NULL; } static const struct clock_handle * ccu_get_parent(const struct clock_handle *clock) { const struct ccu *self = to_ccu(clock->dev); const struct ccu_clock *clk = &self->clocks[clock->id]; return clk->get_parent(self, clk); } uint32_t ccu_get_parent_rate(const struct ccu *self UNUSED, const struct ccu_clock *clk UNUSED, uint32_t rate) { return rate; } static uint32_t ccu_get_rate(const struct clock_handle *clock, uint32_t rate) { const struct ccu *self = to_ccu(clock->dev); const struct ccu_clock *clk = &self->clocks[clock->id]; /* Perform clock-specific adjustments to the parent rate. */ return clk->get_rate(self, clk, rate); } static uint32_t ccu_get_state(const struct clock_handle *clock) { const struct ccu *self = to_ccu(clock->dev); const struct ccu_clock *clk = &self->clocks[clock->id]; uintptr_t regs = self->regs; /* Check the reset line, if present. */ if (clk->reset && !bitmap_get(regs, clk->reset)) return CLOCK_STATE_DISABLED; /* Check the clock gate, if present. */ if (clk->gate && !bitmap_get(regs, clk->gate)) return CLOCK_STATE_GATED; /* Otherwise, the clock is enabled. */ return CLOCK_STATE_ENABLED; } static void ccu_set_state(const struct clock_handle *clock, uint32_t state) { const struct ccu *self = to_ccu(clock->dev); const struct ccu_clock *clk = &self->clocks[clock->id]; bool enable = state > CLOCK_STATE_DISABLED; bool ungate = state > CLOCK_STATE_GATED; uintptr_t regs = self->regs; /* Do nothing if the clock is already in the desired state. */ if (ccu_get_state(clock) == state) return; /* First, (de)assert the reset line. */ if (clk->reset) (enable ? bitmap_set : bitmap_clear)(regs, clk->reset); /* Once the device is in/out of reset, (un)gate the clock. */ if (clk->gate) (ungate ? bitmap_set : bitmap_clear)(regs, clk->gate); /* Apply the changes by setting the update bit, if applicable. */ if (clk->update) mmio_set_32(regs + clk->reg, BIT(clk->update)); /* Wait for the lock bit to be set, if applicable. */ if (clk->lock && ungate) mmio_poll_32(regs + clk->reg, BIT(clk->lock)); } const struct clock_driver ccu_driver = { .drv = { .probe = dummy_probe, .release = dummy_release, }, .ops = { .get_parent = ccu_get_parent, .get_rate = ccu_get_rate, .get_state = ccu_get_state, .set_state = ccu_set_state, }, }; crust-0.5/drivers/clock/ccu.h000066400000000000000000000061471414152222500162200ustar00rootroot00000000000000/* * Copyright © 2019-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef CCU_PRIVATE_H #define CCU_PRIVATE_H #include #include #include #include "clock.h" #define DEFINE_FIXED_PARENT(_name, _dev, _id) \ UNUSED const struct clock_handle * \ _name(const struct ccu *self UNUSED, \ const struct ccu_clock *clk UNUSED) { \ static const struct clock_handle _name ## _handle = { \ .dev = &_dev.dev, \ .id = _id, \ }; \ return &_name ## _handle; \ } #define DEFINE_FIXED_RATE(_name, _rate) \ uint32_t \ _name(const struct ccu *self UNUSED, \ const struct ccu_clock *clk UNUSED, \ uint32_t rate UNUSED) { \ return _rate; \ } struct ccu_clock { /** Hook for determining the parent clock. */ const struct clock_handle *(*get_parent)(const struct ccu *self, const struct ccu_clock *clk); /** Hook for calculating the clock rate from the parent rate. */ uint32_t (*get_rate)(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate); /** Byte offset of the clock configuration register. */ uint16_t reg; /** Offset of the lock bit inside the register (valid if nonzero). */ uint8_t lock; /** Offset of the update bit inside the register (valid if nonzero). */ uint8_t update; /** Bit offset of the clock gate (valid if nonzero). */ uint16_t gate; /** Bit offset of the module reset (valid if nonzero). */ uint16_t reset; }; /* * ccu.c * ===== */ extern const struct clock_driver ccu_driver; /** * Default .get_parent implementation, returns NULL. */ const struct clock_handle *ccu_get_null_parent(const struct ccu *self, const struct ccu_clock *clk); /** * Default .get_rate implementation, returns the parent's rate unmodified. */ uint32_t ccu_get_parent_rate(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate); /* * ccu_helpers.c * ============= */ uint32_t ccu_helper_get_rate_m(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate, uint32_t m_shift, uint32_t m_width); uint32_t ccu_helper_get_rate_mp(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate, uint32_t m_shift, uint32_t m_width, uint32_t p_shift, uint32_t p_width); uint32_t ccu_helper_get_rate_p(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate, uint32_t p_shift, uint32_t p_width); /* * r_ccu_common.c * ============== */ uint32_t r_ccu_common_get_osc16m_rate(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate); void r_ccu_common_suspend(uint8_t depth); void r_ccu_common_resume(void); void r_ccu_common_init(void); #endif /* CCU_PRIVATE_H */ crust-0.5/drivers/clock/ccu_helpers.c000066400000000000000000000022751414152222500177330ustar00rootroot00000000000000/* * Copyright © 2019-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include "ccu.h" uint32_t ccu_helper_get_rate_m(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate, uint32_t m_shift, uint32_t m_width) { uint32_t val = mmio_read_32(self->regs + clk->reg); rate /= bitfield_get(val, m_shift, m_width) + 1; return rate; } uint32_t ccu_helper_get_rate_mp(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate, uint32_t m_shift, uint32_t m_width, uint32_t p_shift, uint32_t p_width) { uint32_t val = mmio_read_32(self->regs + clk->reg); rate /= bitfield_get(val, m_shift, m_width) + 1; rate >>= bitfield_get(val, p_shift, p_width); return rate; } uint32_t ccu_helper_get_rate_p(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate, uint32_t p_shift, uint32_t p_width) { uint32_t val = mmio_read_32(self->regs + clk->reg); rate >>= bitfield_get(val, p_shift, p_width); return rate; } crust-0.5/drivers/clock/clock.c000066400000000000000000000100221414152222500165170ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include "clock.h" /** * Get the ops for the controller device providing this clock. */ static inline const struct clock_driver_ops * clock_ops_for(const struct clock_handle *clock) { const struct clock_driver *drv = container_of(clock->dev->drv, const struct clock_driver, drv); return &drv->ops; } /** * Get the mutable state for this clock. */ static inline struct clock_state * clock_state_for(const struct clock_handle *clock) { struct clock_device_state *state = container_of(clock->dev->state, struct clock_device_state, ds); return &state->cs[clock->id]; } bool clock_active(const struct clock_handle *clock) { return clock_state_for(clock)->refcount; } void clock_disable(const struct clock_handle *clock) { /* Calling this function is only allowed after calling clock_get(). */ assert(clock_active(clock)); clock_ops_for(clock)->set_state(clock, CLOCK_STATE_GATED); } void clock_enable(const struct clock_handle *clock) { const struct clock_driver_ops *ops = clock_ops_for(clock); const struct clock_handle *parent; /* Calling this function is only allowed after calling clock_get(). */ assert(clock_active(clock)); /* If the clock has a parent, ensure the parent is enabled. */ if ((parent = ops->get_parent(clock))) clock_enable(parent); ops->set_state(clock, CLOCK_STATE_ENABLED); } void clock_get(const struct clock_handle *clock) { const struct clock_driver_ops *ops = clock_ops_for(clock); struct clock_state *state = clock_state_for(clock); /* Perform additional setup if this is the first reference. */ if (!state->refcount) { const struct clock_handle *parent; int err UNUSED; /* Ensure the controller's driver is loaded. */ err = device_get(clock->dev); assert(err == SUCCESS); /* Ensure the clock's parent has an active reference. */ if ((parent = ops->get_parent(clock))) clock_get(parent); debug("%s: Clock %u running at %u Hz", clock->dev->name, clock->id, clock_get_rate(clock)); } /* Bump the refcount only after successfully acquiring dependencies. */ ++state->refcount; /* Enable the clock. */ clock_enable(clock); } uint32_t clock_get_rate(const struct clock_handle *clock) { const struct clock_driver_ops *ops = clock_ops_for(clock); const struct clock_handle *parent; uint32_t rate = 0; /* Initialize the rate with the parent's rate or a known safe value. */ if ((parent = ops->get_parent(clock))) rate = clock_get_rate(parent); /* Call the driver function to calculate this clock's rate. */ return ops->get_rate(clock, rate); } uint32_t clock_get_state(const struct clock_handle *clock) { const struct clock_driver_ops *ops = clock_ops_for(clock); const struct clock_handle *parent; uint32_t parent_state; /* If the clock has a parent, check the parent's state. */ if ((parent = ops->get_parent(clock))) { parent_state = clock_get_state(parent); /* If the parent is not enabled, this clock has that state. */ if (parent_state != CLOCK_STATE_ENABLED) return parent_state; } /* Call the driver function to check this clock's state. */ return ops->get_state(clock); } void clock_put(const struct clock_handle *clock) { const struct clock_driver_ops *ops = clock_ops_for(clock); const struct clock_handle *parent; struct clock_state *state = clock_state_for(clock); /* Calling this function is only allowed after calling clock_get(). */ assert(state->refcount); /* Do nothing if there are other consumers of this clock. */ if (--state->refcount) return; debug("%s: Releasing clock %u", clock->dev->name, clock->id); /* Completely disable the clock once the last consumer is gone. */ ops->set_state(clock, CLOCK_STATE_DISABLED); /* Drop the reference to the parent clock. */ if ((parent = ops->get_parent(clock))) clock_put(parent); /* Drop the reference to the controller device. */ device_put(clock->dev); } crust-0.5/drivers/clock/clock.h000066400000000000000000000016611414152222500165350ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef CLOCK_PRIVATE_H #define CLOCK_PRIVATE_H #include #include #include #define CLOCK_DEVICE_STATE_INIT(n) \ (struct device_state *) \ &(char[sizeof_struct(struct clock_device_state, cs, n)]) { 0 } struct clock_state { uint8_t refcount; }; struct clock_device_state { struct device_state ds; struct clock_state cs[]; }; struct clock_driver_ops { const struct clock_handle * (*get_parent)(const struct clock_handle *clock); uint32_t (*get_rate)(const struct clock_handle *clock, uint32_t rate); uint32_t (*get_state)(const struct clock_handle *clock); void (*set_state)(const struct clock_handle *clock, uint32_t state); }; struct clock_driver { struct driver drv; struct clock_driver_ops ops; }; #endif /* CLOCK_PRIVATE_H */ crust-0.5/drivers/clock/r_ccu_common.c000066400000000000000000000057111414152222500201000ustar00rootroot00000000000000/* * Copyright © 2019-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include "ccu.h" #define PLL_CTRL_REG1_MASK (PLL_CTRL_REG1_KEY_FIELD | \ PLL_CTRL_REG1_CRYSTAL_EN | \ PLL_CTRL_REG1_LDO_EN) /* Persist this var as r_ccu_init() may not be called after an exception. */ static uint32_t osc16m_rate = 16000000U; DEFINE_FIXED_RATE(r_ccu_common_get_osc16m_rate, osc16m_rate) /** * Write a value to the lockable bits in PLL_CTRL_REG1. */ static void write_pll_ctrl_reg1(uint32_t new) { uint32_t val = mmio_read_32(PLL_CTRL_REG1) & ~PLL_CTRL_REG1_MASK; /* Unlock if locked, otherwise write value. */ mmio_write_32(PLL_CTRL_REG1, val | new | PLL_CTRL_REG1_KEY); /* Write value if just unlocked, otherwise write same; lock. */ mmio_write_32(PLL_CTRL_REG1, val | new); } void r_ccu_common_suspend(uint8_t depth) { if (depth == SD_NONE) return; if (CONFIG(OSC24M_SRC_X24M)) { write_pll_ctrl_reg1(PLL_CTRL_REG1_LDO_EN); udelay(1); } write_pll_ctrl_reg1(0); if (depth == SD_OSC24M) return; mmio_set_32(VDD_SYS_PWROFF_GATING_REG, AVCC_GATING); if (depth == SD_AVCC) return; mmio_set_32(VDD_SYS_PWROFF_GATING_REG, VDD_CPUS_GATING); mmio_write_32(VDD_SYS_RESET_REG, 0); if (depth == SD_VDD_SYS) return; } void WEAK ATTRIBUTE(alias("r_ccu_common_suspend")) r_ccu_suspend(uint8_t depth); void r_ccu_common_resume(void) { /* * The suspend/resume steps are incremental and idempotent. There is no * need to branch based on the suspend depth; just run them all. This * simplifies handling a firmware restart where the depth is unknown. */ mmio_write_32(VDD_SYS_RESET_REG, VDD_SYS_RESET); mmio_clr_32(VDD_SYS_PWROFF_GATING_REG, VDD_CPUS_GATING | AVCC_GATING); if (!mmio_get_32(PLL_CTRL_REG1, PLL_CTRL_REG1_LDO_EN)) { write_pll_ctrl_reg1(PLL_CTRL_REG1_LDO_EN); if (CONFIG(OSC24M_SRC_X24M)) { udelay(2000); write_pll_ctrl_reg1(PLL_CTRL_REG1_CRYSTAL_EN | PLL_CTRL_REG1_LDO_EN); } } } void WEAK ATTRIBUTE(alias("r_ccu_common_resume")) r_ccu_resume(void); void r_ccu_common_init(void) { uint32_t after, before, end, now; /* Cycle until the interval will not span a counter wraparound. */ do { before = counter_read(); barrier(); now = r_twd_counter_read(); end = now + (REFCLK_HZ >> 9); } while (end < now); /* Cycle until the end of the interval. */ do { after = counter_read(); /* Ensure the counters are read in a consistent order. */ barrier(); now = r_twd_counter_read(); } while (now < end); /* * Convert the number of OSC16M cycles in 1/512 second to Hz. 512 is * chosen because it is the largest power-of-two factor of 24MHz, the * reference clock frequency. */ osc16m_rate = (after - before) << 9; } crust-0.5/drivers/clock/sun50i-a64-ccu.c000066400000000000000000000144201414152222500177150ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include "ccu.h" #define CPUX_AXI_CFG_REG 0x0050 #define AHB1_APB1_CFG_REG 0x0054 #define APB2_CFG_REG 0x0058 #define AHB2_CFG_REG 0x005c #define CPUX_CLK_SRC(x) ((x) << 16) #define CPUX_APB_CLK_M(x) ((x) << 8) #define CPUX_AXI_CLK_M(x) ((x) << 0) #define AHB1_CLK_SRC(x) ((x) << 12) #define APB1_CLK_DIV(x) ((x) << 8) #define AHB1_PRE_DIV(x) ((x) << 6) #define AHB1_CLK_P(x) ((x) << 4) #define APB2_CLK_SRC(x) ((x) << 24) #define APB2_CLK_P(x) ((x) << 16) #define APB2_CLK_M(x) ((x) << 0) #define AHB2_CLK_SRC(n) ((n) << 0) static DEFINE_FIXED_RATE(ccu_get_pll_periph0_rate, 600000000U) /* * While APB2 has a mux, assume its parent is OSC24M. Reparenting APB2 * to PLL_PERIPH0 in Linux for faster UART clocks is unsupported. */ static DEFINE_FIXED_PARENT(ccu_get_apb2_parent, r_ccu, CLK_OSC24M) static DEFINE_FIXED_PARENT(ccu_get_apb2, ccu, CLK_APB2) static const struct clock_handle ccu_dram_parents[] = { { .dev = &ccu.dev, .id = CLK_PLL_DDR0, }, #if CONFIG(SOC_A64) { .dev = &ccu.dev, .id = CLK_PLL_DDR1, }, #else { .dev = &ccu.dev, .id = CLK_PLL_PERIPH0, /* 2x */ }, #endif }; static const struct clock_handle * ccu_get_dram_parent(const struct ccu *self, const struct ccu_clock *clk) { uint32_t val = mmio_read_32(self->regs + clk->reg); return &ccu_dram_parents[bitfield_get(val, 20, 1)]; } static const struct ccu_clock ccu_clocks[SUN50I_A64_CCU_CLOCKS] = { [CLK_PLL_CPUX] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .reg = 0x0000, .lock = 28, .gate = BITMAP_INDEX(0x0000, 31), }, [CLK_PLL_DDR0] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .reg = 0x0020, .lock = 28, .update = 20, .gate = BITMAP_INDEX(0x0020, 31), }, [CLK_PLL_PERIPH0] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_pll_periph0_rate, }, #if CONFIG(SOC_A64) [CLK_PLL_DDR1] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .reg = 0x004c, .lock = 28, .update = 30, .gate = BITMAP_INDEX(0x004c, 31), }, #endif [CLK_APB2] = { .get_parent = ccu_get_apb2_parent, .get_rate = ccu_get_parent_rate, }, /* Reset requires re-training DRAM, so ignore it. */ [CLK_BUS_DRAM] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0060, 14), }, [CLK_BUS_MSGBOX] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0064, 21), .reset = BITMAP_INDEX(0x02c4, 21), }, [CLK_BUS_PIO] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0068, 5), }, #if CONFIG(SERIAL_DEV_UART0) [CLK_BUS_UART0] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 16), .reset = BITMAP_INDEX(0x02d8, 16), }, #elif CONFIG(SERIAL_DEV_UART1) [CLK_BUS_UART1] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 17), .reset = BITMAP_INDEX(0x02d8, 17), }, #elif CONFIG(SERIAL_DEV_UART2) [CLK_BUS_UART2] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 18), .reset = BITMAP_INDEX(0x02d8, 18), }, #elif CONFIG(SERIAL_DEV_UART3) [CLK_BUS_UART3] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 19), .reset = BITMAP_INDEX(0x02d8, 19), }, #elif CONFIG(SERIAL_DEV_UART4) /* depends on SOC_A64 */ [CLK_BUS_UART4] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 20), .reset = BITMAP_INDEX(0x02d8, 20), }, #endif [CLK_DRAM] = { .get_parent = ccu_get_dram_parent, .get_rate = ccu_get_parent_rate, .reg = 0x00f4, .update = 16, .reset = BITMAP_INDEX(0x00f4, 31), }, [CLK_MBUS] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x015c, 31), #if !CONFIG(SOC_H5) /* MBUS reset intermittently breaks DRAM resume on H5. */ .reset = BITMAP_INDEX(0x00fc, 31), #endif }, }; const struct ccu ccu = { .dev = { .name = "ccu", .drv = &ccu_driver.drv, .state = CLOCK_DEVICE_STATE_INIT(SUN50I_A64_CCU_CLOCKS), }, .clocks = ccu_clocks, .regs = DEV_CCU, }; static const struct clock_handle pll_cpux = { .dev = &ccu.dev, .id = CLK_PLL_CPUX, }; void ccu_suspend(void) { /* Set AHB1 to LOSC/1 (32kHz), APB1 to AHB1/2 (16kHz). */ mmio_write_32(DEV_CCU + AHB1_APB1_CFG_REG, AHB1_CLK_SRC(0) | APB1_CLK_DIV(1) | AHB1_PRE_DIV(2) | AHB1_CLK_P(0)); /* Set AHB2 to AHB1/1 (32kHz). */ mmio_write_32(DEV_CCU + AHB2_CFG_REG, AHB2_CLK_SRC(0)); clock_put(&pll_cpux); } void ccu_suspend_cluster(uint32_t cluster UNUSED) { /* Set CPUX to LOSC (32kHz), APB to CPUX/4, AXI to CPUX/3. */ mmio_write_32(DEV_CCU + CPUX_AXI_CFG_REG, CPUX_CLK_SRC(0) | CPUX_APB_CLK_M(3) | CPUX_AXI_CLK_M(2)); } void ccu_resume(void) { clock_get(&pll_cpux); /* Set AHB1 to PLL_PERIPH0/3 (200MHz), APB1 to AHB1/2 (100MHz). */ mmio_write_32(DEV_CCU + AHB1_APB1_CFG_REG, AHB1_CLK_SRC(3) | APB1_CLK_DIV(1) | AHB1_PRE_DIV(2) | AHB1_CLK_P(0)); /* Set AHB2 to PLL_PERIPH0/2 (300MHz). */ mmio_write_32(DEV_CCU + AHB2_CFG_REG, AHB2_CLK_SRC(1)); } void ccu_resume_cluster(uint32_t cluster UNUSED) { /* Set CPUX to PLL_CPUX, APB to CPUX/4, AXI to CPUX/3. */ mmio_write_32(DEV_CCU + CPUX_AXI_CFG_REG, CPUX_CLK_SRC(2) | CPUX_APB_CLK_M(3) | CPUX_AXI_CLK_M(2)); } void ccu_init(void) { /* Set APB2 to OSC24M/1 (24MHz). */ mmio_write_32(DEV_CCU + APB2_CFG_REG, APB2_CLK_SRC(1) | APB2_CLK_P(0) | APB2_CLK_M(0)); ccu_resume(); ccu_resume_cluster(0); } crust-0.5/drivers/clock/sun50i-h6-ccu.c000066400000000000000000000126751414152222500176520ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include "ccu.h" #define CPUX_AXI_CFG_REG 0x0500 #define PSI_CFG_REG 0x0510 #define AHB3_CFG_REG 0x051c #define APB1_CFG_REG 0x0520 #define APB2_CFG_REG 0x0524 #define CPUX_CLK_SRC(x) ((x) << 24) #define CPUX_APB_CLK_M(x) ((x) << 8) #define CPUX_AXI_CLK_M(x) ((x) << 0) #define PSI_CLK_SRC(x) ((x) << 24) #define PSI_CLK_P(x) ((x) << 8) #define PSI_CLK_M(x) ((x) << 0) #define AHB3_CLK_SRC(x) ((x) << 24) #define AHB3_CLK_P(x) ((x) << 8) #define AHB3_CLK_M(x) ((x) << 0) #define APB1_CLK_SRC(x) ((x) << 24) #define APB1_CLK_P(x) ((x) << 8) #define APB1_CLK_M(x) ((x) << 0) #define APB2_CLK_SRC(x) ((x) << 24) #define APB2_CLK_P(x) ((x) << 8) #define APB2_CLK_M(x) ((x) << 0) static DEFINE_FIXED_RATE(ccu_get_pll_periph0_rate, 600000000U) static DEFINE_FIXED_PARENT(ccu_get_pll_ddr0, ccu, CLK_PLL_DDR0) /* * While APB2 has a mux, assume its parent is OSC24M. Reparenting APB2 * to PLL_PERIPH0 in Linux for faster UART clocks is unsupported. */ static DEFINE_FIXED_PARENT(ccu_get_apb2_parent, r_ccu, CLK_OSC24M) static DEFINE_FIXED_PARENT(ccu_get_apb2, ccu, CLK_APB2) static const struct ccu_clock ccu_clocks[SUN50I_H6_CCU_CLOCKS] = { [CLK_PLL_DDR0] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .reg = 0x0010, .lock = 28, .gate = BITMAP_INDEX(0x0010, 31), }, [CLK_PLL_PERIPH0] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_pll_periph0_rate, }, [CLK_APB2] = { .get_parent = ccu_get_apb2_parent, .get_rate = ccu_get_parent_rate, }, [CLK_MBUS] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0540, 31), .reset = BITMAP_INDEX(0x0540, 30), }, [CLK_BUS_MSGBOX] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x071c, 0), .reset = BITMAP_INDEX(0x071c, 16), }, [CLK_DRAM] = { .get_parent = ccu_get_pll_ddr0, .get_rate = ccu_get_parent_rate, .reg = 0x0800, .update = 27, .reset = BITMAP_INDEX(0x0800, 30), }, /* Reset requires re-training DRAM, so ignore it. */ [CLK_BUS_DRAM] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x080c, 0), }, [CLK_BUS_PIO] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, }, #if CONFIG(SERIAL_DEV_UART0) [CLK_BUS_UART0] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x090c, 0), .reset = BITMAP_INDEX(0x090c, 16), }, #elif CONFIG(SERIAL_DEV_UART1) [CLK_BUS_UART1] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x090c, 1), .reset = BITMAP_INDEX(0x090c, 17), }, #elif CONFIG(SERIAL_DEV_UART2) [CLK_BUS_UART2] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x090c, 2), .reset = BITMAP_INDEX(0x090c, 18), }, #elif CONFIG(SERIAL_DEV_UART3) [CLK_BUS_UART3] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x090c, 3), .reset = BITMAP_INDEX(0x090c, 19), }, #endif }; const struct ccu ccu = { .dev = { .name = "ccu", .drv = &ccu_driver.drv, .state = CLOCK_DEVICE_STATE_INIT(SUN50I_H6_CCU_CLOCKS), }, .clocks = ccu_clocks, .regs = DEV_CCU, }; void ccu_suspend(void) { /* Set PSI/AHB1/AHB2 to LOSC/1 (32kHz). */ mmio_write_32(DEV_CCU + PSI_CFG_REG, PSI_CLK_SRC(1) | PSI_CLK_P(0) | PSI_CLK_M(0)); /* Set AHB3 to LOSC/1 (32kHz). */ mmio_write_32(DEV_CCU + AHB3_CFG_REG, AHB3_CLK_SRC(1) | AHB3_CLK_P(0) | AHB3_CLK_M(0)); /* Set APB1 to LOSC/2 (16kHz). */ mmio_write_32(DEV_CCU + APB1_CFG_REG, APB1_CLK_SRC(1) | APB1_CLK_P(1) | APB1_CLK_M(0)); } void ccu_suspend_cluster(uint32_t cluster UNUSED) { /* Set CPUX to LOSC (32kHz), APB to CPUX/4, AXI to CPUX/3. */ mmio_write_32(DEV_CCU + CPUX_AXI_CFG_REG, CPUX_CLK_SRC(1) | CPUX_APB_CLK_M(3) | CPUX_AXI_CLK_M(2)); } void ccu_resume(void) { /* Set PSI/AHB1/AHB2 to PLL_PERIPH0/3 (200MHz). */ mmio_write_32(DEV_CCU + PSI_CFG_REG, PSI_CLK_SRC(3) | PSI_CLK_P(0) | PSI_CLK_M(2)); /* Set AHB3 to PLL_PERIPH0/3 (200MHz). */ mmio_write_32(DEV_CCU + AHB3_CFG_REG, AHB3_CLK_SRC(3) | AHB3_CLK_P(0) | AHB3_CLK_M(2)); /* Set APB1 to PLL_PERIPH0/6 (100MHz). */ mmio_write_32(DEV_CCU + APB1_CFG_REG, APB1_CLK_SRC(3) | APB1_CLK_P(1) | APB1_CLK_M(2)); } void ccu_resume_cluster(uint32_t cluster UNUSED) { /* Set CPUX to PLL_CPUX, APB to CPUX/4, AXI to CPUX/3. */ mmio_write_32(DEV_CCU + CPUX_AXI_CFG_REG, CPUX_CLK_SRC(3) | CPUX_APB_CLK_M(3) | CPUX_AXI_CLK_M(2)); } void ccu_init(void) { /* Set APB2 to OSC24M/1 (24MHz). */ mmio_write_32(DEV_CCU + APB2_CFG_REG, APB2_CLK_SRC(0) | APB2_CLK_P(0) | APB2_CLK_M(0)); ccu_resume(); ccu_resume_cluster(0); } crust-0.5/drivers/clock/sun50i-h6-r-ccu.c000066400000000000000000000126571414152222500201110ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include "ccu.h" static DEFINE_FIXED_RATE(r_ccu_get_osc24m_rate, 24000000U) static DEFINE_FIXED_RATE(r_ccu_get_osc32k_rate, 32768U) static const struct clock_handle r_ccu_bus_parents[] = { { .dev = &r_ccu.dev, .id = CLK_OSC24M, }, { .dev = &r_ccu.dev, .id = CLK_OSC32K, }, { .dev = &r_ccu.dev, .id = CLK_OSC16M, }, { .dev = &ccu.dev, .id = CLK_PLL_PERIPH0, }, }; static const struct clock_handle * r_ccu_get_bus_parent(const struct ccu *self, const struct ccu_clock *clk) { uint32_t val = mmio_read_32(self->regs + clk->reg); return &r_ccu_bus_parents[bitfield_get(val, 24, 2)]; } static uint32_t r_ccu_get_mp_rate(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate) { /* For AR100 and R_APB2, this assumes the pre-divider for PLL_PERIPH0 * (parent 3) will only be set if parent 3 is selected in the mux. */ return ccu_helper_get_rate_mp(self, clk, rate, 0, 5, 8, 2); } static DEFINE_FIXED_PARENT(r_ccu_get_ar100, r_ccu, CLK_AR100) static DEFINE_FIXED_PARENT(r_ccu_get_r_ahb, r_ccu, CLK_R_AHB) static uint32_t r_ccu_get_r_apb1_rate(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate) { return ccu_helper_get_rate_m(self, clk, rate, 0, 2); } static DEFINE_FIXED_PARENT(r_ccu_get_r_apb1, r_ccu, CLK_R_APB1) static DEFINE_FIXED_PARENT(r_ccu_get_r_apb2, r_ccu, CLK_R_APB2) static const struct clock_handle r_ccu_module_parents[] = { { .dev = &r_ccu.dev, .id = CLK_OSC32K, }, { .dev = &r_ccu.dev, .id = CLK_OSC24M, }, }; static const struct clock_handle * r_ccu_get_module_parent(const struct ccu *self, const struct ccu_clock *clk) { uint32_t val = mmio_read_32(self->regs + clk->reg); return &r_ccu_module_parents[bitfield_get(val, 24, 1)]; } static const struct ccu_clock r_ccu_clocks[SUN50I_H6_R_CCU_CLOCKS] = { [CLK_OSC16M] = { .get_parent = ccu_get_null_parent, .get_rate = r_ccu_common_get_osc16m_rate, }, [CLK_OSC24M] = { .get_parent = ccu_get_null_parent, .get_rate = r_ccu_get_osc24m_rate, }, [CLK_OSC32K] = { .get_parent = ccu_get_null_parent, .get_rate = r_ccu_get_osc32k_rate, }, [CLK_AR100] = { .get_parent = r_ccu_get_bus_parent, .get_rate = r_ccu_get_mp_rate, .reg = 0x0000, }, [CLK_R_AHB] = { .get_parent = r_ccu_get_ar100, .get_rate = ccu_get_parent_rate, }, [CLK_R_APB1] = { .get_parent = r_ccu_get_r_ahb, .get_rate = r_ccu_get_r_apb1_rate, .reg = 0x000c, }, [CLK_R_APB2] = { .get_parent = r_ccu_get_bus_parent, .get_rate = r_ccu_get_mp_rate, .reg = 0x0010, }, [CLK_BUS_R_PIO] = { .get_parent = r_ccu_get_r_apb1, .get_rate = ccu_get_parent_rate, }, [CLK_BUS_R_TIMER] = { .get_parent = r_ccu_get_r_apb1, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x011c, 0), .reset = BITMAP_INDEX(0x011c, 16), }, [CLK_BUS_R_TWD] = { /* Parent omitted to allow enabling before CCU init. */ .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x012c, 0), .reset = BITMAP_INDEX(0x012c, 16), }, [CLK_BUS_R_PWM] = { .get_parent = r_ccu_get_r_apb1, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x013c, 0), .reset = BITMAP_INDEX(0x013c, 16), }, [CLK_BUS_R_UART] = { .get_parent = r_ccu_get_r_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x018c, 0), .reset = BITMAP_INDEX(0x018c, 16), }, [CLK_BUS_R_I2C] = { .get_parent = r_ccu_get_r_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x019c, 0), .reset = BITMAP_INDEX(0x019c, 16), }, [CLK_BUS_R_RSB] = { .get_parent = r_ccu_get_r_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x01bc, 0), .reset = BITMAP_INDEX(0x01bc, 16), }, [CLK_BUS_R_CIR] = { .get_parent = r_ccu_get_r_apb1, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x01cc, 0), .reset = BITMAP_INDEX(0x01cc, 16), }, [CLK_BUS_R_W1] = { .get_parent = r_ccu_get_r_apb1, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x01ec, 0), .reset = BITMAP_INDEX(0x01ec, 16), }, [CLK_R_CIR] = { .get_parent = r_ccu_get_module_parent, .get_rate = r_ccu_get_mp_rate, .reg = 0x01c0, .gate = BITMAP_INDEX(0x01c0, 31), }, [CLK_R_W1] = { .get_parent = r_ccu_get_module_parent, .get_rate = r_ccu_get_mp_rate, .reg = 0x01e0, .gate = BITMAP_INDEX(0x01e0, 31), }, }; const struct ccu r_ccu = { .dev = { .name = "r_ccu", .drv = &ccu_driver.drv, .state = CLOCK_DEVICE_STATE_INIT(SUN50I_H6_R_CCU_CLOCKS), }, .clocks = r_ccu_clocks, .regs = DEV_R_PRCM, }; void r_ccu_init(void) { /* Set CPUS to OSC16M/1 (16MHz). */ mmio_write_32(CPUS_CLK_REG, CPUS_CLK_REG_CLK_SRC(2) | CPUS_CLK_REG_PRE_DIV(0) | CPUS_CLK_REG_DIV_P(0)); /* Set R_APB2 to OSC16M/1 (16MHz). */ mmio_write_32(R_APB2_CLK_REG, R_APB2_CLK_REG_CLK_SRC(2) | R_APB2_CLK_REG_PRE_DIV(0) | R_APB2_CLK_REG_DIV_P(0)); r_ccu_common_init(); } crust-0.5/drivers/clock/sun8i-a83t-ccu.c000066400000000000000000000056261414152222500200350ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include "ccu.h" #define APB2_CFG_REG 0x0058 #define APB2_CLK_SRC(x) ((x) << 24) #define APB2_CLK_P(x) ((x) << 16) #define APB2_CLK_M(x) ((x) << 0) static DEFINE_FIXED_RATE(ccu_get_pll_periph0_rate, 600000000U) /* * While APB2 has a mux, assume its parent is OSC24M. Reparenting APB2 * to PLL_PERIPH0 in Linux for faster UART clocks is unsupported. */ static DEFINE_FIXED_PARENT(ccu_get_apb2_parent, r_ccu, CLK_OSC24M) static DEFINE_FIXED_PARENT(ccu_get_apb2, ccu, CLK_APB2) static const struct ccu_clock ccu_clocks[SUN8I_A83T_CCU_CLOCKS] = { [CLK_PLL_PERIPH0] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_pll_periph0_rate, }, [CLK_APB2] = { .get_parent = ccu_get_apb2_parent, .get_rate = ccu_get_parent_rate, }, [CLK_BUS_MSGBOX] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0064, 21), .reset = BITMAP_INDEX(0x02c4, 21), }, [CLK_BUS_PIO] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0068, 5), }, #if CONFIG(SERIAL_DEV_UART0) [CLK_BUS_UART0] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 16), .reset = BITMAP_INDEX(0x02d8, 16), }, #elif CONFIG(SERIAL_DEV_UART1) [CLK_BUS_UART1] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 17), .reset = BITMAP_INDEX(0x02d8, 17), }, #elif CONFIG(SERIAL_DEV_UART2) [CLK_BUS_UART2] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 18), .reset = BITMAP_INDEX(0x02d8, 18), }, #elif CONFIG(SERIAL_DEV_UART3) [CLK_BUS_UART3] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 19), .reset = BITMAP_INDEX(0x02d8, 19), }, #elif CONFIG(SERIAL_DEV_UART4) [CLK_BUS_UART4] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 20), .reset = BITMAP_INDEX(0x02d8, 20), }, #endif }; const struct ccu ccu = { .dev = { .name = "ccu", .drv = &ccu_driver.drv, .state = CLOCK_DEVICE_STATE_INIT(SUN8I_A83T_CCU_CLOCKS), }, .clocks = ccu_clocks, .regs = DEV_CCU, }; void ccu_suspend(void) { } void ccu_suspend_cluster(uint32_t cluster UNUSED) { } void ccu_resume(void) { } void ccu_resume_cluster(uint32_t cluster UNUSED) { } void ccu_init(void) { /* Set APB2 to OSC24M/1 (24MHz). */ mmio_write_32(DEV_CCU + APB2_CFG_REG, APB2_CLK_SRC(1) | APB2_CLK_P(0) | APB2_CLK_M(0)); ccu_resume(); ccu_resume_cluster(0); } crust-0.5/drivers/clock/sun8i-h3-ccu.c000066400000000000000000000132571414152222500175670ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include "ccu.h" #define CPUX_AXI_CFG_REG 0x0050 #define AHB1_APB1_CFG_REG 0x0054 #define APB2_CFG_REG 0x0058 #define AHB2_CFG_REG 0x005c #define CPUX_CLK_SRC(x) ((x) << 16) #define CPUX_APB_CLK_M(x) ((x) << 8) #define CPUX_AXI_CLK_M(x) ((x) << 0) #define AHB1_CLK_SRC(x) ((x) << 12) #define APB1_CLK_DIV(x) ((x) << 8) #define AHB1_PRE_DIV(x) ((x) << 6) #define AHB1_CLK_P(x) ((x) << 4) #define APB2_CLK_SRC(x) ((x) << 24) #define APB2_CLK_P(x) ((x) << 16) #define APB2_CLK_M(x) ((x) << 0) #define AHB2_CLK_SRC(n) ((n) << 0) static DEFINE_FIXED_RATE(ccu_get_pll_periph0_rate, 600000000U) /* * While APB2 has a mux, assume its parent is OSC24M. Reparenting APB2 * to PLL_PERIPH0 in Linux for faster UART clocks is unsupported. */ static DEFINE_FIXED_PARENT(ccu_get_apb2_parent, r_ccu, CLK_OSC24M) static DEFINE_FIXED_PARENT(ccu_get_apb2, ccu, CLK_APB2) static const struct clock_handle ccu_dram_parents[] = { { .dev = &ccu.dev, .id = CLK_PLL_DDR0, }, { .dev = &ccu.dev, .id = CLK_PLL_PERIPH0, /* 2x */ }, }; static const struct clock_handle * ccu_get_dram_parent(const struct ccu *self, const struct ccu_clock *clk) { uint32_t val = mmio_read_32(self->regs + clk->reg); return &ccu_dram_parents[bitfield_get(val, 20, 1)]; } static const struct ccu_clock ccu_clocks[SUN8I_H3_CCU_CLOCKS] = { [CLK_PLL_CPUX] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .reg = 0x0000, .lock = 28, .gate = BITMAP_INDEX(0x0000, 31), }, [CLK_PLL_DDR0] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .reg = 0x0020, .lock = 28, .update = 20, .gate = BITMAP_INDEX(0x0020, 31), }, [CLK_PLL_PERIPH0] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_pll_periph0_rate, }, [CLK_APB2] = { .get_parent = ccu_get_apb2_parent, .get_rate = ccu_get_parent_rate, }, /* Reset requires re-training DRAM, so ignore it. */ [CLK_BUS_DRAM] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0060, 14), }, [CLK_BUS_MSGBOX] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0064, 21), .reset = BITMAP_INDEX(0x02c4, 21), }, [CLK_BUS_PIO] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0068, 5), }, #if CONFIG(SERIAL_DEV_UART0) [CLK_BUS_UART0] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 16), .reset = BITMAP_INDEX(0x02d8, 16), }, #elif CONFIG(SERIAL_DEV_UART1) [CLK_BUS_UART1] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 17), .reset = BITMAP_INDEX(0x02d8, 17), }, #elif CONFIG(SERIAL_DEV_UART2) [CLK_BUS_UART2] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 18), .reset = BITMAP_INDEX(0x02d8, 18), }, #elif CONFIG(SERIAL_DEV_UART3) [CLK_BUS_UART3] = { .get_parent = ccu_get_apb2, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x006c, 19), .reset = BITMAP_INDEX(0x02d8, 19), }, #endif [CLK_DRAM] = { .get_parent = ccu_get_dram_parent, .get_rate = ccu_get_parent_rate, .reg = 0x00f4, .update = 16, .reset = BITMAP_INDEX(0x00f4, 31), }, /* MBUS reset breaks DRAM resume on H3. */ [CLK_MBUS] = { .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x015c, 31), }, }; const struct ccu ccu = { .dev = { .name = "ccu", .drv = &ccu_driver.drv, .state = CLOCK_DEVICE_STATE_INIT(SUN8I_H3_CCU_CLOCKS), }, .clocks = ccu_clocks, .regs = DEV_CCU, }; static const struct clock_handle pll_cpux = { .dev = &ccu.dev, .id = CLK_PLL_CPUX, }; void ccu_suspend(void) { /* Set AHB1 to LOSC/1 (32kHz), APB1 to AHB1/2 (16kHz). */ mmio_write_32(DEV_CCU + AHB1_APB1_CFG_REG, AHB1_CLK_SRC(0) | APB1_CLK_DIV(1) | AHB1_PRE_DIV(2) | AHB1_CLK_P(0)); /* Set AHB2 to AHB1/1 (32kHz). */ mmio_write_32(DEV_CCU + AHB2_CFG_REG, AHB2_CLK_SRC(0)); clock_put(&pll_cpux); } void ccu_suspend_cluster(uint32_t cluster UNUSED) { /* Set CPUX to LOSC (32kHz), APB to CPUX/4, AXI to CPUX/3. */ mmio_write_32(DEV_CCU + CPUX_AXI_CFG_REG, CPUX_CLK_SRC(0) | CPUX_APB_CLK_M(3) | CPUX_AXI_CLK_M(2)); } void ccu_resume(void) { clock_get(&pll_cpux); /* Set AHB1 to PLL_PERIPH0/3 (200MHz), APB1 to AHB1/2 (100MHz). */ mmio_write_32(DEV_CCU + AHB1_APB1_CFG_REG, AHB1_CLK_SRC(3) | APB1_CLK_DIV(1) | AHB1_PRE_DIV(2) | AHB1_CLK_P(0)); /* Set AHB2 to PLL_PERIPH0/2 (300MHz). */ mmio_write_32(DEV_CCU + AHB2_CFG_REG, AHB2_CLK_SRC(1)); } void ccu_resume_cluster(uint32_t cluster UNUSED) { /* Set CPUX to PLL_CPUX, APB to CPUX/4, AXI to CPUX/3. */ mmio_write_32(DEV_CCU + CPUX_AXI_CFG_REG, CPUX_CLK_SRC(2) | CPUX_APB_CLK_M(3) | CPUX_AXI_CLK_M(2)); } void ccu_init(void) { /* Set APB2 to OSC24M/1 (24MHz). */ mmio_write_32(DEV_CCU + APB2_CFG_REG, APB2_CLK_SRC(1) | APB2_CLK_P(0) | APB2_CLK_M(0)); ccu_resume(); ccu_resume_cluster(0); } crust-0.5/drivers/clock/sun8i-r-ccu.c000066400000000000000000000113151414152222500175070ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include "ccu.h" static DEFINE_FIXED_RATE(r_ccu_get_osc24m_rate, 24000000U) static DEFINE_FIXED_RATE(r_ccu_get_osc32k_rate, 32768U) static const struct clock_handle r_ccu_ar100_parents[] = { { .dev = &r_ccu.dev, .id = CLK_OSC32K, }, { .dev = &r_ccu.dev, .id = CLK_OSC24M, }, { .dev = &ccu.dev, .id = CLK_PLL_PERIPH0, }, { .dev = &r_ccu.dev, .id = CLK_OSC16M, }, }; static const struct clock_handle * r_ccu_get_ar100_parent(const struct ccu *self, const struct ccu_clock *clk) { uint32_t val = mmio_read_32(self->regs + clk->reg); return &r_ccu_ar100_parents[bitfield_get(val, 16, 2)]; } static uint32_t r_ccu_get_ar100_rate(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate) { /* This assumes the pre-divider for PLL_PERIPH0 (parent 2) * will only be set if parent 2 is selected in the mux. */ return ccu_helper_get_rate_mp(self, clk, rate, 8, 5, 4, 2); } static DEFINE_FIXED_PARENT(r_ccu_get_ar100, r_ccu, CLK_AR100) static DEFINE_FIXED_PARENT(r_ccu_get_ahb0, r_ccu, CLK_AHB0) static uint32_t r_ccu_get_apb0_rate(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate) { return ccu_helper_get_rate_m(self, clk, rate, 0, 2); } static DEFINE_FIXED_PARENT(r_ccu_get_apb0, r_ccu, CLK_APB0) static const struct clock_handle r_ccu_r_cir_parents[] = { { .dev = &r_ccu.dev, .id = CLK_OSC32K, }, { .dev = &r_ccu.dev, .id = CLK_OSC24M, }, }; static const struct clock_handle * ccu_get_r_cir_parent(const struct ccu *self, const struct ccu_clock *clk) { uint32_t val = mmio_read_32(self->regs + clk->reg); return &r_ccu_r_cir_parents[bitfield_get(val, 24, 1)]; } static uint32_t ccu_get_r_cir_rate(const struct ccu *self, const struct ccu_clock *clk, uint32_t rate) { return ccu_helper_get_rate_mp(self, clk, rate, 0, 4, 16, 2); } static const struct ccu_clock r_ccu_clocks[SUN8I_R_CCU_CLOCKS] = { [CLK_OSC16M] = { .get_parent = ccu_get_null_parent, .get_rate = r_ccu_common_get_osc16m_rate, }, [CLK_OSC24M] = { .get_parent = ccu_get_null_parent, .get_rate = r_ccu_get_osc24m_rate, }, [CLK_OSC32K] = { .get_parent = ccu_get_null_parent, .get_rate = r_ccu_get_osc32k_rate, }, [CLK_AR100] = { .get_parent = r_ccu_get_ar100_parent, .get_rate = r_ccu_get_ar100_rate, .reg = 0x0000, }, [CLK_AHB0] = { .get_parent = r_ccu_get_ar100, .get_rate = ccu_get_parent_rate, }, [CLK_APB0] = { .get_parent = r_ccu_get_ahb0, .get_rate = r_ccu_get_apb0_rate, .reg = 0x000c, }, [CLK_BUS_R_PIO] = { .get_parent = r_ccu_get_apb0, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0028, 0), }, [CLK_BUS_R_CIR] = { .get_parent = r_ccu_get_apb0, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0028, 1), .reset = BITMAP_INDEX(0x00b0, 1), }, [CLK_BUS_R_TIMER] = { .get_parent = r_ccu_get_apb0, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0028, 2), .reset = BITMAP_INDEX(0x00b0, 2), }, #if CONFIG(HAVE_RSB) [CLK_BUS_R_RSB] = { .get_parent = r_ccu_get_apb0, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0028, 3), .reset = BITMAP_INDEX(0x00b0, 3), }, #endif [CLK_BUS_R_UART] = { .get_parent = r_ccu_get_apb0, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0028, 4), .reset = BITMAP_INDEX(0x00b0, 4), }, [CLK_BUS_R_I2C] = { .get_parent = r_ccu_get_apb0, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0028, 6), .reset = BITMAP_INDEX(0x00b0, 6), }, [CLK_BUS_R_TWD] = { /* Parent omitted to allow enabling before CCU init. */ .get_parent = ccu_get_null_parent, .get_rate = ccu_get_parent_rate, .gate = BITMAP_INDEX(0x0028, 7), }, [CLK_R_CIR] = { .get_parent = ccu_get_r_cir_parent, .get_rate = ccu_get_r_cir_rate, .reg = 0x0054, .gate = BITMAP_INDEX(0x0054, 31), }, }; const struct ccu r_ccu = { .dev = { .name = "r_ccu", .drv = &ccu_driver.drv, .state = CLOCK_DEVICE_STATE_INIT(SUN8I_R_CCU_CLOCKS), }, .clocks = r_ccu_clocks, .regs = DEV_R_PRCM, }; void r_ccu_init(void) { /* Set CPUS to OSC16M/1 (16MHz). */ mmio_write_32(CPUS_CLK_REG, CPUS_CLK_REG_CLK_SRC(3) | CPUS_CLK_REG_PRE_DIV(0) | CPUS_CLK_REG_DIV_P(0)); r_ccu_common_init(); } crust-0.5/drivers/css/000077500000000000000000000000001414152222500147625ustar00rootroot00000000000000crust-0.5/drivers/css/Makefile000066400000000000000000000005251414152222500164240ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += css.o obj-y += css_default.o obj-y += css_helpers.o obj-y += css_power_state.o obj-$(CONFIG_PLATFORM_A64) += sun50i-a64-css.o obj-$(CONFIG_PLATFORM_H3) += sun8i-h3-css.o obj-$(CONFIG_PLATFORM_H6) += sun50i-h6-css.o crust-0.5/drivers/css/css.c000066400000000000000000000076101414152222500157220ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include "css.h" /* The register layout assumes a maximum of two clusters. */ #define FIQ_BIT BIT(2 * MAX_CORES_PER_CLUSTER) #define IRQ_BIT BIT(0) static uint8_t lead_cluster, lead_core; int css_get_power_state(uint32_t cluster, uint32_t *cluster_state, uint32_t *online_cores) { uint32_t mask = 0; if (cluster >= css_get_cluster_count()) return SCPI_E_PARAM; *cluster_state = power_state.cluster[cluster]; for (uint32_t core = 0; core < MAX_CORES_PER_CLUSTER; ++core) { if (power_state.core[cluster][core] != SCPI_CSS_OFF) mask |= BIT(core); } *online_cores = mask; return SCPI_OK; } int css_set_power_state(uint32_t cluster, uint32_t core, uint32_t core_state, uint32_t cluster_state, uint32_t css_state) { uint8_t *core_ps = &power_state.core[cluster][core]; uint8_t *cluster_ps = &power_state.cluster[cluster]; uint8_t *css_ps = &power_state.css; if (cluster >= css_get_cluster_count()) return SCPI_E_PARAM; if (core >= css_get_core_count(cluster)) return SCPI_E_PARAM; /* * This implementation takes advantage of two restrictions on power * state requests: * * 1. A request to suspend a core may only be sent from that core. * Therefore, at the time any such request is received, it is safe * to assume that the core and all of its ancestor power domains * are in the "on" state. * * 2. No power domain may be in a deeper power state than any of its * children. Therefore, any request to turn on a core must also * turn on all of its ancestor power domains, regardless of their * previous or requested states. */ if (core_state != SCPI_CSS_ON) { uint8_t *cluster_cores = power_state.core[cluster]; uint8_t *css_clusters = power_state.cluster; record_step(STEP_SUSPEND_CORE); css_suspend_core(cluster, core, core_state); *core_ps = core_state; /* A cluster must be on if any of its cores is on. */ for (uint32_t i = 0; i < css_get_core_count(cluster); ++i) { if (cluster_cores[i] < cluster_state) cluster_state = cluster_cores[i]; } record_step(STEP_SUSPEND_CLUSTER); css_suspend_cluster(cluster, cluster_state); *cluster_ps = cluster_state; /* The CSS must be on if any of its clusters is on. */ for (uint32_t i = 0; i < css_get_cluster_count(); ++i) { if (css_clusters[i] < css_state) css_state = css_clusters[i]; } record_step(STEP_SUSPEND_CSS); css_suspend_css(css_state); *css_ps = css_state; /* Suspend the system when powering off the CSS. */ if (css_state == SCPI_CSS_OFF) { system_suspend(); /* Remember the last active core. */ lead_cluster = cluster; lead_core = core; } } else { record_step(STEP_RESUME_CSS); css_resume_css(*css_ps); *css_ps = SCPI_CSS_ON; record_step(STEP_RESUME_CLUSTER); css_resume_cluster(cluster, *cluster_ps); *cluster_ps = SCPI_CSS_ON; record_step(STEP_RESUME_CORE); css_resume_core(cluster, core, *core_ps); *core_ps = SCPI_CSS_ON; } return SCPI_OK; } static void css_wake_one_cpu(uint32_t cluster, uint32_t core) { css_set_power_state(cluster, core, SCPI_CSS_ON, SCPI_CSS_ON, SCPI_CSS_ON); } void css_resume(void) { css_wake_one_cpu(lead_cluster, lead_core); } void css_poll(void) { uint32_t status = css_get_irq_status(); for (uint32_t i = 0; i < css_get_cluster_count(); ++i) { /* Assume each cluster is allocated the same number of bits. */ for (uint32_t j = 0; j < MAX_CORES_PER_CLUSTER; ++j) { if ((status & (FIQ_BIT | IRQ_BIT)) && (power_state.core[i][j] == SCPI_CSS_OFF)) css_wake_one_cpu(i, j); /* Shift the next core status on top of the mask. */ status >>= 1; } } } crust-0.5/drivers/css/css.h000066400000000000000000000056011414152222500157250ustar00rootroot00000000000000/* * Copyright © 2019-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef CSS_PRIVATE_H #define CSS_PRIVATE_H #include #include #include struct power_state { uint8_t core[MAX_CLUSTERS][MAX_CORES_PER_CLUSTER]; uint8_t cluster[MAX_CLUSTERS]; uint8_t css; }; extern struct power_state power_state; /** * Get the number of cores present in a cluster. * * The number returned cannot be greater than 8. * * @param cluster The index of the cluster. */ uint32_t css_get_core_count(uint32_t cluster) ATTRIBUTE(const); /** * Get the pending IRQ status for each core in the CSS. * * Cores with pending IRQs will be woken up. * * @return Each set bit means some IRQ is pending for that core. */ uint32_t css_get_irq_status(void); /** * Suspend the compute subsystem (CSS). * * This function assumes that the previous CSS power state was "on". * * @param new_state The new coordinated power state for the CSS. */ void css_suspend_css(uint32_t new_state); /** * Prepare the compute subsystem (CSS) to resume execution. * * @param old_state The previous coordinated power state for the CSS. */ void css_resume_css(uint32_t old_state); /** * Suspend a cluster. * * This function assumes that the previous cluster power state was "on". * * @param cluster The index of the cluster. * @param new_state The new coordinated power state for this cluster. */ void css_suspend_cluster(uint32_t cluster, uint32_t new_state); /** * Prepare a cluster to resume execution. * * @param cluster The index of the cluster. * @param old_state The previous coordinated power state for this cluster. */ void css_resume_cluster(uint32_t cluster, uint32_t old_state); /** * Suspend a core. * * This function assumes that the previous core power state was "on". * * @param cluster The index of the cluster. * @param core The index of the core within the cluster. * @param new_state The new coordinated power state for this core. */ void css_suspend_core(uint32_t cluster, uint32_t core, uint32_t new_state); /** * Begin or resume execution on a core. * * @param cluster The index of the cluster. * @param core The index of the core within the cluster. * @param old_state The previous coordinated power state for this core. */ void css_resume_core(uint32_t cluster, uint32_t core, uint32_t old_state); /** * Enable or disable power to a core or cluster power domain. * * When enabling a power switch, the power domain will be turned on gradually * to minimize inrush current and voltage drops. * * The mapping of cores/clusters to register addresses is platform-dependent. * * @param addr The address of the register controlling the power switch. * @param enable Whether to enable or disable the power switch. */ void css_set_power_switch(uintptr_t addr, bool enable); #endif /* CSS_PRIVATE_H */ crust-0.5/drivers/css/css_default.c000066400000000000000000000023141414152222500174220ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include "css.h" /* * Generic implementation using the platform-provided constants. */ uint32_t WEAK css_get_cluster_count(void) { return MAX_CLUSTERS; } uint32_t WEAK css_get_core_count(uint32_t cluster UNUSED) { /* Assume each cluster contains the same number of cores. */ return MAX_CORES_PER_CLUSTER; } /* * Generic implementation used when no platform support is available. */ uint32_t WEAK css_get_irq_status(void) { return 0; } /* * Generic implementation used when no platform customization is needed. */ void WEAK css_suspend_css(uint32_t new_state UNUSED) { } void WEAK css_resume_css(uint32_t old_state UNUSED) { } void WEAK css_suspend_cluster(uint32_t cluster UNUSED, uint32_t new_state UNUSED) { } void WEAK css_resume_cluster(uint32_t cluster UNUSED, uint32_t old_state UNUSED) { } void WEAK css_suspend_core(uint32_t cluster UNUSED, uint32_t core UNUSED, uint32_t new_state UNUSED) { } void WEAK css_resume_core(uint32_t cluster UNUSED, uint32_t core UNUSED, uint32_t old_state UNUSED) { } void WEAK css_init(void) { } crust-0.5/drivers/css/css_helpers.c000066400000000000000000000016011414152222500174360ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include "css.h" static const uint8_t power_switch_on_sequence[] = { 0xfe, 0xf8, 0xe0, 0xc0, 0x80, 0x00, }; void css_set_power_switch(uintptr_t addr, bool enable) { if (enable) { /* Avoid killing the power if the switch is already enabled. */ if (mmio_read_32(addr) == 0x00) return; /* Allwinner's blob uses 10, 20, and 30μs delays, depending on * the iteration. However, the same code works fine in ATF with * no delays. The 10μs delay is here just to be extra safe. */ const uint8_t *sequence = power_switch_on_sequence; do { mmio_write_32(addr, *sequence); udelay(10); } while (*sequence++ != 0x00); } else { mmio_write_32(addr, 0xff); } } crust-0.5/drivers/css/css_power_state.c000066400000000000000000000015621414152222500203360ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include "css.h" #define CONCAT(a, b) a ## b #define DEC_1 0 #define DEC_2 1 #define DEC_3 2 #define DEC_4 3 #define DEC(v) CONCAT(DEC_, v) #define REP_0(...) #define REP_1(...) __VA_ARGS__ #define REP_2(...) REP_1(__VA_ARGS__), __VA_ARGS__ #define REP_3(...) REP_2(__VA_ARGS__), __VA_ARGS__ #define REP_4(...) REP_3(__VA_ARGS__), __VA_ARGS__ #define REP(n, ...) CONCAT(REP_, n)(__VA_ARGS__) struct power_state power_state = { .core = { { SCPI_CSS_ON, REP(DEC(MAX_CORES_PER_CLUSTER), SCPI_CSS_OFF) }, REP(DEC(MAX_CLUSTERS), { REP(MAX_CORES_PER_CLUSTER, SCPI_CSS_OFF) }) }, .cluster = { SCPI_CSS_ON, REP(DEC(MAX_CLUSTERS), SCPI_CSS_OFF) }, .css = SCPI_CSS_ON, }; crust-0.5/drivers/css/sun50i-a64-css.c000066400000000000000000000110461414152222500174310ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include "css.h" /* Reset Vector Base Address. */ static uint32_t rvba; uint32_t css_get_irq_status(void) { return mmio_read_32(IRQ_FIQ_STATUS_REG); } void css_suspend_css(uint32_t new_state) { if (new_state < SCPI_CSS_OFF) return; /* Assert the CPU subsystem reset (active-low). */ mmio_write_32(CPU_SYS_RESET_REG, 0); } void css_resume_css(uint32_t old_state) { if (old_state < SCPI_CSS_OFF) return; /* Deassert the CPU subsystem reset (active-low). */ mmio_write_32(CPU_SYS_RESET_REG, CPU_SYS_RESET); } void css_suspend_cluster(uint32_t cluster, uint32_t new_state) { if (new_state < SCPI_CSS_RETENTION) return; /* Assert L2FLUSHREQ to clean the cluster L2 cache. */ mmio_set_32(C0_CTRL_REG2, C0_CTRL_REG2_L2FLUSHREQ); /* Wait for L2FLUSHDONE to go high. */ mmio_poll_32(L2_STATUS_REG, L2_STATUS_REG_L2FLUSHDONE); /* Deassert L2FLUSHREQ. */ mmio_clr_32(C0_CTRL_REG2, C0_CTRL_REG2_L2FLUSHREQ); /* Remove the cluster from coherency (assert ACINACTM). */ mmio_write_32(C0_CTRL_REG1, C0_CTRL_REG1_ACINACTM); /* Wait for the cluster (L2 cache) to be idle. */ mmio_poll_32(C0_CPU_STATUS_REG, C0_CPU_STATUS_REG_STANDBYWFIL2); /* Lower the cluster clock frequency. */ ccu_suspend_cluster(cluster); if (new_state < SCPI_CSS_OFF) return; /* Activate the cluster output clamps. */ mmio_set_32(C0_PWROFF_GATING_REG, C0_PWROFF_GATING); } void css_resume_cluster(uint32_t cluster, uint32_t old_state) { if (old_state < SCPI_CSS_RETENTION) return; /* Raise the cluster clock frequency. */ ccu_resume_cluster(cluster); if (old_state < SCPI_CSS_OFF) { /* Enable coherency (deassert ACINACTM). */ mmio_write_32(C0_CTRL_REG1, 0); return; } /* Release the cluster output clamps. */ mmio_clr_32(C0_PWROFF_GATING_REG, C0_PWROFF_GATING); /* Program all cores to start in AArch64 mode. */ mmio_write_32(C0_CTRL_REG0, C0_CTRL_REG0_SYSBARDISABLE | C0_CTRL_REG0_AA64nAA32_MASK); /* Deassert DBGPWRDUP for all cores. */ mmio_write_32(DBG_REG0, 0); /* Restore the reset vector base addresses for all cores. */ for (uint32_t i = 0; i < css_get_core_count(cluster); ++i) mmio_write_32(RVBA_LO_REG(i), rvba); } void css_suspend_core(uint32_t cluster UNUSED, uint32_t core, uint32_t new_state) { if (new_state < SCPI_CSS_OFF) return; /* Wait for the core to be in WFI and ready to shut down. */ mmio_poll_32(C0_CPU_STATUS_REG, C0_CPU_STATUS_REG_STANDBYWFI(core)); /* Deassert DBGPWRDUP (prevent debug access to the core). */ mmio_clr_32(DBG_REG0, DBG_REG0_DBGPWRDUP(core)); /* Core 0 does not have a separate power domain. */ if (core > 0) { /* Activate the core output clamps. */ mmio_set_32(C0_PWROFF_GATING_REG, C0_CPUn_PWROFF_GATING(core)); /* Remove power from the core power domain. */ css_set_power_switch(C0_CPUn_PWR_SWITCH_REG(core), false); } else { /* Assert core reset (active-low). */ mmio_clr_32(C0_RST_CTRL_REG, C0_RST_CTRL_REG_nCORERESET(core)); /* Assert core power-on reset (active-low). */ mmio_clr_32(C0_PWRON_RESET_REG, C0_PWRON_RESET_REG_nCPUPORESET(core)); } } void css_resume_core(uint32_t cluster UNUSED, uint32_t core, uint32_t old_state) { if (old_state < SCPI_CSS_OFF) return; /* Core 0 does not have a separate power domain. */ if (core > 0) { /* Assert core reset (active-low). */ mmio_clr_32(C0_RST_CTRL_REG, C0_RST_CTRL_REG_nCORERESET(core)); /* Assert core power-on reset (active-low). */ mmio_clr_32(C0_PWRON_RESET_REG, C0_PWRON_RESET_REG_nCPUPORESET(core)); /* Turn on power to the core power domain. */ css_set_power_switch(C0_CPUn_PWR_SWITCH_REG(core), true); /* Release the core output clamps. */ mmio_clr_32(C0_PWROFF_GATING_REG, C0_CPUn_PWROFF_GATING(core)); } /* Deassert core power-on reset (active-low). */ mmio_set_32(C0_PWRON_RESET_REG, C0_PWRON_RESET_REG_nCPUPORESET(core)); /* Deassert core reset (active-low). */ mmio_set_32(C0_RST_CTRL_REG, C0_RST_CTRL_REG_nCORERESET(core)); /* Assert DBGPWRDUP (allow debug access to the core). */ mmio_set_32(DBG_REG0, DBG_REG0_DBGPWRDUP(core)); } void css_init(void) { /* Program all cores to start in AArch64 mode. */ mmio_write_32(C0_CTRL_REG0, C0_CTRL_REG0_SYSBARDISABLE | C0_CTRL_REG0_AA64nAA32_MASK); /* Save the power-on reset vector base address from core 0. */ rvba = mmio_read_32(RVBA_LO_REG(0)); } crust-0.5/drivers/css/sun50i-h6-css.c000066400000000000000000000104541414152222500173560ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include "css.h" /* Reset Vector Base Address. */ static uint32_t rvba; uint32_t css_get_irq_status(void) { return mmio_read_32(CPUIDLE_PEND_REG); } void css_suspend_css(uint32_t new_state) { if (new_state < SCPI_CSS_OFF) return; /* Assert the CPU subsystem reset (active-low). */ mmio_write_32(CPU_SYS_RESET_REG, 0); } void css_resume_css(uint32_t old_state) { if (old_state < SCPI_CSS_OFF) return; /* Deassert the CPU subsystem reset (active-low). */ mmio_write_32(CPU_SYS_RESET_REG, CPU_SYS_RESET); } void css_suspend_cluster(uint32_t cluster, uint32_t new_state) { if (new_state < SCPI_CSS_RETENTION) return; /* Assert L2FLUSHREQ to clean the cluster L2 cache. */ mmio_set_32(C0_CTRL_REG2, C0_CTRL_REG2_L2FLUSHREQ); /* Wait for L2FLUSHDONE to go high. */ mmio_poll_32(L2_STATUS_REG, L2_STATUS_REG_L2FLUSHDONE); /* Deassert L2FLUSHREQ. */ mmio_clr_32(C0_CTRL_REG2, C0_CTRL_REG2_L2FLUSHREQ); /* Remove the cluster from coherency (assert ACINACTM). */ mmio_write_32(C0_CTRL_REG1, C0_CTRL_REG1_ACINACTM); /* Wait for the cluster (L2 cache) to be idle. */ mmio_poll_32(C0_CPU_STATUS_REG, C0_CPU_STATUS_REG_STANDBYWFIL2); /* Lower the cluster clock frequency. */ ccu_suspend_cluster(cluster); if (new_state < SCPI_CSS_OFF) return; /* Activate the cluster output clamps. */ mmio_set_32(C0_PWROFF_GATING_REG, C0_PWROFF_GATING); } void css_resume_cluster(uint32_t cluster, uint32_t old_state) { if (old_state < SCPI_CSS_RETENTION) return; /* Raise the cluster clock frequency. */ ccu_resume_cluster(cluster); if (old_state < SCPI_CSS_OFF) { /* Enable coherency (deassert ACINACTM). */ mmio_write_32(C0_CTRL_REG1, 0); return; } /* Assert all power-on resets (active-low). */ mmio_write_32(C0_PWRON_RESET_REG, 0); /* Release the cluster output clamps. */ mmio_clr_32(C0_PWROFF_GATING_REG, C0_PWROFF_GATING); /* Deassert the cluster hard reset (active-low). */ mmio_write_32(C0_PWRON_RESET_REG, C0_PWRON_RESET_REG_nH_RST); /* Program all cores to start in AArch64 mode. */ mmio_write_32(C0_CTRL_REG0, C0_CTRL_REG0_SYSBARDISABLE | C0_CTRL_REG0_AA64nAA32_MASK); /* Deassert DBGPWRDUP for all cores. */ mmio_write_32(DBG_REG0, 0); /* Restore the reset vector base addresses for all cores. */ for (uint32_t i = 0; i < css_get_core_count(cluster); ++i) mmio_write_32(RVBA_LO_REG(i), rvba); } void css_suspend_core(uint32_t cluster UNUSED, uint32_t core, uint32_t new_state) { if (new_state < SCPI_CSS_OFF) return; /* Wait for the core to be in WFI and ready to shut down. */ mmio_poll_32(C0_CPU_STATUS_REG, C0_CPU_STATUS_REG_STANDBYWFI(core)); /* Deassert DBGPWRDUP (prevent debug access to the core). */ mmio_clr_32(DBG_REG0, DBG_REG0_DBGPWRDUP(core)); /* Activate the core output clamps. */ mmio_set_32(C0_PWROFF_GATING_REG, C0_CPUn_PWROFF_GATING(core)); /* Remove power from the core power domain. */ css_set_power_switch(C0_CPUn_PWR_SWITCH_REG(core), false); } void css_resume_core(uint32_t cluster UNUSED, uint32_t core, uint32_t old_state) { if (old_state < SCPI_CSS_OFF) return; /* Assert core reset (active-low). */ mmio_clr_32(C0_RST_CTRL_REG, C0_RST_CTRL_REG_nCORERESET(core)); /* Assert core power-on reset (active-low). */ mmio_clr_32(C0_PWRON_RESET_REG, C0_PWRON_RESET_REG_nCPUPORESET(core)); /* Turn on power to the core power domain. */ css_set_power_switch(C0_CPUn_PWR_SWITCH_REG(core), true); /* Release the core output clamps. */ mmio_clr_32(C0_PWROFF_GATING_REG, C0_CPUn_PWROFF_GATING(core)); /* Deassert core power-on reset (active-low). */ mmio_set_32(C0_PWRON_RESET_REG, C0_PWRON_RESET_REG_nCPUPORESET(core)); /* Deassert core reset (active-low). */ mmio_set_32(C0_RST_CTRL_REG, C0_RST_CTRL_REG_nCORERESET(core)); /* Assert DBGPWRDUP (allow debug access to the core). */ mmio_set_32(DBG_REG0, DBG_REG0_DBGPWRDUP(core)); } void css_init(void) { /* Program all cores to start in AArch64 mode. */ mmio_write_32(C0_CTRL_REG0, C0_CTRL_REG0_SYSBARDISABLE | C0_CTRL_REG0_AA64nAA32_MASK); /* Save the power-on reset vector base address from core 0. */ rvba = mmio_read_32(RVBA_LO_REG(0)); } crust-0.5/drivers/css/sun8i-h3-css.c000066400000000000000000000072771414152222500173070ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include "css.h" #define PLL_PERIPH0_CTRL_REG 0x0028 #define CPUX_AXI_CFG_REG 0x0050 #define AHB1_APB1_CFG_REG 0x0054 uint32_t css_get_irq_status(void) { return mmio_read_32(IRQ_FIQ_STATUS_REG); } void css_suspend_css(uint32_t new_state) { if (new_state < SCPI_CSS_OFF) return; /* Assert the CPU subsystem reset (active-low). */ mmio_write_32(CPU_SYS_RESET_REG, 0); } void css_resume_css(uint32_t old_state) { if (old_state < SCPI_CSS_OFF) return; /* Deassert the CPU subsystem reset (active-low). */ mmio_write_32(CPU_SYS_RESET_REG, CPU_SYS_RESET); } void css_suspend_cluster(uint32_t cluster, uint32_t new_state) { if (new_state < SCPI_CSS_RETENTION) return; /* Lower the cluster clock frequency. */ ccu_suspend_cluster(cluster); } void css_resume_cluster(uint32_t cluster, uint32_t old_state) { if (old_state < SCPI_CSS_RETENTION) return; /* Raise the cluster clock frequency. */ ccu_resume_cluster(cluster); } void css_suspend_core(uint32_t cluster UNUSED, uint32_t core, uint32_t new_state) { if (new_state < SCPI_CSS_OFF) return; /* Wait for the core to be in WFI and ready to shut down. */ mmio_poll_32(CPUn_STATUS_REG(core), CPUn_STATUS_REG_STANDBYWFI); /* Deassert DBGPWRDUP (prevent debug access to the core). */ mmio_clr_32(DBG_CTRL_REG1, DBG_CTRL_REG1_DBGPWRDUP(core)); /* Core 0 does not have a separate power domain. */ if (core > 0) { /* Activate the core output clamps. */ mmio_set_32(C0_PWROFF_GATING_REG, C0_CPUn_PWROFF_GATING(core)); /* Remove power from the core power domain. */ css_set_power_switch(C0_CPUn_PWR_SWITCH_REG(core), false); } else { /* Assert core reset and power-on reset (active-low). */ mmio_write_32(CPUn_RST_CTRL_REG(core), 0); } } void css_resume_core(uint32_t cluster UNUSED, uint32_t core, uint32_t old_state) { uint32_t bus_clk, cpu_clk; if (old_state < SCPI_CSS_OFF) return; /* Core 0 does not have a separate power domain. */ if (core > 0) { /* Assert core reset and power-on reset (active-low). */ mmio_write_32(CPUn_RST_CTRL_REG(core), 0); /* Turn on power to the core power domain. */ css_set_power_switch(C0_CPUn_PWR_SWITCH_REG(core), true); /* Release the core output clamps. */ mmio_clr_32(C0_PWROFF_GATING_REG, C0_CPUn_PWROFF_GATING(core)); } else { /* Save registers that will be clobbered by the BROM. */ cpu_clk = mmio_read_32(DEV_CCU + CPUX_AXI_CFG_REG); bus_clk = mmio_read_32(DEV_CCU + AHB1_APB1_CFG_REG); /* Bypass PLL_PERIPH0 so AHB1 frequency does not spike. */ mmio_set_32(DEV_CCU + PLL_PERIPH0_CTRL_REG, BIT(25)); } /* Deassert core reset and power-on reset (active-low). */ mmio_write_32(CPUn_RST_CTRL_REG(core), CPUn_RST_CTRL_REG_nCORERESET | CPUn_RST_CTRL_REG_nCPUPORESET); if (core == 0) { /* Spin until the BROM has clobbered the clock registers. */ mmio_pollz_32(DEV_CCU + AHB1_APB1_CFG_REG, BIT(13)); /* Disable PLL_PERIPH0 bypass. */ mmio_clr_32(DEV_CCU + PLL_PERIPH0_CTRL_REG, BIT(25)); /* Restore the clobbered registers. */ mmio_write_32(DEV_CCU + CPUX_AXI_CFG_REG, cpu_clk); mmio_write_32(DEV_CCU + AHB1_APB1_CFG_REG, bus_clk); } /* Assert DBGPWRDUP (allow debug access to the core). */ mmio_set_32(DBG_CTRL_REG1, DBG_CTRL_REG1_DBGPWRDUP(core)); } void css_init(void) { /* Enable hardware L1/L2 cache flush for all cores (active-low). */ mmio_clr_32(GEN_CTRL_REG, GEN_CTRL_REG_L2RSTDISABLE | GEN_CTRL_REG_L1RSTDISABLE_MASK); } crust-0.5/drivers/dram/000077500000000000000000000000001414152222500151155ustar00rootroot00000000000000crust-0.5/drivers/dram/Makefile000066400000000000000000000004161414152222500165560ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += dram.o obj-$(CONFIG_PLATFORM_A64) += sun8i-h3-dram.o obj-$(CONFIG_PLATFORM_H3) += sun8i-h3-dram.o obj-$(CONFIG_PLATFORM_H6) += sun50i-h6-dram.o crust-0.5/drivers/dram/dram.c000066400000000000000000000003531414152222500162050ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include void WEAK dram_suspend(void) { } void WEAK dram_resume(void) { } void WEAK dram_init(void) { } crust-0.5/drivers/dram/sun50i-h6-dram.c000066400000000000000000000066541414152222500176530ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #define MC_MAER0 (DEV_DRAMCOM + 0x0020) #define MC_MAER0_VALUE 0xffffffff #define MC_MAER1 (DEV_DRAMCOM + 0x0024) #define MC_MAER1_VALUE 0x000007ff #define MC_MAER2 (DEV_DRAMCOM + 0x0028) #define MC_MAER2_VALUE 0x0000ffff #define STATR (DEV_DRAMCTL + 0x0004) #define STATR_OP_MODE (0x7 << 0) #define STATR_OP_MODE_NORMAL (0x1 << 0) #define STATR_OP_MODE_SELFREF (0x3 << 0) #define CLKEN (DEV_DRAMCTL + 0x000c) #define CLKEN_VALUE 0x00008100 #define PWRCTL (DEV_DRAMCTL + 0x0030) #define PWRCTL_SELFREF_EN (0x1 << 0) #define PGCR3 (DEV_DRAMPHY + 0x001c) #define PGCR3_CKEN (0xf << 16) #define PGCR3_CKEN_DISABLED (0x0 << 16) #define PGCR3_CKEN_INVERTED (0x5 << 16) #define PGCR3_CKEN_NORMAL (0xa << 16) #define PGCR3_CKNEN (0xf << 20) #define PGCR3_CKNEN_DISABLED (0x0 << 20) #define PGCR3_CKNEN_INVERTED (0x5 << 20) #define PGCR3_CKNEN_NORMAL (0xa << 20) /* Clocks needed by this driver. */ enum { BUS_DRAM, DRAM, MBUS, }; static const struct clock_handle dram_clocks[] = { [BUS_DRAM] = { .dev = &ccu.dev, .id = CLK_BUS_DRAM, }, [DRAM] = { .dev = &ccu.dev, .id = CLK_DRAM, }, [MBUS] = { .dev = &ccu.dev, .id = CLK_MBUS, }, }; void dram_suspend(void) { /* Enable DRAM controller register access. */ clock_get(&dram_clocks[BUS_DRAM]); /* Disable all controller masters. */ mmio_write_32(MC_MAER0, 0); mmio_write_32(MC_MAER1, 0); mmio_write_32(MC_MAER2, 0); /* Enable DRAM self-refresh. */ mmio_set_32(PWRCTL, PWRCTL_SELFREF_EN); /* Wait until the DRAM controller enters self-refresh. */ mmio_polleq_32(STATR, STATR_OP_MODE, STATR_OP_MODE_SELFREF); /* Disable CKEN and CKNEN. */ mmio_clrset_32(PGCR3, PGCR3_CKEN | PGCR3_CKNEN, PGCR3_CKEN_DISABLED | PGCR3_CKNEN_DISABLED); /* Disable DRAM controller clocks. */ mmio_write_32(CLKEN, 0); clock_put(&dram_clocks[DRAM]); clock_put(&dram_clocks[MBUS]); /* Disable further DRAM controller register access. */ clock_put(&dram_clocks[BUS_DRAM]); } void dram_resume(void) { /* Enable DRAM controller register access. */ clock_get(&dram_clocks[BUS_DRAM]); /* Enable DRAM controller clocks. */ clock_get(&dram_clocks[MBUS]); clock_get(&dram_clocks[DRAM]); udelay(10); mmio_write_32(CLKEN, CLKEN_VALUE); udelay(10); /* Enable CKEN and CKNEN. */ mmio_clrset_32(PGCR3, PGCR3_CKEN | PGCR3_CKNEN, PGCR3_CKEN_NORMAL | PGCR3_CKNEN_NORMAL); /* Disable DRAM self refresh. */ mmio_clr_32(PWRCTL, PWRCTL_SELFREF_EN); /* Wait until the DRAM controller exits self-refresh. */ mmio_polleq_32(STATR, STATR_OP_MODE, STATR_OP_MODE_NORMAL); /* Enable all controller masters. */ mmio_write_32(MC_MAER0, MC_MAER0_VALUE); mmio_write_32(MC_MAER1, MC_MAER1_VALUE); mmio_write_32(MC_MAER2, MC_MAER2_VALUE); /* Disable further DRAM controller register access. */ clock_put(&dram_clocks[BUS_DRAM]); } void dram_init(void) { /* Get references to clocks that are already running. */ clock_get(&dram_clocks[MBUS]); clock_get(&dram_clocks[DRAM]); } crust-0.5/drivers/dram/sun8i-h3-dram.c000066400000000000000000000207011414152222500175600ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include /* mctl_reg-sun8iw11.h:102 */ #define PWRCTL (DEV_DRAMCTL + 0x0004) /* mctl_standby-sun8iw11.c:120,878,891 */ #define PWRCTL_SELFREF_EN (0x1 << 0) /* mctl_standby-sun8iw11.c:121 */ #define PWRCTL_PORT_DIS (0x1 << 8) /* mctl_reg-sun8iw11.h:104 */ #define CLKEN (DEV_DRAMCTL + 0x000c) /* dram_sunxi_dw.c:410 */ #define CLKEN_VALUE 0xc00e /* mctl_reg-sun8iw11.h:107 */ #define STATR (DEV_DRAMCTL + 0x0018) /* mctl_standby-sun8iw11.c:124 */ #define STATR_OP_MODE (0x7 << 0) #define STATR_OP_MODE_NORMAL (0x1 << 0) #define STATR_OP_MODE_SELFREF (0x3 << 0) /* mctl_reg-sun8iw11.h:141 */ #define PGCR3 (DEV_DRAMCTL + 0x010c) /* mctl_standby-sun8iw11.c:130 */ #define PGCR3_CKEN (0xf << 16) #define PGCR3_CKEN_DISABLED (0x0 << 16) #define PGCR3_CKEN_INVERTED (0x5 << 16) #define PGCR3_CKEN_NORMAL (0xa << 16) /* mctl_standby-sun8iw11.c:130 */ #define PGCR3_CKNEN (0xf << 20) #define PGCR3_CKNEN_DISABLED (0x0 << 20) #define PGCR3_CKNEN_INVERTED (0x5 << 20) #define PGCR3_CKNEN_NORMAL (0xa << 20) /* mctl_standby-sun8iw11.c:872-873,904-905,950 */ #define PGCR3_UNK25 (0x3 << 25) /* mctl_reg-sun8iw11.h:146 */ #define ZQCR (DEV_DRAMCTL + 0x0140) #define ZQCR_ZQPD (0x1 << 31) #define ZQCR_ZQPD_DISABLED (0x0 << 31) #define ZQCR_ZQPD_ENABLED (0x1 << 31) /* mctl_reg-sun8iw11.h:160 */ #define ACIOCR0 (DEV_DRAMCTL + 0x0208) /* mctl_standby-sun8iw11.c:154 */ #define ACIOCR0_ACPDD (0x1 << 0) #define ACIOCR0_ACPDD_DISABLED (0x0 << 0) #define ACIOCR0_ACPDD_ENABLED (0x1 << 0) /* mctl_standby-sun8iw11.c:154,762 */ #define ACIOCR0_ACPDR (0x1 << 1) #define ACIOCR0_ACPDR_DISABLED (0x0 << 1) #define ACIOCR0_ACPDR_ENABLED (0x1 << 1) /* mctl_standby-sun8iw11.c:152 */ #define ACIOCR0_ACOE (0x1 << 3) #define ACIOCR0_ACOE_DISABLED (0x0 << 3) #define ACIOCR0_ACOE_ENABLED (0x1 << 3) /* mctl_standby-sun8iw11.c:153 */ #define ACIOCR0_ACIOM (0x1 << 4) #define ACIOCR0_ACIOM_SSTL (0x0 << 4) #define ACIOCR0_ACIOM_CMOS (0x1 << 4) /* mctl_standby-sun8iw11.c:151 */ #define ACIOCR0_CKOE (0x3 << 6) #define ACIOCR0_CKOE_DISABLED (0x0 << 6) #define ACIOCR0_CKOE_ENABLED (0x3 << 6) /* mctl_standby-sun8iw11.c:150 */ #define ACIOCR0_CKEOE (0x3 << 8) #define ACIOCR0_CKEOE_DISABLED (0x0 << 8) #define ACIOCR0_CKEOE_ENABLED (0x3 << 8) /* mctl_standby-sun8iw11.c:763 */ #define ACIOCR0_UNK11 (0x1 << 11) /* mctl_reg-sun8iw11.h:173 */ #define DXnGCR0(n) (DEV_DRAMCTL + 0x0344 + 0x80 * (n)) /* mctl_standby-sun8iw11.c:146,710-711 */ #define DXnGCR0_DXEN (0x1 << 0) #define DXnGCR0_DXEN_DISABLED (0x0 << 0) #define DXnGCR0_DXEN_ENABLED (0x1 << 0) /* mctl_standby-sun8iw11.c:145,749 */ #define DXnGCR0_DXIOM (0x1 << 1) #define DXnGCR0_DXIOM_SSTL (0x0 << 1) #define DXnGCR0_DXIOM_CMOS (0x1 << 1) /* mctl_standby-sun8iw11.c:144,750 */ #define DXnGCR0_DXOEO (0x3 << 2) #define DXnGCR0_DXOEO_DYNAMIC (0x0 << 2) #define DXnGCR0_DXOEO_ENABLED (0x1 << 2) #define DXnGCR0_DXOEO_DISABLED (0x2 << 2) /* mctl_standby-sun8iw11.c:748 */ #define DXnGCR0_DXODT (0x3 << 4) #define DXnGCR0_DXODT_DYNAMIC (0x0 << 4) #define DXnGCR0_DXODT_ENABLED (0x1 << 4) #define DXnGCR0_DXODT_DISABLED (0x2 << 4) /* mctl_standby-sun8iw11.c:754-755 */ #define DXnGCR0_UNK09 (0x3 << 9) #define DXnGCR0_UNK09_DYNAMIC (0x0 << 9) #define DXnGCR0_UNK09_ENABLED (0x1 << 9) #define DXnGCR0_UNK09_DISABLED (0x2 << 9) /* mctl_standby-sun8iw11.c:143,751 */ #define DXnGCR0_DXPDR (0x3 << 12) #define DXnGCR0_DXPDR_DYNAMIC (0x0 << 12) #define DXnGCR0_DXPDR_ENABLED (0x1 << 12) #define DXnGCR0_DXPDR_DISABLED (0x2 << 12) /* mctl_standby-sun8iw11.c:143,752 */ #define DXnGCR0_DXPDD (0x3 << 14) #define DXnGCR0_DXPDD_DYNAMIC (0x0 << 14) #define DXnGCR0_DXPDD_ENABLED (0x1 << 14) #define DXnGCR0_DXPDD_DISABLED (0x2 << 14) /* mctl_standby-sun8iw11.c:141 */ #define DXnGCR0_DQSRPD (0x3 << 22) #define DXnGCR0_DQSRPD_DYNAMIC (0x0 << 22) #define DXnGCR0_DQSRPD_ENABLED (0x1 << 22) #define DXnGCR0_DQSRPD_DISABLED (0x2 << 22) /* Clocks needed by this driver. */ enum { BUS_DRAM, MBUS, DRAM, }; static const struct clock_handle dram_clocks[] = { [BUS_DRAM] = { .dev = &ccu.dev, .id = CLK_BUS_DRAM, }, [MBUS] = { .dev = &ccu.dev, .id = CLK_MBUS, }, [DRAM] = { .dev = &ccu.dev, .id = CLK_DRAM, }, }; static uint32_t pwrctl; void dram_suspend(void) { /* Enable DRAM controller register access. */ clock_get(&dram_clocks[BUS_DRAM]); /* Disable controller port access and enable DRAM self-refresh. */ pwrctl = mmio_read_32(PWRCTL); mmio_write_32(PWRCTL, pwrctl | PWRCTL_PORT_DIS | PWRCTL_SELFREF_EN); /* Wait until the DRAM controller enters self-refresh. */ mmio_polleq_32(STATR, STATR_OP_MODE, STATR_OP_MODE_SELFREF); udelay(1); /* Disable CKEN and CKNEN. */ mmio_clrset_32(PGCR3, PGCR3_CKEN | PGCR3_CKNEN, PGCR3_CKEN_DISABLED | PGCR3_CKNEN_DISABLED); /* Configure DX pads. */ for (uint8_t n = 0; n < 4; ++n) { mmio_clrset_32(DXnGCR0(n), DXnGCR0_DXIOM | DXnGCR0_DXOEO | DXnGCR0_DXPDR | DXnGCR0_DXPDD | DXnGCR0_DQSRPD, DXnGCR0_DXIOM_CMOS | DXnGCR0_DXOEO_DISABLED | DXnGCR0_DXPDR_ENABLED | DXnGCR0_DXPDD_ENABLED | DXnGCR0_DQSRPD_ENABLED); } /* Configure AC pads. */ mmio_clrset_32(ACIOCR0, ACIOCR0_ACPDD | ACIOCR0_ACPDR | ACIOCR0_ACOE | ACIOCR0_ACIOM | ACIOCR0_CKOE | ACIOCR0_CKEOE, ACIOCR0_ACPDD_ENABLED | ACIOCR0_ACPDR_ENABLED | ACIOCR0_ACOE_ENABLED * CONFIG(PLATFORM_H3) | ACIOCR0_ACIOM_CMOS | ACIOCR0_CKOE_DISABLED | ACIOCR0_CKEOE_ENABLED); /* Enable pad hold. */ udelay(10); mmio_set_32(VDD_SYS_PWROFF_GATING_REG, GENMASK(1, 0)); udelay(10); /* Disable DRAM controller clocks. */ mmio_write_32(CLKEN, 0); clock_put(&dram_clocks[DRAM]); clock_put(&dram_clocks[MBUS]); /* Disable further DRAM controller register access. */ clock_put(&dram_clocks[BUS_DRAM]); } void dram_resume(void) { /* Enable DRAM controller register access. */ clock_get(&dram_clocks[BUS_DRAM]); /* Enable DRAM controller clocks. */ clock_get(&dram_clocks[MBUS]); clock_get(&dram_clocks[DRAM]); mmio_write_32(CLKEN, CLKEN_VALUE); /* Configure AC pads. */ mmio_clrset_32(ACIOCR0, ACIOCR0_ACPDD | ACIOCR0_ACPDR | ACIOCR0_ACOE | ACIOCR0_ACIOM | ACIOCR0_CKOE | ACIOCR0_CKEOE, ACIOCR0_ACPDD_DISABLED | ACIOCR0_ACPDR_ENABLED | ACIOCR0_ACOE_ENABLED | ACIOCR0_ACIOM_SSTL | ACIOCR0_CKOE_ENABLED | ACIOCR0_CKEOE_ENABLED); /* Configure DX pads. */ for (uint8_t n = 0; n < 4; ++n) { mmio_clrset_32(DXnGCR0(n), DXnGCR0_DXIOM | DXnGCR0_DXOEO | DXnGCR0_DXPDR | DXnGCR0_DXPDD | DXnGCR0_DQSRPD, DXnGCR0_DXIOM_SSTL | DXnGCR0_DXOEO_DYNAMIC | DXnGCR0_DXPDR_DYNAMIC | DXnGCR0_DXPDD_DYNAMIC | DXnGCR0_DQSRPD_DYNAMIC); } /* Enable CKEN and CKNEN. */ mmio_clrset_32(PGCR3, PGCR3_CKEN | PGCR3_CKNEN, PGCR3_CKEN_NORMAL | PGCR3_CKNEN_NORMAL); /* Disable pad hold. */ udelay(10); mmio_clr_32(VDD_SYS_PWROFF_GATING_REG, GENMASK(1, 0)); udelay(10); /* Restore self refresh state and enable controller port access. */ mmio_write_32(PWRCTL, pwrctl); /* Disable further DRAM controller register access. */ clock_put(&dram_clocks[BUS_DRAM]); } void dram_init(void) { /* Get references to clocks that are already running. */ clock_get(&dram_clocks[MBUS]); clock_get(&dram_clocks[DRAM]); } crust-0.5/drivers/gpio/000077500000000000000000000000001414152222500151305ustar00rootroot00000000000000crust-0.5/drivers/gpio/Makefile000066400000000000000000000002311414152222500165640ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += gpio.o obj-y += sunxi-gpio.o crust-0.5/drivers/gpio/gpio.c000066400000000000000000000023011414152222500162260ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include "gpio.h" /** * Get the ops for the controller device providing this GPIO pin. */ static inline const struct gpio_driver_ops * gpio_ops_for(const struct gpio_handle *gpio) { const struct gpio_driver *drv = container_of(gpio->dev->drv, const struct gpio_driver, drv); return &drv->ops; } int gpio_get(const struct gpio_handle *gpio) { int err; /* Ensure the controller's driver is loaded. */ if ((err = device_get(gpio->dev))) return err; /* Set the GPIO pin mode, drive strength, and pull-up or pull-down. */ if ((err = gpio_ops_for(gpio)->init_pin(gpio))) goto err_put_device; return SUCCESS; err_put_device: device_put(gpio->dev); return err; } int gpio_get_value(const struct gpio_handle *gpio, bool *value) { return gpio_ops_for(gpio)->get_value(gpio, value); } void gpio_put(const struct gpio_handle *gpio) { device_put(gpio->dev); } int gpio_set_value(const struct gpio_handle *gpio, bool value) { return gpio_ops_for(gpio)->set_value(gpio, value); } crust-0.5/drivers/gpio/gpio.h000066400000000000000000000010371414152222500162400ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef GPIO_PRIVATE_H #define GPIO_PRIVATE_H #include #include #include struct gpio_driver_ops { int (*get_value)(const struct gpio_handle *gpio, bool *value); int (*init_pin)(const struct gpio_handle *gpio); int (*set_value)(const struct gpio_handle *gpio, bool value); }; struct gpio_driver { struct driver drv; struct gpio_driver_ops ops; }; #endif /* GPIO_PRIVATE_H */ crust-0.5/drivers/gpio/sunxi-gpio.c000066400000000000000000000070501414152222500174000ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include "gpio.h" #define MODE_WIDTH 4 #define MODE_PPW (WORD_BIT / MODE_WIDTH) #define MODE_REG(port, pin) (0x0000 + 0x24 * (port) + 4 * ((pin) / MODE_PPW)) #define MODE_BIT(pin) (MODE_WIDTH * ((pin) % MODE_PPW)) #define DATA_WIDTH 1 #define DATA_PPW (WORD_BIT / DATA_WIDTH) #define DATA_REG(port) (0x0010 + 0x24 * (port)) #define DATA_BIT(pin) (pin) #define DRIVE_WIDTH 2 #define DRIVE_PPW (WORD_BIT / DRIVE_WIDTH) #define DRIVE_REG(port, pin) (0x0014 + 0x24 * (port) + 4 * ((pin) / DRIVE_PPW)) #define DRIVE_BIT(pin) (DRIVE_WIDTH * ((pin) % DRIVE_PPW)) #define PULL_WIDTH 2 #define PULL_PPW (WORD_BIT / PULL_WIDTH) #define PULL_REG(port, pin) (0x001c + 0x24 * (port) + 4 * ((pin) / PULL_PPW)) #define PULL_BIT(pin) (PULL_WIDTH * ((pin) % PULL_PPW)) #define PINS_PER_PORT 32 #define GET_PORT(gpio) ((gpio)->id / PINS_PER_PORT) #define GET_PIN(gpio) ((gpio)->id % PINS_PER_PORT) static int sunxi_gpio_get_value(const struct gpio_handle *gpio, bool *value) { const struct simple_device *self = to_simple_device(gpio->dev); uint8_t port = GET_PORT(gpio); uint8_t pin = GET_PIN(gpio); uintptr_t regs = self->regs; *value = mmio_get_bitfield_32(regs + DATA_REG(port), DATA_BIT(pin), DATA_WIDTH); return SUCCESS; } static int sunxi_gpio_init_pin(const struct gpio_handle *gpio) { const struct simple_device *self = to_simple_device(gpio->dev); uint8_t port = GET_PORT(gpio); uint8_t pin = GET_PIN(gpio); uintptr_t regs = self->regs; /* Set pin function configuration (mode). */ if (gpio->mode != MODE_OUTPUT) mmio_set_bitfield_32(regs + MODE_REG(port, pin), MODE_BIT(pin), MODE_WIDTH, gpio->mode); /* Set pin drive strength. */ mmio_set_bitfield_32(regs + DRIVE_REG(port, pin), DRIVE_BIT(pin), DRIVE_WIDTH, gpio->drive); /* Set pin pull-up or pull-down. */ mmio_set_bitfield_32(regs + PULL_REG(port, pin), PULL_BIT(pin), PULL_WIDTH, gpio->pull); return SUCCESS; } static int sunxi_gpio_set_value(const struct gpio_handle *gpio, bool value) { const struct simple_device *self = to_simple_device(gpio->dev); uint8_t port = GET_PORT(gpio); uint8_t pin = GET_PIN(gpio); uintptr_t regs = self->regs; mmio_set_bitfield_32(regs + DATA_REG(port), DATA_BIT(pin), DATA_WIDTH, value); mmio_set_bitfield_32(regs + MODE_REG(port, pin), MODE_BIT(pin), MODE_WIDTH, MODE_OUTPUT); return SUCCESS; } static const struct gpio_driver sunxi_gpio_driver = { .drv = { .probe = simple_device_probe, .release = simple_device_release, }, .ops = { .get_value = sunxi_gpio_get_value, .init_pin = sunxi_gpio_init_pin, .set_value = sunxi_gpio_set_value, }, }; const struct simple_device pio = { .dev = { .name = "pio", .drv = &sunxi_gpio_driver.drv, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_PIO }, .regs = DEV_PIO, }; const struct simple_device r_pio = { .dev = { .name = "r_pio", .drv = &sunxi_gpio_driver.drv, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &r_ccu.dev, .id = CLK_BUS_R_PIO }, .regs = DEV_R_PIO, }; crust-0.5/drivers/irq/000077500000000000000000000000001414152222500147655ustar00rootroot00000000000000crust-0.5/drivers/irq/Makefile000066400000000000000000000005121414152222500164230ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += irq.o obj-$(CONFIG_PLATFORM_A64) += sun6i-a31-r-intc.o obj-$(CONFIG_PLATFORM_A83T) += sun6i-a31-r-intc.o obj-$(CONFIG_PLATFORM_H3) += sun6i-a31-r-intc.o obj-$(CONFIG_PLATFORM_H6) += sun6i-a31-r-intc.o crust-0.5/drivers/irq/irq.c000066400000000000000000000004621414152222500157260ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include uint32_t WEAK irq_needs_avcc(void) { return 0; } uint32_t WEAK irq_needs_vdd_sys(void) { return 0; } uint32_t WEAK irq_poll(void) { return 0; } crust-0.5/drivers/irq/sun6i-a31-r-intc.c000066400000000000000000000050401414152222500177500ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include "irq.h" #define INTC_VECTOR_REG 0x0000 #define INTC_BASE_ADDR_REG 0x0004 #define INTC_PROTECT_REG 0x0008 #define INTC_NMI_CTRL_REG 0x000c #define INTC_IRQ_PEND_REG(n) (0x0010 + 4 * (n)) #define INTC_IRQ_EN_REG(n) (0x0040 + 4 * (n)) #define INTC_IRQ_MASK_REG(n) (0x0050 + 4 * (n)) #define INTC_IRQ_RESP_REG(n) (0x0060 + 4 * (n)) #define INTC_MUX_EN_REG(n) (0x00c0 + 4 * (n)) #define NUM_IRQ_REGS (CONFIG(PLATFORM_H6) ? 2 : 1) #define NUM_MUX_REGS 4 /* Gating AVCC will prevent receiving any of these interrupts. */ static const uint32_t mux_needs_avcc[NUM_MUX_REGS] = { #if CONFIG(PLATFORM_A64) || CONFIG(PLATFORM_H3) [0] = BIT(62 - 32), /* LRADC */ #else 0 /* No applicable interrupts */ #endif }; /* Gating VDD_SYS will prevent receiving any of these interrupts. */ static const uint32_t mux_needs_vdd_sys[NUM_MUX_REGS] = { #if CONFIG(PLATFORM_A64) && CONFIG(SOC_A64) [0] = BIT(43 - 32) | /* Port B */ BIT(49 - 32) | /* Port G */ BIT(53 - 32), /* Port H */ #elif CONFIG(PLATFORM_A64) && CONFIG(SOC_H5) [0] = BIT(43 - 32) | /* Port A */ BIT(49 - 32) | /* Port F */ BIT(55 - 32), /* Port G */ #elif CONFIG(PLATFORM_A83T) [0] = BIT(47 - 32) | /* Port B */ BIT(49 - 32), /* Port G */ #elif CONFIG(PLATFORM_H3) [0] = BIT(43 - 32) | /* Port A */ BIT(49 - 32), /* Port G */ #elif CONFIG(PLATFORM_H6) [1] = BIT(83 - 64) | /* Port B */ BIT(85 - 64) | /* Port F */ BIT(86 - 64) | /* Port G */ BIT(91 - 64), /* Port H */ #else 0 /* No applicable interrupts */ #endif }; uint32_t irq_needs_avcc(void) { uint32_t enabled = 0; /* Only read registers with relevant bits. */ for (int i = 0; i < NUM_MUX_REGS; ++i) { if (!mux_needs_avcc[i]) continue; enabled |= mmio_read_32(DEV_R_INTC + INTC_MUX_EN_REG(i)) & mux_needs_avcc[i]; } return enabled; } uint32_t irq_needs_vdd_sys(void) { uint32_t enabled = 0; /* Only read registers with relevant bits. */ for (int i = 0; i < NUM_MUX_REGS; ++i) { if (!mux_needs_vdd_sys[i]) continue; enabled |= mmio_read_32(DEV_R_INTC + INTC_MUX_EN_REG(i)) & mux_needs_vdd_sys[i]; } return enabled; } uint32_t irq_poll(void) { uint32_t pending = 0; for (int i = 0; i < NUM_IRQ_REGS; ++i) pending |= mmio_read_32(DEV_R_INTC + INTC_IRQ_PEND_REG(i)); return pending; } crust-0.5/drivers/mfd/000077500000000000000000000000001414152222500147405ustar00rootroot00000000000000crust-0.5/drivers/mfd/Kconfig000066400000000000000000000014741414152222500162510ustar00rootroot00000000000000# # Copyright © 2020-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # config MFD_AXP20X bool "Multi-function driver for X-Powers PMICs" depends on I2C || RSB help This driver enables both the power sequencing and voltage regulator functionality of an AXP2xx or AXP8xx series power management IC. comment "X-Powers PMIC will communicate via I2C" depends on MFD_AXP20X && !RSB comment "X-Powers PMIC will communicate via RSB" depends on MFD_AXP20X && RSB choice bool "X-Powers PMIC variant" depends on MFD_AXP20X default MFD_AXP803 if PLATFORM_A64 default MFD_AXP805 if PLATFORM_H6 config MFD_AXP803 bool "AXP803" help This PMIC is usually paired with the A64 SoC. config MFD_AXP805 bool "AXP805" help This PMIC is usually paired with the H6 SoC. endchoice crust-0.5/drivers/mfd/Makefile000066400000000000000000000002271414152222500164010ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-$(CONFIG_MFD_AXP20X) += axp20x.o crust-0.5/drivers/mfd/axp20x.c000066400000000000000000000025341414152222500162320ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #define IC_TYPE_REG 0x03 #define IC_TYPE_MASK 0xcf #if CONFIG(MFD_AXP803) #define IC_TYPE_VALUE 0x41 #define I2C_ADDRESS 0x34 #define RSB_ADDRESS (0x2d << 16 | 0x3a3) #elif CONFIG(MFD_AXP805) #define IC_TYPE_VALUE 0x40 #define I2C_ADDRESS 0x36 #define RSB_ADDRESS (0x3a << 16 | 0x745) #endif static int axp20x_probe(const struct device *dev) { const struct regmap_device *self = to_regmap_device(dev); uint8_t reg; int err; if ((err = regmap_device_probe(dev))) return err; if ((err = regmap_read(&self->map, IC_TYPE_REG, ®))) goto err_release; if ((reg & IC_TYPE_MASK) != IC_TYPE_VALUE) { err = ENODEV; goto err_release; } return SUCCESS; err_release: regmap_device_release(dev); return err; } static const struct driver axp20x_driver = { .probe = axp20x_probe, .release = regmap_device_release, }; const struct regmap_device axp20x = { .dev = { .name = "axp20x", .drv = &axp20x_driver, .state = DEVICE_STATE_INIT, }, .map = { .dev = CONFIG(RSB) ? &r_rsb.dev : &r_i2c.dev, .id = CONFIG(RSB) ? RSB_ADDRESS : I2C_ADDRESS, }, }; crust-0.5/drivers/msgbox/000077500000000000000000000000001414152222500154715ustar00rootroot00000000000000crust-0.5/drivers/msgbox/Makefile000066400000000000000000000002341414152222500171300ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += msgbox.o obj-y += sunxi-msgbox.o crust-0.5/drivers/msgbox/msgbox.c000066400000000000000000000017641414152222500171440ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include "msgbox.h" /** * Get the ops for the msgbox controller device. */ static inline const struct msgbox_driver_ops * msgbox_ops_for(const struct device *dev) { const struct msgbox_driver *drv = container_of(dev->drv, const struct msgbox_driver, drv); return &drv->ops; } void msgbox_ack_rx(const struct device *dev, uint8_t chan) { msgbox_ops_for(dev)->ack_rx(dev, chan); } bool msgbox_last_tx_done(const struct device *dev, uint8_t chan) { return msgbox_ops_for(dev)->last_tx_done(dev, chan); } int msgbox_receive(const struct device *dev, uint8_t chan, uint32_t *message) { return msgbox_ops_for(dev)->receive(dev, chan, message); } int msgbox_send(const struct device *dev, uint8_t chan, uint32_t message) { return msgbox_ops_for(dev)->send(dev, chan, message); } crust-0.5/drivers/msgbox/msgbox.h000066400000000000000000000012531414152222500171420ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef MSGBOX_PRIVATE_H #define MSGBOX_PRIVATE_H #include #include #include struct msgbox_driver_ops { void (*ack_rx)(const struct device *dev, uint8_t chan); bool (*last_tx_done)(const struct device *dev, uint8_t chan); int (*receive)(const struct device *dev, uint8_t chan, uint32_t *message); int (*send)(const struct device *dev, uint8_t chan, uint32_t message); }; struct msgbox_driver { struct driver drv; struct msgbox_driver_ops ops; }; #endif /* MSGBOX_PRIVATE_H */ crust-0.5/drivers/msgbox/sunxi-msgbox.c000066400000000000000000000071601414152222500203040ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include "msgbox.h" #define CTRL_REG0 0x0000 #define CTRL_REG1 0x0004 #define CTRL_NORMAL 0x01100110 #define IRQ_EN_REG 0x0040 #define IRQ_STAT_REG 0x0050 #define REMOTE_IRQ_EN_REG 0x0060 #define REMOTE_IRQ_STAT_REG 0x0070 #define RX_IRQ(n) BIT(0 + 2 * (n)) #define RX_IRQ_MASK 0x5555 #define TX_IRQ(n) BIT(1 + 2 * (n)) #define TX_IRQ_MASK 0xaaaa #define FIFO_STAT_REG(n) (0x0100 + 0x4 * (n)) #define FIFO_STAT_MASK BIT(0) #define MSG_STAT_REG(n) (0x0140 + 0x4 * (n)) #define MSG_STAT_MASK GENMASK(2, 0) #define MSG_DATA_REG(n) (0x0180 + 0x4 * (n)) static bool sunxi_msgbox_peek_data(const struct device *dev, uint8_t chan) { const struct simple_device *self = to_simple_device(dev); return mmio_read_32(self->regs + MSG_STAT_REG(chan)) & MSG_STAT_MASK; } static void sunxi_msgbox_ack_rx(const struct device *dev, uint8_t chan) { const struct simple_device *self = to_simple_device(dev); mmio_write_32(self->regs + IRQ_STAT_REG, RX_IRQ(chan)); } static bool sunxi_msgbox_last_tx_done(const struct device *dev, uint8_t chan) { const struct simple_device *self = to_simple_device(dev); assert(chan < SUNXI_MSGBOX_CHANS); return !(mmio_read_32(self->regs + REMOTE_IRQ_STAT_REG) & RX_IRQ(chan)); } static int sunxi_msgbox_receive(const struct device *dev, uint8_t chan, uint32_t *msg) { const struct simple_device *self = to_simple_device(dev); assert(chan < SUNXI_MSGBOX_CHANS); /* Check if a new message is available before reading it. */ if (!sunxi_msgbox_peek_data(dev, chan)) return ENOENT; *msg = mmio_read_32(self->regs + MSG_DATA_REG(chan)); return SUCCESS; } static int sunxi_msgbox_send(const struct device *dev, uint8_t chan, uint32_t msg) { const struct simple_device *self = to_simple_device(dev); assert(chan < SUNXI_MSGBOX_CHANS); /* Reject the message if the FIFO is full. */ if (mmio_read_32(self->regs + FIFO_STAT_REG(chan)) & FIFO_STAT_MASK) return EBUSY; mmio_write_32(self->regs + MSG_DATA_REG(chan), msg); return SUCCESS; } static int sunxi_msgbox_probe(const struct device *dev) { const struct simple_device *self = to_simple_device(dev); int err; if ((err = simple_device_probe(dev))) return err; /* Set even channels ARM -> SCP and odd channels SCP -> ARM. */ mmio_write_32(self->regs + CTRL_REG0, CTRL_NORMAL); mmio_write_32(self->regs + CTRL_REG1, CTRL_NORMAL); /* Drain messages in RX channels (required to clear IRQs). */ for (uint8_t chan = 0; chan < SUNXI_MSGBOX_CHANS; chan += 2) { while (sunxi_msgbox_peek_data(dev, chan)) mmio_read_32(self->regs + MSG_DATA_REG(chan)); } /* Disable and clear all IRQ. */ mmio_write_32(self->regs + IRQ_EN_REG, 0); mmio_write_32(self->regs + IRQ_STAT_REG, GENMASK(15, 0)); return SUCCESS; } static const struct msgbox_driver sunxi_msgbox_driver = { .drv = { .probe = sunxi_msgbox_probe, .release = simple_device_release, }, .ops = { .ack_rx = sunxi_msgbox_ack_rx, .last_tx_done = sunxi_msgbox_last_tx_done, .receive = sunxi_msgbox_receive, .send = sunxi_msgbox_send, }, }; const struct simple_device msgbox = { .dev = { .name = "msgbox", .drv = &sunxi_msgbox_driver.drv, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_MSGBOX }, .regs = DEV_MSGBOX, }; crust-0.5/drivers/pmic/000077500000000000000000000000001414152222500151225ustar00rootroot00000000000000crust-0.5/drivers/pmic/Kconfig000066400000000000000000000004501414152222500164240ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # menu "Power management controllers" config PMIC bool config PMIC_AXP803 bool select PMIC default MFD_AXP803 config PMIC_AXP805 bool select PMIC default MFD_AXP805 endmenu crust-0.5/drivers/pmic/Makefile000066400000000000000000000003411414152222500165600ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += pmic.o obj-$(CONFIG_PMIC_AXP803) += axp20x.o axp803.o obj-$(CONFIG_PMIC_AXP805) += axp20x.o axp805.o crust-0.5/drivers/pmic/axp20x.c000066400000000000000000000020761414152222500164150ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include "axp20x.h" int axp20x_pmic_reset(const struct device *dev) { const struct axp20x_pmic *self = to_axp20x_pmic(dev); /* Trigger soft power restart. */ return regmap_set_bits(self->map, WAKEUP_CTRL_REG, BIT(6)); } int axp20x_pmic_resume(const struct device *dev) { const struct axp20x_pmic *self = to_axp20x_pmic(dev); /* Trigger soft power resume. */ return regmap_set_bits(self->map, WAKEUP_CTRL_REG, BIT(5)); } int axp20x_pmic_shutdown(const struct device *dev) { const struct axp20x_pmic *self = to_axp20x_pmic(dev); /* Trigger soft power off. */ return regmap_set_bits(self->map, POWER_DISABLE_REG, BIT(7)); } int axp20x_pmic_probe(const struct device *dev) { const struct axp20x_pmic *self = to_axp20x_pmic(dev); return regmap_user_probe(self->map); } void axp20x_pmic_release(const struct device *dev) { const struct axp20x_pmic *self = to_axp20x_pmic(dev); regmap_user_release(self->map); } crust-0.5/drivers/pmic/axp20x.h000066400000000000000000000015641414152222500164230ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef AXP20X_PRIVATE_H #define AXP20X_PRIVATE_H #include #include #include "pmic.h" #define WAKEUP_CTRL_REG 0x31 #define POWER_DISABLE_REG 0x32 static inline const struct axp20x_pmic * to_axp20x_pmic(const struct device *dev) { return container_of(dev, const struct axp20x_pmic, dev); } /* Valid for AXP221, AXP223, AXP803. */ int axp20x_pmic_reset(const struct device *dev); /* Valid for AXP221, AXP223, AXP803, AXP805, AXP813. */ int axp20x_pmic_resume(const struct device *dev); /* Valid for AXP221, AXP223, AXP803, AXP805, AXP813. */ int axp20x_pmic_shutdown(const struct device *dev); int axp20x_pmic_probe(const struct device *dev); void axp20x_pmic_release(const struct device *dev); #endif /* AXP20X_PRIVATE_H */ crust-0.5/drivers/pmic/axp803.c000066400000000000000000000021311414152222500163060ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include "axp20x.h" #define PIN_FUNCTION_REG 0x8f static int axp803_pmic_suspend(const struct device *dev) { const struct axp20x_pmic *self = to_axp20x_pmic(dev); int err; /* Remember previous voltages when waking up from suspend. */ if ((err = regmap_set_bits(self->map, PIN_FUNCTION_REG, BIT(1)))) return err; /* Enable resume, allow IRQs during suspend. */ return regmap_set_bits(self->map, WAKEUP_CTRL_REG, BIT(4) | BIT(3)); } static const struct pmic_driver axp803_pmic_driver = { .drv = { .probe = axp20x_pmic_probe, .release = axp20x_pmic_release, }, .ops = { .reset = axp20x_pmic_reset, .resume = axp20x_pmic_resume, .shutdown = axp20x_pmic_shutdown, .suspend = axp803_pmic_suspend, }, }; const struct axp20x_pmic axp803_pmic = { .dev = { .name = "axp803-pmic", .drv = &axp803_pmic_driver.drv, .state = DEVICE_STATE_INIT, }, .map = &axp20x.map, }; crust-0.5/drivers/pmic/axp805.c000066400000000000000000000022551414152222500163170ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include "axp20x.h" static int axp805_pmic_reset(const struct device *dev) { const struct axp20x_pmic *self = to_axp20x_pmic(dev); /* Trigger soft power restart. */ return regmap_set_bits(self->map, POWER_DISABLE_REG, BIT(6)); } static int axp805_pmic_suspend(const struct device *dev) { const struct axp20x_pmic *self = to_axp20x_pmic(dev); /* Enable resume, remember voltages, and allow IRQs during suspend. */ return regmap_set_bits(self->map, WAKEUP_CTRL_REG, BIT(6) | BIT(4) | BIT(3)); } static const struct pmic_driver axp805_pmic_driver = { .drv = { .probe = axp20x_pmic_probe, .release = axp20x_pmic_release, }, .ops = { .reset = axp805_pmic_reset, .resume = axp20x_pmic_resume, .shutdown = axp20x_pmic_shutdown, .suspend = axp805_pmic_suspend, }, }; const struct axp20x_pmic axp805_pmic = { .dev = { .name = "axp805-pmic", .drv = &axp805_pmic_driver.drv, .state = DEVICE_STATE_INIT, }, .map = &axp20x.map, }; crust-0.5/drivers/pmic/pmic.c000066400000000000000000000021171414152222500162170ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include "pmic.h" /** * Get the ops for the pmic controller device. */ static inline const struct pmic_driver_ops * pmic_ops_for(const struct device *dev) { const struct pmic_driver *drv = container_of(dev->drv, const struct pmic_driver, drv); return &drv->ops; } const struct device * pmic_get(void) { const struct device *pmic = NULL; if (CONFIG(PMIC_AXP803)) pmic = device_get_or_null(&axp803_pmic.dev); if (!pmic && CONFIG(PMIC_AXP805)) pmic = device_get_or_null(&axp805_pmic.dev); return pmic; } int pmic_reset(const struct device *dev) { return pmic_ops_for(dev)->reset(dev); } int pmic_resume(const struct device *dev) { return pmic_ops_for(dev)->resume(dev); } int pmic_shutdown(const struct device *dev) { return pmic_ops_for(dev)->shutdown(dev); } int pmic_suspend(const struct device *dev) { return pmic_ops_for(dev)->suspend(dev); } crust-0.5/drivers/pmic/pmic.h000066400000000000000000000010031414152222500162150ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PMIC_PRIVATE_H #define PMIC_PRIVATE_H #include #include struct pmic_driver_ops { int (*reset)(const struct device *dev); int (*resume)(const struct device *dev); int (*shutdown)(const struct device *dev); int (*suspend)(const struct device *dev); }; struct pmic_driver { struct driver drv; struct pmic_driver_ops ops; }; #endif /* PMIC_PRIVATE_H */ crust-0.5/drivers/regmap/000077500000000000000000000000001414152222500154455ustar00rootroot00000000000000crust-0.5/drivers/regmap/Kconfig000066400000000000000000000026741414152222500167610ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # choice bool "I2C controller pin selection" depends on HAVE_I2C default I2C_PINS_NONE if HAVE_RSB default I2C_PINS_PL0_PL1 help Choose which pair of pins is used for the I2C bus. config I2C_PINS_NONE bool "None" help Select this option if your board does not have a PMIC or regulator connected via I2C. This is usually the right choice if your SoC supports RSB. config I2C_PINS_PL0_PL1 bool "PL0/PL1" help Select this option if your board has a PMIC or regulator connected via I2C using the normal set of pins. Note that if your SoC contains an RSB controller, choosing this option will prevent you from using it. config I2C_PINS_PL8_PL9 bool "PL8/PL9" depends on HAVE_RSB help Select this option if your board has a PMIC or regulator connected via I2C using the alternate set of pins. endchoice config I2C_PIN_MODE_PL0_PL1 int depends on I2C_PINS_PL0_PL1 default 3 if HAVE_RSB default 2 help Provide the pin mode that configures PL0/PL1 as I2C SCL/SDA. config I2C bool default HAVE_I2C && (!I2C_PINS_NONE || COMPILE_TEST) help This option is selected if the chosen pin configuration allows the I2C controller to be used. config RSB bool default HAVE_RSB && (!I2C_PINS_PL0_PL1 || COMPILE_TEST) help This option is selected if the chosen pin configuration allows the RSB controller to be used. crust-0.5/drivers/regmap/Makefile000066400000000000000000000003651414152222500171110ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += regmap.o obj-$(CONFIG_I2C) += regmap-i2c.o obj-$(CONFIG_I2C) += sun6i-i2c.o obj-$(CONFIG_RSB) += sunxi-rsb.o crust-0.5/drivers/regmap/regmap-i2c.c000066400000000000000000000037531414152222500175470ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include "regmap-i2c.h" /** * Get the ops for the I²C controller device. */ static inline const struct regmap_i2c_driver_ops * regmap_i2c_ops_for(const struct regmap *map) { const struct regmap_i2c_driver *drv = container_of(map->dev->drv, const struct regmap_i2c_driver, drv.drv); return &drv->ops; } int regmap_i2c_prepare(const struct regmap *map) { const struct regmap_i2c_driver_ops *ops = regmap_i2c_ops_for(map); uint8_t dummy; int err; /* Start a read transaction. */ if ((err = ops->start(map, I2C_READ))) goto abort; /* Read data to avoid putting the device in an inconsistent state. */ if (ops->read(map, &dummy)) goto abort; abort: /* Finish the transaction. */ ops->stop(map); return err ? ENODEV : SUCCESS; } int regmap_i2c_read(const struct regmap *map, uint8_t reg, uint8_t *val) { const struct regmap_i2c_driver_ops *ops = regmap_i2c_ops_for(map); int err; /* Start a write transaction. */ if ((err = ops->start(map, I2C_WRITE))) goto abort; /* Write the register address. */ if ((err = ops->write(map, reg))) goto abort; /* Restart as a read transaction. */ if ((err = ops->start(map, I2C_READ))) goto abort; /* Read the register value. */ if ((err = ops->read(map, val))) goto abort; abort: /* Finish the transaction. */ ops->stop(map); return err; } int regmap_i2c_write(const struct regmap *map, uint8_t reg, uint8_t val) { const struct regmap_i2c_driver_ops *ops = regmap_i2c_ops_for(map); int err; /* Start a write transaction. */ if ((err = ops->start(map, I2C_WRITE))) goto abort; /* Write the register address. */ if ((err = ops->write(map, reg))) goto abort; /* Write the register value. */ if ((err = ops->write(map, val))) goto abort; abort: /* Finish the transaction. */ ops->stop(map); return err; } crust-0.5/drivers/regmap/regmap-i2c.h000066400000000000000000000014731414152222500175510ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef REGMAP_I2C_PRIVATE_H #define REGMAP_I2C_PRIVATE_H #include "regmap.h" enum { I2C_WRITE = 0, I2C_READ = 1, }; struct regmap_i2c_driver_ops { int (*read)(const struct regmap *map, uint8_t *data); int (*start)(const struct regmap *map, uint8_t direction); void (*stop)(const struct regmap *map); int (*write)(const struct regmap *map, uint8_t data); }; struct regmap_i2c_driver { struct regmap_driver drv; struct regmap_i2c_driver_ops ops; }; int regmap_i2c_prepare(const struct regmap *map); int regmap_i2c_read(const struct regmap *map, uint8_t reg, uint8_t *val); int regmap_i2c_write(const struct regmap *map, uint8_t reg, uint8_t val); #endif /* REGMAP_I2C_PRIVATE_H */ crust-0.5/drivers/regmap/regmap.c000066400000000000000000000040111414152222500170600ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include "regmap.h" /** * Get the ops for the regmap device. */ static inline const struct regmap_driver_ops * regmap_ops_for(const struct regmap *map) { const struct regmap_driver *drv = container_of(map->dev->drv, const struct regmap_driver, drv); return &drv->ops; } int regmap_get(const struct regmap *map) { int err; /* Ensure the controller's driver is loaded. */ if ((err = device_get(map->dev))) return err; if ((err = regmap_ops_for(map)->prepare(map))) goto err_put_device; return SUCCESS; err_put_device: device_put(map->dev); return err; } void regmap_put(const struct regmap *map) { device_put(map->dev); } int regmap_read(const struct regmap *map, uint8_t reg, uint8_t *val) { return regmap_ops_for(map)->read(map, reg, val); } int regmap_write(const struct regmap *map, uint8_t reg, uint8_t val) { return regmap_ops_for(map)->write(map, reg, val); } int regmap_update_bits(const struct regmap *map, uint8_t reg, uint8_t mask, uint8_t val) { const struct regmap_driver_ops *ops = regmap_ops_for(map); uint8_t tmp; int err; if ((err = ops->read(map, reg, &tmp))) return err; return ops->write(map, reg, tmp ^ ((val ^ tmp) & mask)); } int regmap_device_probe(const struct device *dev) { const struct regmap_device *self = to_regmap_device(dev); return regmap_get(&self->map); } void regmap_device_release(const struct device *dev) { const struct regmap_device *self = to_regmap_device(dev); regmap_put(&self->map); } static inline const struct device * regmap_to_device(const struct regmap *map) { return &container_of(map, const struct regmap_device, map)->dev; } int regmap_user_probe(const struct regmap *map) { return device_get(regmap_to_device(map)); } void regmap_user_release(const struct regmap *map) { device_put(regmap_to_device(map)); } crust-0.5/drivers/regmap/regmap.h000066400000000000000000000010561414152222500170730ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef REGMAP_PRIVATE_H #define REGMAP_PRIVATE_H #include #include #include struct regmap_driver_ops { int (*prepare)(const struct regmap *map); int (*read)(const struct regmap *map, uint8_t reg, uint8_t *val); int (*write)(const struct regmap *map, uint8_t reg, uint8_t val); }; struct regmap_driver { struct driver drv; struct regmap_driver_ops ops; }; #endif /* REGMAP_PRIVATE_H */ crust-0.5/drivers/regmap/sun6i-i2c.c000066400000000000000000000146101414152222500173320ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include "regmap-i2c.h" #define I2C_ADDR_REG 0x00 #define I2C_XADDR_REG 0x04 #define I2C_DATA_REG 0x08 #define I2C_CTRL_REG 0x0c #define I2C_STAT_REG 0x10 #define I2C_CCR_REG 0x14 #define I2C_SRST_REG 0x18 #define I2C_EFR_REG 0x1c #define I2C_LCR_REG 0x20 enum { START_COND_TX = 0x08, START_COND_TX_REPEAT = 0x10, ADDR_WRITE_TX_ACK = 0x18, ADDR_WRITE_TX_NACK = 0x20, DATA_TX_ACK = 0x28, DATA_TX_NACK = 0x30, ADDR_READ_TX_ACK = 0x40, ADDR_READ_TX_NACK = 0x48, DATA_RX_ACK = 0x50, DATA_RX_NACK = 0x58, IDLE = 0xf8, }; static int sun6i_i2c_wait_idle(const struct simple_device *self) { /* With a single master on the bus, this should only take one cycle. */ int timeout = 2; while (mmio_read_32(self->regs + I2C_CTRL_REG) & (BIT(5) | BIT(4))) { /* 10μs is one 100kHz bus cycle. */ udelay(10); if (timeout-- == 0) return EIO; } if (mmio_read_32(self->regs + I2C_STAT_REG) != IDLE) return EIO; return SUCCESS; } static int sun6i_i2c_wait_start(const struct simple_device *self) { /* With a single master on the bus, this should only take one cycle. */ int timeout = 2; while (mmio_read_32(self->regs + I2C_CTRL_REG) & BIT(5)) { /* 10μs is one 100kHz bus cycle. */ udelay(10); if (timeout-- == 0) return EIO; } return SUCCESS; } static int sun6i_i2c_wait_state(const struct simple_device *self, uint8_t state) { /* Wait for up to 8 transfer cycles, one ACK, and one extra cycle. */ int timeout = 10; while (!(mmio_read_32(self->regs + I2C_CTRL_REG) & BIT(3))) { /* 10μs is one 100kHz bus cycle. */ udelay(10); if (timeout-- == 0) return EIO; } if (mmio_read_32(self->regs + I2C_STAT_REG) != state) return EIO; return SUCCESS; } static int sun6i_i2c_read(const struct regmap *map, uint8_t *data) { const struct simple_device *self = to_simple_device(map->dev); int err; /* Disable sending an ACK and trigger a state change. */ mmio_clrset_32(self->regs + I2C_CTRL_REG, BIT(2), BIT(3)); /* Wait for data to arrive. */ if ((err = sun6i_i2c_wait_state(self, DATA_RX_NACK))) return err; /* Read the data. */ *data = mmio_read_32(self->regs + I2C_DATA_REG); return SUCCESS; } static int sun6i_i2c_start(const struct regmap *map, uint8_t direction) { const struct simple_device *self = to_simple_device(map->dev); uint8_t init_state = mmio_read_32(self->regs + I2C_STAT_REG); uint8_t state; int err; /* Send a start condition. */ mmio_set_32(self->regs + I2C_CTRL_REG, BIT(5) | BIT(3)); /* Wait for the start condition to be sent. */ if ((err = sun6i_i2c_wait_start(self))) return err; /* Wait for the start state if the bus was previously idle; otherwise, * wait for the repeated start state. */ state = init_state == IDLE ? START_COND_TX : START_COND_TX_REPEAT; if ((err = sun6i_i2c_wait_state(self, state))) return err; /* Write the address and direction, then trigger a state change. */ mmio_write_32(self->regs + I2C_DATA_REG, (map->id << 1) | direction); mmio_set_32(self->regs + I2C_CTRL_REG, BIT(3)); /* Check for address acknowledgement. */ state = direction == I2C_WRITE ? ADDR_WRITE_TX_ACK : ADDR_READ_TX_ACK; if ((err = sun6i_i2c_wait_state(self, state))) return err; return SUCCESS; } static void sun6i_i2c_stop(const struct regmap *map) { const struct simple_device *self = to_simple_device(map->dev); /* Send a stop condition. */ mmio_set_32(self->regs + I2C_CTRL_REG, BIT(4) | BIT(3)); /* Wait for the bus to go idle. */ sun6i_i2c_wait_idle(self); } static int sun6i_i2c_write(const struct regmap *map, uint8_t data) { const struct simple_device *self = to_simple_device(map->dev); int err; /* Write data, then trigger a state change. */ mmio_write_32(self->regs + I2C_DATA_REG, data); mmio_set_32(self->regs + I2C_CTRL_REG, BIT(3)); /* Wait for data to be sent. */ if ((err = sun6i_i2c_wait_state(self, DATA_TX_ACK))) return err; return SUCCESS; } static int sun6i_i2c_probe(const struct device *dev) { const struct simple_device *self = to_simple_device(dev); int err; if ((err = simple_device_probe(dev))) return err; /* Set I2C bus clock divider for 400 KHz operation. */ mmio_write_32(self->regs + I2C_CCR_REG, 0x00000011); /* Clear slave address (this driver only supports master mode). */ mmio_write_32(self->regs + I2C_ADDR_REG, 0); mmio_write_32(self->regs + I2C_XADDR_REG, 0); /* Enable I2C bus and stop any current transaction. Disable interrupts * and don't send an ACK for received bytes. */ mmio_write_32(self->regs + I2C_CTRL_REG, BIT(6) | BIT(4)); /* Soft reset the controller. */ mmio_set_32(self->regs + I2C_SRST_REG, BIT(0)); /* Wait for the bus to go idle. */ if ((err = sun6i_i2c_wait_idle(self))) goto err_release; return SUCCESS; err_release: simple_device_release(dev); return err; } static const struct regmap_i2c_driver sun6i_i2c_driver = { .drv = { .drv = { .probe = sun6i_i2c_probe, .release = simple_device_release, }, .ops = { .prepare = regmap_i2c_prepare, .read = regmap_i2c_read, .write = regmap_i2c_write, }, }, .ops = { .read = sun6i_i2c_read, .start = sun6i_i2c_start, .stop = sun6i_i2c_stop, .write = sun6i_i2c_write, }, }; const struct simple_device r_i2c = { .dev = { .name = "r_i2c", .drv = &sun6i_i2c_driver.drv.drv, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &r_ccu.dev, .id = CLK_BUS_R_I2C }, #if CONFIG(I2C_PINS_PL0_PL1) .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 0), .drive = DRIVE_10mA, .mode = CONFIG_I2C_PIN_MODE_PL0_PL1, .pull = PULL_UP, }, { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 1), .drive = DRIVE_10mA, .mode = CONFIG_I2C_PIN_MODE_PL0_PL1, .pull = PULL_UP, }, }, #elif CONFIG(I2C_PINS_PL8_PL9) .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 8), .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 9), .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, #endif .regs = DEV_R_I2C, }; crust-0.5/drivers/regmap/sunxi-rsb.c000066400000000000000000000072511414152222500175500ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include "regmap.h" #define RSB_CTRL_REG 0x00 #define RSB_CCR_REG 0x04 #define RSB_INT_EN_REG 0x08 #define RSB_STAT_REG 0x0c #define RSB_ADDR_REG 0x10 #define RSB_DLEN_REG 0x18 #define RSB_DATA_REG 0x1c #define RSB_LCR_REG 0x24 #define RSB_PMCR_REG 0x28 #define RSB_CMD_REG 0x2c #define RSB_SADDR_REG 0x30 #define I2C_BCAST_ADDR 0 #define PMIC_MODE_REG 0x3e #define PMIC_MODE_VAL 0x7c enum { RSB_SRTA = 0xe8, RSB_RD8 = 0x8b, RSB_RD16 = 0x9c, RSB_RD32 = 0xa6, RSB_WR8 = 0x4e, RSB_WR16 = 0x59, RSB_WR32 = 0x63, }; static int sunxi_rsb_do_command(const struct regmap *map, uint32_t cmd) { const struct simple_device *self = to_simple_device(map->dev); mmio_write_32(self->regs + RSB_CMD_REG, cmd); mmio_write_32(self->regs + RSB_SADDR_REG, map->id); mmio_write_32(self->regs + RSB_CTRL_REG, BIT(7)); mmio_pollz_32(self->regs + RSB_CTRL_REG, BIT(7)); return mmio_read_32(self->regs + RSB_STAT_REG) == BIT(0) ? SUCCESS : EIO; } static int sunxi_rsb_prepare(const struct regmap *map) { /* Set the device's runtime address. */ return sunxi_rsb_do_command(map, RSB_SRTA); } static int sunxi_rsb_read(const struct regmap *map, uint8_t addr, uint8_t *data) { const struct simple_device *self = to_simple_device(map->dev); int err; mmio_write_32(self->regs + RSB_ADDR_REG, addr); if ((err = sunxi_rsb_do_command(map, RSB_RD8))) return err; *data = mmio_read_32(self->regs + RSB_DATA_REG); return SUCCESS; } static int sunxi_rsb_write(const struct regmap *map, uint8_t addr, uint8_t data) { const struct simple_device *self = to_simple_device(map->dev); mmio_write_32(self->regs + RSB_ADDR_REG, addr); mmio_write_32(self->regs + RSB_DATA_REG, data); return sunxi_rsb_do_command(map, RSB_WR8); } static void sunxi_rsb_set_rate(const struct simple_device *self, uint32_t rate) { uint32_t divider = (clock_get_rate(&self->clock) + rate) / (2 * rate); if (divider > 0) divider = divider - 1; if (divider > 0xff) divider = 0xff; mmio_write_32(self->regs + RSB_CCR_REG, 1U << 8 | divider); } static int sunxi_rsb_probe(const struct device *dev) { const struct simple_device *self = to_simple_device(dev); int err; if ((err = simple_device_probe(dev))) return err; /* Soft-reset the controller. */ mmio_write_32(self->regs + RSB_CTRL_REG, BIT(0)); mmio_pollz_32(self->regs + RSB_CTRL_REG, BIT(0)); /* Set the bus clock rate to its default value (3 MHz). */ sunxi_rsb_set_rate(self, 3000000); /* Switch all devices to RSB mode. */ mmio_write_32(self->regs + RSB_PMCR_REG, I2C_BCAST_ADDR | PMIC_MODE_REG << 8 | PMIC_MODE_VAL << 16 | BIT(31)); mmio_pollz_32(self->regs + RSB_PMCR_REG, BIT(31)); return SUCCESS; } static const struct regmap_driver sunxi_rsb_driver = { .drv = { .probe = sunxi_rsb_probe, .release = simple_device_release, }, .ops = { .prepare = sunxi_rsb_prepare, .read = sunxi_rsb_read, .write = sunxi_rsb_write, }, }; const struct simple_device r_rsb = { .dev = { .name = "r_rsb", .drv = &sunxi_rsb_driver.drv, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &r_ccu.dev, .id = CLK_BUS_R_RSB }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 0), .drive = DRIVE_30mA, .mode = 2, .pull = PULL_UP, }, { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 1), .drive = DRIVE_30mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_R_RSB, }; crust-0.5/drivers/regulator/000077500000000000000000000000001414152222500161765ustar00rootroot00000000000000crust-0.5/drivers/regulator/Kconfig000066400000000000000000000044711414152222500175070ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # menu "Regulators (voltage/current)" config REGULATOR_AXP803 bool default MFD_AXP803 config REGULATOR_AXP805 bool default MFD_AXP805 config REGULATOR_GPIO bool menuconfig REGULATOR_GPIO_CPU bool "GPIO-controlled CPU power supply" select REGULATOR_GPIO help Select this option if your board supplies power to the CPU (VDD-CPUX) via a GPIO-controlled voltage regulator. This regulator will be disabled during both the suspend and shutdown procedures. Note: This GPIO pin must NOT also control VDD-SYS. VDD-SYS must remain powered while the system is asleep. if REGULATOR_GPIO_CPU config REGULATOR_GPIO_CPU_PIN int "GPIO pin (port L)" default 8 range 0 31 help Select the GPIO pin used to control the CPU supply. endif menuconfig REGULATOR_GPIO_DRAM bool "GPIO-controlled DRAM power supply" select REGULATOR_GPIO help Select this option if your board supplies power to the DRAM (VCC-DRAM) via a GPIO-controlled voltage regulator. This regulator will be disabled during the shutdown procedure. if REGULATOR_GPIO_DRAM config REGULATOR_GPIO_DRAM_PIN int "GPIO pin (port L)" default 9 range 0 31 help Select the GPIO pin used to control the DRAM supply. endif menuconfig REGULATOR_GPIO_VCC_PLL bool "GPIO-controlled VCC-PLL power supply" select REGULATOR_GPIO help Select this option if your board supplies power to the PLLs (VCC-PLL) via a GPIO-controlled voltage regulator. This regulator will be disabled during the shutdown procedure. if REGULATOR_GPIO_VCC_PLL config REGULATOR_GPIO_VCC_PLL_PIN int "GPIO pin (port L)" default 0 range 0 31 help Select the GPIO pin used to control the VCC-PLL supply. endif menuconfig REGULATOR_GPIO_VDD_SYS bool "GPIO-controlled VDD-SYS power supply" select REGULATOR_GPIO help Select this option if your board supplies power to the SoC (VDD-SYS) via a GPIO-controlled voltage regulator. This regulator will be disabled during the shutdown procedure. if REGULATOR_GPIO_VDD_SYS config REGULATOR_GPIO_VDD_SYS_PIN int "GPIO pin (port L)" default 8 range 0 31 help Select the GPIO pin used to control the VDD-SYS supply. endif config REGULATOR_SY8106A bool "Silergy SY8106A voltage regulator" depends on I2C endmenu crust-0.5/drivers/regulator/Makefile000066400000000000000000000005111414152222500176330ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += regulator.o obj-$(CONFIG_REGULATOR_AXP803) += axp20x.o axp803.o obj-$(CONFIG_REGULATOR_AXP805) += axp20x.o axp805.o obj-$(CONFIG_REGULATOR_GPIO) += gpio.o obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a.o crust-0.5/drivers/regulator/axp20x.c000066400000000000000000000040521414152222500174650ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include "axp20x.h" #define GPIO_LDO_MASK 0x7 #define GPIO_LDO_ON 0x3 #define GPIO_LDO_OFF 0x4 static int axp20x_regulator_get_state(const struct regulator_handle *handle, bool *enabled) { const struct axp20x_regulator *self = to_axp20x_regulator(handle->dev); uint8_t addr = self->info[handle->id].enable_register; uint8_t mask = self->info[handle->id].enable_mask; uint8_t val; int err; if ((err = regmap_read(self->map, addr, &val))) return err; /* * GPIO LDOs have a pin function, not an enable bit. Their * distinguishing feature is a mask containing more than one bit. */ if (mask == GPIO_LDO_MASK) *enabled = (val & mask) == GPIO_LDO_ON; else *enabled = (val & mask); return SUCCESS; } static int axp20x_regulator_set_state(const struct regulator_handle *handle, bool enabled) { const struct axp20x_regulator *self = to_axp20x_regulator(handle->dev); uint8_t addr = self->info[handle->id].enable_register; uint8_t mask = self->info[handle->id].enable_mask; uint8_t val; /* * GPIO LDOs have a pin function, not an enable bit. Their * distinguishing feature is a mask containing more than one bit. */ if (mask == GPIO_LDO_MASK) val = enabled ? GPIO_LDO_ON : GPIO_LDO_OFF; else val = enabled ? mask : 0; return regmap_update_bits(self->map, addr, mask, val); } static int axp20x_regulator_probe(const struct device *dev) { const struct axp20x_regulator *self = to_axp20x_regulator(dev); return regmap_user_probe(self->map); } static void axp20x_regulator_release(const struct device *dev) { const struct axp20x_regulator *self = to_axp20x_regulator(dev); regmap_user_release(self->map); } const struct regulator_driver axp20x_regulator_driver = { .drv = { .probe = axp20x_regulator_probe, .release = axp20x_regulator_release, }, .ops = { .get_state = axp20x_regulator_get_state, .set_state = axp20x_regulator_set_state, }, }; crust-0.5/drivers/regulator/axp20x.h000066400000000000000000000012121414152222500174650ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef AXP20X_PRIVATE_H #define AXP20X_PRIVATE_H #include #include #include #include "regulator.h" struct axp20x_regulator_info { uint8_t enable_register; uint8_t enable_mask; uint8_t value_register; uint8_t value_mask; }; extern const struct regulator_driver axp20x_regulator_driver; static inline const struct axp20x_regulator * to_axp20x_regulator(const struct device *dev) { return container_of(dev, const struct axp20x_regulator, dev); } #endif /* AXP20X_PRIVATE_H */ crust-0.5/drivers/regulator/axp803.c000066400000000000000000000077641414152222500174030ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include "axp20x.h" #define OUTPUT_POWER_CONTROL1 0x10 #define OUTPUT_POWER_CONTROL2 0x12 #define OUTPUT_POWER_CONTROL3 0x13 static const struct axp20x_regulator_info axp803_regulators[] = { [AXP803_REGL_DCDC1] = { .enable_register = OUTPUT_POWER_CONTROL1, .enable_mask = BIT(0), .value_register = 0x20, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_DCDC2] = { .enable_register = OUTPUT_POWER_CONTROL1, .enable_mask = BIT(1), .value_register = 0x21, .value_mask = GENMASK(6, 0), }, [AXP803_REGL_DCDC3] = { .enable_register = OUTPUT_POWER_CONTROL1, .enable_mask = BIT(2), .value_register = 0x22, .value_mask = GENMASK(6, 0), }, [AXP803_REGL_DCDC4] = { .enable_register = OUTPUT_POWER_CONTROL1, .enable_mask = BIT(3), .value_register = 0x23, .value_mask = GENMASK(6, 0), }, [AXP803_REGL_DCDC5] = { .enable_register = OUTPUT_POWER_CONTROL1, .enable_mask = BIT(4), .value_register = 0x24, .value_mask = GENMASK(6, 0), }, [AXP803_REGL_DCDC6] = { .enable_register = OUTPUT_POWER_CONTROL1, .enable_mask = BIT(5), .value_register = 0x25, .value_mask = GENMASK(6, 0), }, [AXP803_REGL_DC1SW] = { .enable_register = OUTPUT_POWER_CONTROL2, .enable_mask = BIT(7), .value_register = 0x20, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_ALDO1] = { .enable_register = OUTPUT_POWER_CONTROL3, .enable_mask = BIT(5), .value_register = 0x28, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_ALDO2] = { .enable_register = OUTPUT_POWER_CONTROL3, .enable_mask = BIT(6), .value_register = 0x29, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_ALDO3] = { .enable_register = OUTPUT_POWER_CONTROL3, .enable_mask = BIT(7), .value_register = 0x2a, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_DLDO1] = { .enable_register = OUTPUT_POWER_CONTROL2, .enable_mask = BIT(3), .value_register = 0x15, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_DLDO2] = { .enable_register = OUTPUT_POWER_CONTROL2, .enable_mask = BIT(4), .value_register = 0x16, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_DLDO3] = { .enable_register = OUTPUT_POWER_CONTROL2, .enable_mask = BIT(5), .value_register = 0x17, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_DLDO4] = { .enable_register = OUTPUT_POWER_CONTROL2, .enable_mask = BIT(6), .value_register = 0x18, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_ELDO1] = { .enable_register = OUTPUT_POWER_CONTROL2, .enable_mask = BIT(0), .value_register = 0x19, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_ELDO2] = { .enable_register = OUTPUT_POWER_CONTROL2, .enable_mask = BIT(1), .value_register = 0x1a, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_ELDO3] = { .enable_register = OUTPUT_POWER_CONTROL2, .enable_mask = BIT(2), .value_register = 0x1b, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_FLDO1] = { .enable_register = OUTPUT_POWER_CONTROL3, .enable_mask = BIT(2), .value_register = 0x1c, .value_mask = GENMASK(3, 0), }, [AXP803_REGL_FLDO2] = { .enable_register = OUTPUT_POWER_CONTROL3, .enable_mask = BIT(3), .value_register = 0x1d, .value_mask = GENMASK(3, 0), }, [AXP803_REGL_GPIO0] = { .enable_register = 0x90, .enable_mask = GENMASK(2, 0), .value_register = 0x91, .value_mask = GENMASK(4, 0), }, [AXP803_REGL_GPIO1] = { .enable_register = 0x92, .enable_mask = GENMASK(2, 0), .value_register = 0x93, .value_mask = GENMASK(4, 0), }, }; const struct axp20x_regulator axp803_regulator = { .dev = { .name = "axp803-regulator", .drv = &axp20x_regulator_driver.drv, .state = DEVICE_STATE_INIT, }, .map = &axp20x.map, .info = axp803_regulators, }; crust-0.5/drivers/regulator/axp805.c000066400000000000000000000061541414152222500173750ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include "axp20x.h" #define POWER_ONOFF_CTRL_REG1 0x10 #define POWER_ONOFF_CTRL_REG2 0x11 static const struct axp20x_regulator_info axp805_regulators[] = { [AXP805_REGL_DCDCA] = { .enable_register = POWER_ONOFF_CTRL_REG1, .enable_mask = BIT(0), .value_register = 0x12, .value_mask = GENMASK(6, 0), }, [AXP805_REGL_DCDCB] = { .enable_register = POWER_ONOFF_CTRL_REG1, .enable_mask = BIT(1), .value_register = 0x13, .value_mask = GENMASK(4, 0), }, [AXP805_REGL_DCDCC] = { .enable_register = POWER_ONOFF_CTRL_REG1, .enable_mask = BIT(2), .value_register = 0x14, .value_mask = GENMASK(6, 0), }, [AXP805_REGL_DCDCD] = { .enable_register = POWER_ONOFF_CTRL_REG1, .enable_mask = BIT(3), .value_register = 0x15, .value_mask = GENMASK(5, 0), }, [AXP805_REGL_DCDCE] = { .enable_register = POWER_ONOFF_CTRL_REG1, .enable_mask = BIT(4), .value_register = 0x16, .value_mask = GENMASK(4, 0), }, [AXP805_REGL_ALDO1] = { .enable_register = POWER_ONOFF_CTRL_REG1, .enable_mask = BIT(5), .value_register = 0x17, .value_mask = GENMASK(4, 0), }, [AXP805_REGL_ALDO2] = { .enable_register = POWER_ONOFF_CTRL_REG1, .enable_mask = BIT(6), .value_register = 0x18, .value_mask = GENMASK(4, 0), }, [AXP805_REGL_ALDO3] = { .enable_register = POWER_ONOFF_CTRL_REG1, .enable_mask = BIT(7), .value_register = 0x19, .value_mask = GENMASK(4, 0), }, [AXP805_REGL_BLDO1] = { .enable_register = POWER_ONOFF_CTRL_REG2, .enable_mask = BIT(0), .value_register = 0x20, .value_mask = GENMASK(3, 0), }, [AXP805_REGL_BLDO2] = { .enable_register = POWER_ONOFF_CTRL_REG2, .enable_mask = BIT(1), .value_register = 0x21, .value_mask = GENMASK(3, 0), }, [AXP805_REGL_BLDO3] = { .enable_register = POWER_ONOFF_CTRL_REG2, .enable_mask = BIT(2), .value_register = 0x22, .value_mask = GENMASK(3, 0), }, [AXP805_REGL_BLDO4] = { .enable_register = POWER_ONOFF_CTRL_REG2, .enable_mask = BIT(3), .value_register = 0x23, .value_mask = GENMASK(3, 0), }, [AXP805_REGL_CLDO1] = { .enable_register = POWER_ONOFF_CTRL_REG2, .enable_mask = BIT(4), .value_register = 0x24, .value_mask = GENMASK(4, 0), }, [AXP805_REGL_CLDO2] = { .enable_register = POWER_ONOFF_CTRL_REG2, .enable_mask = BIT(5), .value_register = 0x25, .value_mask = GENMASK(4, 0), }, [AXP805_REGL_CLDO3] = { .enable_register = POWER_ONOFF_CTRL_REG2, .enable_mask = BIT(6), .value_register = 0x26, .value_mask = GENMASK(4, 0), }, [AXP805_REGL_DCSW] = { .enable_register = POWER_ONOFF_CTRL_REG2, .enable_mask = BIT(7), }, }; const struct axp20x_regulator axp805_regulator = { .dev = { .name = "axp805-regulator", .drv = &axp20x_regulator_driver.drv, .state = DEVICE_STATE_INIT, }, .map = &axp20x.map, .info = axp805_regulators, }; crust-0.5/drivers/regulator/gpio.c000066400000000000000000000052651414152222500173100ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include "regulator.h" static inline const struct gpio_regulator * to_gpio_regulator(const struct device *dev) { return container_of(dev, const struct gpio_regulator, dev); } static int gpio_regulator_get_state(const struct regulator_handle *handle, bool *enabled) { const struct gpio_regulator *self = to_gpio_regulator(handle->dev); return gpio_get_value(&self->pin, enabled); } static int gpio_regulator_set_state(const struct regulator_handle *handle, bool enabled) { const struct gpio_regulator *self = to_gpio_regulator(handle->dev); return gpio_set_value(&self->pin, enabled); } static int gpio_regulator_probe(const struct device *dev) { const struct gpio_regulator *self = to_gpio_regulator(dev); return gpio_get(&self->pin); } static void gpio_regulator_release(const struct device *dev) { const struct gpio_regulator *self = to_gpio_regulator(dev); gpio_put(&self->pin); } static const struct regulator_driver gpio_regulator_driver = { .drv = { .probe = gpio_regulator_probe, .release = gpio_regulator_release, }, .ops = { .get_state = gpio_regulator_get_state, .set_state = gpio_regulator_set_state, }, }; #if CONFIG(REGULATOR_GPIO_CPU) const struct gpio_regulator gpio_cpu_regulator = { .dev = { .name = "vdd-cpux", .drv = &gpio_regulator_driver.drv, .state = DEVICE_STATE_INIT, }, .pin = { .dev = &r_pio.dev, .id = CONFIG_REGULATOR_GPIO_CPU_PIN, .drive = DRIVE_10mA, .mode = MODE_OUTPUT, .pull = PULL_NONE, }, }; #endif #if CONFIG(REGULATOR_GPIO_DRAM) const struct gpio_regulator gpio_dram_regulator = { .dev = { .name = "vcc-dram", .drv = &gpio_regulator_driver.drv, .state = DEVICE_STATE_INIT, }, .pin = { .dev = &r_pio.dev, .id = CONFIG_REGULATOR_GPIO_DRAM_PIN, .drive = DRIVE_10mA, .mode = MODE_OUTPUT, .pull = PULL_NONE, }, }; #endif #if CONFIG(REGULATOR_GPIO_VCC_PLL) const struct gpio_regulator gpio_vcc_pll_regulator = { .dev = { .name = "vcc-pll", .drv = &gpio_regulator_driver.drv, .state = DEVICE_STATE_INIT, }, .pin = { .dev = &r_pio.dev, .id = CONFIG_REGULATOR_GPIO_VCC_PLL_PIN, .drive = DRIVE_10mA, .mode = MODE_OUTPUT, .pull = PULL_NONE, }, }; #endif #if CONFIG(REGULATOR_GPIO_VDD_SYS) const struct gpio_regulator gpio_vdd_sys_regulator = { .dev = { .name = "vdd-sys", .drv = &gpio_regulator_driver.drv, .state = DEVICE_STATE_INIT, }, .pin = { .dev = &r_pio.dev, .id = CONFIG_REGULATOR_GPIO_VDD_SYS_PIN, .drive = DRIVE_10mA, .mode = MODE_OUTPUT, .pull = PULL_NONE, }, }; #endif crust-0.5/drivers/regulator/regulator.c000066400000000000000000000024001414152222500203420ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include "regulator.h" /** * Get the ops for the regulator controller device. */ static inline const struct regulator_driver_ops * regulator_ops_for(const struct device *dev) { const struct regulator_driver *drv = container_of(dev->drv, const struct regulator_driver, drv); return &drv->ops; } static int regulator_set_state(const struct regulator_handle *handle, bool enable) { int err; if ((err = device_get(handle->dev))) return err; err = regulator_ops_for(handle->dev)->set_state(handle, enable); device_put(handle->dev); return err; } int regulator_disable(const struct regulator_handle *handle) { return regulator_set_state(handle, false); } int regulator_enable(const struct regulator_handle *handle) { return regulator_set_state(handle, true); } int regulator_get_state(const struct regulator_handle *handle, bool *enabled) { int err; if ((err = device_get(handle->dev))) return err; err = regulator_ops_for(handle->dev)->get_state(handle, enabled); device_put(handle->dev); return err; } crust-0.5/drivers/regulator/regulator.h000066400000000000000000000010431414152222500203510ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef REGULATOR_PRIVATE_H #define REGULATOR_PRIVATE_H #include #include #include struct regulator_driver_ops { int (*get_state)(const struct regulator_handle *handle, bool *enabled); int (*set_state)(const struct regulator_handle *handle, bool enable); }; struct regulator_driver { struct driver drv; struct regulator_driver_ops ops; }; #endif /* REGULATOR_PRIVATE_H */ crust-0.5/drivers/regulator/sy8106a.c000066400000000000000000000025761414152222500174670ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include "regulator.h" #define VOUT_SEL_REG 0x01 #define VOUT_COM_REG 0x02 #define SYS_STATUS_REG 0x06 static int sy8106a_get_state(const struct regulator_handle *handle, bool *enabled) { const struct regmap_device *self = to_regmap_device(handle->dev); uint8_t val; int err; if ((err = regmap_read(&self->map, VOUT_COM_REG, &val))) return err; *enabled = !(val & BIT(0)); return SUCCESS; } static int sy8106a_set_state(const struct regulator_handle *handle, bool enabled) { const struct regmap_device *self = to_regmap_device(handle->dev); int err; if ((err = regmap_update_bits(&self->map, VOUT_COM_REG, BIT(0), !enabled))) return err; return SUCCESS; } static const struct regulator_driver sy8106a_driver = { .drv = { .probe = regmap_device_probe, .release = regmap_device_release, }, .ops = { .get_state = sy8106a_get_state, .set_state = sy8106a_set_state, }, }; const struct regmap_device sy8106a = { .dev = { .name = "sy8106a", .drv = &sy8106a_driver.drv, .state = DEVICE_STATE_INIT, }, .map = { .dev = &r_i2c.dev, .id = SY8106A_I2C_ADDRESS, }, }; crust-0.5/drivers/serial/000077500000000000000000000000001414152222500154515ustar00rootroot00000000000000crust-0.5/drivers/serial/Kconfig000066400000000000000000000024141414152222500167550ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # config SERIAL bool "Serial input/output support" default y help Use a serial port (UART) for error logs and debugging. Note that depending on the port and options selected, this may interfere with other users of that serial port. If you are unsure, use the default port and baud rate. For maximum power savings, select the R_UART device or disable this option entirely. if SERIAL choice bool "Device" default SERIAL_DEV_UART0 config SERIAL_DEV_UART0 bool "UART0" config SERIAL_DEV_UART1 bool "UART1" config SERIAL_DEV_UART2 bool "UART2" config SERIAL_DEV_UART3 bool "UART3" config SERIAL_DEV_UART4 bool "UART4" depends on HAVE_UART4 config SERIAL_DEV_R_UART bool "R_UART" endchoice config SERIAL_BAUD int "Baud" if !SERIAL_DEV_UART0 range 0 1500000 default 0 if SERIAL_DEV_UART0 default 9600 if SERIAL_DEV_R_UART default 115200 help If a positive number is entered, the firmware will set up the serial port with that baud rate before using it. If zero is entered, the firmware will assume the serial port is already configured and keep the existing baud rate. Use this option if the port is shared with other users. endif crust-0.5/drivers/serial/Makefile000066400000000000000000000005741414152222500171170ustar00rootroot00000000000000# # Copyright © 2019-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += serial.o uart.o obj-$(CONFIG_PLATFORM_A83T) += sun8i-a83t-uart.o obj-$(CONFIG_PLATFORM_H3) += sun8i-h3-uart.o obj-$(CONFIG_PLATFORM_H6) += sun50i-h6-uart.o obj-$(CONFIG_SOC_A64) += sun50i-a64-uart.o obj-$(CONFIG_SOC_H5) += sun8i-h3-uart.o crust-0.5/drivers/serial/serial.c000066400000000000000000000016221414152222500170750ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include "uart.h" char serial_getc(void) { if (!mmio_get_32(uart.regs + UART_LSR, UART_LSR_DR)) return 0; return mmio_read_32(uart.regs + UART_RBR); } void serial_putc(char c) { if (c == '\n') serial_putc('\r'); mmio_poll_32(uart.regs + UART_LSR, UART_LSR_THRE); mmio_write_32(uart.regs + UART_THR, c); } void serial_puts(const char *s) { char c; while ((c = *s++)) serial_putc(c); } void serial_init(void) { device_get(&uart.dev); } bool serial_ready(void) { bool active = device_active(&uart.dev); /* * If the UART is shared with other users, its clock may have been * gated. Ensure the clock is running before accessing the device. */ if (active) clock_enable(&uart.clock); return active || CONFIG_SERIAL_BAUD == 0; } crust-0.5/drivers/serial/sun50i-a64-uart.c000066400000000000000000000066621414152222500203130ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include "uart.h" #if CONFIG(SERIAL_DEV_UART0) const struct simple_device uart = { .dev = { .name = "uart0", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART0 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(1, 8), /* PB8 */ .drive = DRIVE_10mA, .mode = 4, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(1, 9), /* PB9 */ .drive = DRIVE_10mA, .mode = 4, .pull = PULL_UP, }, }, .regs = DEV_UART0, }; #elif CONFIG(SERIAL_DEV_UART1) const struct simple_device uart = { .dev = { .name = "uart1", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART1 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(6, 6), /* PG6 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(6, 7), /* PG7 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_UART1, }; #elif CONFIG(SERIAL_DEV_UART2) const struct simple_device uart = { .dev = { .name = "uart2", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART2 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(1, 0), /* PB0 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(1, 1), /* PB1 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_UART2, }; #elif CONFIG(SERIAL_DEV_UART3) const struct simple_device uart = { .dev = { .name = "uart3", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART3 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(7, 4), /* PH4 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(7, 5), /* PH5 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_UART3, }; #elif CONFIG(SERIAL_DEV_UART4) const struct simple_device uart = { .dev = { .name = "uart4", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART4 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(3, 2), /* PD2 */ .drive = DRIVE_10mA, .mode = 3, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(3, 3), /* PD3 */ .drive = DRIVE_10mA, .mode = 3, .pull = PULL_UP, }, }, .regs = DEV_UART4, }; #elif CONFIG(SERIAL_DEV_R_UART) const struct simple_device uart = { .dev = { .name = "r_uart", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &r_ccu.dev, .id = CLK_BUS_R_UART }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 2), /* PL2 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 3), /* PL3 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_R_UART, }; #endif crust-0.5/drivers/serial/sun50i-h6-uart.c000066400000000000000000000056311414152222500202310ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include "uart.h" #if CONFIG(SERIAL_DEV_UART0) const struct simple_device uart = { .dev = { .name = "uart0", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART0 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(7, 0), /* PH0 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(7, 1), /* PH1 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_UART0, }; #elif CONFIG(SERIAL_DEV_UART1) const struct simple_device uart = { .dev = { .name = "uart1", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART1 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(6, 6), /* PG6 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(6, 7), /* PG7 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_UART1, }; #elif CONFIG(SERIAL_DEV_UART2) const struct simple_device uart = { .dev = { .name = "uart2", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART2 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(3, 19), /* PD19 */ .drive = DRIVE_10mA, .mode = 4, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(3, 20), /* PD20 */ .drive = DRIVE_10mA, .mode = 4, .pull = PULL_UP, }, }, .regs = DEV_UART2, }; #elif CONFIG(SERIAL_DEV_UART3) const struct simple_device uart = { .dev = { .name = "uart3", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART3 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(3, 23), /* PD23 */ .drive = DRIVE_10mA, .mode = 4, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(3, 24), /* PD24 */ .drive = DRIVE_10mA, .mode = 4, .pull = PULL_UP, }, }, .regs = DEV_UART3, }; #elif CONFIG(SERIAL_DEV_R_UART) const struct simple_device uart = { .dev = { .name = "r_uart", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &r_ccu.dev, .id = CLK_BUS_R_UART }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 2), /* PL2 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 3), /* PL3 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_R_UART, }; #endif crust-0.5/drivers/serial/sun8i-a83t-uart.c000066400000000000000000000066741414152222500204260ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include "uart.h" #if CONFIG(SERIAL_DEV_UART0) const struct simple_device uart = { .dev = { .name = "uart0", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART0 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(1, 9), /* PB9 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(1, 10), /* PB10 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_UART0, }; #elif CONFIG(SERIAL_DEV_UART1) const struct simple_device uart = { .dev = { .name = "uart1", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART1 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(6, 6), /* PG6 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(6, 7), /* PG7 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_UART1, }; #elif CONFIG(SERIAL_DEV_UART2) const struct simple_device uart = { .dev = { .name = "uart2", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART2 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(1, 0), /* PB0 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(1, 1), /* PB1 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_UART2, }; #elif CONFIG(SERIAL_DEV_UART3) const struct simple_device uart = { .dev = { .name = "uart3", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART3 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(6, 10), /* PG10 */ .drive = DRIVE_10mA, .mode = 3, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(6, 11), /* PG11 */ .drive = DRIVE_10mA, .mode = 3, .pull = PULL_UP, }, }, .regs = DEV_UART3, }; #elif CONFIG(SERIAL_DEV_UART4) const struct simple_device uart = { .dev = { .name = "uart4", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART4 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(4, 10), /* PE10 */ .drive = DRIVE_10mA, .mode = 3, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(4, 11), /* PE11 */ .drive = DRIVE_10mA, .mode = 3, .pull = PULL_UP, }, }, .regs = DEV_UART4, }; #elif CONFIG(SERIAL_DEV_R_UART) const struct simple_device uart = { .dev = { .name = "r_uart", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &r_ccu.dev, .id = CLK_BUS_R_UART }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 2), /* PL2 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 3), /* PL3 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_R_UART, }; #endif crust-0.5/drivers/serial/sun8i-h3-uart.c000066400000000000000000000056251414152222500201540ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include "uart.h" #if CONFIG(SERIAL_DEV_UART0) const struct simple_device uart = { .dev = { .name = "uart0", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART0 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(0, 4), /* PA4 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(0, 5), /* PA5 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_UART0, }; #elif CONFIG(SERIAL_DEV_UART1) const struct simple_device uart = { .dev = { .name = "uart1", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART1 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(6, 6), /* PG6 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(6, 7), /* PG7 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_UART1, }; #elif CONFIG(SERIAL_DEV_UART2) const struct simple_device uart = { .dev = { .name = "uart2", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART2 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(0, 0), /* PA0 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(0, 1), /* PA1 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_UART2, }; #elif CONFIG(SERIAL_DEV_UART3) const struct simple_device uart = { .dev = { .name = "uart3", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &ccu.dev, .id = CLK_BUS_UART3 }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(0, 13), /* PA13 */ .drive = DRIVE_10mA, .mode = 3, .pull = PULL_NONE, }, { .dev = &pio.dev, .id = SUNXI_GPIO_PIN(0, 14), /* PA14 */ .drive = DRIVE_10mA, .mode = 3, .pull = PULL_UP, }, }, .regs = DEV_UART3, }; #elif CONFIG(SERIAL_DEV_R_UART) const struct simple_device uart = { .dev = { .name = "r_uart", .drv = &uart_driver, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &r_ccu.dev, .id = CLK_BUS_R_UART }, .pins = SIMPLE_DEVICE_PINS_INIT { { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 2), /* PL2 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_NONE, }, { .dev = &r_pio.dev, .id = SUNXI_GPIO_PIN(0, 3), /* PL3 */ .drive = DRIVE_10mA, .mode = 2, .pull = PULL_UP, }, }, .regs = DEV_R_UART, }; #endif crust-0.5/drivers/serial/uart.c000066400000000000000000000020071414152222500165670ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include "uart.h" static int uart_probe(const struct device *dev) { const struct simple_device *self = to_simple_device(dev); int err; if ((err = simple_device_probe(dev))) return err; if (CONFIG_SERIAL_BAUD) { uint32_t rate = clock_get_rate(&self->clock); uint32_t divisor = udiv_round(rate, 16 * CONFIG_SERIAL_BAUD); uintptr_t regs = self->regs; /* Set the clock divisor. */ mmio_write_32(regs + UART_LCR, UART_LCR_DLAB); mmio_write_32(regs + UART_DLH, divisor >> 8); mmio_write_32(regs + UART_DLL, divisor); /* Set the UART to 8 data bits, no parity, 1 stop bit. */ mmio_write_32(regs + UART_LCR, UART_LCR_DLS8); /* Enable the FIFOs. */ mmio_write_32(regs + UART_FCR, UART_FCR_FIFOE); } return SUCCESS; } const struct driver uart_driver = { .probe = uart_probe, .release = simple_device_release, }; crust-0.5/drivers/serial/uart.h000066400000000000000000000011761414152222500166020ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef UART_PRIVATE_H #define UART_PRIVATE_H #include #include enum { UART_RBR = 0x0000, UART_THR = 0x0000, UART_DLL = 0x0000, UART_DLH = 0x0004, UART_FCR = 0x0008, UART_LCR = 0x000c, UART_LSR = 0x0014, }; enum { UART_FCR_FIFOE = BIT(0), }; enum { UART_LCR_DLAB = BIT(7), UART_LCR_DLS8 = GENMASK(1, 0), }; enum { UART_LSR_DR = BIT(0), UART_LSR_THRE = BIT(5), }; extern const struct driver uart_driver; extern const struct simple_device uart; #endif /* UART_PRIVATE_H */ crust-0.5/drivers/watchdog/000077500000000000000000000000001414152222500157725ustar00rootroot00000000000000crust-0.5/drivers/watchdog/Makefile000066400000000000000000000002341414152222500174310ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # obj-y += watchdog.o obj-y += sunxi-twd.o crust-0.5/drivers/watchdog/sunxi-twd.c000066400000000000000000000047421414152222500201070ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include "watchdog.h" #define TWD_STATUS_REG 0x00 #define TWD_CTRL_REG 0x10 #define TWD_RESTART_REG 0x14 #define TWD_LOW_CNT_REG 0x20 #define TWD_INTV_REG 0x30 #define TWD_RESTART_KEY (0xD14 << 16) #define TWD_TIMEOUT (5 * REFCLK_HZ) /* 5 seconds */ static void sunxi_twd_restart(const struct device *dev) { const struct simple_device *self = to_simple_device(dev); mmio_write_32(self->regs + TWD_RESTART_REG, TWD_RESTART_KEY | BIT(0)); } static void sunxi_twd_set_timeout(const struct device *dev, uint32_t timeout) { const struct simple_device *self = to_simple_device(dev); mmio_write_32(self->regs + TWD_INTV_REG, timeout); } static int sunxi_twd_probe(const struct device *dev) { const struct simple_device *self = to_simple_device(dev); uintptr_t regs = self->regs; int err; if ((err = simple_device_probe(dev))) return err; /* Clear the watchdog configuration. */ mmio_write_32(regs + TWD_CTRL_REG, BIT(0)); mmio_pollz_32(regs + TWD_CTRL_REG, BIT(0)); /* Set counter clock source to OSC24M. */ mmio_set_32(regs + TWD_CTRL_REG, BIT(31)); /* Program a conservative default timeout. */ mmio_write_32(regs + TWD_INTV_REG, TWD_TIMEOUT); /* Update the comparator to (counter + timeout). */ sunxi_twd_restart(dev); /* Start the watchdog counter; enable system reset. */ mmio_clrset_32(regs + TWD_CTRL_REG, BIT(1), BIT(9)); return SUCCESS; } static void sunxi_twd_release(const struct device *dev) { const struct simple_device *self = to_simple_device(dev); /* Disable system reset; stop the watchdog counter. */ mmio_clrset_32(self->regs + TWD_CTRL_REG, BIT(9), BIT(1)); simple_device_release(dev); } static const struct watchdog_driver sunxi_twd_driver = { .drv = { .probe = sunxi_twd_probe, .release = sunxi_twd_release, }, .ops = { .restart = sunxi_twd_restart, .set_timeout = sunxi_twd_set_timeout, }, }; const struct simple_device r_twd = { .dev = { .name = "r_twd", .drv = &sunxi_twd_driver.drv, .state = DEVICE_STATE_INIT, }, .clock = { .dev = &r_ccu.dev, .id = CLK_BUS_R_TWD }, .regs = DEV_R_TWD, }; uint32_t r_twd_counter_read(void) { assert(device_active(&r_twd.dev)); return mmio_read_32(DEV_R_TWD + TWD_LOW_CNT_REG); } crust-0.5/drivers/watchdog/watchdog.c000066400000000000000000000013161414152222500177370ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include "watchdog.h" /** * Get the ops for the watchdog controller device. */ static inline const struct watchdog_driver_ops * watchdog_ops_for(const struct device *dev) { const struct watchdog_driver *drv = container_of(dev->drv, const struct watchdog_driver, drv); return &drv->ops; } void watchdog_restart(const struct device *dev) { watchdog_ops_for(dev)->restart(dev); } void watchdog_set_timeout(const struct device *dev, uint32_t timeout) { watchdog_ops_for(dev)->set_timeout(dev, timeout); } crust-0.5/drivers/watchdog/watchdog.h000066400000000000000000000007451414152222500177510ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef WATCHDOG_PRIVATE_H #define WATCHDOG_PRIVATE_H #include #include struct watchdog_driver_ops { void (*restart)(const struct device *dev); void (*set_timeout)(const struct device *dev, uint32_t timeout); }; struct watchdog_driver { struct driver drv; struct watchdog_driver_ops ops; }; #endif /* WATCHDOG_PRIVATE_H */ crust-0.5/include/000077500000000000000000000000001414152222500141375ustar00rootroot00000000000000crust-0.5/include/common/000077500000000000000000000000001414152222500154275ustar00rootroot00000000000000crust-0.5/include/common/debug.h000066400000000000000000000036311414152222500166710ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COMMON_DEBUG_H #define COMMON_DEBUG_H #include #include #define LOG_STRING_ERROR "\x01" #define LOG_STRING_WARNING "\x02" #define LOG_STRING_INFO "\x03" #define LOG_STRING_DEBUG "\x04" #if CONFIG(ASSERT) #if CONFIG(ASSERT_VERBOSE) #define assert(e) ((void)((e) || (error("Assertion failed: %s (%s:%d)", #e, \ __FILE__, __LINE__), trap(), 0))) #else #define assert(e) ((void)((e) || (trap(), 0))) #endif #else #define assert(e) ((void)0) #endif enum { LOG_LEVEL_ERROR, LOG_LEVEL_WARNING, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVELS }; void hexdump(uintptr_t addr, uint32_t bytes); void log(const char *fmt, ...) ATTRIBUTE(format(printf, 1, 2)); #define panic(...) (error(__VA_ARGS__), trap()) #define error(...) log(LOG_STRING_ERROR __VA_ARGS__) #define warn(...) log(LOG_STRING_WARNING __VA_ARGS__) #define info(...) log(LOG_STRING_INFO __VA_ARGS__) #if CONFIG(DEBUG_LOG) #define debug(...) log(LOG_STRING_DEBUG __VA_ARGS__) #else #define debug(...) ((void)0) #endif #if CONFIG(DEBUG_MONITOR) void debug_monitor(void); #else static inline void debug_monitor(void) { } #endif #if CONFIG(DEBUG_PRINT_BATTERY) void debug_print_battery(void); #else static inline void debug_print_battery(void) { } #endif #if CONFIG(DEBUG_PRINT_LATENCY) void debug_print_latency(uint8_t current_state); #else static inline void debug_print_latency(uint8_t current_state UNUSED) { } #endif #if CONFIG(DEBUG_PRINT_SPRS) void debug_print_sprs(void); #else static inline void debug_print_sprs(void) { } #endif #if CONFIG(DEBUG_VERIFY_DRAM) void dram_save_checksum(void); void dram_verify_checksum(void); #else static inline void dram_save_checksum(void) { } static inline void dram_verify_checksum(void) { } #endif #endif /* COMMON_DEBUG_H */ crust-0.5/include/common/delay.h000066400000000000000000000006031414152222500166750ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COMMON_DELAY_H #define COMMON_DELAY_H #include /** * Spin (do nothing) for at least the given number of microseconds. * * @param useconds The number of microseconds to delay for. */ void udelay(uint32_t useconds); #endif /* COMMON_DELAY_H */ crust-0.5/include/common/device.h000066400000000000000000000044711414152222500170450ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COMMON_DEVICE_H #define COMMON_DEVICE_H #include #include /** * Default initializer for the device state pointer. * * The state pointer must be initialized for all devices. If additional mutable * state is needed, wrap struct device_state intrusively. Otherwise, use this * macro. */ #define DEVICE_STATE_INIT &(struct device_state) { 0 } struct device_state; struct driver; struct device { /** A unique name for this device. */ const char *name; /** The driver for this device. */ const struct driver *drv; /** Mutable state for this device. */ struct device_state *state; }; struct device_state { /** Reference count for this device. */ uint8_t refcount; }; struct driver { /** A function called to detect and initialize new devices. */ int (*probe)(const struct device *dev); /** A function called to uninitialize devices and free resources. */ void (*release)(const struct device *dev); }; /** * Determine if a device is active (if it has any outstanding references). * * @param dev A device. * @return The state of the device. */ bool device_active(const struct device *dev); /** * Get a reference to a device. * * If this is the first reference to a device, that device's driver will be * initialized. Otherwise, this function only updates the reference count. * * The device will remain running as long as the reference is held (that is, * until calling device_put()). * * If this function returns an error, do not call device_put(). * * @param dev A device. * * @return Zero on success; an error code on failure. */ int device_get(const struct device *dev); /** * Get a reference to a device. * * @return A reference to the device that was acquired, or a NULL pointer. */ const struct device *device_get_or_null(const struct device *dev); /** * Release a reference to a device. * * @param dev A reference to a device. */ void device_put(const struct device *dev); /** * Implementation of the device probe function that does nothing. */ int dummy_probe(const struct device *dev); /** * Implementation of the device release function that does nothing. */ void dummy_release(const struct device *dev); #endif /* COMMON_DEVICE_H */ crust-0.5/include/common/regulator_list.h000066400000000000000000000011701414152222500206360ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COMMON_REGULATOR_LIST_H #define COMMON_REGULATOR_LIST_H #include /** * The regulator supplying VDD-CPUX. */ extern const struct regulator_handle cpu_supply; /** * The regulator supplying VCC-DRAM. */ extern const struct regulator_handle dram_supply; /** * The regulator supplying VCC-PLL. */ extern const struct regulator_handle vcc_pll_supply; /** * The regulator supplying VDD-SYS. */ extern const struct regulator_handle vdd_sys_supply; #endif /* COMMON_REGULATOR_LIST_H */ crust-0.5/include/common/scpi.h000066400000000000000000000031701414152222500165370ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COMMON_SCPI_H #define COMMON_SCPI_H #include #include #include /** * The SCPI implementation has two clients: Linux and ATF. CPU/cluster and * system power state change requests are required to go through PSCI, so ATF * can coordinate with the secure OS (if present). These requests must only be * allowed if they arrive on the "secure" channel. */ enum { SCPI_CLIENT_EL3 = 0, /**< Client 0: Secure EL3 (ATF). */ SCPI_CLIENT_EL2 = 1, /**< Client 1: Nonsec EL2 (Linux). */ SCPI_CLIENTS, }; /** * Create and send an SCPI message. This is used for commands initiated by the * SCP. * * @param mailbox The mailbox used to send the message. * @param client The client that should receive the message. * @param command The command number to include in the message. */ void scpi_create_message(const struct device *mailbox, uint8_t client, uint8_t command); /** * Handle a received SCPI command. This function parses the message, performs * any requested actions, and possibly generates a reply message. * * @param client The client from which the message was received. * @param mem The shared memory area containing the request and reply. * @return If the reply message is valid and should be sent to the client. */ bool scpi_handle_cmd(uint8_t client, struct scpi_mem *mem); /** * Handle incoming SCPI commands and send replies as buffers become available. */ void scpi_poll(const struct device *mailbox); #endif /* COMMON_SCPI_H */ crust-0.5/include/common/simple_device.h000066400000000000000000000032111414152222500204050ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COMMON_SIMPLE_DEVICE_H #define COMMON_SIMPLE_DEVICE_H #include #include #include #include #define SIMPLE_DEVICE_PINS 2 #define SIMPLE_DEVICE_PINS_INIT (const struct gpio_handle[SIMPLE_DEVICE_PINS]) struct simple_device { struct device dev; struct clock_handle clock; const struct gpio_handle *pins; uintptr_t regs; }; /** * Downcast a pointer to a simple device. */ static inline const struct simple_device * to_simple_device(const struct device *dev) { return container_of(dev, const struct simple_device, dev); } /** * Probe a simple device, which uses a clock and 0-2 GPIO pins. * * This function can be used to implement a device's .probe hook. */ int simple_device_probe(const struct device *dev); /** * Release a simple device. * * This function can be used to implement a device's .release hook. */ void simple_device_release(const struct device *dev); /** * Adjust the refcount of a device to match the state Linux left it in. * * If a device has no references, but its clock is running, acquire a * persistent reference to prevent the device and its resources from being * released. * * Similarly, if the only reference to a device is one previously acquired by * this function, and the device's clock is not running, release the reference. * * This function must not be called if other references to the device are held. */ void simple_device_sync(const struct simple_device *sdev); #endif /* COMMON_SIMPLE_DEVICE_H */ crust-0.5/include/common/steps.h000066400000000000000000000022521414152222500167370ustar00rootroot00000000000000/* * Copyright © 2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COMMON_STEPS_H #define COMMON_STEPS_H #include #include #define STEP_KIND(n) (VERSION_MAJOR << 12 | VERSION_MINOR << 8 | (n) << 4) enum { STEP_NONE, STEP_SUSPEND = STEP_KIND(0), STEP_SUSPEND_CORE, STEP_SUSPEND_CLUSTER, STEP_SUSPEND_CSS, STEP_SUSPEND_DEVICES, STEP_SUSPEND_DRAM, STEP_SUSPEND_CCU, STEP_SUSPEND_PRCM, STEP_SUSPEND_PMIC, STEP_SUSPEND_REGULATORS, STEP_SUSPEND_COMPLETE, STEP_RESUME = STEP_KIND(1), STEP_RESUME_PMIC, STEP_RESUME_REGULATORS, STEP_RESUME_PRCM, STEP_RESUME_CCU, STEP_RESUME_DRAM, STEP_RESUME_DRAM_CHECKSUM, STEP_RESUME_DEVICES, STEP_RESUME_CSS, STEP_RESUME_CLUSTER, STEP_RESUME_CORE, STEP_RESUME_COMPLETE, }; #if CONFIG(DEBUG_RECORD_STEPS) void record_exception(uint32_t exception, uint32_t pc); void record_step(uint32_t step); void report_last_step(void); #else static inline void record_exception(uint32_t exception UNUSED, uint32_t pc UNUSED) { } static inline void record_step(uint32_t step UNUSED) { } static inline void report_last_step(void) { } #endif #endif /* COMMON_STEPS_H */ crust-0.5/include/common/system.h000066400000000000000000000027721414152222500171340ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COMMON_SYSTEM_H #define COMMON_SYSTEM_H #include enum { SD_NONE, /**< Perform no extra suspend actions. */ SD_OSC24M, /**< Power down the high-speed oscillator and PLLs. */ SD_AVCC, /**< Gate the AVCC power domain. */ SD_VDD_SYS, /**< Gate and reset the VDD_SYS power domain. */ }; /** * Perform system state management. * * This is the main loop of the firmware, which never returns. * * @param exception Exception information provided by startup assembly code. */ noreturn void system_state_machine(uint32_t exception); /** * Reboot the board, including the SoC and external peripherals. * * Must only be called while the system is awake. */ void system_reboot(void); /** * Reset the SoC, including all CPUs and internal peripherals. * * Must only be called while the system is awake. */ void system_reset(void); /** * Shut down the SoC, and turn off all possible power domains. * * Must only be called while the system is awake. */ void system_shutdown(void); /** * Suspend the SoC, and turn off all non-wakeup power domains. * * Must only be called while the system is awake. */ void system_suspend(void); /** * Wake the system. * * Transition to a state where the rich OS is awake and running, by resetting * the SoC or the entire board if necessary. * * May be called at any time. */ void system_wake(void); #endif /* COMMON_SYSTEM_H */ crust-0.5/include/common/timeout.h000066400000000000000000000014471414152222500172740ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COMMON_TIMEOUT_H #define COMMON_TIMEOUT_H #include #include #define USEC_PER_MSEC 1000U #define USEC_PER_SEC 1000000U /** * Check if a timeout has expired. * * @param timeout The timeout. * @return Whether or not the timeout has expired. */ bool timeout_expired(uint32_t timeout); /** * Set a timeout for some point in the near future. * * Timeouts must not be set for greater than approximately one minute. * * @param useconds The delay in microseconds before the timeout expires. * @return An opaque number that can be passed to timeout_expired(). */ uint32_t timeout_set(uint32_t useconds); #endif /* COMMON_TIMEOUT_H */ crust-0.5/include/drivers/000077500000000000000000000000001414152222500156155ustar00rootroot00000000000000crust-0.5/include/drivers/cec.h000066400000000000000000000015751414152222500165300ustar00rootroot00000000000000/* * Copyright © 2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CEC_H #define DRIVERS_CEC_H #include #include #include #if CONFIG(CEC) /** * Acquire a reference to the CEC receiver device suitable for polling. * * The reference must be released before resuming the rich OS. * * @return A reference to the CEC receiver device. */ const struct device *cec_get(void); /** * Check the CEC receiver for a wakeup condition. * * @param dev A reference to the CEC receiver device. * @return Nonzero if the system should wake up, else zero. */ uint32_t cec_poll(const struct device *dev); #else static inline const struct device * cec_get(void) { return NULL; } static inline uint32_t cec_poll(const struct device *dev UNUSED) { return 0; } #endif #endif /* DRIVERS_CEC_H */ crust-0.5/include/drivers/cec/000077500000000000000000000000001414152222500163475ustar00rootroot00000000000000crust-0.5/include/drivers/cec/dw-hdmi-cec.h000066400000000000000000000007511414152222500206040ustar00rootroot00000000000000/* * Copyright © 2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CEC_DW_HDMI_CEC_H #define DRIVERS_CEC_DW_HDMI_CEC_H #include #include #include struct dw_hdmi_cec { struct device dev; struct clock_handle bus_clock; uintptr_t regs; }; extern const struct dw_hdmi_cec hdmi_cec; uint32_t dw_hdmi_cec_poll(const struct device *dev); #endif /* DRIVERS_CEC_DW_HDMI_CEC_H */ crust-0.5/include/drivers/cir.h000066400000000000000000000016021414152222500165420ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CIR_H #define DRIVERS_CIR_H #include #include #include #if CONFIG(CIR) /** * Acquire a reference to the CIR receiver device suitable for polling. * * The reference must be released before resuming the rich OS. * * @return A reference to the CIR receiver device. */ const struct device *cir_get(void); /** * Check the CIR receiver for a wakeup condition. * * @param dev A reference to the CIR receiver device. * @return Nonzero if the system should wake up, else zero. */ uint32_t cir_poll(const struct device *dev); #else static inline const struct device * cir_get(void) { return NULL; } static inline uint32_t cir_poll(const struct device *dev UNUSED) { return 0; } #endif #endif /* DRIVERS_CIR_H */ crust-0.5/include/drivers/cir/000077500000000000000000000000001414152222500163725ustar00rootroot00000000000000crust-0.5/include/drivers/cir/sunxi-cir.h000066400000000000000000000010561414152222500204660ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CIR_SUNXI_CIR_H #define DRIVERS_CIR_SUNXI_CIR_H #include #include #include #include struct sunxi_cir { struct device dev; struct clock_handle bus_clock; struct clock_handle mod_clock; struct gpio_handle pin; uintptr_t regs; }; extern const struct sunxi_cir r_cir_rx; uint32_t sunxi_cir_poll(const struct device *dev); #endif /* DRIVERS_CIR_SUNXI_CIR_H */ crust-0.5/include/drivers/clock.h000066400000000000000000000047221414152222500170660ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CLOCK_H #define DRIVERS_CLOCK_H #include #include enum { CLOCK_STATE_DISABLED, CLOCK_STATE_GATED, CLOCK_STATE_ENABLED, }; struct clock_handle { const struct device *dev; /**< The clock controller device. */ uint8_t id; /**< The device-specific clock identifier. */ }; /** * Determine if a clock is active. * * A clock is active if it has any outstanding references. The state of the * hardware gate, if any, is not considered. * * @param clock A reference to a clock. * @return The state of the clock. */ bool clock_active(const struct clock_handle *clock); /** * Disable a clock. * * If the clock does not have a gate, this may have no effect on the hardware. * The clock's parent, if any, will be left as it is. * * @param clock A reference to a clock. */ void clock_disable(const struct clock_handle *clock); /** * Enable a clock. * * If the clock does not have a gate, this may have no effect on the hardware. * The clock's parent, if any, will also be enabled. * * @param clock A reference to a clock. */ void clock_enable(const struct clock_handle *clock); /** * Get a reference to a clock and its controller device, and enable the clock. * * If the clock does not have a gate, this may have no effect on the hardware. * The clock's parent, if any, will also be enabled. * * @param clock A handle specifying the clock. */ void clock_get(const struct clock_handle *clock); /** * Get the current rate of a clock, as calculated from the hardware. * * This function returns the frequency the clock runs at when ungated, * regardless of if the clock is currently gated. * * @param clock A reference to a clock. * @return The clock frequency in Hz. */ uint32_t clock_get_rate(const struct clock_handle *clock); /** * Get the current state of a clock, as determined from the hardware. * * @param clock A reference to a clock. * @return One of the enumerated clock states. */ uint32_t clock_get_state(const struct clock_handle *clock); /** * Release a reference to a clock and its controller device. * * If this is the last reference to a clock, that clock (and its ancestors * recursively, if they have no other consumers) will be disabled. * * @param clock A reference to a clock. */ void clock_put(const struct clock_handle *clock); #endif /* DRIVERS_CLOCK_H */ crust-0.5/include/drivers/clock/000077500000000000000000000000001414152222500167105ustar00rootroot00000000000000crust-0.5/include/drivers/clock/ccu.h000066400000000000000000000020121414152222500176260ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CLOCK_CCU_H #define DRIVERS_CLOCK_CCU_H #include #include #if CONFIG(PLATFORM_A64) #include #include #elif CONFIG(PLATFORM_A83T) #include #include #elif CONFIG(PLATFORM_H3) #include #include #elif CONFIG(PLATFORM_H6) #include #include #endif struct ccu { struct device dev; const struct ccu_clock *clocks; uintptr_t regs; }; extern const struct ccu ccu; extern const struct ccu r_ccu; void ccu_suspend(void); void ccu_suspend_cluster(uint32_t cluster); void ccu_resume(void); void ccu_resume_cluster(uint32_t cluster); void ccu_init(void); void r_ccu_suspend(uint8_t depth); void r_ccu_resume(void); void r_ccu_init(void); #endif /* DRIVERS_CLOCK_CCU_H */ crust-0.5/include/drivers/clock/sun50i-a64-ccu.h000066400000000000000000000013061414152222500213440ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CLOCK_SUN50I_A64_CCU_H #define DRIVERS_CLOCK_SUN50I_A64_CCU_H enum { CLK_PLL_CPUX, CLK_PLL_DDR0, CLK_PLL_PERIPH0, #if CONFIG(SOC_A64) CLK_PLL_DDR1, #endif CLK_APB2, CLK_BUS_DRAM, CLK_BUS_MSGBOX, CLK_BUS_PIO, #if CONFIG(SERIAL_DEV_UART0) CLK_BUS_UART0, #elif CONFIG(SERIAL_DEV_UART1) CLK_BUS_UART1, #elif CONFIG(SERIAL_DEV_UART2) CLK_BUS_UART2, #elif CONFIG(SERIAL_DEV_UART3) CLK_BUS_UART3, #elif CONFIG(SERIAL_DEV_UART4) /* depends on SOC_A64 */ CLK_BUS_UART4, #endif CLK_DRAM, CLK_MBUS, SUN50I_A64_CCU_CLOCKS }; #endif /* DRIVERS_CLOCK_SUN50I_A64_CCU_H */ crust-0.5/include/drivers/clock/sun50i-h6-ccu.h000066400000000000000000000011011414152222500212600ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CLOCK_SUN50I_H6_CCU_H #define DRIVERS_CLOCK_SUN50I_H6_CCU_H enum { CLK_PLL_DDR0, CLK_PLL_PERIPH0, CLK_APB2, CLK_MBUS, CLK_BUS_MSGBOX, CLK_DRAM, CLK_BUS_DRAM, CLK_BUS_PIO, #if CONFIG(SERIAL_DEV_UART0) CLK_BUS_UART0, #elif CONFIG(SERIAL_DEV_UART1) CLK_BUS_UART1, #elif CONFIG(SERIAL_DEV_UART2) CLK_BUS_UART2, #elif CONFIG(SERIAL_DEV_UART3) CLK_BUS_UART3, #endif SUN50I_H6_CCU_CLOCKS }; #endif /* DRIVERS_CLOCK_SUN50I_H6_CCU_H */ crust-0.5/include/drivers/clock/sun50i-h6-r-ccu.h000066400000000000000000000010321414152222500215220ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CLOCK_SUN50I_H6_R_CCU_H #define DRIVERS_CLOCK_SUN50I_H6_R_CCU_H enum { CLK_OSC16M, CLK_OSC24M, CLK_OSC32K, CLK_AR100, CLK_R_AHB, CLK_R_APB1, CLK_R_APB2, CLK_BUS_R_PIO, CLK_BUS_R_TIMER, CLK_BUS_R_TWD, CLK_BUS_R_PWM, CLK_BUS_R_UART, CLK_BUS_R_I2C, CLK_BUS_R_RSB, CLK_BUS_R_CIR, CLK_BUS_R_W1, CLK_R_CIR, CLK_R_W1, SUN50I_H6_R_CCU_CLOCKS }; #endif /* DRIVERS_CLOCK_SUN50I_H6_R_CCU_H */ crust-0.5/include/drivers/clock/sun8i-a83t-ccu.h000066400000000000000000000011001414152222500214440ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CLOCK_SUN8I_A83T_CCU_H #define DRIVERS_CLOCK_SUN8I_A83T_CCU_H enum { CLK_PLL_PERIPH0, CLK_APB2, CLK_BUS_MSGBOX, CLK_BUS_PIO, #if CONFIG(SERIAL_DEV_UART0) CLK_BUS_UART0, #elif CONFIG(SERIAL_DEV_UART1) CLK_BUS_UART1, #elif CONFIG(SERIAL_DEV_UART2) CLK_BUS_UART2, #elif CONFIG(SERIAL_DEV_UART3) CLK_BUS_UART3, #elif CONFIG(SERIAL_DEV_UART4) CLK_BUS_UART4, #endif SUN8I_A83T_CCU_CLOCKS }; #endif /* DRIVERS_CLOCK_SUN8I_A83T_CCU_H */ crust-0.5/include/drivers/clock/sun8i-h3-ccu.h000066400000000000000000000011141414152222500212040ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CLOCK_SUN8I_H3_CCU_H #define DRIVERS_CLOCK_SUN8I_H3_CCU_H enum { CLK_PLL_CPUX, CLK_PLL_DDR0, CLK_PLL_PERIPH0, CLK_APB2, CLK_BUS_DRAM, CLK_BUS_MSGBOX, CLK_BUS_PIO, #if CONFIG(SERIAL_DEV_UART0) CLK_BUS_UART0, #elif CONFIG(SERIAL_DEV_UART1) CLK_BUS_UART1, #elif CONFIG(SERIAL_DEV_UART2) CLK_BUS_UART2, #elif CONFIG(SERIAL_DEV_UART3) CLK_BUS_UART3, #endif CLK_DRAM, CLK_MBUS, SUN8I_H3_CCU_CLOCKS }; #endif /* DRIVERS_CLOCK_SUN8I_H3_CCU_H */ crust-0.5/include/drivers/clock/sun8i-r-ccu.h000066400000000000000000000007541414152222500211440ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_CLOCK_SUN8I_R_CCU_H #define DRIVERS_CLOCK_SUN8I_R_CCU_H enum { CLK_OSC16M, CLK_OSC24M, CLK_OSC32K, CLK_AR100, CLK_AHB0, CLK_APB0, CLK_BUS_R_PIO, CLK_BUS_R_CIR, CLK_BUS_R_TIMER, #if CONFIG(HAVE_RSB) CLK_BUS_R_RSB, #endif CLK_BUS_R_UART, CLK_BUS_R_I2C, CLK_BUS_R_TWD, CLK_R_CIR, SUN8I_R_CCU_CLOCKS }; #endif /* DRIVERS_CLOCK_SUN8I_R_CCU_H */ crust-0.5/include/drivers/css.h000066400000000000000000000042671414152222500165670ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COMMON_CSS_H #define COMMON_CSS_H #include /** * Get the number of clusters in the compute subsystem. * * The number returned cannot be greater than 8. */ uint32_t css_get_cluster_count(void) ATTRIBUTE(const); /** * Get the state of a cluster and the cores it contains. * * The state of the cluster is returned in cluster_state. * * A bitmap representing the state of each core in the cluster is returned in * online_cores. A zero bit indicates that a core is completely off (it has no * execution context). Any other state is represented by a set bit. * * @param cluster The index of the cluster. * @param cluster_state Where to store the cluster state. * @param online_cores Where to store the bitmap of online cores. * @return An SCPI success or error status. */ int css_get_power_state(uint32_t cluster, uint32_t *cluster_state, uint32_t *online_cores); /** * Initialize the CSS driver, assuming the CSS is already running. Since the * firmware starts after the CSS, the driver may need to synchronize its state * with the actual state of the hardware. */ void css_init(void); /** * Set the state of a CPU core and its ancestor power domains. There are no * restrictions on the requested power states; the best available power state * will be computed for each power domain. * * @param cluster The index of the cluster. * @param core The index of the core within the cluster. * @param core_state The requested power state for the core. * @param cluster_state The requested power state for the core's cluster. * @param css_state The requested power state for the CSS. * @return An SCPI success or error status. */ int css_set_power_state(uint32_t cluster, uint32_t core, uint32_t core_state, uint32_t cluster_state, uint32_t css_state); /** * Resume execution on the most recently active core in the CSS. */ void css_resume(void); /** * Poll for CPUs that must wake up to handle pending IRQs. */ void css_poll(void); #endif /* COMMON_CSS_H */ crust-0.5/include/drivers/dram.h000066400000000000000000000006721414152222500167160ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_DRAM_H #define DRIVERS_DRAM_H /** * Initialize the DRAM controller driver. */ void dram_init(void); /** * Resume the DRAM controller and exit self-refresh. */ void dram_resume(void); /** * Enter self-refresh and suspend the DRAM controller. */ void dram_suspend(void); #endif /* DRIVERS_DRAM_H */ crust-0.5/include/drivers/gpio.h000066400000000000000000000032641414152222500167310ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_GPIO_H #define DRIVERS_GPIO_H #include #include #include struct gpio_handle { const struct device *dev; uint8_t id; uint8_t drive; uint8_t mode; uint8_t pull; }; /** * Get a reference to a GPIO pin and its controller device, and set up the pin. * * The pin's mode, drive strength, and pull-up or pull-down will be set based * on the values in the handle. * * @param gpio A handle specifying the GPIO pin. * @return Zero on success; an error code on failure. */ int gpio_get(const struct gpio_handle *gpio); /** * Get the value of a GPIO pin. * * If the pin mode is not "input", this may not get the actual hardware state. * * @param gpio A reference to a GPIO pin. * @param value The location to store the value read from the pin. * @return Zero on success; an error code on failure. */ int gpio_get_value(const struct gpio_handle *gpio, bool *value); /** * Release a reference to a GPIO pin and its controller device. * * If this is the last reference to a GPIO pin, that pin will be disabled. * * @param gpio A reference to a GPIO pin. */ void gpio_put(const struct gpio_handle *gpio); /** * Set the value of a GPIO pin. * * If the pin mode is not "output", this may have no effect on the hardware. * * @param gpio A reference to a GPIO pin. * @param value The value to set for the pin. * @return Zero on success; an error code on failure. */ int gpio_set_value(const struct gpio_handle *gpio, bool value); #endif /* DRIVERS_GPIO_H */ crust-0.5/include/drivers/gpio/000077500000000000000000000000001414152222500165535ustar00rootroot00000000000000crust-0.5/include/drivers/gpio/sunxi-gpio.h000066400000000000000000000011511414152222500210240ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_GPIO_SUNXI_GPIO_H #define DRIVERS_GPIO_SUNXI_GPIO_H #include #include #define SUNXI_GPIO_PIN(port, pin) (32 * (port) + (pin)) enum { DRIVE_10mA = 0, DRIVE_20mA = 1, DRIVE_30mA = 2, DRIVE_40mA = 3, }; enum { MODE_INPUT = 0, MODE_OUTPUT = 1, MODE_DISABLE = 7, }; enum { PULL_NONE = 0, PULL_UP = 1, PULL_DOWN = 2, }; extern const struct simple_device pio; extern const struct simple_device r_pio; #endif /* DRIVERS_GPIO_SUNXI_GPIO_H */ crust-0.5/include/drivers/irq.h000066400000000000000000000013321414152222500165600ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_IRQ_H #define DRIVERS_IRQ_H #include /** * Determine if any enabled IRQ requires AVCC in order to be received. * * @return Nonzero if some IRQ requires AVCC, else zero. */ uint32_t irq_needs_avcc(void); /** * Determine if any enabled IRQ requires VDD_SYS in order to be received. * * @return Nonzero if some IRQ requires VDD_SYS, else zero. */ uint32_t irq_needs_vdd_sys(void); /** * Get a bitmask of the IRQs that are both enabled and pending. * * @return Nonzero if some IRQ is enabled and pending, else zero. */ uint32_t irq_poll(void); #endif /* DRIVERS_IRQ_H */ crust-0.5/include/drivers/mfd/000077500000000000000000000000001414152222500163635ustar00rootroot00000000000000crust-0.5/include/drivers/mfd/axp20x.h000066400000000000000000000004241414152222500176560ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_MFD_AXP20X_H #define DRIVERS_MFD_AXP20X_H #include extern const struct regmap_device axp20x; #endif /* DRIVERS_MFD_AXP20X_H */ crust-0.5/include/drivers/msgbox.h000066400000000000000000000025461414152222500172740ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_MSGBOX_H #define DRIVERS_MSGBOX_H #include #include #include /** * Acknowledge a message received on a message box channel. * * @param dev The message box device. * @param chan The message box channel. */ void msgbox_ack_rx(const struct device *dev, uint8_t chan); /** * Check if the last transmission on a message box channel has completed, or if * it is still pending. A message is pending until the reception IRQ has been * cleared on the remote interface. * * @param dev The message box device. * @param chan The message box channel. */ bool msgbox_last_tx_done(const struct device *dev, uint8_t chan); /** * Receive a message via a message box channel. * * @param dev The message box device. * @param chan The channel to use within the message box. * @param message The message to receive. */ int msgbox_receive(const struct device *dev, uint8_t chan, uint32_t *message); /** * Send a message via a message box channel. * * @param dev The message box device. * @param chan The channel to use within the message box. * @param message The message to send. */ int msgbox_send(const struct device *dev, uint8_t chan, uint32_t message); #endif /* DRIVERS_MSGBOX_H */ crust-0.5/include/drivers/msgbox/000077500000000000000000000000001414152222500171145ustar00rootroot00000000000000crust-0.5/include/drivers/msgbox/sunxi-msgbox.h000066400000000000000000000006531414152222500217340ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_MSGBOX_SUNXI_MSGBOX_H #define DRIVERS_MSGBOX_SUNXI_MSGBOX_H #include #include /* The message box hardware provides 8 unidirectional channels. */ #define SUNXI_MSGBOX_CHANS 8 extern const struct simple_device msgbox; #endif /* DRIVERS_MSGBOX_SUNXI_MSGBOX_H */ crust-0.5/include/drivers/pmic.h000066400000000000000000000017051414152222500167210ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_PMIC_H #define DRIVERS_PMIC_H #include /** * Get a reference to an available PMIC device. * * @return A reference to a PMIC device. */ const struct device *pmic_get(void); /** * Initiate the PMIC reset process. * * @param dev The device containing the PMIC functionality. */ int pmic_reset(const struct device *dev); /** * Initiate the PMIC resume process. * * @param dev The device containing the PMIC functionality. */ int pmic_resume(const struct device *dev); /** * Initiate the PMIC shutdown process. * * @param dev The device containing the PMIC functionality. */ int pmic_shutdown(const struct device *dev); /** * Initiate the PMIC suspend process. * * @param dev The device containing the PMIC functionality. */ int pmic_suspend(const struct device *dev); #endif /* DRIVERS_PMIC_H */ crust-0.5/include/drivers/pmic/000077500000000000000000000000001414152222500165455ustar00rootroot00000000000000crust-0.5/include/drivers/pmic/axp20x.h000066400000000000000000000005411414152222500200400ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_PMIC_AXP20X_H #define DRIVERS_PMIC_AXP20X_H #include #include #include struct axp20x_pmic { struct device dev; const struct regmap *map; }; #endif /* DRIVERS_PMIC_AXP20X_H */ crust-0.5/include/drivers/pmic/axp803.h000066400000000000000000000004371414152222500177450ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_PMIC_AXP803_H #define DRIVERS_PMIC_AXP803_H #include extern const struct axp20x_pmic axp803_pmic; #endif /* DRIVERS_PMIC_AXP803_H */ crust-0.5/include/drivers/pmic/axp805.h000066400000000000000000000004371414152222500177470ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_PMIC_AXP805_H #define DRIVERS_PMIC_AXP805_H #include extern const struct axp20x_pmic axp805_pmic; #endif /* DRIVERS_PMIC_AXP805_H */ crust-0.5/include/drivers/regmap.h000066400000000000000000000104371414152222500172460ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_REGMAP_H #define DRIVERS_REGMAP_H #include #include #include struct regmap { const struct device *dev; uint32_t id; }; struct regmap_device { struct device dev; struct regmap map; }; /** * Downcast a pointer to a regmap device. */ static inline const struct regmap_device * to_regmap_device(const struct device *dev) { return container_of(dev, const struct regmap_device, dev); } /** * Get a reference to a regmap and its provider bus/device. * * This function will verify the existence of the device containing the regmap. * * This function may fail with: * EIO There was a problem communicating with the hardware. * ENODEV The device was not found on its provider bus. * * @param map A specifier for the bus/device providing the regmap. * @return Zero on success; an error code on failure. */ int regmap_get(const struct regmap *map); /** * Release a reference to a regmap. * * @param map A reference to a regmap. */ void regmap_put(const struct regmap *map); /** * Read a value from a regmap. * * This function may fail with: * EIO There was a problem communicating with the hardware. * * @param map A reference to the regmap. * @param reg The register to read. * @param val The location to save the value read from the register. * @return Zero on success; an error code on failure. */ int regmap_read(const struct regmap *map, uint8_t reg, uint8_t *val); /** * Write a value to a regmap. * * This function may fail with: * EIO There was a problem communicating with the hardware. * * @param map A reference to the regmap. * @param reg The register to write. * @param val The value to write to the register. * @return Zero on success; an error code on failure. */ int regmap_write(const struct regmap *map, uint8_t reg, uint8_t val); /** * Update a bitfield in a regmap register. * * This function may fail with: * EIO There was a problem communicating with the hardware. * * @param map A reference to the regmap. * @param reg The register to modify. * @param mask A mask of bits representing a bitfield. * @param val The new value of the bitfield. * @return Zero on success; an error code on failure. */ int regmap_update_bits(const struct regmap *map, uint8_t reg, uint8_t mask, uint8_t val); /** * Clear bits in a regmap register. * * This function may fail with: * EIO There was a problem communicating with the hardware. * * @param map A reference to the regmap. * @param reg The register to modify. * @param clr The mask of bits to clear in the register. * @return Zero on success; an error code on failure. */ static inline int regmap_clr_bits(const struct regmap *map, uint8_t reg, uint8_t clr) { return regmap_update_bits(map, reg, clr, 0); } /** * Set bits in a regmap register. * * This function may fail with: * EIO There was a problem communicating with the hardware. * * @param map A reference to the regmap. * @param reg The register to modify. * @param set The mask of bits to set in the register. * @return Zero on success; an error code on failure. */ static inline int regmap_set_bits(const struct regmap *map, uint8_t reg, uint8_t set) { return regmap_update_bits(map, reg, set, set); } /** * Probe a device that owns a regmap. * * This function can be used to implement a device's .probe hook. */ int regmap_device_probe(const struct device *dev); /** * Release a device that owns a regmap. * * This function can be used to implement a device's .release hook. */ void regmap_device_release(const struct device *dev); /** * Probe a device that uses a regmap owned by another device. * * This function assumes that the regmap's owner is a regmap_device. * * This function can be used to implement a device's .probe hook. */ int regmap_user_probe(const struct regmap *map); /** * Release a device that uses a regmap owned by another device. * * This function assumes that the regmap's owner is a regmap_device. * * This function can be used to implement a device's .release hook. */ void regmap_user_release(const struct regmap *map); #endif /* DRIVERS_REGMAP_H */ crust-0.5/include/drivers/regmap/000077500000000000000000000000001414152222500170705ustar00rootroot00000000000000crust-0.5/include/drivers/regmap/sun6i-i2c.h000066400000000000000000000005001414152222500207530ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_REGMAP_SUN6I_I2C_H #define DRIVERS_REGMAP_SUN6I_I2C_H #include #include extern const struct simple_device r_i2c; #endif /* DRIVERS_REGMAP_SUN6I_I2C_H */ crust-0.5/include/drivers/regmap/sunxi-rsb.h000066400000000000000000000005001414152222500211660ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_REGMAP_SUNXI_RSB_H #define DRIVERS_REGMAP_SUNXI_RSB_H #include #include extern const struct simple_device r_rsb; #endif /* DRIVERS_REGMAP_SUNXI_RSB_H */ crust-0.5/include/drivers/regulator.h000066400000000000000000000036011414152222500177720ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_REGULATOR_H #define DRIVERS_REGULATOR_H #include #include struct regulator_handle { const struct device *dev; /**< The regulator supplier device. */ uint8_t id; /**< The device-specific identifier. */ }; /** * Disable the output of a regulator. If the regulator does not have * output on/off control, this function may have no effect on the hardware. * * This function will acquire and release a reference to the supplier device. * * This function may fail with: * EIO There was a problem communicating with the hardware. * * @param handle A reference to a regulator and its supplier. * @return Zero on success; a defined error code on failure. */ int regulator_disable(const struct regulator_handle *handle); /** * Enable the output of a regulator. If the regulator does not have * output on/off control, this function may have no effect on the hardware. * * This function will acquire and release a reference to the supplier device. * * This function may fail with: * EIO There was a problem communicating with the hardware. * * @param handle A reference to a regulator and its supplier. * @return Zero on success; a defined error code on failure. */ int regulator_enable(const struct regulator_handle *handle); /** * Get the current state of a regulator, as determined from the hardware. * * This function may fail with: * EIO There was a problem communicating with the hardware. * * @param handle A reference to a regulator and its supplier. * @param enabled Pointer to where the state is stored. * @return Zero on success; a defined error code on failure. */ int regulator_get_state(const struct regulator_handle *handle, bool *enabled); #endif /* DRIVERS_REGULATOR_H */ crust-0.5/include/drivers/regulator/000077500000000000000000000000001414152222500176215ustar00rootroot00000000000000crust-0.5/include/drivers/regulator/axp20x.h000066400000000000000000000007031414152222500211140ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_REGULATOR_AXP20X_H #define DRIVERS_REGULATOR_AXP20X_H #include #include #include struct axp20x_regulator { struct device dev; const struct regmap *map; const struct axp20x_regulator_info *info; }; #endif /* DRIVERS_REGULATOR_AXP20X_H */ crust-0.5/include/drivers/regulator/axp803.h000066400000000000000000000014001414152222500210100ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_REGULATOR_AXP803_H #define DRIVERS_REGULATOR_AXP803_H #include enum { AXP803_REGL_DCDC1, AXP803_REGL_DCDC2, AXP803_REGL_DCDC3, AXP803_REGL_DCDC4, AXP803_REGL_DCDC5, AXP803_REGL_DCDC6, AXP803_REGL_DC1SW, AXP803_REGL_ALDO1, AXP803_REGL_ALDO2, AXP803_REGL_ALDO3, AXP803_REGL_DLDO1, AXP803_REGL_DLDO2, AXP803_REGL_DLDO3, AXP803_REGL_DLDO4, AXP803_REGL_ELDO1, AXP803_REGL_ELDO2, AXP803_REGL_ELDO3, AXP803_REGL_FLDO1, AXP803_REGL_FLDO2, AXP803_REGL_GPIO0, AXP803_REGL_GPIO1, AXP803_REGL_COUNT, }; extern const struct axp20x_regulator axp803_regulator; #endif /* DRIVERS_REGULATOR_AXP803_H */ crust-0.5/include/drivers/regulator/axp805.h000066400000000000000000000012331414152222500210160ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_REGULATOR_AXP805_H #define DRIVERS_REGULATOR_AXP805_H #include enum { AXP805_REGL_DCDCA, AXP805_REGL_DCDCB, AXP805_REGL_DCDCC, AXP805_REGL_DCDCD, AXP805_REGL_DCDCE, AXP805_REGL_ALDO1, AXP805_REGL_ALDO2, AXP805_REGL_ALDO3, AXP805_REGL_BLDO1, AXP805_REGL_BLDO2, AXP805_REGL_BLDO3, AXP805_REGL_BLDO4, AXP805_REGL_CLDO1, AXP805_REGL_CLDO2, AXP805_REGL_CLDO3, AXP805_REGL_DCSW, AXP805_REGL_COUNT, }; extern const struct axp20x_regulator axp805_regulator; #endif /* DRIVERS_REGULATOR_AXP805_H */ crust-0.5/include/drivers/regulator/gpio.h000066400000000000000000000013421414152222500207300ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_REGULATOR_GPIO_H #define DRIVERS_REGULATOR_GPIO_H #include #include struct gpio_regulator { struct device dev; struct gpio_handle pin; }; #if CONFIG(REGULATOR_GPIO_CPU) extern const struct gpio_regulator gpio_cpu_regulator; #endif #if CONFIG(REGULATOR_GPIO_DRAM) extern const struct gpio_regulator gpio_dram_regulator; #endif #if CONFIG(REGULATOR_GPIO_VCC_PLL) extern const struct gpio_regulator gpio_vcc_pll_regulator; #endif #if CONFIG(REGULATOR_GPIO_VDD_SYS) extern const struct gpio_regulator gpio_vdd_sys_regulator; #endif #endif /* DRIVERS_REGULATOR_GPIO_H */ crust-0.5/include/drivers/regulator/sy8106a.h000066400000000000000000000005431414152222500211070ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_REGULATOR_SY8106A_H #define DRIVERS_REGULATOR_SY8106A_H #include #include #define SY8106A_I2C_ADDRESS 0x65 extern const struct regmap_device sy8106a; #endif /* DRIVERS_REGULATOR_SY8106A_H */ crust-0.5/include/drivers/serial.h000066400000000000000000000017351414152222500172530ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_SERIAL_H #define DRIVERS_SERIAL_H #include #if CONFIG(SERIAL) /** * Read a character from the UART. * * @return The character read, or 0 if no character is available. */ char serial_getc(void); void serial_putc(char c); void serial_puts(const char *s); /** * Initialize the UART. */ void serial_init(void); /** * Verify that the UART is ready to use. * * This function must be called before performing any I/O. Other serial I/O * functions may only be called if this function returns true. */ bool serial_ready(void); #else static inline char serial_getc(void) { return 0; } static inline void serial_putc(char c UNUSED) { } static inline void serial_puts(const char *s UNUSED) { } static inline void serial_init(void) { } static inline bool serial_ready(void) { return false; } #endif #endif /* DRIVERS_SERIAL_H */ crust-0.5/include/drivers/watchdog.h000066400000000000000000000012161414152222500175660ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_WATCHDOG_H #define DRIVERS_WATCHDOG_H #include /** * Restart the watchdog. This must be called before the watchdog times out. * * @param dev The watchdog device. */ void watchdog_restart(const struct device *dev); /** * Set the watchdog timeout. It will take effect after the next restart. * * @param dev The watchdog device. * @param timeout The new watchdog timeout in clock cycles. */ void watchdog_set_timeout(const struct device *dev, uint32_t timeout); #endif /* DRIVERS_WATCHDOG_H */ crust-0.5/include/drivers/watchdog/000077500000000000000000000000001414152222500174155ustar00rootroot00000000000000crust-0.5/include/drivers/watchdog/sunxi-twd.h000066400000000000000000000010071414152222500215260ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef DRIVERS_WATCHDOG_SUNXI_TWD_H #define DRIVERS_WATCHDOG_SUNXI_TWD_H #include #include #include extern const struct simple_device r_twd; /** * Read the low 32 bits of the trusted watchdog counter. * * This counter increments at 24MHz while the watchdog is enabled. */ uint32_t r_twd_counter_read(void); #endif /* DRIVERS_WATCHDOG_SUNXI_TWD_H */ crust-0.5/include/lib/000077500000000000000000000000001414152222500147055ustar00rootroot00000000000000crust-0.5/include/lib/bitfield.h000066400000000000000000000032171414152222500166430ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef LIB_BITFIELD_H #define LIB_BITFIELD_H #include /** * Get the value in a bitfield. * * @param word A word containing a bitfield. * @param start The offset of the starting bit (LSB) of the bitfield. * @param width The width of the bitfield in bits. */ uint32_t bitfield_get(uint32_t word, uint32_t start, uint32_t width); /** * Set the value in a bitfield. * * @param word A word containing a bitfield. * @param start The offset of the starting bit (LSB) of the bitfield. * @param width The width of the bitfield in bits. * @param value The value to place in the bitfield. * @return The original word, with the value of the bitfield replaced. */ uint32_t bitfield_set(uint32_t word, uint32_t start, uint32_t width, uint32_t value); /** * Get the value in a bitfield accessed via MMIO. * * @param addr The address of a word containing a bitfield. * @param start The offset of the starting bit (LSB) of the bitfield. * @param width The width of the bitfield in bits. */ uint32_t mmio_get_bitfield_32(uintptr_t addr, uint32_t start, uint32_t width); /** * Set the value in a bitfield accessed via MMIO. * * @param addr The address of a word containing a bitfield. * @param start The offset of the starting bit (LSB) of the bitfield. * @param width The width of the bitfield in bits. * @param value The value to place in the bitfield. */ void mmio_set_bitfield_32(uintptr_t addr, uint32_t start, uint32_t width, uint32_t value); #endif /* LIB_BITFIELD_H */ crust-0.5/include/lib/bitmap.h000066400000000000000000000035221414152222500163340ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef LIB_BITMAP_H #define LIB_BITMAP_H #include #include #include #include #include /** * Calculate a bit index from a word offset (in bytes) and a bit offset * within that word. * * @param word The offset of the word from the beginning of the bitmap. * @param bit The offset (arithmetic shift) of the bit within the word. */ #define BITMAP_INDEX(word, bit) (CHAR_BIT * (word) + (bit)) /** * Extract the word offset (in bytes) from a bitmap index. * * @param index An index into a bitmap, in bits. */ #define BITMAP_WORD(index) ((index) / WORD_BIT * (WORD_BIT / CHAR_BIT)) /** * Extract the bit offset (arithmetic shift) from a bitmap index. * * @param index An index into a bitmap, in bits. */ #define BITMAP_BIT(index) ((index) % WORD_BIT) /** * Clear a bit in a bitmap. * * @param base The address of the start of the bitmap. * @param index The index into the bitmap (in bits) of the bit to clear. */ static inline void bitmap_clear(uintptr_t base, uint32_t index) { mmio_clr_32(base + BITMAP_WORD(index), BIT(BITMAP_BIT(index))); } /** * Get a bit in a bitmap. * * @param base The address of the start of the bitmap. * @param index The index into the bitmap (in bits) of the bit to get. */ static inline bool bitmap_get(uintptr_t base, uint32_t index) { return mmio_get_32(base + BITMAP_WORD(index), BIT(BITMAP_BIT(index))); } /** * Set a bit in a bitmap. * * @param base The address of the start of the bitmap. * @param index The index into the bitmap (in bits) of the bit to set. */ static inline void bitmap_set(uintptr_t base, uint32_t index) { mmio_set_32(base + BITMAP_WORD(index), BIT(BITMAP_BIT(index))); } #endif /* LIB_BITMAP_H */ crust-0.5/include/lib/byteswap.h000066400000000000000000000007731414152222500167230ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef LIB_BYTESWAP_H #define LIB_BYTESWAP_H #include static inline uint16_t bswap16(uint16_t n) { return ((n << 8) & 0xff00U) | \ ((n >> 8) & 0xffU); } static inline uint32_t bswap32(uint32_t n) { return ((n << 24) & 0xff000000U) | \ ((n << 8) & 0xff0000U) | \ ((n >> 8) & 0xff00U) | \ ((n >> 24) & 0xffU); } #endif /* LIB_BYTESWAP_H */ crust-0.5/include/lib/compiler.h000066400000000000000000000023311414152222500166670ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef LIB_COMPILER_H #define LIB_COMPILER_H /* Attributes */ #define ATTRIBUTE(...) __attribute__((__VA_ARGS__)) #define UNUSED __attribute__((unused)) #define WEAK __attribute__((weak)) /* Barriers */ #define barrier() asm volatile ("" : : : "memory") /* Builtins */ #define likely(e) __builtin_expect(!!(e), 1) #define unlikely(e) __builtin_expect(e, 0) #define unreachable() __builtin_unreachable() /* Keywords */ #define alignas _Alignas #define alignof _Alignof #define asm __asm__ #define fallthrough __attribute__((__fallthrough__)) #define noreturn _Noreturn #define static_assert _Static_assert /** * Calculate the size of a struct containing a flexible array member. * * @param type The name of the structure type. * @param member The name of the flexible array member. * @param elements The number of elements in the flexible array member. * @return The total size of the type. */ #define sizeof_struct(type, member, elements) \ (sizeof(type) + sizeof(*((type *)0)->member) * elements) #endif /* LIB_COMPILER_H */ crust-0.5/include/lib/division.h000066400000000000000000000012251414152222500167020ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef LIB_DIVISION_H #define LIB_DIVISION_H #include #define UDIV_ROUND(dividend, divisor) \ (((dividend) + (divisor) / 2) / (divisor)) /** * Perform correctly-rounded unsigned division. */ static inline uint32_t udiv_round(uint32_t dividend, uint32_t divisor) { return (dividend + divisor / 2) / divisor; } /** * Perform unsigned division. * * This function replaces the dividend with the quotient and returns the * remainder. */ uint32_t udivmod(uint32_t *dividend, uint32_t divisor); #endif /* LIB_DIVISION_H */ crust-0.5/include/lib/error.h000066400000000000000000000014271414152222500162130ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef LIB_ERROR_H #define LIB_ERROR_H #define SUCCESS 0 /**< The operation succeeded. */ #define EBUSY (-1) /**< Try again later. */ #define EEXIST (-2) /**< The object already exists. */ #define EINVAL (-3) /**< The argument to the function was invalid. */ #define EIO (-4) /**< Communication with the hardware failed. */ #define ENODEV (-5) /**< The device does not exist. */ #define ENOENT (-6) /**< The entry does not exist. */ #define ENOTSUP (-7) /**< The operation is not supported. */ #define EPERM (-8) /**< The operation is not permitted. */ #define ERANGE (-9) /**< The argument to the function was out of range. */ #endif /* LIB_ERROR_H */ crust-0.5/include/lib/intrusive.h000066400000000000000000000005151414152222500171070ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef LIB_INTRUSIVE_H #define LIB_INTRUSIVE_H #include #include #define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member))) #endif /* LIB_INTRUSIVE_H */ crust-0.5/include/lib/kconfig.h000066400000000000000000000043221414152222500164770ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef LIB_KCONFIG_H #define LIB_KCONFIG_H /* These macros define the option values that will match (here just 1), as * well as the action that will be taken when a match is found. They work by * expanding a single argument into two arguments in the case of a match. */ #define __kcm_self_1 "this is junk", __kca_self #define __kcm_init_1 "this is junk", __kca_init #define __kcm_true_1 "this is junk", __kca_true /* These macros define the result actions/expressions. Since __kc_indirect * consumes exactly two arguments, actions in the matching case have an extra * parameter (the one inserted by the match macros above). */ #define __kca_null(...) #define __kca_self(junk, ...) __VA_ARGS__ #define __kca_init(junk, ...) __VA_ARGS__, #define __kca_false(...) 0 #define __kca_true(junk, ...) 1 /* These macros split the comma-separated match expression (if present) into * two arguments, and then call the action named in the second argument. */ #define __kc_concat(a, b) a ## b #define __kc_do(junk, action, ...) action(__VA_ARGS__) #define __kc_expand(...) __kc_do(__VA_ARGS__) #define __kc_tokenize(...) __VA_ARGS__ /** * Expands to the remainder of the argument list if the option is enabled; * otherwise expands to the empty string. */ #define IF_ENABLED(option, ...) \ __kc_expand(__kc_concat(__kcm_self_, option), __kca_null, __VA_ARGS__) /** * Expands to the remainder of the argument list plus a comma if the option is * enabled; otherwise expands to the empty string. This is generally useful in * structure and array initializers. */ #define IF_ENABLED_INIT(option, ...) \ __kc_expand(__kc_concat(__kcm_init_, option), __kca_null, __VA_ARGS__) /** * Expands to the token 1 if the option is enabled; otherwise expands to 0. */ #define IS_ENABLED(option) \ __kc_expand(__kc_concat(__kcm_true_, option), __kca_false, junk) /** * Shorthand for IS_ENABLED(CONFIG_option). * * Expands to the token 1 if the option is enabled; otherwise expands to 0. */ #define CONFIG(option) \ IS_ENABLED(__kc_tokenize(CONFIG_ ## option)) #endif /* LIB_KCONFIG_H */ crust-0.5/include/lib/macros.S000066400000000000000000000013731414152222500163210ustar00rootroot00000000000000/* * Copyright © 2013-2017, ARM Limited and Contributors. All rights reserved. * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef MACROS_S #define MACROS_S /* This macro marks a global data declaration. */ .macro data name .section .data.\name, "aw", @progbits .global \name .type \name, %object .align 4 \name: .endm /* This macro marks the beginning of a function. */ .macro func name .section .text.\name, "ax", @progbits .global \name .type \name, %function .func \name .cfi_sections .debug_frame .cfi_startproc .align 4 \name: .endm /* This macro marks the end of a function. */ .macro endfunc name .cfi_endproc .endfunc .size \name, . - \name .endm #endif /* MACROS_S */ crust-0.5/include/lib/mmio.h000066400000000000000000000064071414152222500160260ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef LIB_MMIO_H #define LIB_MMIO_H #include /** * Clear bits in a 32-bit MMIO register. * * @param addr The address of the register. * @param clr The bits to clear. */ static inline void mmio_clr_32(uintptr_t addr, uint32_t clr) { volatile uint32_t *ptr = (void *)addr; *ptr &= ~clr; } /** * Clear and set bits in a 32-bit MMIO register. * * @param addr The address of the register. * @param clr The bits to clear. * @param set The bits to set. */ static inline void mmio_clrset_32(uintptr_t addr, uint32_t clr, uint32_t set) { volatile uint32_t *ptr = (void *)addr; *ptr = (*ptr & ~clr) | set; } /** * Get bits from a 32-bit MMIO register. * * @param addr The address of the register. * @param get The bits to get. */ static inline uint32_t mmio_get_32(uintptr_t addr, uint32_t get) { volatile uint32_t *ptr = (void *)addr; return *ptr & get; } /** * Spin until all bits in a mask are set in a 32-bit MMIO register. * * @param addr The address of the register. * @param mask The bits that must all be set. */ static inline void mmio_poll_32(uintptr_t addr, uint32_t mask) { volatile uint32_t *ptr = (void *)addr; while ((*ptr & mask) != mask) { /* Do nothing. */ } } /** * Spin until a value is present in a 32-bit MMIO register. * * @param addr The address of the register. * @param mask The bits to check. * @param val The expected value for those bits. */ static inline void mmio_polleq_32(uintptr_t addr, uint32_t mask, uint32_t val) { volatile uint32_t *ptr = (void *)addr; while ((*ptr & mask) != val) { /* Do nothing. */ } } /** * Spin until all bits in a mask are cleared in a 32-bit MMIO register. * * @param addr The address of the register. * @param mask The bits that must all be cleared. */ static inline void mmio_pollz_32(uintptr_t addr, uint32_t mask) { volatile uint32_t *ptr = (void *)addr; while ((*ptr & mask) != 0) { /* Do nothing. */ } } /** * Read a 32-bit MMIO register. * * @param addr The address of the register. * @return The value of the register. */ static inline uint32_t mmio_read_32(uintptr_t addr) { volatile uint32_t *ptr = (void *)addr; return *ptr; } /** * Read a 8-bit MMIO register. * * @param addr The address of the register. * @return The value of the register. */ static inline uint8_t mmio_read_8(uintptr_t addr) { volatile uint8_t *ptr = (void *)(addr ^ 3); return *ptr; } /** * Set bits in a 32-bit MMIO register. * * @param addr The address of the register. * @param set The bits to set. */ static inline void mmio_set_32(uintptr_t addr, uint32_t set) { volatile uint32_t *ptr = (void *)addr; *ptr |= set; } /** * Write a 32-bit MMIO register. * * @param addr The address of the register. * @param val The new value of the register. */ static inline void mmio_write_32(uintptr_t addr, uint32_t val) { volatile uint32_t *ptr = (void *)addr; *ptr = val; } /** * Write a 8-bit MMIO register. * * @param addr The address of the register. * @param val The new value of the register. */ static inline void mmio_write_8(uintptr_t addr, uint8_t val) { volatile uint8_t *ptr = (void *)(addr ^ 3); *ptr = val; } #endif /* LIB_MMIO_H */ crust-0.5/include/lib/scpi_protocol.h000066400000000000000000000132431414152222500177400ustar00rootroot00000000000000/* * Copyright © 2014-2017, ARM Limited and Contributors. All rights reserved. * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef COMMON_SCPI_PROTOCOL_H #define COMMON_SCPI_PROTOCOL_H #include #include /** The SCPI message header is specified to be 64 bits long. */ #define SCPI_HEADER_SIZE sizeof(uint64_t) /** The implementation-defined maximum size of an SCPI message. */ #define SCPI_MESSAGE_SIZE 0x100 /** The payload can use all but the 64 bits reserved for the header. */ #define SCPI_PAYLOAD_SIZE (SCPI_MESSAGE_SIZE - SCPI_HEADER_SIZE) /** The payload is represented as an array of 32-bit words. */ #define SCPI_PAYLOAD_WORDS (SCPI_PAYLOAD_SIZE / sizeof(uint32_t)) /** The SCP must identify itself as sender 0 for messages it initiates. */ #define SCPI_SENDER_SCP 0 /** * The virtual channel number is defined by the SCPI specification. * Theoretically, this allows reusing the same shared memory area for different * types of messages, if a different virtual channel number is given. In this * implementation, the virtual channel number is the contents of the mailbox * message. Messages with any other virtual channel are ignored. */ #define SCPI_VIRTUAL_CHANNEL BIT(0) /** * The set of standard SCPI commands, defined by the SCPI specification. */ enum { SCPI_CMD_SCP_READY = 0x01, /**< SCP ready. */ SCPI_CMD_GET_SCP_CAP = 0x02, /**< Get SCP capability. */ SCPI_CMD_SET_CSS_POWER = 0x03, /**< Set CSS power state. */ SCPI_CMD_GET_CSS_POWER = 0x04, /**< Get CSS power state. */ SCPI_CMD_SET_SYS_POWER = 0x05, /**< Set system power state. */ SCPI_CMD_SET_CPU_TIMER = 0x06, /**< Set CPU timer. */ SCPI_CMD_CANCEL_CPU_TIMER = 0x07, /**< Cancel CPU timer. */ SCPI_CMD_GET_DVFS_CAP = 0x08, /**< Get DVFS capability. */ SCPI_CMD_GET_DVFS_INFO = 0x09, /**< Get DVFS info. */ SCPI_CMD_SET_DVFS = 0x0a, /**< Set DVFS. */ SCPI_CMD_GET_DVFS = 0x0b, /**< Get DVFS. */ SCPI_CMD_GET_DVFS_STATS = 0x0c, /**< Get DVFS statistics. */ SCPI_CMD_GET_CLOCK_CAP = 0x0d, /**< Get clock capability. */ SCPI_CMD_GET_CLOCK_INFO = 0x0e, /**< Get clock info. */ SCPI_CMD_SET_CLOCK = 0x0f, /**< Set clock value. */ SCPI_CMD_GET_CLOCK = 0x10, /**< Get clock value. */ SCPI_CMD_GET_PSU_CAP = 0x11, /**< Get power supply capability. */ SCPI_CMD_GET_PSU_INFO = 0x12, /**< Get power supply info. */ SCPI_CMD_SET_PSU = 0x13, /**< Set power supply. */ SCPI_CMD_GET_PSU = 0x14, /**< Get power supply. */ SCPI_CMD_GET_SENSOR_CAP = 0x15, /**< Get sensor capability. */ SCPI_CMD_GET_SENSOR_INFO = 0x16, /**< Get sensor info. */ SCPI_CMD_GET_SENSOR = 0x17, /**< Get sensor value. */ SCPI_CMD_CFG_SENSOR_PERIOD = 0x18, /**< Configure sensor period. */ SCPI_CMD_CFG_SENSOR_BOUNDS = 0x19, /**< Configure sensor bounds. */ SCPI_CMD_ASYNC_SENSOR = 0x1a, /**< Asynchronous sensor value. */ SCPI_CMD_SET_DEV_POWER = 0x1b, /**< Set device power state. */ SCPI_CMD_GET_DEV_POWER = 0x1c, /**< Get device power state. */ }; /** * The set of possible status codes in an SCPI message, defined by the SCPI * specification. */ enum { SCPI_OK = 0, /**< Success. */ SCPI_E_PARAM = 1, /**< Invalid parameter(s). */ SCPI_E_ALIGN = 2, /**< Invalid alignment. */ SCPI_E_SIZE = 3, /**< Invalid size. */ SCPI_E_HANDLER = 4, /**< Invalid handler or callback. */ SCPI_E_ACCESS = 5, /**< Invalid access or permission denied. */ SCPI_E_RANGE = 6, /**< Value out of range. */ SCPI_E_TIMEOUT = 7, /**< Timeout has occurred. */ SCPI_E_NOMEM = 8, /**< Invalid memory area or pointer. */ SCPI_E_PWRSTATE = 9, /**< Invalid power state. */ SCPI_E_SUPPORT = 10, /**< Feature not supported or disabled. */ SCPI_E_DEVICE = 11, /**< Device error. */ SCPI_E_BUSY = 12, /**< Device is busy. */ SCPI_E_OS = 13, /**< RTOS error occurred. */ SCPI_E_DATA = 14, /**< Unexpected or invalid data received. */ SCPI_E_STATE = 15, /**< Invalid or unattainable state requested. */ }; /** * Possible CSS power domain states, as used in existing SCPI implementations. */ enum { SCPI_CSS_ON = 0, SCPI_CSS_RETENTION = 1, SCPI_CSS_OFF = 3, }; /** * Possible system power states, defined by the SCPI protocol specification. */ enum { SCPI_SYSTEM_SHUTDOWN = 0, SCPI_SYSTEM_REBOOT = 1, SCPI_SYSTEM_RESET = 2, }; /** * The memory structure representing an SCPI message, defined by the SCPI * specification. * * The structure below does not exactly follow the specification. The set ID * bit has been merged into the command number, because it is semantically * meaningless. And the reserved bits have been merged into the payload size. * Any command with reserved bits set will be interpreted as being "too large" * and will be rejected. * * The fields in the first 32-bit word are reversed from their order in the * specification to account for hardware byte swapping. The payload is * represented as an array of 32-bit words to reduce the amount of byte * swapping needed in command implementations. */ struct scpi_msg { #ifdef __or1k__ uint16_t size; uint8_t sender; uint8_t command; #else uint8_t command; uint8_t sender; uint16_t size; #endif uint32_t status; #ifdef __or1k__ uint32_t payload[SCPI_PAYLOAD_WORDS]; #else uint8_t payload[SCPI_PAYLOAD_SIZE]; #endif }; /** * The structure representing an SCPI shared memory area, defined by the SCPI * specification. */ struct scpi_mem { struct scpi_msg tx_msg; /**< Server to client message. */ struct scpi_msg rx_msg; /**< Client to server message. */ }; #endif /* COMMON_SCPI_PROTOCOL_H */ crust-0.5/include/lib/util.h000066400000000000000000000006731414152222500160410ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef LIB_UTIL_H #define LIB_UTIL_H #ifdef __ASSEMBLER__ #define U(n) (n) #else #define U(n) (n ## U) #endif #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #define BIT(n) (U(1) << (n)) #define GENMASK(h, l) ((U(0xffffffff) << (l)) & (U(0xffffffff) >> (31 - (h)))) #endif /* LIB_UTIL_H */ crust-0.5/include/stdlib/000077500000000000000000000000001414152222500154205ustar00rootroot00000000000000crust-0.5/include/stdlib/ctype.h000066400000000000000000000021261414152222500167160ustar00rootroot00000000000000/* * Copyright © 2005-2014 Rich Felker, et al. * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: MIT */ #ifndef STDLIB_CTYPE_H #define STDLIB_CTYPE_H #include #define isalpha(a) (((unsigned)(a) | 32) - 'a' < 26) #define isascii(a) ((unsigned)(a) < 128) #define isdigit(a) ((unsigned)(a) - '0' < 10) #define isgraph(a) ((unsigned)(a) - 0x21 < 0x5e) #define islower(a) ((unsigned)(a) - 'a' < 26) #define isprint(a) ((unsigned)(a) - 0x20 < 0x5f) #define isupper(a) ((unsigned)(a) - 'A' < 26) #define tolower(a) ((a) | 0x20) #define toupper(a) ((a) & 0x5f) static inline bool isalnum(char c) { return isalpha(c) || isdigit(c); } static inline bool isblank(char c) { return c == ' ' || c == '\t'; } static inline bool iscntrl(char c) { return (unsigned)c < 0x20 || c == 0x7f; } static inline bool ispunct(char c) { return isgraph(c) && !isalnum(c); } static inline bool isspace(char c) { return c == ' ' || (unsigned)c - '\t' < 5; } static inline bool isxdigit(char c) { return isdigit(c) || ((unsigned)c | 32) - 'a' < 6; } #endif /* STDLIB_CTYPE_H */ crust-0.5/include/stdlib/limits.h000066400000000000000000000015251414152222500170750ustar00rootroot00000000000000/* * Copyright © 2005-2014 Rich Felker, et al. * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef STDLIB_LIMITS_H #define STDLIB_LIMITS_H #define CHAR_BIT 8 #define CHAR_MAX 0xff #define CHAR_MIN 0 #define WORD_BIT 32 #define INT_MAX 0x7fffffff #define INT_MIN (-1 - 0x7fffffff) #define LONG_BIT 32 #define LONG_MAX 0x7fffffffL #define LONG_MIN (-0x7fffffffL - 1) #define LLONG_MAX 0x7fffffffffffffffLL #define LLONG_MIN (-0x7fffffffffffffffLL - 1) #define SCHAR_MAX 0x7f #define SCHAR_MIN (-1 - 0x7f) #define SHRT_MAX 0x7fff #define SHRT_MIN (-1 - 0x7fff) #define UCHAR_MAX 0xff #define UINT_MAX 0xffffffffU #define ULONG_MAX 0xffffffffUL #define ULLONG_MAX 0xffffffffffffffffULL #define USHRT_MAX 0xffff #endif /* STDLIB_LIMITS_H */ crust-0.5/include/stdlib/stdarg.h000066400000000000000000000007201414152222500170540ustar00rootroot00000000000000/* * Copyright © 2005-2014 Rich Felker, et al. * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef STDLIB_STDARG_H #define STDLIB_STDARG_H #define va_arg(v, l) __builtin_va_arg(v, l) #define va_copy(d, s) __builtin_va_copy(d, s) #define va_end(v) __builtin_va_end(v) #define va_start(v, l) __builtin_va_start(v, l) typedef __builtin_va_list va_list; #endif /* STDLIB_STDARG_H */ crust-0.5/include/stdlib/stdbool.h000066400000000000000000000003751414152222500172440ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef STDLIB_STDBOOL_H #define STDLIB_STDBOOL_H #define bool _Bool #define false 0 #define true 1 #endif /* STDLIB_STDBOOL_H */ crust-0.5/include/stdlib/stddef.h000066400000000000000000000006351414152222500170460ustar00rootroot00000000000000/* * Copyright © 2005-2014 Rich Felker, et al. * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef STDLIB_STDDEF_H #define STDLIB_STDDEF_H #define NULL ((void *)0) #define offsetof(type, member) __builtin_offsetof(type, member) typedef int ptrdiff_t; typedef unsigned int size_t; #endif /* STDLIB_STDDEF_H */ crust-0.5/include/stdlib/stdint.h000066400000000000000000000025221414152222500170770ustar00rootroot00000000000000/* * Copyright © 2005-2014 Rich Felker, et al. * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef STDLIB_STDINT_H #define STDLIB_STDINT_H #define INT8_MAX 0x7f #define INT8_MIN (-1 - 0x7f) #define INT16_MAX 0x7fff #define INT16_MIN (-1 - 0x7fff) #define INT32_MAX 0x7fffffff #define INT32_MIN (-1 - 0x7fffffff) #define INT64_MAX 0x7fffffffffffffff #define INT64_MIN (-1 - 0x7fffffffffffffff) #define INTMAX_MAX INT64_MAX #define INTMAX_MIN INT64_MIN #define INTPTR_MAX INT32_MAX #define INTPTR_MIN INT32_MIN #define PTRDIFF_MAX INT32_MAX #define PTRDIFF_MIN INT32_MIN #define SIZE_MAX UINT32_MAX #define UINT8_MAX 0xff #define UINT16_MAX 0xffff #define UINT32_MAX 0xffffffffU #define UINT64_MAX 0xffffffffffffffffU #define UINTMAX_MAX UINT64_MAX #define UINTPTR_MAX UINT32_MAX typedef signed char int8_t; typedef short int16_t; typedef int int32_t; typedef long long int64_t; typedef long long intmax_t; typedef int intptr_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned uint32_t; typedef unsigned long long uint64_t; typedef unsigned long long uintmax_t; typedef unsigned uintptr_t; #endif /* STDLIB_STDINT_H */ crust-0.5/lib/000077500000000000000000000000001414152222500132625ustar00rootroot00000000000000crust-0.5/lib/Makefile000066400000000000000000000002061414152222500147200ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # lib-y += bitfield.o crust-0.5/lib/bitfield.c000066400000000000000000000015011414152222500152050ustar00rootroot00000000000000/* * Copyright © 2019-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include uint32_t bitfield_get(uint32_t word, uint32_t start, uint32_t width) { return (word >> start) & (BIT(width) - 1); } uint32_t bitfield_set(uint32_t word, uint32_t start, uint32_t width, uint32_t value) { return word ^ ((value << start ^ word) & ((BIT(width) - 1) << start)); } uint32_t mmio_get_bitfield_32(uintptr_t addr, uint32_t start, uint32_t width) { return bitfield_get(mmio_read_32(addr), start, width); } void mmio_set_bitfield_32(uintptr_t addr, uint32_t start, uint32_t width, uint32_t value) { uint32_t word = mmio_read_32(addr); mmio_write_32(addr, bitfield_set(word, start, width, value)); } crust-0.5/platform/000077500000000000000000000000001414152222500143405ustar00rootroot00000000000000crust-0.5/platform/Kconfig000066400000000000000000000023501414152222500156430ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # config PLATFORM string default "a64" if PLATFORM_A64 default "a83t" if PLATFORM_A83T default "h3" if PLATFORM_H3 default "h6" if PLATFORM_H6 choice prompt "Platform selection" config PLATFORM_A64 bool "A64/H5" depends on ARCH_OR1K select HAVE_DRAM_SUSPEND config PLATFORM_A83T bool "A83T" depends on ARCH_OR1K select HAVE_RSB select HAVE_UART4 config PLATFORM_H3 bool "H3" depends on ARCH_OR1K select HAVE_DRAM_SUSPEND config PLATFORM_H6 bool "H6" depends on ARCH_OR1K select HAVE_DCXO select HAVE_DRAM_SUSPEND select HAVE_RSB endchoice if PLATFORM_A64 source "a64/Kconfig" endif config HAVE_DCXO bool help Select this option on platforms with a DCXO in the RTC domain in addition to X24M pads in the PLL domain. config HAVE_DRAM_SUSPEND bool help Select this option on platforms with the ability to suspend the DRAM controller. config HAVE_I2C bool default y help Select this option on platforms with an I2C controller. config HAVE_RSB bool help Select this option on platforms with an RSB controller. config HAVE_UART4 bool help Select this option on platforms with a UART4. crust-0.5/platform/a64/000077500000000000000000000000001414152222500147325ustar00rootroot00000000000000crust-0.5/platform/a64/Kconfig000066400000000000000000000003521414152222500162350ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # choice bool "SoC" config SOC_A64 bool "A64" select HAVE_RSB select HAVE_UART4 config SOC_H5 bool "H5" endchoice crust-0.5/platform/a64/include/000077500000000000000000000000001414152222500163555ustar00rootroot00000000000000crust-0.5/platform/a64/include/platform/000077500000000000000000000000001414152222500202015ustar00rootroot00000000000000crust-0.5/platform/a64/include/platform/cpucfg.h000066400000000000000000000110161414152222500216200ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_CPUCFG_H #define PLATFORM_CPUCFG_H #include #include #define C0_CTRL_REG0 (DEV_CPUCFG + 0x0000) #define C0_CTRL_REG0_SYSBARDISABLE BIT(31) #define C0_CTRL_REG0_BROADCASTINNER BIT(30) #define C0_CTRL_REG0_BROADCASTOUTER BIT(29) #define C0_CTRL_REG0_BROADCASTCACHEMAINT BIT(28) #define C0_CTRL_REG0_AA64nAA32(n) BIT(24 + (n)) #define C0_CTRL_REG0_AA64nAA32_MASK (0xf << 24) #define C0_CTRL_REG0_CP15SDISABLE(n) BIT(8 + (n)) #define C0_CTRL_REG0_L2RSTDISABLE BIT(4) #define C0_CTRL_REG1 (DEV_CPUCFG + 0x0004) #define C0_CTRL_REG1_ACINACTM BIT(0) #define CACHE_CFG_REG0 (DEV_CPUCFG + 0x0008) #define CACHE_CFG_REG0_L1SDT_DELAY (0x7 << 28) #define CACHE_CFG_REG0_L1TLB_DELAY (0x7 << 24) #define CACHE_CFG_REG0_BTAC_DELAY (0x7 << 20) #define CACHE_CFG_REG0_L1DY_DELAY (0x7 << 16) #define CACHE_CFG_REG0_L1DT_DELAY (0x7 << 12) #define CACHE_CFG_REG0_L1DD_DELAY (0x7 << 8) #define CACHE_CFG_REG0_L1IT_DELAY (0x7 << 4) #define CACHE_CFG_REG0_L1ID_DELAY (0x7 << 0) #define CACHE_CFG_REG1 (DEV_CPUCFG + 0x000c) #define CACHE_CFG_REG1_EMAW (0x7 << 24) #define CACHE_CFG_REG1_EMA (0x7 << 16) #define CACHE_CFG_REG1_L2V_DELAY (0x7 << 12) #define CACHE_CFG_REG1_L2T_DELAY (0x7 << 4) #define DBG_REG0 (DEV_CPUCFG + 0x0020) /* This bit is present; its meaning is guessed from the H6 manual */ #define DBG_REG0_DBGL1RSTDISABLE BIT(16) #define DBG_REG0_DBGPWRDUP(n) BIT(0 + (n)) #define DBG_REG0_DBGPWRDUP_MASK (0xf << 0) /* Name taken from H6 manual. */ #define C0_CTRL_REG2 (DEV_CPUCFG + 0x0028) #define C0_CTRL_REG2_EVENTI BIT(24) #define C0_CTRL_REG2_EXM_CLR(n) BIT(20 + (n)) #define C0_CTRL_REG2_CLREXMONREQ BIT(16) #define C0_CTRL_REG2_CRYPTODISABLE(n) BIT(12 + (n)) #define C0_CTRL_REG2_L2FLUSHREQ BIT(8) #define C0_CTRL_REG2_GICCDISABLE BIT(4) #define C0_CPU_STATUS_REG (DEV_CPUCFG + 0x0030) #define C0_CPU_STATUS_REG_SMPnAMP(n) BIT(24 + (n)) #define C0_CPU_STATUS_REG_STANDBYWFI(n) BIT(16 + (n)) #define C0_CPU_STATUS_REG_STANDBYWFI_MASK (0xf << 16) #define C0_CPU_STATUS_REG_STANDBYWFE(n) BIT(8 + (n)) #define C0_CPU_STATUS_REG_STANDBYWFE_MASK (0xf << 8) #define C0_CPU_STATUS_REG_STANDBYWFIL2 BIT(0) #define L2_STATUS_REG (DEV_CPUCFG + 0x003c) #define L2_STATUS_REG_L2FLUSHDONE BIT(10) #define L2_STATUS_REG_EVENTO BIT(9) #define L2_STATUS_REG_CLREXMONACK BIT(8) #define C0_RST_CTRL_REG (DEV_CPUCFG + 0x0080) #define C0_RST_CTRL_REG_nDDR_RST BIT(28) #define C0_RST_CTRL_REG_nSOC_DBG_RST BIT(24) #define C0_RST_CTRL_REG_nMBISTRESET BIT(20) #define C0_RST_CTRL_REG_nH_RST BIT(12) #define C0_RST_CTRL_REG_nL2RESET BIT(8) #define C0_RST_CTRL_REG_nCORERESET(n) BIT(0 + (n)) #define C0_RST_CTRL_REG_MASK (C0_RST_CTRL_REG_nDDR_RST | \ C0_RST_CTRL_REG_nSOC_DBG_RST | \ C0_RST_CTRL_REG_nMBISTRESET | \ C0_RST_CTRL_REG_nH_RST | \ C0_RST_CTRL_REG_nL2RESET) #define RVBA_LO_REG(n) (DEV_CPUCFG + 0x00a0 + 0x08 * (n)) #define RVBA_HI_REG(n) (DEV_CPUCFG + 0x00a4 + 0x08 * (n)) #define CPUS_RESET_REG (DEV_R_CPUCFG + 0x0000) #define CPUS_RESET BIT(0) /* Four of these ranges exist. */ #define CPUS_BYTE_SWAP_EN_REG (DEV_R_CPUCFG + 0x000c) #define CPUS_BYTE_SWAP_EN_REG_RANGEn(n) BIT(0 + (n)) #define CPUS_BYTE_SWAP_LO_REG(n) (DEV_R_CPUCFG + 0x0010 + 0x08 * (n)) #define CPUS_BYTE_SWAP_HI_REG(n) (DEV_R_CPUCFG + 0x0014 + 0x08 * (n)) #define C0_PWRON_RESET_REG (DEV_R_CPUCFG + 0x0030) #define C0_PWRON_RESET_REG_nCPUPORESET(n) BIT(0 + (n)) #define CPU_SYS_RESET_REG (DEV_R_CPUCFG + 0x0140) #define CPU_SYS_RESET BIT(0) #define IRQ_FIQ_STATUS_REG (DEV_R_CPUCFG + 0x014c) #define C0_IRQ_OUT(n) BIT(0 + (n)) #define C0_IRQ_OUT_MASK (0xf << 0) #endif /* PLATFORM_CPUCFG_H */ crust-0.5/platform/a64/include/platform/css.h000066400000000000000000000004031414152222500211370ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_CSS_H #define PLATFORM_CSS_H #define MAX_CLUSTERS 1 #define MAX_CORES_PER_CLUSTER 4 #endif /* PLATFORM_CSS_H */ crust-0.5/platform/a64/include/platform/devices.h000066400000000000000000000061261414152222500220010ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_DEVICES_H #define PLATFORM_DEVICES_H #define DEV_DE 0x01000000 #define DEV_DEBUG 0x01400000 #define DEV_CPU_BIST 0x01502000 #define DEV_CPUCFG 0x01700000 #define DEV_SYSCON 0x01c00000 #define DEV_DMA 0x01c02000 #define DEV_NAND 0x01c03000 #define DEV_TSC 0x01c06000 #define DEV_KEYMEM 0x01c0b000 #define DEV_TCON0 0x01c0c000 #define DEV_TCON1 0x01c0d000 #define DEV_VE 0x01c0e000 #define DEV_MMC0 0x01c0f000 #define DEV_MMC1 0x01c10000 #define DEV_MMC2 0x01c11000 #define DEV_SID 0x01c14000 #define DEV_CE 0x01c15000 #define DEV_MSGBOX 0x01c17000 #define DEV_SPINLOCK 0x01c18000 #define DEV_USBOTG 0x01c19000 #define DEV_USB0 0x01c1a000 #define DEV_USB1 0x01c1b000 #if CONFIG(SOC_H5) #define DEV_USB2 0x01c1c000 #define DEV_USB3 0x01c1d000 #endif #define DEV_TZASC 0x01c1e000 #define DEV_CCU 0x01c20000 #define DEV_PIO 0x01c20800 #define DEV_TIMER 0x01c20c00 #define DEV_SPDIF 0x01c21000 #define DEV_PWM 0x01c21400 #define DEV_KEYADC 0x01c21800 #define DEV_I2S0 0x01c22000 #define DEV_I2S1 0x01c22400 #define DEV_I2S2 0x01c22800 #define DEV_CODEC 0x01c22c00 #define DEV_SPC 0x01c23400 #define DEV_THS 0x01c25000 #define DEV_UART0 0x01c28000 #define DEV_UART1 0x01c28400 #define DEV_UART2 0x01c28800 #define DEV_UART3 0x01c28c00 #if CONFIG(SOC_A64) #define DEV_UART4 0x01c29000 #endif #define DEV_I2C0 0x01c2ac00 #define DEV_I2C1 0x01c2b000 #define DEV_I2C2 0x01c2b400 #define DEV_SCR0 0x01c2c400 #if CONFIG(SOC_H5) #define DEV_SCR1 0x01c2c800 #endif #define DEV_EMAC 0x01c30000 #if CONFIG(SOC_A64) #define DEV_GPU 0x01c40000 #endif #define DEV_HSTIMER 0x01c60000 #define DEV_DRAMCOM 0x01c62000 #define DEV_DRAMCTL 0x01c63000 #define DEV_DRAMPHY 0x01c65000 #define DEV_SPI0 0x01c68000 #define DEV_SPI1 0x01c69000 #define DEV_SCU 0x01c80000 #define DEV_GICD 0x01c81000 #define DEV_GICC 0x01c82000 #if CONFIG(SOC_A64) #define DEV_MIPI_DSI 0x01ca0000 #define DEV_MIPI_DPHY 0x01ca1000 #endif #define DEV_CSI 0x01cb0000 #define DEV_DEINTERLACE 0x01e00000 #if CONFIG(SOC_H5) #define DEV_TVE 0x01e40000 #define DEV_GPU 0x01e80000 #endif #define DEV_HDMI 0x01ee0000 #define DEV_HDMI_PHY 0x01ef0000 #define DEV_RTC 0x01f00000 #define DEV_R_TIMER 0x01f00800 #define DEV_R_INTC 0x01f00c00 #define DEV_R_WDOG 0x01f01000 #define DEV_R_PRCM 0x01f01400 #define DEV_R_TWD 0x01f01800 #define DEV_R_CPUCFG 0x01f01c00 #define DEV_R_CIR_RX 0x01f02000 #define DEV_R_I2C 0x01f02400 #define DEV_R_UART 0x01f02800 #define DEV_R_PIO 0x01f02c00 #if CONFIG(SOC_A64) #define DEV_R_RSB 0x01f03400 #endif #define DEV_R_PWM 0x01f03800 #endif /* PLATFORM_DEVICES_H */ crust-0.5/platform/a64/include/platform/irq.h000066400000000000000000000011461414152222500211470ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_IRQ_H #define PLATFORM_IRQ_H #define IRQ_NMI 0x00 #define IRQ_R_TIMER0 0x01 #define IRQ_R_TIMER1 0x02 #define IRQ_R_WDOG 0x04 #define IRQ_R_CIR_RX 0x05 #define IRQ_R_UART 0x06 #if CONFIG(SOC_A64) #define IRQ_R_RSB 0x07 #endif #define IRQ_R_ALARM0 0x08 #define IRQ_R_ALARM1 0x09 #define IRQ_R_TIMER2 0x0a #define IRQ_R_TIMER3 0x0b #define IRQ_R_I2C 0x0c #define IRQ_R_PIO_PL 0x0d #define IRQ_R_TWD 0x0e #define IRQ_MSGBOX 0x11 #endif /* PLATFORM_IRQ_H */ crust-0.5/platform/a64/include/platform/memory.h000066400000000000000000000013441414152222500216640ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_MEMORY_H #define PLATFORM_MEMORY_H #define FIRMWARE_BASE 0x00010000 #define FIRMWARE_LIMIT SCPI_MEM_BASE #define FIRMWARE_SIZE (FIRMWARE_LIMIT - FIRMWARE_BASE) #define SCPI_MEM_BASE (SRAM_A2_LIMIT - 0x400) #define SCPI_MEM_LIMIT SRAM_A2_LIMIT #define SCPI_MEM_SIZE (SCPI_MEM_LIMIT - SCPI_MEM_BASE) #define SRAM_A2_BASE 0x00000000 #define SRAM_A2_LIMIT 0x00014000 #define SRAM_A2_SIZE (SRAM_A2_LIMIT - SRAM_A2_BASE) /* Difference between SRAM_A2_BASE in the AR100 and ARM address spaces. */ #define SRAM_A2_OFFSET 0x00040000 #define STACK_SIZE 0x00000400 #endif /* PLATFORM_MEMORY_H */ crust-0.5/platform/a64/include/platform/prcm.h000066400000000000000000000072311414152222500213160ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_PRCM_H #define PLATFORM_PRCM_H #include #include #define CPUS_CLK_REG (DEV_R_PRCM + 0x0000) #define CPUS_CLK_REG_CLK_SRC(x) ((x) << 16) #define CPUS_CLK_REG_CLK_SRC_MASK (0x3 << 16) #define CPUS_CLK_REG_PRE_DIV(x) ((x) << 8) #define CPUS_CLK_REG_PRE_DIV_MASK (0x1f << 8) #define CPUS_CLK_REG_DIV_P(x) ((x) << 4) #define CPUS_CLK_REG_DIV_P_MASK (0x3 << 4) #define APB0_CLK_REG (DEV_R_PRCM + 0x000c) #define APB0_CLK_REG_DIV_M(x) ((x) << 0) #define APB0_CLK_REG_DIV_M_MASK (0x3 << 0) /* See r_ccu driver for bit definitions */ #define APB0_GATE_REG (DEV_R_PRCM + 0x0028) #define RTC_GATE_REG (DEV_R_PRCM + 0x002c) /* Documented in A23/A31s manual; all bits are present on A64 */ #define PLL_CTRL_REG0 (DEV_R_PRCM + 0x0040) #define PLL_CTRL_REG0_TEST_CLK_SEL BIT(24) #define PLL_CTRL_REG0_OSC24M_CLK_SEL(x) ((x) << 20) #define PLL_CTRL_REG0_OSC24M_CLK_SEL_MASK (0x3 << 20) #define PLL_CTRL_REG0_PLL_INPUT_SEL(x) ((x) << 12) #define PLL_CTRL_REG0_PLL_INPUT_SEL_MASK (0x3 << 12) #define PLL_CTRL_REG0_USB24M_CLK_SEL(x) ((x) << 4) #define PLL_CTRL_REG0_USB24M_CLK_SEL_MASK (0x3 << 4) #define PLL_CTRL_REG0_OSC24M_GAIN_ENHANCE BIT(1) #define PLL_CTRL_REG0_PLL_BIAS_EN BIT(0) /* Documented in A23/A31s manual; bits 3 and 15 are not verified on A64 */ #define PLL_CTRL_REG1 (DEV_R_PRCM + 0x0044) #define PLL_CTRL_REG1_KEY (0xa7 << 24) #define PLL_CTRL_REG1_KEY_FIELD (0xff << 24) #define PLL_CTRL_REG1_PLL_LDO_OUT(x) ((x) << 16) #define PLL_CTRL_REG1_PLL_LDO_OUT_MASK (0x7 << 16) #define PLL_CTRL_REG1_PLL_IN_PWR_SEL BIT(15) #define PLL_CTRL_REG1_CLKTEST_EN BIT(3) #define PLL_CTRL_REG1_CRYSTAL_EN BIT(2) #define PLL_CTRL_REG1_LDO_EN (0x3 << 0) /* See r_ccu driver for bit definitions */ #define R_CIR_RX_CLK_REG (DEV_R_PRCM + 0x0054) #define APB0_RESET_REG (DEV_R_PRCM + 0x00b0) /* CPU0 does not have its own gating */ #define C0_PWROFF_GATING_REG (DEV_R_PRCM + 0x0100) #define C0_CPUn_PWROFF_GATING(n) BIT(0 + (n)) #define C0_PWROFF_GATING BIT(0) #define VDD_SYS_PWROFF_GATING_REG (DEV_R_PRCM + 0x0110) #define VDD_CPUS_GATING BIT(3) #define AVCC_GATING BIT(2) #define DRAM_PAD_HOLD (0x3 << 0) #define GPU_PWROFF_GATING_REG (DEV_R_PRCM + 0x0118) #define GPU_PWROFF_GATING BIT(0) #define VDD_SYS_RESET_REG (DEV_R_PRCM + 0x0120) #define VDD_SYS_RESET BIT(0) #define C0_CPUn_PWR_SWITCH_REG(n) (DEV_R_PRCM + 0x0140 + 0x04 * (n)) #define ADDA_PR_CFG_REG (DEV_R_PRCM + 0x01c0) #define ADDA_PR_CFG_REG_RESET BIT(28) #define ADDA_PR_CFG_REG_RW BIT(24) #define ADDA_PR_CFG_REG_ADDR(x) ((x) << 16) #define ADDA_PR_CFG_REG_ADDR_MASK (0x1f << 16) #define ADDA_PR_CFG_REG_WDAT(x) ((x) << 8) #define ADDA_PR_CFG_REG_WDAT_MASK (0xff << 8) #define ADDA_PR_CFG_REG_RDAT(x) ((x) << 0) #define ADDA_PR_CFG_REG_RDAT_MASK (0xff << 0) #define PRCM_SEC_SWITCH_REG (DEV_R_PRCM + 0x01d0) #define PRCM_SEC_SWITCH_REG_POWER_SEC BIT(2) #define PRCM_SEC_SWITCH_REG_PLL_SEC BIT(1) #define PRCM_SEC_SWITCH_REG_CPUS_CLK_SEC BIT(0) #endif /* PLATFORM_PRCM_H */ crust-0.5/platform/a64/include/platform/time.h000066400000000000000000000006251414152222500213130ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_TIME_H #define PLATFORM_TIME_H #define CPUCLK_MHz 16 #define CPUCLK_kHz (CPUCLK_MHz * 1000) #define CPUCLK_Hz (CPUCLK_MHz * 1000000) #define REFCLK_MHZ 24 #define REFCLK_KHZ (REFCLK_MHZ * 1000) #define REFCLK_HZ (REFCLK_MHZ * 1000000) #endif /* PLATFORM_TIME_H */ crust-0.5/platform/a83t/000077500000000000000000000000001414152222500151175ustar00rootroot00000000000000crust-0.5/platform/a83t/include/000077500000000000000000000000001414152222500165425ustar00rootroot00000000000000crust-0.5/platform/a83t/include/platform/000077500000000000000000000000001414152222500203665ustar00rootroot00000000000000crust-0.5/platform/a83t/include/platform/css.h000066400000000000000000000004031414152222500213240ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_CSS_H #define PLATFORM_CSS_H #define MAX_CLUSTERS 2 #define MAX_CORES_PER_CLUSTER 4 #endif /* PLATFORM_CSS_H */ crust-0.5/platform/a83t/include/platform/devices.h000066400000000000000000000052041414152222500221620ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_DEVICES_H #define PLATFORM_DEVICES_H #define DEV_DE 0x01000000 #define DEV_DEBUG 0x01400000 #define DEV_CPU_BIST_C0 0x01502000 #define DEV_CPU_BIST_C1 0x01602000 #define DEV_CPUCFG 0x01700000 #define DEV_TIMESTAMP 0x01710000 #define DEV_CCI 0x01790000 #define DEV_SYSCON 0x01c00000 #define DEV_DMA 0x01c02000 #define DEV_NAND 0x01c03000 #define DEV_KEYMEM 0x01c0b000 #define DEV_TCON0 0x01c0c000 #define DEV_TCON1 0x01c0d000 #define DEV_VE 0x01c0e000 #define DEV_MMC0 0x01c0f000 #define DEV_MMC1 0x01c10000 #define DEV_MMC2 0x01c11000 #define DEV_SID 0x01c14000 #define DEV_SS 0x01c15000 #define DEV_MSGBOX 0x01c17000 #define DEV_SPINLOCK 0x01c18000 #define DEV_USBOTG 0x01c19000 #define DEV_USB0 0x01c1a000 #define DEV_USB1 0x01c1b000 #define DEV_TZASC 0x01c1e000 #define DEV_CCU 0x01c20000 #define DEV_PIO 0x01c20800 #define DEV_TIMER 0x01c20c00 #define DEV_SPDIF 0x01c21000 #define DEV_PWM 0x01c21400 #define DEV_I2S0 0x01c22000 #define DEV_I2S1 0x01c22400 #define DEV_I2S2 0x01c22800 #define DEV_TDM 0x01c23000 #define DEV_SPC 0x01c23400 #define DEV_DSI 0x01c26000 #define DEV_UART0 0x01c28000 #define DEV_UART1 0x01c28400 #define DEV_UART2 0x01c28800 #define DEV_UART3 0x01c28c00 #define DEV_UART4 0x01c29000 #define DEV_I2C0 0x01c2ac00 #define DEV_I2C1 0x01c2b000 #define DEV_I2C2 0x01c2b400 #define DEV_EMAC 0x01c30000 #define DEV_GPU 0x01c40000 #define DEV_HSTIMER 0x01c60000 #define DEV_DRAMCOM 0x01c62000 #define DEV_DRAMCTL 0x01c63000 #define DEV_DRAMPHY 0x01c65000 #define DEV_SPI0 0x01c68000 #define DEV_SPI1 0x01c69000 #define DEV_GIC 0x01c80000 #define DEV_GICD 0x01c81000 #define DEV_GICC 0x01c82000 #define DEV_CSI 0x01cb0000 #define DEV_HDMI 0x01ee0000 #define DEV_R_TIMER 0x01f00800 #define DEV_R_INTC 0x01f00c00 #define DEV_R_WDOG 0x01f01000 #define DEV_R_PRCM 0x01f01400 #define DEV_R_TWD 0x01f01800 #define DEV_R_CPUCFG 0x01f01c00 #define DEV_R_CIR_RX 0x01f02000 #define DEV_R_I2C 0x01f02400 #define DEV_R_UART 0x01f02800 #define DEV_R_PIO 0x01f02c00 #define DEV_R_RSB 0x01f03400 #define DEV_R_PWM 0x01f03800 #define DEV_R_LRADC 0x01f03c00 #define DEV_R_THS 0x01f04000 #endif /* PLATFORM_DEVICES_H */ crust-0.5/platform/a83t/include/platform/irq.h000066400000000000000000000010261414152222500213310ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_IRQ_H #define PLATFORM_IRQ_H #define IRQ_NMI 0x00 #define IRQ_R_TIMER0 0x01 #define IRQ_R_TIMER1 0x02 #define IRQ_R_WDOG 0x04 #define IRQ_R_CIR_RX 0x05 #define IRQ_R_UART 0x06 #define IRQ_R_RSB 0x07 #define IRQ_R_THS 0x09 #define IRQ_R_LRADC 0x0a #define IRQ_R_I2C 0x0c #define IRQ_R_PIO_PL 0x0d #define IRQ_R_TWD 0x0e #define IRQ_MSGBOX 0x11 #endif /* PLATFORM_IRQ_H */ crust-0.5/platform/a83t/include/platform/memory.h000066400000000000000000000013441414152222500220510ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_MEMORY_H #define PLATFORM_MEMORY_H #define FIRMWARE_BASE 0x00010000 #define FIRMWARE_LIMIT SCPI_MEM_BASE #define FIRMWARE_SIZE (FIRMWARE_LIMIT - FIRMWARE_BASE) #define SCPI_MEM_BASE (SRAM_A2_LIMIT - 0x400) #define SCPI_MEM_LIMIT SRAM_A2_LIMIT #define SCPI_MEM_SIZE (SCPI_MEM_LIMIT - SCPI_MEM_BASE) #define SRAM_A2_BASE 0x00000000 #define SRAM_A2_LIMIT 0x00014000 #define SRAM_A2_SIZE (SRAM_A2_LIMIT - SRAM_A2_BASE) /* Difference between SRAM_A2_BASE in the AR100 and ARM address spaces. */ #define SRAM_A2_OFFSET 0x00040000 #define STACK_SIZE 0x00000400 #endif /* PLATFORM_MEMORY_H */ crust-0.5/platform/a83t/include/platform/prcm.h000066400000000000000000000071521414152222500215050ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_PRCM_H #define PLATFORM_PRCM_H #include #include #define CPUS_CLK_REG (DEV_R_PRCM + 0x0000) #define CPUS_CLK_REG_CLK_SRC(x) ((x) << 16) #define CPUS_CLK_REG_CLK_SRC_MASK (0x3 << 16) #define CPUS_CLK_REG_PRE_DIV(x) ((x) << 8) #define CPUS_CLK_REG_PRE_DIV_MASK (0x1f << 8) #define CPUS_CLK_REG_DIV_P(x) ((x) << 4) #define CPUS_CLK_REG_DIV_P_MASK (0x3 << 4) #define APB0_CLK_REG (DEV_R_PRCM + 0x000c) #define APB0_CLK_REG_DIV_M(x) ((x) << 0) #define APB0_CLK_REG_DIV_M_MASK (0x3 << 0) /* See r_ccu driver for bit definitions */ #define APB0_GATE_REG (DEV_R_PRCM + 0x0028) /* Documented in A23/A31s manual */ #define PLL_CTRL_REG0 (DEV_R_PRCM + 0x0040) #define PLL_CTRL_REG0_TEST_CLK_SEL BIT(24) #define PLL_CTRL_REG0_OSC24M_CLK_SEL(x) ((x) << 20) #define PLL_CTRL_REG0_OSC24M_CLK_SEL_MASK (0x3 << 20) #define PLL_CTRL_REG0_PLL_INPUT_SEL(x) ((x) << 12) #define PLL_CTRL_REG0_PLL_INPUT_SEL_MASK (0x3 << 12) #define PLL_CTRL_REG0_USB24M_CLK_SEL(x) ((x) << 4) #define PLL_CTRL_REG0_USB24M_CLK_SEL_MASK (0x3 << 4) #define PLL_CTRL_REG0_OSC24M_GAIN_ENHANCE BIT(1) #define PLL_CTRL_REG0_PLL_BIAS_EN BIT(0) /* Documented in A23/A31s manual; bits 3 and 15 are not verified on A83T */ #define PLL_CTRL_REG1 (DEV_R_PRCM + 0x0044) #define PLL_CTRL_REG1_KEY (0xa7 << 24) #define PLL_CTRL_REG1_KEY_FIELD (0xff << 24) #define PLL_CTRL_REG1_PLL_LDO_OUT(x) ((x) << 16) #define PLL_CTRL_REG1_PLL_LDO_OUT_MASK (0x7 << 16) #define PLL_CTRL_REG1_PLL_IN_PWR_SEL BIT(15) #define PLL_CTRL_REG1_CLKTEST_EN BIT(3) #define PLL_CTRL_REG1_CRYSTAL_EN BIT(2) #define PLL_CTRL_REG1_LDO_EN (0x3 << 0) /* See r_ccu driver for bit definitions */ #define R_CIR_RX_CLK_REG (DEV_R_PRCM + 0x0054) #define APB0_RESET_REG (DEV_R_PRCM + 0x00b0) #define Cm_PWROFF_GATING_REG(m) (DEV_R_PRCM + 0x0100 + 0x04 * (m)) #define Cm_CPUn_PWROFF_GATING(n) BIT((n) ? 0 + (n) : 4) #define Cm_PWROFF_GATING BIT(0) #define VDD_SYS_PWROFF_GATING_REG (DEV_R_PRCM + 0x0110) #define VCC_GPIO_GATING BIT(12) #define VCC_PLL_GATING BIT(8) #define VCC_PLL_LOW_VOLTAGE_GATING BIT(4) #define VDD_CPUS_GATING BIT(3) #define AVCC_GATING 0 /* Not applicable */ #define DRAM_PAD_HOLD (0x3 << 0) #define GPU_PWROFF_GATING_REG (DEV_R_PRCM + 0x0118) #define GPU_PWROFF_GATING BIT(0) #define VDD_SYS_RESET_REG (DEV_R_PRCM + 0x0120) #define VDD_SYS_RESET BIT(0) #define Cm_CPUn_PWR_SWITCH_REG(m, n) (DEV_R_PRCM + 0x0140 + \ 0x10 * (m) + 0x04 * (n)) #define R_PIO_HOLD_REG (DEV_R_PRCM + 0x01f0) #define R_PIO_HOLD_REG_WRITE_PULSE BIT(31) #define R_PIO_HOLD_REG_PIO_REG_ADDR(x) ((x) << 16) #define R_PIO_HOLD_REG_PIO_REG_ADDR_MASK (0x3 << 16) #define R_PIO_HOLD_REG_DATA_WRITE(x) ((x) << 8) #define R_PIO_HOLD_REG_DATA_WRITE_MASK (0xff << 8) #define R_PIO_HOLD_REG_DATA_READ_MASK (0xff << 0) #define OSC24M_CTRL_REG (DEV_R_PRCM + 0x01f4) #define OSC24M_CTRL_REG_OSC24M_SRC_SELECT BIT(1) #define OSC24M_CTRL_REG_OSC16M_ENABLE BIT(0) #endif /* PLATFORM_PRCM_H */ crust-0.5/platform/a83t/include/platform/time.h000066400000000000000000000006251414152222500215000ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_TIME_H #define PLATFORM_TIME_H #define CPUCLK_MHz 16 #define CPUCLK_kHz (CPUCLK_MHz * 1000) #define CPUCLK_Hz (CPUCLK_MHz * 1000000) #define REFCLK_MHZ 24 #define REFCLK_KHZ (REFCLK_MHZ * 1000) #define REFCLK_HZ (REFCLK_MHZ * 1000000) #endif /* PLATFORM_TIME_H */ crust-0.5/platform/h3/000077500000000000000000000000001414152222500146525ustar00rootroot00000000000000crust-0.5/platform/h3/include/000077500000000000000000000000001414152222500162755ustar00rootroot00000000000000crust-0.5/platform/h3/include/platform/000077500000000000000000000000001414152222500201215ustar00rootroot00000000000000crust-0.5/platform/h3/include/platform/cpucfg.h000066400000000000000000000046561414152222500215540ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_CPUCFG_H #define PLATFORM_CPUCFG_H #include #include #define CPUS_RESET_REG (DEV_R_CPUCFG + 0x0000) #define CPUS_RESET BIT(0) /* Four of these ranges exist. */ #define CPUS_BYTE_SWAP_EN_REG (DEV_R_CPUCFG + 0x000c) #define CPUS_BYTE_SWAP_EN_REG_RANGEn(n) BIT(0 + (n)) #define CPUS_BYTE_SWAP_LO_REG(n) (DEV_R_CPUCFG + 0x0010 + 0x08 * (n)) #define CPUS_BYTE_SWAP_HI_REG(n) (DEV_R_CPUCFG + 0x0014 + 0x08 * (n)) #define CPUn_RST_CTRL_REG(n) (DEV_R_CPUCFG + 0x0040 + 0x40 * (n)) #define CPUn_RST_CTRL_REG_nCORERESET BIT(1) #define CPUn_RST_CTRL_REG_nCPUPORESET BIT(0) #define CPUn_CTRL_REG(n) (DEV_R_CPUCFG + 0x0044 + 0x40 * (n)) #define CPUn_CTRL_REG_CP15SDISABLE BIT(0) #define CPUn_STATUS_REG(n) (DEV_R_CPUCFG + 0x0048 + 0x40 * (n)) #define CPUn_STATUS_REG_STANDBYWFI BIT(2) #define CPUn_STATUS_REG_STANDBYWFE BIT(1) #define CPUn_STATUS_REG_SMPnAMP BIT(0) #define CPU_SYS_RESET_REG (DEV_R_CPUCFG + 0x0140) #define CPU_SYS_RESET BIT(0) #define CPU_CLK_GATING_REG (DEV_R_CPUCFG + 0x0144) #define CPU_CLK_GATING_REG_L2_GATING BIT(8) #define CPU_CLK_GATING_REG_CPU_GATING(n) BIT(0 + (n)) #define IRQ_FIQ_STATUS_REG (DEV_R_CPUCFG + 0x014c) #define C0_IRQ_OUT(n) BIT(0 + (n)) #define C0_IRQ_OUT_MASK (0xf << 0) #define C0_FIQ_OUT(n) BIT(8 + (n)) #define C0_FIQ_OUT_MASK (0xf << 8) #define GEN_CTRL_REG (DEV_R_CPUCFG + 0x0184) #define GEN_CTRL_REG_CFGSDISABLE BIT(8) #define GEN_CTRL_REG_ACINACTM BIT(6) #define GEN_CTRL_REG_nL2RESET BIT(5) #define GEN_CTRL_REG_L2RSTDISABLE BIT(4) #define GEN_CTRL_REG_L1RSTDISABLE(n) BIT(0 + (n)) #define GEN_CTRL_REG_L1RSTDISABLE_MASK (0xf << 0) #define EVENT_IN_REG (DEV_R_CPUCFG + 0x0190) #define EVENT_IN BIT(0) #define DBG_CTRL_REG0 (DEV_R_CPUCFG + 0x01e0) #define DBG_CTRL_REG1 (DEV_R_CPUCFG + 0x01e4) #define DBG_CTRL_REG1_DBGPWRDUP(n) BIT(0 + (n)) #define DBG_CTRL_REG1_DBGPWRDUP_MASK (0xf << 0) #endif /* PLATFORM_CPUCFG_H */ crust-0.5/platform/h3/include/platform/css.h000066400000000000000000000004031414152222500210570ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_CSS_H #define PLATFORM_CSS_H #define MAX_CLUSTERS 1 #define MAX_CORES_PER_CLUSTER 4 #endif /* PLATFORM_CSS_H */ crust-0.5/platform/h3/include/platform/devices.h000066400000000000000000000050761414152222500217240ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_DEVICES_H #define PLATFORM_DEVICES_H #define DEV_DE 0x01000000 #define DEV_DEINTERLACE 0x01400000 #define DEV_SYSCON 0x01c00000 #define DEV_DMA 0x01c02000 #define DEV_NAND 0x01c03000 #define DEV_TSC 0x01c06000 #define DEV_KEYMEM 0x01c0b000 #define DEV_TCON0 0x01c0c000 #define DEV_TCON1 0x01c0d000 #define DEV_VE 0x01c0e000 #define DEV_MMC0 0x01c0f000 #define DEV_MMC1 0x01c10000 #define DEV_MMC2 0x01c11000 #define DEV_SID 0x01c14000 #define DEV_CE 0x01c15000 #define DEV_MSGBOX 0x01c17000 #define DEV_SPINLOCK 0x01c18000 #define DEV_USBOTG 0x01c19000 #define DEV_USB0 0x01c1a000 #define DEV_USB1 0x01c1b000 #define DEV_USB2 0x01c1c000 #define DEV_USB3 0x01c1d000 #define DEV_TZASC 0x01c1e000 #define DEV_CCU 0x01c20000 #define DEV_PIO 0x01c20800 #define DEV_TIMER 0x01c20c00 #define DEV_SPDIF 0x01c21000 #define DEV_PWM 0x01c21400 #define DEV_KEYADC 0x01c21800 #define DEV_I2S0 0x01c22000 #define DEV_I2S1 0x01c22400 #define DEV_I2S2 0x01c22800 #define DEV_CODEC 0x01c22c00 #define DEV_SPC 0x01c23400 #define DEV_THS 0x01c25000 #define DEV_UART0 0x01c28000 #define DEV_UART1 0x01c28400 #define DEV_UART2 0x01c28800 #define DEV_UART3 0x01c28c00 #define DEV_I2C0 0x01c2ac00 #define DEV_I2C1 0x01c2b000 #define DEV_I2C2 0x01c2b400 #define DEV_SCR 0x01c2c400 #define DEV_EMAC 0x01c30000 #define DEV_GPU 0x01c40000 #define DEV_HSTIMER 0x01c60000 #define DEV_DRAMCOM 0x01c62000 #define DEV_DRAMCTL 0x01c63000 #define DEV_DRAMPHY 0x01c65000 #define DEV_SPI0 0x01c68000 #define DEV_SPI1 0x01c69000 #define DEV_SCU 0x01c80000 #define DEV_GICD 0x01c81000 #define DEV_GICC 0x01c82000 #define DEV_CSI 0x01cb0000 #define DEV_TVE 0x01e00000 #define DEV_HDMI 0x01ee0000 #define DEV_RTC 0x01f00000 #define DEV_R_TIMER 0x01f00800 #define DEV_R_INTC 0x01f00c00 #define DEV_R_WDOG 0x01f01000 #define DEV_R_PRCM 0x01f01400 #define DEV_R_TWD 0x01f01800 #define DEV_R_CPUCFG 0x01f01c00 #define DEV_R_CIR_RX 0x01f02000 #define DEV_R_I2C 0x01f02400 #define DEV_R_UART 0x01f02800 #define DEV_R_PIO 0x01f02c00 #define DEV_R_PWM 0x01f03800 #endif /* PLATFORM_DEVICES_H */ crust-0.5/platform/h3/include/platform/irq.h000066400000000000000000000010621414152222500210640ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_IRQ_H #define PLATFORM_IRQ_H #define IRQ_NMI 0x00 #define IRQ_R_TIMER0 0x01 #define IRQ_R_TIMER1 0x02 #define IRQ_R_WDOG 0x04 #define IRQ_R_CIR_RX 0x05 #define IRQ_R_UART 0x06 #define IRQ_R_ALARM0 0x08 #define IRQ_R_ALARM1 0x09 #define IRQ_R_TIMER2 0x0a #define IRQ_R_TIMER3 0x0b #define IRQ_R_I2C 0x0c #define IRQ_R_PIO_PL 0x0d #define IRQ_R_TWD 0x0e #define IRQ_MSGBOX 0x11 #endif /* PLATFORM_IRQ_H */ crust-0.5/platform/h3/include/platform/memory.h000066400000000000000000000013441414152222500216040ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_MEMORY_H #define PLATFORM_MEMORY_H #define FIRMWARE_BASE 0x00008000 #define FIRMWARE_LIMIT SCPI_MEM_BASE #define FIRMWARE_SIZE (FIRMWARE_LIMIT - FIRMWARE_BASE) #define SCPI_MEM_BASE (SRAM_A2_LIMIT - 0x400) #define SCPI_MEM_LIMIT SRAM_A2_LIMIT #define SCPI_MEM_SIZE (SCPI_MEM_LIMIT - SCPI_MEM_BASE) #define SRAM_A2_BASE 0x00000000 #define SRAM_A2_LIMIT 0x0000c000 #define SRAM_A2_SIZE (SRAM_A2_LIMIT - SRAM_A2_BASE) /* Difference between SRAM_A2_BASE in the AR100 and ARM address spaces. */ #define SRAM_A2_OFFSET 0x00040000 #define STACK_SIZE 0x00000400 #endif /* PLATFORM_MEMORY_H */ crust-0.5/platform/h3/include/platform/prcm.h000066400000000000000000000070761414152222500212450ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_PRCM_H #define PLATFORM_PRCM_H #include #include #define CPUS_CLK_REG (DEV_R_PRCM + 0x0000) #define CPUS_CLK_REG_CLK_SRC(x) ((x) << 16) #define CPUS_CLK_REG_CLK_SRC_MASK (0x3 << 16) #define CPUS_CLK_REG_PRE_DIV(x) ((x) << 8) #define CPUS_CLK_REG_PRE_DIV_MASK (0x1f << 8) #define CPUS_CLK_REG_DIV_P(x) ((x) << 4) #define CPUS_CLK_REG_DIV_P_MASK (0x3 << 4) #define APB0_CLK_REG (DEV_R_PRCM + 0x000c) #define APB0_CLK_REG_DIV_M(x) ((x) << 0) #define APB0_CLK_REG_DIV_M_MASK (0x3 << 0) /* See r_ccu driver for bit definitions */ #define APB0_GATE_REG (DEV_R_PRCM + 0x0028) #define RTC_GATE_REG (DEV_R_PRCM + 0x002c) /* Documented in A23/A31s manual; all bits are present on H3 */ #define PLL_CTRL_REG0 (DEV_R_PRCM + 0x0040) #define PLL_CTRL_REG0_TEST_CLK_SEL BIT(24) #define PLL_CTRL_REG0_OSC24M_CLK_SEL(x) ((x) << 20) #define PLL_CTRL_REG0_OSC24M_CLK_SEL_MASK (0x3 << 20) #define PLL_CTRL_REG0_PLL_INPUT_SEL(x) ((x) << 12) #define PLL_CTRL_REG0_PLL_INPUT_SEL_MASK (0x3 << 12) #define PLL_CTRL_REG0_USB24M_CLK_SEL(x) ((x) << 4) #define PLL_CTRL_REG0_USB24M_CLK_SEL_MASK (0x3 << 4) #define PLL_CTRL_REG0_OSC24M_GAIN_ENHANCE BIT(1) #define PLL_CTRL_REG0_PLL_BIAS_EN BIT(0) /* Documented in A23/A31s manual; bits 3 and 15 are not verified on H3 */ #define PLL_CTRL_REG1 (DEV_R_PRCM + 0x0044) #define PLL_CTRL_REG1_KEY (0xa7 << 24) #define PLL_CTRL_REG1_KEY_FIELD (0xff << 24) #define PLL_CTRL_REG1_PLL_LDO_OUT(x) ((x) << 16) #define PLL_CTRL_REG1_PLL_LDO_OUT_MASK (0x7 << 16) #define PLL_CTRL_REG1_PLL_IN_PWR_SEL BIT(15) #define PLL_CTRL_REG1_CLKTEST_EN BIT(3) #define PLL_CTRL_REG1_CRYSTAL_EN BIT(2) #define PLL_CTRL_REG1_LDO_EN (0x3 << 0) /* See r_ccu driver for bit definitions */ #define R_CIR_RX_CLK_REG (DEV_R_PRCM + 0x0054) #define APB0_RESET_REG (DEV_R_PRCM + 0x00b0) #define C0_PWROFF_GATING_REG (DEV_R_PRCM + 0x0100) #define C0_CPUn_PWROFF_GATING(n) BIT(0 + (n)) #define VDD_SYS_PWROFF_GATING_REG (DEV_R_PRCM + 0x0110) #define VDD_CPUS_GATING BIT(3) #define AVCC_GATING BIT(2) #define DRAM_PAD_HOLD (0x3 << 0) #define GPU_PWROFF_GATING_REG (DEV_R_PRCM + 0x0118) #define GPU_PWROFF_GATING BIT(0) #define VDD_SYS_RESET_REG (DEV_R_PRCM + 0x0120) #define VDD_SYS_RESET BIT(0) #define C0_CPUn_PWR_SWITCH_REG(n) (DEV_R_PRCM + 0x0140 + 0x04 * (n)) #define ADDA_PR_CFG_REG (DEV_R_PRCM + 0x01c0) #define ADDA_PR_CFG_REG_RESET BIT(28) #define ADDA_PR_CFG_REG_RW BIT(24) #define ADDA_PR_CFG_REG_ADDR(x) ((x) << 16) #define ADDA_PR_CFG_REG_ADDR_MASK (0x1f << 16) #define ADDA_PR_CFG_REG_WDAT(x) ((x) << 8) #define ADDA_PR_CFG_REG_WDAT_MASK (0xff << 8) #define ADDA_PR_CFG_REG_RDAT(x) ((x) << 0) #define ADDA_PR_CFG_REG_RDAT_MASK (0xff << 0) #define PRCM_SEC_SWITCH_REG (DEV_R_PRCM + 0x01d0) #define PRCM_SEC_SWITCH_REG_POWER_SEC BIT(2) #define PRCM_SEC_SWITCH_REG_PLL_SEC BIT(1) #define PRCM_SEC_SWITCH_REG_CPUS_CLK_SEC BIT(0) #endif /* PLATFORM_PRCM_H */ crust-0.5/platform/h3/include/platform/time.h000066400000000000000000000006251414152222500212330ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_TIME_H #define PLATFORM_TIME_H #define CPUCLK_MHz 16 #define CPUCLK_kHz (CPUCLK_MHz * 1000) #define CPUCLK_Hz (CPUCLK_MHz * 1000000) #define REFCLK_MHZ 24 #define REFCLK_KHZ (REFCLK_MHZ * 1000) #define REFCLK_HZ (REFCLK_MHZ * 1000000) #endif /* PLATFORM_TIME_H */ crust-0.5/platform/h6/000077500000000000000000000000001414152222500146555ustar00rootroot00000000000000crust-0.5/platform/h6/include/000077500000000000000000000000001414152222500163005ustar00rootroot00000000000000crust-0.5/platform/h6/include/platform/000077500000000000000000000000001414152222500201245ustar00rootroot00000000000000crust-0.5/platform/h6/include/platform/cpucfg.h000066400000000000000000000147531414152222500215560ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_CPUCFG_H #define PLATFORM_CPUCFG_H #include #include #define C0_RST_CTRL_REG (DEV_CPUCFG + 0x0000) #define C0_RST_CTRL_REG_nDDR_RST BIT(28) #define C0_RST_CTRL_REG_nMBISTRESET BIT(25) #define C0_RST_CTRL_REG_nSOC_DBG_RST BIT(24) #define C0_RST_CTRL_REG_nETM_RST_MASK (0xf << 20) #define C0_RST_CTRL_REG_nDBG_RST_MASK (0xf << 16) #define C0_RST_CTRL_REG_nL2RESET BIT(8) #define C0_RST_CTRL_REG_nCORERESET(n) BIT(0 + (n)) #define C0_RST_CTRL_REG_MASK (C0_RST_CTRL_REG_nDDR_RST | \ C0_RST_CTRL_REG_nMBISTRESET | \ C0_RST_CTRL_REG_nSOC_DBG_RST | \ C0_RST_CTRL_REG_nETM_RST_MASK | \ C0_RST_CTRL_REG_nDBG_RST_MASK | \ C0_RST_CTRL_REG_nL2RESET) #define C0_CTRL_REG0 (DEV_CPUCFG + 0x0010) #define C0_CTRL_REG0_SYSBARDISABLE BIT(31) #define C0_CTRL_REG0_BROADCASTINNER BIT(30) #define C0_CTRL_REG0_BROADCASTOUTER BIT(29) #define C0_CTRL_REG0_BROADCASTCACHEMAINT BIT(28) #define C0_CTRL_REG0_AA64nAA32(n) BIT(24 + (n)) #define C0_CTRL_REG0_AA64nAA32_MASK (0xf << 24) #define C0_CTRL_REG0_CP15SDISABLE(n) BIT(8 + (n)) #define C0_CTRL_REG0_L2RSTDISABLE BIT(4) #define C0_CTRL_REG1 (DEV_CPUCFG + 0x0014) #define C0_CTRL_REG1_ACINACTM BIT(0) #define C0_CTRL_REG2 (DEV_CPUCFG + 0x0018) #define C0_CTRL_REG2_EVENTI BIT(24) #define C0_CTRL_REG2_EXM_CLR(n) BIT(20 + (n)) #define C0_CTRL_REG2_CLREXMONREQ BIT(16) #define C0_CTRL_REG2_CRYPTODISABLE(n) BIT(12 + (n)) #define C0_CTRL_REG2_L2FLUSHREQ BIT(8) #define C0_CTRL_REG2_GICCDISABLE BIT(4) /* Not documented for H6, but probably matches A64 */ #define CACHE_CFG_REG0 (DEV_CPUCFG + 0x0020) #define CACHE_CFG_REG1 (DEV_CPUCFG + 0x0024) /* L2D fields are documented for A50/H616 and present on H6 */ #define CACHE_CFG_REG1_EMA_L2D (0x7 << 19) #define CACHE_CFG_REG1_EMAW_L2D (0x3 << 17) #define CACHE_CFG_REG1_EMAS_L2D BIT(16) #define CACHE_CFG_REG1_EMA (0x7 << 3) #define CACHE_CFG_REG1_EMAW (0x3 << 1) #define CACHE_CFG_REG1_EMAS BIT(0) #define RVBA_LO_REG(n) (DEV_CPUCFG + 0x0040 + 0x08 * (n)) #define RVBA_HI_REG(n) (DEV_CPUCFG + 0x0044 + 0x08 * (n)) #define C0_CPU_STATUS_REG (DEV_CPUCFG + 0x0080) #define C0_CPU_STATUS_REG_SMPnAMP(n) BIT(24 + (n)) #define C0_CPU_STATUS_REG_STANDBYWFI(n) BIT(16 + (n)) #define C0_CPU_STATUS_REG_STANDBYWFI_MASK (0xf << 16) #define C0_CPU_STATUS_REG_STANDBYWFE(n) BIT(8 + (n)) #define C0_CPU_STATUS_REG_STANDBYWFE_MASK (0xf << 8) #define C0_CPU_STATUS_REG_STANDBYWFIL2 BIT(0) #define L2_STATUS_REG (DEV_CPUCFG + 0x0084) #define L2_STATUS_REG_L2FLUSHDONE BIT(10) #define L2_STATUS_REG_EVENTO BIT(9) #define L2_STATUS_REG_CLREXMONACK BIT(8) #define DBG_REG0 (DEV_CPUCFG + 0x00c0) #define DBG_REG0_DBGL1RSTDISABLE BIT(16) #define DBG_REG0_DBGRESTART(n) BIT(8 + (n)) #define DBG_REG0_DBGPWRDUP(n) BIT(0 + (n)) #define DBG_REG0_DBGPWRDUP_MASK (0xf << 0) #define DBG_REG1 (DEV_CPUCFG + 0x00c4) #define DBG_REG1_DBGRESTARTED(n) BIT(12 + (n)) #define DBG_REG1_DBGRSTREQ(n) BIT(8 + (n)) #define DBG_REG1_DBGNOPWRDWN(n) BIT(4 + (n)) #define DBG_REG1_DBGPWRUPREQ(n) BIT(0 + (n)) #define CPUS_RESET_REG (DEV_R_CPUCFG + 0x0000) #define CPUS_RESET BIT(0) /* Four of these ranges exist. */ #define CPUS_BYTE_SWAP_EN_REG (DEV_R_CPUCFG + 0x000c) #define CPUS_BYTE_SWAP_EN_REG_RANGEn(n) BIT(0 + (n)) #define CPUS_BYTE_SWAP_LO_REG(n) (DEV_R_CPUCFG + 0x0010 + 0x08 * (n)) #define CPUS_BYTE_SWAP_HI_REG(n) (DEV_R_CPUCFG + 0x0014 + 0x08 * (n)) #define C0_PWRON_RESET_REG (DEV_R_CPUCFG + 0x0040) #define C0_PWRON_RESET_REG_nH_RST BIT(16) #define C0_PWRON_RESET_REG_nCPUPORESET(n) BIT(0 + (n)) #define C0_PWROFF_GATING_REG (DEV_R_CPUCFG + 0x0044) #define C0_PWROFF_GATING BIT(4) #define C0_CPUn_PWROFF_GATING(n) BIT(0 + (n)) #define C0_CPUn_PWR_SWITCH_REG(n) (DEV_R_CPUCFG + 0x0050 + 0x04 * (n)) #define CPU_SYS_RESET_REG (DEV_R_CPUCFG + 0x00a0) #define CPU_SYS_RESET BIT(0) #define CPUIDLE_EN_REG (DEV_R_CPUCFG + 0x0100) #define CPUIDLE_EN_REG_KEY0 (0x16aa << 16) #define CPUIDLE_EN_REG_KEY1 (0xaa16 << 16) #define CPUIDLE_EN_REG_KEY_FIELD (0xffff << 16) #define CPUIDLE_EN_REG_CPUIDLE_EN BIT(0) #define CLOSE_FLAG_REG (DEV_R_CPUCFG + 0x0104) #define CLOSE_FLAG_REG_CPUn_AND_C0(n) BIT(16 + (n)) #define CLOSE_FLAG_REG_CPUn_AND_C0_MASK (0xf << 16) #define CLOSE_FLAG_REG_CPUn(n) BIT(0 + (n)) #define CLOSE_FLAG_REG_CPUn_MASK (0xf << 0) #define CPUIDLE_PEND_REG (DEV_R_CPUCFG + 0x0108) #define CPUIDLE_PEND_REG_CPUn_IRQ(n) BIT(0 + (n)) #define CPUIDLE_PEND_REG_CPUn_IRQ_MASK (0xf << 0) #define CPUIDLE_WAKE_REG (DEV_R_CPUCFG + 0x010c) #define CPUIDLE_WAKE_REG_CPUn_IRQ(n) BIT(0 + (n)) #define CPUIDLE_WAKE_REG_CPUn_IRQ_MASK (0xf << 0) #define CPUIDLE_STAT_REG (DEV_R_CPUCFG + 0x0110) #define CPUIDLE_STAT_REG_C0_IDLE BIT(13) #define CPUIDLE_STAT_REG_C0_OFF BIT(12) #define CPUIDLE_STAT_REG_CPUn_VOTE(n) BIT(8 + (n)) #define CPUIDLE_STAT_REG_CPUn_VOTE_MASK (0xf << 8) #define CPUIDLE_STAT_REG_CPUn_OFF(n) BIT(4 + (n)) #define CPUIDLE_STAT_REG_CPUn_OFF_MASK (0xf << 4) #define CPUIDLE_STAT_REG_CPUn_IDLE(n) BIT(0 + (n)) #define CPUIDLE_STAT_REG_CPUn_IDLE_MASK (0xf << 0) #define PWR_SW_DELAY_REG (DEV_R_CPUCFG + 0x0140) #define CONFIG_DELAY_REG (DEV_R_CPUCFG + 0x0144) #define PWR_DOWN_CFG_REG (DEV_R_CPUCFG + 0x0148) /* Six of these registers exist. */ #define PWR_UP_CFG_REG(n) (DEV_R_CPUCFG + 0x0150 + 0x04 * (n)) #endif /* PLATFORM_CPUCFG_H */ crust-0.5/platform/h6/include/platform/css.h000066400000000000000000000004031414152222500210620ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_CSS_H #define PLATFORM_CSS_H #define MAX_CLUSTERS 1 #define MAX_CORES_PER_CLUSTER 4 #endif /* PLATFORM_CSS_H */ crust-0.5/platform/h6/include/platform/devices.h000066400000000000000000000057041414152222500217250ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_DEVICES_H #define PLATFORM_DEVICES_H #define DEV_DE 0x01000000 #define DEV_DEBUG 0x01420000 #define DEV_GPU 0x01800000 #define DEV_CE_NS 0x01904000 #define DEV_CE_S 0x01904800 #define DEV_EMCE 0x01905000 #define DEV_KEYMEM 0x01908000 #define DEV_VP9 0x01c00000 #define DEV_VE 0x01c0e000 #define DEV_SYSCON 0x03000000 #define DEV_CCU 0x03001000 #define DEV_DMA 0x03002000 #define DEV_MSGBOX 0x03003000 #define DEV_SPINLOCK 0x03004000 #define DEV_HSTIMER 0x03005000 #define DEV_SID 0x03006000 #define DEV_TZASC 0x03007000 #define DEV_SPC 0x03008000 #define DEV_TIMER 0x03009000 #define DEV_PWM 0x0300a000 #define DEV_PIO 0x0300b000 #define DEV_PSI 0x0300c000 #define DEV_DCU 0x03010000 #define DEV_SCU 0x03020000 #define DEV_GICD 0x03021000 #define DEV_GICC 0x03022000 #define DEV_IOMMU 0x030f0000 #define DEV_DRAMCOM 0x04002000 #define DEV_DRAMCTL 0x04003000 #define DEV_DRAMPHY 0x04005000 #define DEV_NAND 0x04011000 #define DEV_MMC0 0x04020000 #define DEV_MMC1 0x04021000 #define DEV_MMC2 0x04022000 #define DEV_UART0 0x05000000 #define DEV_UART1 0x05000400 #define DEV_UART2 0x05000800 #define DEV_UART3 0x05000c00 #define DEV_I2C0 0x05002000 #define DEV_I2C1 0x05002400 #define DEV_I2C2 0x05002800 #define DEV_I2C3 0x05002c00 #define DEV_SCR0 0x05005000 #define DEV_SCR1 0x05005400 #define DEV_SPI0 0x05010000 #define DEV_SPI1 0x05011000 #define DEV_EMAC 0x05020000 #define DEV_TS0 0x05060000 #define DEV_THS 0x05070400 #define DEV_CIR_TX 0x05061000 #define DEV_I2S3 0x0508f000 #define DEV_I2S0 0x05090000 #define DEV_I2S1 0x05091000 #define DEV_I2S2 0x05092000 #define DEV_SPDIF 0x05093000 #define DEV_DMIC 0x05095000 #define DEV_AHUB 0x05097000 #define DEV_USB0 0x05100000 #define DEV_USB1 0x05200000 #define DEV_USB3 0x05311000 #define DEV_PCIE 0x05400000 #define DEV_HDMI 0x06000000 #define DEV_TCON_TOP 0x06510000 #define DEV_TCON0 0x06511000 #define DEV_TCON1 0x06515000 #define DEV_CSI 0x06620000 #define DEV_RTC 0x07000000 #define DEV_R_CPUCFG 0x07000400 #define DEV_R_PRCM 0x07010000 #define DEV_R_TIMER 0x07020000 #define DEV_R_WDOG 0x07020400 #define DEV_R_TWD 0x07020800 #define DEV_R_PWM 0x07020c00 #define DEV_R_INTC 0x07021000 #define DEV_R_PIO 0x07022000 #define DEV_R_CIR_RX 0x07040000 #define DEV_R_W1 0x07040400 #define DEV_R_UART 0x07080000 #define DEV_R_I2C 0x07081400 #define DEV_R_RSB 0x07083000 #define DEV_CPUSYSCFG 0x08100000 #define DEV_CNT_R 0x08110000 #define DEV_CNT_C 0x08120000 #define DEV_CPUCFG 0x09010000 #define DEV_CPU_BIST 0x09020000 #endif /* PLATFORM_DEVICES_H */ crust-0.5/platform/h6/include/platform/irq.h000066400000000000000000000011761414152222500210750ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_IRQ_H #define PLATFORM_IRQ_H #define IRQ_NMI 0x00 #define IRQ_R_TIMER0 0x01 #define IRQ_R_TIMER1 0x02 #define IRQ_R_TIMER2 0x03 #define IRQ_R_TIMER3 0x04 #define IRQ_R_ALARM0 0x05 #define IRQ_R_ALARM1 0x06 #define IRQ_R_WDOG 0x07 #define IRQ_R_TWD 0x08 #define IRQ_R_PIO_PL 0x09 #define IRQ_R_UART 0x0a #define IRQ_R_I2C 0x0b #define IRQ_R_RSB 0x0c #define IRQ_R_CIR_RX 0x0d #define IRQ_R_OWC 0x0e #define IRQ_R_PIO_PM 0x0f #define IRQ_MSGBOX 0x13 #endif /* PLATFORM_IRQ_H */ crust-0.5/platform/h6/include/platform/memory.h000066400000000000000000000013441414152222500216070ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_MEMORY_H #define PLATFORM_MEMORY_H #define FIRMWARE_BASE 0x00014000 #define FIRMWARE_LIMIT SCPI_MEM_BASE #define FIRMWARE_SIZE (FIRMWARE_LIMIT - FIRMWARE_BASE) #define SCPI_MEM_BASE (SRAM_A2_LIMIT - 0x400) #define SCPI_MEM_LIMIT SRAM_A2_LIMIT #define SCPI_MEM_SIZE (SCPI_MEM_LIMIT - SCPI_MEM_BASE) #define SRAM_A2_BASE 0x00000000 #define SRAM_A2_LIMIT 0x00018000 #define SRAM_A2_SIZE (SRAM_A2_LIMIT - SRAM_A2_BASE) /* Difference between SRAM_A2_BASE in the AR100 and ARM address spaces. */ #define SRAM_A2_OFFSET 0x00100000 #define STACK_SIZE 0x00000400 #endif /* PLATFORM_MEMORY_H */ crust-0.5/platform/h6/include/platform/prcm.h000066400000000000000000000075331414152222500212460ustar00rootroot00000000000000/* * Copyright © 2020-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_PRCM_H #define PLATFORM_PRCM_H #include #include #define CPUS_CLK_REG (DEV_R_PRCM + 0x0000) #define CPUS_CLK_REG_CLK_SRC(x) ((x) << 24) #define CPUS_CLK_REG_CLK_SRC_MASK (0x3 << 24) #define CPUS_CLK_REG_DIV_P(x) ((x) << 8) #define CPUS_CLK_REG_DIV_P_MASK (0x3 << 8) #define CPUS_CLK_REG_PRE_DIV(x) ((x) << 0) #define CPUS_CLK_REG_PRE_DIV_MASK (0x1f << 0) #define R_APB1_CLK_REG (DEV_R_PRCM + 0x000c) #define R_APB1_CLK_REG_DIV_M(x) ((x) << 0) #define R_APB1_CLK_REG_DIV_M_MASK (0x3 << 0) #define R_APB2_CLK_REG (DEV_R_PRCM + 0x0010) #define R_APB2_CLK_REG_CLK_SRC(x) ((x) << 24) #define R_APB2_CLK_REG_CLK_SRC_MASK (0x3 << 24) #define R_APB2_CLK_REG_DIV_P(x) ((x) << 8) #define R_APB2_CLK_REG_DIV_P_MASK (0x3 << 8) #define R_APB2_CLK_REG_PRE_DIV(x) ((x) << 0) #define R_APB2_CLK_REG_PRE_DIV_MASK (0x1f << 0) /* See r_ccu driver for bit definitions */ #define R_TIMER_GATE_REG (DEV_R_PRCM + 0x011c) #define R_TWDOG_GATE_REG (DEV_R_PRCM + 0x012c) #define R_PWM_GATE_REG (DEV_R_PRCM + 0x013c) #define R_UART_GATE_REG (DEV_R_PRCM + 0x018c) #define R_I2C_GATE_REG (DEV_R_PRCM + 0x019c) #define R_RSB_GATE_REG (DEV_R_PRCM + 0x01bc) #define R_CIR_RX_CLK_REG (DEV_R_PRCM + 0x01c0) #define R_CIR_RX_GATE_REG (DEV_R_PRCM + 0x01cc) #define R_OWC_CLK_REG (DEV_R_PRCM + 0x01e0) #define R_OWC_GATE_REG (DEV_R_PRCM + 0x01ec) #define R_RTC_GATE_REG (DEV_R_PRCM + 0x020c) /* Differs from earlier generations; only the bits below are valid */ #define PLL_CTRL_REG0 (DEV_R_PRCM + 0x0240) #define PLL_CTRL_REG0_UNK_BIT_24 BIT(24) #define PLL_CTRL_REG0_UNK_BIT_02 BIT(2) #define PLL_CTRL_REG0_UNK_BIT_01 BIT(1) #define PLL_CTRL_REG0_UNK_BIT_00 BIT(0) /* Documented in A23/A31s manual; bits 3 and 15 are not verified on H6 */ #define PLL_CTRL_REG1 (DEV_R_PRCM + 0x0244) #define PLL_CTRL_REG1_KEY (0xa7 << 24) #define PLL_CTRL_REG1_KEY_FIELD (0xff << 24) #define PLL_CTRL_REG1_PLL_LDO_OUT(x) ((x) << 16) #define PLL_CTRL_REG1_PLL_LDO_OUT_MASK (0x7 << 16) #define PLL_CTRL_REG1_PLL_IN_PWR_SEL BIT(15) #define PLL_CTRL_REG1_CLKTEST_EN BIT(3) #define PLL_CTRL_REG1_CRYSTAL_EN BIT(2) #define PLL_CTRL_REG1_LDO_EN BIT(0) #define VDD_SYS_PWROFF_GATING_REG (DEV_R_PRCM + 0x0250) #define VDD_CPUS_GATING BIT(3) #define AVCC_GATING BIT(2) #define DRAM_PAD_HOLD (0x3 << 0) #define GPU_PWROFF_GATING_REG (DEV_R_PRCM + 0x0254) #define GPU_PWROFF_GATING BIT(0) #define VDD_SYS_RESET_REG (DEV_R_PRCM + 0x0260) #define VDD_SYS_RESET BIT(0) /* Bits are documented for A64 and assumed to match here */ #define PRCM_SEC_SWITCH_REG (DEV_R_PRCM + 0x0290) #define PRCM_SEC_SWITCH_REG_POWER_SEC BIT(2) #define PRCM_SEC_SWITCH_REG_PLL_SEC BIT(1) #define PRCM_SEC_SWITCH_REG_CPUS_CLK_SEC BIT(0) #define RES_CAL_CTRL_REG (DEV_R_PRCM + 0x0310) /* set in dram_power_up_process() */ #define RES_CAL_CTRL_REG_UNK_BIT_08 BIT(8) /* "calibration circuits analog enable" */ #define RES_CAL_CTRL_REG_CAL_ANA_EN BIT(1) #define RES_CAL_CTRL_REG_CAL_EN BIT(0) #define UNK_REG_0318 (DEV_R_PRCM + 0x0318) /* cleared in dram_power_up_process() */ #define UNK_REG_0318_UNK_BIT_00_05 (0x3f << 0) #endif /* PLATFORM_PRCM_H */ crust-0.5/platform/h6/include/platform/time.h000066400000000000000000000006251414152222500212360ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #ifndef PLATFORM_TIME_H #define PLATFORM_TIME_H #define CPUCLK_MHz 16 #define CPUCLK_kHz (CPUCLK_MHz * 1000) #define CPUCLK_Hz (CPUCLK_MHz * 1000000) #define REFCLK_MHZ 24 #define REFCLK_KHZ (REFCLK_MHZ * 1000) #define REFCLK_HZ (REFCLK_MHZ * 1000000) #endif /* PLATFORM_TIME_H */ crust-0.5/scripts/000077500000000000000000000000001414152222500142035ustar00rootroot00000000000000crust-0.5/scripts/Makefile.format000066400000000000000000000011031414152222500171250ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # find_files = $(sort $(wildcard $(foreach X,$2,$(1:%=$(SRC)/%/$X)))) format_dirs := arch/* common drivers/* include/* lib platform/* tools format_globs := *.c */*.c *.h */*.h */*/*.h format_files := $(call find_files,$(format_dirs),$(format_globs)) check-format: $(format_files) $(Q) uncrustify -c $(SRC)/.uncrustify -l C -q --check $^ format: $(format_files) $(Q) uncrustify -c $(SRC)/.uncrustify -l C -q --no-backup --replace $^ .PHONY: check-format format crust-0.5/scripts/Makefile.kbuild000066400000000000000000000160031414152222500171140ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # # # Variables for use in included Makefiles # src = $(SRC)/$(subdir) obj = $(OBJ)/$(subdir) tgt = $(TGT)/$(subdir) # # Variables for use in the main Makefile # buildprogs-all := hostprogs-all := lib-all := obj-all := test-all := tools-all := # # Internal variables # dir_AFLAGS = $(asflags-y) dir_CFLAGS = $(ccflags-y) dir_CPPFLAGS = $(cppflags-y) dir_LDFLAGS = $(ldflags-y) dir_BUILDAFLAGS = $(asflags-y) dir_BUILDCFLAGS = $(ccflags-y) dir_BUILDCPPFLAGS= $(cppflags-y) dir_BUILDLDFLAGS = $(ldflags-y) dir_HOSTAFLAGS = $(asflags-y) dir_HOSTCFLAGS = $(ccflags-y) dir_HOSTCPPFLAGS = $(cppflags-y) dir_HOSTLDFLAGS = $(ldflags-y) flags = AFLAGS CFLAGS CPPFLAGS LDFLAGS LDLIBS flags_object := $(filter-out LD%,$(flags)) flags_program := $(filter CFLAGS LD%,$(flags)) vars = buildprogs hostprogs lib obj test tools # # Defined functions and procedures (those starting with '_' are internal) # # # Adds compiler/linker flags to the target-specific variables for this file. # # $1: File name (no path) # $2: List of flags, e.g. $(flags_object) or $(flags_program) # build_file_flags = $(foreach f,$(2:%=BUILD%),$(call _file_flag,$(obj),$1,$f)) host_file_flags = $(foreach f,$(2:%=HOST%),$(call _file_flag,$(obj),$1,$f)) target_file_flags = $(foreach f,$2,$(call _file_flag,$(tgt),$1,$f)) # # Internal helper to add target-specific variables. $(eval) provides the # necessary eager binding. Also avoids adding blank/whitespace variables. # # $1: Directory # $2: File name # $3: Flag name # _file_flag = $(if $(strip $(dir_$3)$($3_$2)), \ $(eval $1/$2: $3 += $(dir_$3) $($3_$2))) # # Overrides compiler/linker paths and flags to the correct variable for the # type of object (build vs host). # # $1: File name (no path) # $2: List of flags, e.g. $(flags_object) or $(flags_program) # build_overrides = $(foreach f,AR CC $2,$(eval $(obj)/$1: HOST$f = $$(BUILD$f))) # # Adds the necessary dependencies and flags for a list of build object files. # # $1: List of object file names (no path) # build_objects = $(foreach object,$1,$(eval $(value _build_object))) define _build_object $(call build_file_flags,$(object),$(flags_object)) $(call build_overrides,$(object),$(flags_object)) $(obj)/$(object): | $(obj)/. $(OBJ_DEPS) -include $(obj)/$(object:.o=.d) endef # # Adds the necessary dependencies and flags for a list of build programs. # # $1: List of program names (no path) # build_programs = $(foreach program,$1,$(eval $(value _build_program))) define _build_program $(call build_file_flags,$(program),$(flags_program)) $(call build_overrides,$(program),$(flags_program)) ifeq ($($(program)-objs),) $(program)-objs := $(program).o endif $(obj)/$(program): $($(program)-objs:%=$(obj)/%) | $(obj)/. $(call build_objects,$($(program)-objs)) endef # # Adds the necessary dependencies and flags for a list of host object files. # # $1: List of object file names (no path) # host_objects = $(foreach object,$1,$(eval $(value _host_object))) define _host_object $(call host_file_flags,$(object),$(flags_object)) $(obj)/$(object): | $(obj)/. $(OBJ_DEPS) -include $(obj)/$(object:.o=.d) endef # # Adds the necessary dependencies and flags for a list of host programs. # # $1: List of program names (no path) # host_programs = $(foreach program,$1,$(eval $(value _host_program))) define _host_program $(call host_file_flags,$(program),$(flags_program)) ifeq ($($(program)-objs),) $(program)-objs := $(program).o endif $(obj)/$(program): $($(program)-objs:%=$(obj)/%) | $(obj)/. $(call host_objects,$($(program)-objs)) endef # # Includes a list of object files in the main static library (both host and # target versions). Also adds the necessary dependencies and flags for each # object file. # # $1: List of object file names (no path) # library_objects = $(foreach object,$1,$(eval $(value _library_object))) define _library_object # Add the host version of this library object $(OBJ)/lib.a: $(obj)/$(object) $(call host_objects,$(object)) # Add the target version of this library object $(TGT)/lib.a: $(tgt)/$(object) $(call target_objects,$(object)) endef # # Adds the necessary dependencies and flags for a list of target object files. # # $1: List of object file names (no path) # target_objects = $(foreach object,$1,$(eval $(value _target_object))) define _target_object $(call target_file_flags,$(object),$(flags_object)) $(tgt)/$(object): | $(tgt)/. $(OBJ_DEPS) -include $(tgt)/$(object:.o=.d) endef # # Descend into each of a list of directories. For every directory entered, # recursively read and evaluate rules from its Makefile. # # $1: List of directories (relative to the root of the source tree) # descend = $(foreach subdir,$1,$(eval $(value _descend))) define _descend # Clear all per-directory variables $(foreach var,$(filter %-objs %-y,$(.VARIABLES)),$(eval $(var) :=)) # Read the Makefile in this directory $(src)/Makefile:; include $(src)/Makefile # Filter out directory dependencies dirs-y := $(filter %/,$(foreach var,$(vars),$($(var)-y))) buildprogs-y := $(filter-out %/,$(buildprogs-y)) hostprogs-y := $(filter-out %/,$(hostprogs-y)) lib-y := $(filter-out %/,$(lib-y)) obj-y := $(filter-out %/,$(obj-y)) test-y := $(filter-out %/,$(test-y)) tools-y := $(filter-out %/,$(tools-y)) # Combine all sources of flags asflags-y += $(parent-asflags) $(subdir-asflags-y) ccflags-y += $(parent-ccflags) $(subdir-ccflags-y) cppflags-y += $(parent-cppflags) $(subdir-cppflags-y) ldflags-y += $(parent-ldflags) $(subdir-ldflags-y) # Handle file rules $(call build_programs, $(buildprogs-y)) $(call host_programs, $(hostprogs-y)) $(call library_objects,$(lib-y)) $(call target_objects, $(obj-y)) $(call host_programs, $(test-y)) $(call host_programs, $(tools-y)) # Accumulate file names (library objects are handled above) buildprogs-all+= $(buildprogs-y:%=$(obj)/%) hostprogs-all += $(hostprogs-y:%=$(obj)/%) obj-all += $(obj-y:%=$(tgt)/%) test-all += $(test-y:%=$(obj)/%) tools-all += $(tools-y:%=$(obj)/%) # Push recursive flags $(subdir)-asflags := $(parent-asflags) $(subdir)-ccflags := $(parent-ccflags) $(subdir)-cppflags := $(parent-cppflags) $(subdir)-ldflags := $(parent-ldflags) parent-asflags += $(subdir-asflags-y) parent-ccflags += $(subdir-ccflags-y) parent-cppflags += $(subdir-cppflags-y) parent-ldflags += $(subdir-ldflags-y) # Recursively descend into sub-directories $(call descend,$(dirs-y:%/=$(subdir)/%)) # Pop recursive flags parent-asflags := $($(subdir)-asflags) parent-ccflags := $($(subdir)-ccflags) parent-cppflags := $($(subdir)-cppflags) parent-ldflags := $($(subdir)-ldflags) # Clear this directory's flags ifneq ($(filter undefine,$(.FEATURES)),) undefine $(subdir)-asflags undefine $(subdir)-ccflags undefine $(subdir)-cppflags undefine $(subdir)-ldflags else $(subdir)-asflags := $(subdir)-ccflags := $(subdir)-cppflags := $(subdir)-ldflags := endif endef crust-0.5/scripts/test.sh000077500000000000000000000004551414152222500155250ustar00rootroot00000000000000#!/bin/sh -eu # # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # binary=$1 output=$2 if "$binary" > "${output}.tmp"; then mv -f "${output}.tmp" "$output" else error=$? cat "${output}.tmp" rm -f "${output}.tmp" exit "$error" fi crust-0.5/scripts/version.sh000077500000000000000000000015071414152222500162320ustar00rootroot00000000000000#!/bin/sh -eu # # Copyright © 2020-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # major=0 minor=5 patch=10000 srcdir=$1 output=$2 if test "$srcdir" = "$(git -C "$srcdir" rev-parse --show-toplevel 2>/dev/null)"; then set -- $(git -C "$srcdir" describe --dirty --long --match 'v[0-9]*' | tr '.-' ' ') if test "$#" -ge 4; then major=${1#v} minor=$2 patch=$3 fi if test "$#" -ge 5 && test "$5" = "dirty"; then patch=$((patch+10000)) fi fi trap 'rm -f "$output.tmp"' EXIT cat > "$output.tmp" << EOF #define VERSION_MAJOR $major #define VERSION_MINOR $minor #define VERSION_PATCH $patch #define VERSION_STRING "v${major}.${minor}.${patch}" EOF if ! test -f "$output" || ! test "$(cat "$output.tmp")" = "$(cat "$output")"; then mv -f "$output.tmp" "$output" fi crust-0.5/tools/000077500000000000000000000000001414152222500136545ustar00rootroot00000000000000crust-0.5/tools/Makefile000066400000000000000000000002221414152222500153100ustar00rootroot00000000000000# # Copyright © 2017-2021 The Crust Firmware Authors. # SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only # tools-y += load tools-y += test crust-0.5/tools/load.c000066400000000000000000000064301414152222500147420ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PAGESIZE #define PAGESIZE 0x1000 #endif #define PAGE_BASE(addr) ((addr) & ~(PAGESIZE - 1)) #define PAGE_OFFSET(addr) ((addr) & (PAGESIZE - 1)) int main(int argc, char *argv[]) { struct stat st; char *file; char *sram; uintptr_t r_cpucfg; void *r_cpucfg_map; int fd; if (argc < 2 || strcmp("--help", argv[1]) == 0) { puts("ARISC firmware loader for " CONFIG_PLATFORM); printf("usage: %s [--help] --reset | \n", argv[0]); return argc < 2 ? EXIT_FAILURE : EXIT_SUCCESS; } fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd < 0) { perror("Failed to open /dev/mem"); return EXIT_FAILURE; } r_cpucfg_map = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PAGE_BASE(DEV_R_CPUCFG)); if (r_cpucfg_map == MAP_FAILED) { perror("Failed to mmap R_CPUCFG"); return EXIT_FAILURE; } sram = mmap(NULL, SRAM_A2_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PAGE_BASE(SRAM_A2_OFFSET + SRAM_A2_BASE)); if (sram == MAP_FAILED) { perror("Failed to mmap SRAM A2"); return EXIT_FAILURE; } close(fd); r_cpucfg = (uintptr_t)r_cpucfg_map + PAGE_OFFSET(DEV_R_CPUCFG); if (strcmp("--reset", argv[1]) == 0) { if (!mmio_get_32(r_cpucfg, BIT(0))) { puts("ARISC is already in reset"); return EXIT_SUCCESS; } puts("Asserting ARISC reset"); mmio_clr_32(r_cpucfg, BIT(0)); if (mmio_get_32(r_cpucfg, BIT(0))) { puts("Failed to assert ARISC reset"); return EXIT_FAILURE; } return EXIT_SUCCESS; } fd = open(argv[1], O_RDONLY); if (fd < 0) { perror("Failed to open firmware file"); return EXIT_FAILURE; } if (fstat(fd, &st)) { perror("Failed to stat firmware file"); return EXIT_FAILURE; } if (st.st_size > FIRMWARE_SIZE) { puts("Firmware will not fit in available SRAM"); return EXIT_FAILURE; } file = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (file == MAP_FAILED) { perror("Failed to mmap firmware file"); return EXIT_FAILURE; } close(fd); puts("Asserting ARISC reset"); mmio_clr_32(r_cpucfg, BIT(0)); if (mmio_get_32(r_cpucfg, BIT(0))) { puts("Failed to assert ARISC reset"); return EXIT_FAILURE; } puts("Writing exception vectors"); for (int i = RESET_EXCEPTION; i <= TRAP_EXCEPTION; ++i) { uint32_t jump = EXCEPTION_VECTOR_ADDRESS(i); uint32_t offset = FIRMWARE_BASE - jump; mmio_write_32((uintptr_t)(sram + jump), offset >> 2); } printf("Writing firmware (%jd/%d bytes used)\n", (intmax_t)st.st_size, FIRMWARE_SIZE); memcpy(sram + FIRMWARE_BASE, file, st.st_size); msync(sram + FIRMWARE_BASE, st.st_size, MS_SYNC); puts("Deasserting ARISC reset"); mmio_set_32(r_cpucfg, BIT(0)); if (!mmio_get_32(r_cpucfg, BIT(0))) { puts("Failed to deassert ARISC reset"); return EXIT_FAILURE; } munmap(file, st.st_size); munmap(r_cpucfg_map, PAGESIZE); munmap(sram, SRAM_A2_SIZE); return EXIT_SUCCESS; } crust-0.5/tools/test.c000066400000000000000000000745251414152222500150140ustar00rootroot00000000000000/* * Copyright © 2017-2021 The Crust Firmware Authors. * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* SCPI device (clock/power supply) flags. */ #define FLAG_READABLE BIT(0) #define FLAG_WRITABLE BIT(1) /* Simplified version of the message box register definitions. This tool only * uses virtual channel 0 (hardware channels 0/1). */ #define MSGBOX_LOCAL_IRQ_STATUS_REG 0x0070 #define MSGBOX_LOCAL_RX_IRQ BIT(2) #define MSGBOX_ARISC_IRQ_STATUS_REG 0x0050 #define MSGBOX_ARISC_RX_IRQ BIT(0) #define MSGBOX_RX_MSG_STATUS_REG 0x0144 #define MSGBOX_TX_MSG_STATUS_REG 0x0140 #define MSGBOX_MSG_STATUS_MASK GENMASK(2, 0) #define MSGBOX_RX_MSG_DATA_REG 0x0184 #define MSGBOX_TX_MSG_DATA_REG 0x0180 #define SENSOR_CLASS_TEMPERATURE 0 #define SENSOR_CLASS_COUNT 5 /* PAGESIZE is not defined on architectures with variable-sized pages. Assume * 4k pages if the size is unknown. */ #ifndef PAGESIZE #define PAGESIZE 0x1000 #endif #define PAGE_BASE(addr) ((addr) & ~(PAGESIZE - 1)) #define PAGE_OFFSET(addr) ((addr) & (PAGESIZE - 1)) /** Shorthand for the virtual mmapped address of the shared memory area. */ #define SCPI_SHMEM (&((struct scpi_mem *)sram)[1]) /** Arbitrary value to identify messages sent by the test program. */ #define SCPI_SENDER_TEST 0xaa /** Arbitrary value to ensure the firmware correctly handles status codes. */ #define SCPI_STATUS_TEST 0xcafef00d /** Convert a symbol to a string. */ #define STRINGIFY(token) #token /** Fail the current test if some condition is false. */ #define test_assert(x) ((void)((x) || (test_assert_fail(#x, __LINE__), 0))) /** Begin a test. */ #define test_begin(id) \ do { \ tests_attempted |= BIT(id); \ if (sigsetjmp(test_buf, 0)) { \ test_fail(id); \ return; \ } \ } while (0) /** Severities for log levels. */ enum { LOG_INFO, /**< An informational message. */ LOG_WARN, /**< A warning. The current test will continue. */ LOG_ERR, /**< An error. The current test cannot continue. */ LOG_FATAL, /**< A fatal error. Testing the firmware cannot continue. */ }; /** Identifiers for the various tests. */ enum { TEST_BOOT, TEST_INTERRUPT, TEST_SCP_CAP, TEST_CLOCK_CAP, TEST_CLOCK_CMDS, TEST_CLOCK_INFO, TEST_CLOCK_CTRL, TEST_CSS_INFO, TEST_DVFS_CAP, TEST_DVFS_CMDS, TEST_DVFS_INFO, TEST_DVFS_CTRL, TEST_PSU_CAP, TEST_PSU_CMDS, TEST_PSU_INFO, TEST_PSU_CTRL, TEST_SENSOR_CAP, TEST_SENSOR_CMDS, TEST_SENSOR_INFO, TEST_SENSOR_READ, TEST_SYS_POWER, TEST_COUNT, }; /** A structure containing the timing data needed to analyze an SCPI call. */ struct scpi_call_times { struct timespec start; /**< Before doing any processing. */ struct timespec send; /**< Before the call to msgbox_send. */ struct timespec send_ack; /**< After return from msgbox_send. */ struct timespec receive; /**< After return from msgbox_receive. */ struct timespec receive_ack; /**< After return from msgbox_ack. */ struct timespec finish; /**< After all processing is complete. */ }; /** Context for returning to the main program on failure. */ static sigjmp_buf main_buf; /** Context for continuing with the next test on failure. */ static sigjmp_buf test_buf; /** Prefixes for the various log levels. */ static const char *const log_prefixes[] = { "", "warning: ", "error: ", "fatal: " }; /** Virtual address of the mmapped msgbox registers. */ static uintptr_t mbox; /** Virtual address of the mmapped SCPI shared memory in SRAM A2. */ static uintptr_t sram; /** SCPI command names printed with timing information. */ static const char *const scpi_command_names[] = { "INVALID SCPI COMMAND", STRINGIFY(SCPI_CMD_SCP_READY), STRINGIFY(SCPI_CMD_GET_SCP_CAP), STRINGIFY(SCPI_CMD_SET_CSS_POWER), STRINGIFY(SCPI_CMD_GET_CSS_POWER), STRINGIFY(SCPI_CMD_SET_SYS_POWER), STRINGIFY(SCPI_CMD_SET_CPU_TIMER), STRINGIFY(SCPI_CMD_CANCEL_CPU_TIMER), STRINGIFY(SCPI_CMD_GET_DVFS_CAP), STRINGIFY(SCPI_CMD_GET_DVFS_INFO), STRINGIFY(SCPI_CMD_SET_DVFS), STRINGIFY(SCPI_CMD_GET_DVFS), STRINGIFY(SCPI_CMD_GET_DVFS_STATS), STRINGIFY(SCPI_CMD_GET_CLOCK_CAP), STRINGIFY(SCPI_CMD_GET_CLOCK_INFO), STRINGIFY(SCPI_CMD_SET_CLOCK), STRINGIFY(SCPI_CMD_GET_CLOCK), STRINGIFY(SCPI_CMD_GET_PSU_CAP), STRINGIFY(SCPI_CMD_GET_PSU_INFO), STRINGIFY(SCPI_CMD_SET_PSU), STRINGIFY(SCPI_CMD_GET_PSU), STRINGIFY(SCPI_CMD_GET_SENSOR_CAP), STRINGIFY(SCPI_CMD_GET_SENSOR_INFO), STRINGIFY(SCPI_CMD_GET_SENSOR), STRINGIFY(SCPI_CMD_CFG_SENSOR_PERIOD), STRINGIFY(SCPI_CMD_CFG_SENSOR_BOUNDS), STRINGIFY(SCPI_CMD_ASYNC_SENSOR), STRINGIFY(SCPI_CMD_SET_DEV_POWER), STRINGIFY(SCPI_CMD_GET_DEV_POWER), }; /** A bitmap of the available SCPI commands, from SCPI_CMD_GET_SCP_CAP. */ static uint32_t scpi_commands_available; /** Human-readable names for the various tests. */ static const char *const test_names[TEST_COUNT] = { "Boot", "Interrupt", "SCP capability", "Clock capability", "Clock commands", "Clock info", "Clock control", "CSS info", "DVFS capability", "DVFS commands", "DVFS info", "DVFS control", "PSU capability", "PSU commands", "PSU info", "PSU control", "Sensor capability", "Sensor commands", "Sensor info", "Sensor read", "System power", }; /** A bitmap of attempted tests. */ static unsigned long tests_attempted; /** A bitmap of failed tests. */ static unsigned long tests_failed; /** A bitmap of passed tests. */ static unsigned long tests_passed; /** * Get the number of set bits in a bitmap. */ static unsigned bitmap_weight(unsigned long bits) { return __builtin_popcount(bits); } /** * Clean and invalidate lines in the data cache associated with a range of * virtual addresses. */ static void data_cache_clean(void *start, size_t length) { #ifdef __aarch64__ uintptr_t addr = (uintptr_t)start; uintptr_t end = (uintptr_t)start + length; uintptr_t order, stride; /* Ensure the compiler doesn't move stores past the cache cleaning. */ barrier(); /* Extract the order of the size of the smallest cache line. */ asm ("mrs %0, CTR_EL0\n\t" "ubfx %0, %0, #16, #4" : "=r" (order)); /* Get the actual stride length from its order. */ stride = BIT(order); /* Align the address to the stride, and clean the cache by VA. */ for (addr &= ~(stride - 1); addr < end; addr += stride) asm volatile ("dc civac, %0" : : "r" (addr)); asm volatile ("dsb sy"); #else #error "No cache cleaning implementation available for this architecture" #endif } /** * Get the difference between two timespecs in nanoseconds. */ static long difftimespec(const struct timespec *x, const struct timespec *y) { return 1000000000L * (x->tv_sec - y->tv_sec) + x->tv_nsec - y->tv_nsec; } /** * Log a message to standard error, and possibly abort testing. */ static void log(unsigned level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fflush(NULL); fputs(log_prefixes[level], stderr); vfprintf(stderr, fmt, ap); fputc('\n', stderr); va_end(ap); if (level >= LOG_FATAL) { /* Fatal error: print summary and exit. */ siglongjmp(main_buf, 1); } else if (level >= LOG_ERR) { /* Non-fatal error: fail the test and resume with the next. */ siglongjmp(test_buf, 1); } } /** * Acknowledge that message has been received and its associated shared memory * is again available for use. */ static void msgbox_ack(void) { /* Clear the local message reception IRQ. */ mmio_write_32(mbox + MSGBOX_LOCAL_IRQ_STATUS_REG, MSGBOX_LOCAL_RX_IRQ); } /** * Wait for a message, and ensure that it is an SCPI message. */ static void msgbox_receive(void) { uint32_t msgs, reg; /* Wait for a message reception IRQ. */ mmio_poll_32(mbox + MSGBOX_LOCAL_IRQ_STATUS_REG, MSGBOX_LOCAL_RX_IRQ); /* Read the number of available messages. */ reg = mmio_read_32(mbox + MSGBOX_RX_MSG_STATUS_REG); if (unlikely((msgs = reg & MSGBOX_MSG_STATUS_MASK) > 1)) log(LOG_WARN, "Expected one message, received %u", msgs); else if (unlikely(msgs == 0)) log(LOG_FATAL, "Received an IRQ, but no messages"); reg = mmio_read_32(mbox + MSGBOX_RX_MSG_DATA_REG); if (unlikely(reg != SCPI_VIRTUAL_CHANNEL)) log(LOG_FATAL, "Expected SCPI, received protocol 0x%08x", reg); } /** * Send a message, and wait for it to be acknowledged. */ static void msgbox_send(void) { /* Send the message. */ mmio_write_32(mbox + MSGBOX_TX_MSG_DATA_REG, SCPI_VIRTUAL_CHANNEL); /* Wait for the firmware to acknowledge the message. */ mmio_pollz_32(mbox + MSGBOX_ARISC_IRQ_STATUS_REG, MSGBOX_ARISC_RX_IRQ); } /** * Get the actual size of an SCPI message in bytes. */ static size_t scpi_msg_size(struct scpi_msg *msg) { return SCPI_HEADER_SIZE + msg->size; } /** * Copy an SCPI message, ensuring cache coherency. */ static void scpi_copy_msg(struct scpi_msg *dest, struct scpi_msg *src) { size_t length = scpi_msg_size(src); /* Only copy the necessary number of bytes from the message. */ memcpy(dest, src, length); /* Ensure that there is no stale cache of the shared memory area. */ if (dest == &SCPI_SHMEM->rx_msg) data_cache_clean(dest, length); else if (src == &SCPI_SHMEM->tx_msg) data_cache_clean(src, length); } /** * Check if a command is in the set of available SCPI commands. */ static bool scpi_has_command(uint8_t id) { if (id >= CHAR_BIT * sizeof(scpi_commands_available)) return false; return (scpi_commands_available & BIT(id)) == BIT(id); } /** * Initialize the header of an SCPI message. */ static void scpi_prepare_msg(struct scpi_msg *msg, uint8_t command) { msg->command = command; msg->sender = SCPI_SENDER_TEST; msg->size = 0; msg->status = SCPI_STATUS_TEST; } /** * Send an SCPI request and verify its success. */ static bool scpi_send_request(struct scpi_msg *msg, struct scpi_call_times *times) { /* Save the original command and sender to compare with the reply. */ uint8_t sender = msg->sender; uint32_t command = msg->command; clock_gettime(CLOCK_MONOTONIC, ×->start); /* Copy the message to shared memory. */ scpi_copy_msg(&SCPI_SHMEM->rx_msg, msg); /* Send the message. */ clock_gettime(CLOCK_MONOTONIC, ×->send); msgbox_send(); clock_gettime(CLOCK_MONOTONIC, ×->send_ack); /* Wait for a response message. */ msgbox_receive(); clock_gettime(CLOCK_MONOTONIC, ×->receive); /* Copy the message from shared memory to DRAM. */ scpi_copy_msg(msg, &SCPI_SHMEM->tx_msg); /* Acknowledge the response, now that it's safely stored in DRAM. */ msgbox_ack(); clock_gettime(CLOCK_MONOTONIC, ×->receive_ack); /* Stop the timer. */ clock_gettime(CLOCK_MONOTONIC, ×->finish); /* Verify that the received message is an appropriate response. */ if (unlikely(msg->command != command)) { log(LOG_ERR, "Expected reply for command %u, received %u", command, msg->command); return false; } if (unlikely(msg->sender != sender)) { log(LOG_ERR, "Expected reply to sender %u, received %u", sender, msg->sender); return false; } return true; } /** * Wait for an SCPI request and send a reply. */ static bool scpi_serve_reply(struct scpi_msg *msg, struct scpi_call_times *times, uint32_t command) { clock_gettime(CLOCK_MONOTONIC, ×->start); /* Wait for a message to arrive. */ msgbox_receive(); clock_gettime(CLOCK_MONOTONIC, ×->receive); /* Copy the message from shared memory to DRAM. */ scpi_copy_msg(msg, &SCPI_SHMEM->tx_msg); /* Acknowledge that the message was received. */ msgbox_ack(); clock_gettime(CLOCK_MONOTONIC, ×->receive_ack); /* Prepare the reply message (a copy of the received message). */ if (likely(msg->command == command && msg->sender == SCPI_SENDER_SCP)) msg->status = SCPI_OK; else msg->status = SCPI_E_PARAM; scpi_copy_msg(&SCPI_SHMEM->rx_msg, msg); /* Send the reply message. */ clock_gettime(CLOCK_MONOTONIC, ×->send); msgbox_send(); clock_gettime(CLOCK_MONOTONIC, ×->send_ack); /* Verify that the received message is as expected. */ if (unlikely(msg->command != command)) { log(LOG_ERR, "Expected command %u, received %u", command, msg->command); return false; } if (unlikely(msg->sender != SCPI_SENDER_SCP)) { log(LOG_ERR, "Expected sender %u, got sender %u", SCPI_SENDER_SCP, msg->sender); return false; } return true; } /** * Handle failure of a test. */ static noreturn void test_assert_fail(const char *expr, unsigned line) { log(LOG_ERR, "Assertion failed: %d: '%s'", line, expr); siglongjmp(test_buf, -1); } /** * Mark a test as completed (passed). */ static void test_complete(uint8_t id) { if (tests_passed & BIT(id)) return; tests_passed |= BIT(id); printf("PASS: %s\n", test_names[id]); } /** * Mark a test as failed. */ static void test_fail(uint8_t id) { if (tests_failed & BIT(id)) return; tests_failed |= BIT(id); printf("FAIL: %s\n", test_names[id]); } /** * Send an SCPI request as part of a test. */ static void test_send_request(struct scpi_msg *msg) { struct scpi_call_times times; test_assert(scpi_send_request(msg, ×)); printf("TIME: %-25s │ send: %10ldns │ receive: %10ldns │ busy: %10ldns" " │ total: %10ldns\n", scpi_command_names[msg->command], difftimespec(×.send_ack, ×.send), difftimespec(×.receive_ack, ×.receive), difftimespec(×.receive_ack, ×.send), difftimespec(×.finish, ×.start)); } /** * Wait for an SCPI request as part of a test. */ static void test_serve_reply(struct scpi_msg *msg, long *wait_time) { struct scpi_call_times times; test_assert(scpi_serve_reply(msg, ×, msg->command)); *wait_time = difftimespec(×.receive, ×.start); } /** * Print a summary of tests run. */ static void test_summary(void) { printf("DONE: %u tests, %u passed, %u failed, %u skipped\n", TEST_COUNT, bitmap_weight(tests_passed), bitmap_weight(tests_failed), TEST_COUNT - bitmap_weight(tests_attempted)); } /* * Test: Basic commands (SCP capability). */ static void try_basic(void) { struct scpi_msg msg; /* Send an invalid command to test basic message processing. */ test_begin(TEST_INTERRUPT); scpi_prepare_msg(&msg, 0); test_send_request(&msg); test_assert(msg.status == SCPI_E_SUPPORT); test_assert(msg.size == 0); /* Send a valid command with the wrong payload size. */ scpi_prepare_msg(&msg, SCPI_CMD_GET_SCP_CAP); msg.size = 4; test_send_request(&msg); test_assert(msg.status == SCPI_E_SIZE); test_assert(msg.size == 0); test_complete(TEST_INTERRUPT); /* Query the firmware's capabilities. Save the resulting set of * supported commands in a global variable for later use. */ test_begin(TEST_SCP_CAP); scpi_prepare_msg(&msg, SCPI_CMD_GET_SCP_CAP); test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size == 28); /* The supported SCPI standard must be revision 1.2. */ test_assert(((uint16_t *)msg.payload)[0] == 2); test_assert(((uint16_t *)msg.payload)[1] == 1); /* The supported payload size must match the header definitions. */ test_assert(((uint16_t *)msg.payload)[2] == SCPI_PAYLOAD_SIZE); test_assert(((uint16_t *)msg.payload)[3] == SCPI_PAYLOAD_SIZE); scpi_commands_available = ((uint32_t *)msg.payload)[3]; /* Ensure the supported commands word is sane (that the firmware claims * to support the commands we just sent). */ test_assert(scpi_has_command(SCPI_CMD_SCP_READY)); test_assert(scpi_has_command(SCPI_CMD_GET_SCP_CAP)); test_complete(TEST_SCP_CAP); } /* * Test: Boot (SCP ready). */ static void try_boot(void) { struct scpi_msg msg; long boot_time; /* Seeing "SCP ready" means the firmware booted successfully. */ log(LOG_INFO, "Waiting for firmware..."); test_begin(TEST_BOOT); scpi_prepare_msg(&msg, SCPI_CMD_SCP_READY); test_serve_reply(&msg, &boot_time); log(LOG_INFO, "Firmware booted in %ld ns", boot_time); test_complete(TEST_BOOT); } /* * Test: Clocks. */ static void try_clocks(void) { struct scpi_msg msg; uint16_t clocks = 0; /* Skip this test if the required commands are not available. */ if (!scpi_has_command(SCPI_CMD_GET_CLOCK_CAP)) return; /* Get the number of clocks. */ test_begin(TEST_CLOCK_CAP); scpi_prepare_msg(&msg, SCPI_CMD_GET_CLOCK_CAP); test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size == 2); clocks = ((uint16_t *)msg.payload)[0]; test_complete(TEST_CLOCK_CAP); /* If the firmware has clocks, it must fully support clock control. */ if (clocks > 0) { test_begin(TEST_CLOCK_CMDS); test_assert(scpi_has_command(SCPI_CMD_GET_CLOCK_INFO)); test_assert(scpi_has_command(SCPI_CMD_SET_CLOCK)); test_assert(scpi_has_command(SCPI_CMD_GET_CLOCK)); test_complete(TEST_CLOCK_CMDS); } for (volatile uint16_t i = 0; i < clocks; ++i) { uint8_t flags; uint32_t max_rate, min_rate; test_begin(TEST_CLOCK_INFO); scpi_prepare_msg(&msg, SCPI_CMD_GET_CLOCK_INFO); /* Send the clock ID. */ msg.size = 2; ((uint16_t *)msg.payload)[0] = i; test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size >= 12); /* Assert that we got the same ID back. */ test_assert(((uint16_t *)msg.payload)[0] == i); /* Assert that there are no reserved flags. */ flags = ((uint16_t *)msg.payload)[1]; test_assert((flags & ~(FLAG_READABLE | FLAG_WRITABLE)) == 0); /* Assert that the minimum and maximum rates make sense. */ min_rate = ((uint32_t *)msg.payload)[1]; max_rate = ((uint32_t *)msg.payload)[2]; test_assert(max_rate >= min_rate); test_complete(TEST_CLOCK_INFO); /* Try setting both the minimum and maximum rates. */ test_begin(TEST_CLOCK_CTRL); scpi_prepare_msg(&msg, SCPI_CMD_SET_CLOCK); msg.size = 8; ((uint32_t *)msg.payload)[0] = i; ((uint32_t *)msg.payload)[1] = min_rate; test_send_request(&msg); /* Assert that the firmware respects its permission flags. */ if (!(flags & FLAG_WRITABLE)) { test_assert(msg.status == SCPI_E_ACCESS); } else { /* Set the clock may still fail if it is in use. */ test_assert(msg.status == SCPI_OK || msg.status == SCPI_E_ACCESS); } test_assert(msg.size == 0); scpi_prepare_msg(&msg, SCPI_CMD_SET_CLOCK); msg.size = 8; ((uint32_t *)msg.payload)[0] = i; ((uint32_t *)msg.payload)[1] = max_rate; test_send_request(&msg); /* Assert that the firmware respects its permission flags. */ if (!(flags & FLAG_WRITABLE)) { test_assert(msg.status == SCPI_E_ACCESS); } else { /* Set the clock may still fail if it is in use. */ test_assert(msg.status == SCPI_OK || msg.status == SCPI_E_ACCESS); } test_assert(msg.size == 0); /* Read back clock rate and ensure it is in range. */ scpi_prepare_msg(&msg, SCPI_CMD_GET_CLOCK); msg.size = 2; ((uint16_t *)msg.payload)[0] = i; test_send_request(&msg); /* Assert that the firmware respects its permission flags. */ if (!(flags & FLAG_READABLE)) { test_assert(msg.status == SCPI_E_ACCESS); test_complete(TEST_CLOCK_CTRL); continue; } test_assert(msg.status == SCPI_OK); test_assert(msg.size == 4); test_assert(((uint32_t *)msg.payload)[0] >= min_rate); test_assert(((uint32_t *)msg.payload)[0] <= max_rate); test_complete(TEST_CLOCK_CTRL); } /* Test the failure case of sending an invalid clock ID. */ test_begin(TEST_CLOCK_INFO); scpi_prepare_msg(&msg, SCPI_CMD_GET_CLOCK_INFO); msg.size = 2; ((uint16_t *)msg.payload)[0] = clocks; test_send_request(&msg); test_assert(msg.status == SCPI_E_PARAM); test_complete(TEST_CLOCK_INFO); } /* * Test: CSS power. */ static void try_css_power(void) { struct scpi_msg msg; /* Skip this test if the required commands are not available. */ if (!scpi_has_command(SCPI_CMD_GET_CSS_POWER)) return; test_begin(TEST_CSS_INFO); scpi_prepare_msg(&msg, SCPI_CMD_GET_CSS_POWER); test_send_request(&msg); test_assert(msg.status == SCPI_OK); /* Assert that there is at least one cluster. */ test_assert(msg.size >= 2); /* Each descriptor is 2 bytes long. */ test_assert(msg.size % 2 == 0); /* Assert that all clusters are on. */ for (size_t i = 0; i < msg.size / 2; ++i) test_assert((((uint16_t *)msg.payload)[i] & 0x0f) == 0); test_complete(TEST_CSS_INFO); } /* * Test: DVFS. */ static void try_dvfs(void) { struct scpi_msg msg; uint8_t domains = 0, opps; /* Skip this test if the required commands are not available. */ if (!scpi_has_command(SCPI_CMD_GET_DVFS_CAP)) return; /* Get the number of DVFS domains. */ test_begin(TEST_DVFS_CAP); scpi_prepare_msg(&msg, SCPI_CMD_GET_DVFS_CAP); test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size == 1); domains = ((uint8_t *)msg.payload)[0]; test_complete(TEST_DVFS_CAP); /* If the firmware has DVFS domains, it must fully support DVFS. */ if (domains > 0) { test_begin(TEST_DVFS_CMDS); test_assert(scpi_has_command(SCPI_CMD_GET_DVFS_INFO)); test_assert(scpi_has_command(SCPI_CMD_SET_DVFS)); test_assert(scpi_has_command(SCPI_CMD_GET_DVFS)); test_complete(TEST_DVFS_CMDS); } for (volatile uint8_t i = 0; i < domains; ++i) { test_begin(TEST_DVFS_INFO); scpi_prepare_msg(&msg, SCPI_CMD_GET_DVFS_INFO); /* Send the domain ID. */ msg.size = 1; ((uint8_t *)msg.payload)[0] = i; test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size >= 4); /* Assert that we got the same ID back. */ test_assert(((uint8_t *)msg.payload)[0] == i); /* Get the number of operating performance points. */ opps = ((uint8_t *)msg.payload)[1]; /* Ensure the number of OPPs matches the message size. */ test_assert(opps > 0); test_assert(msg.size == 8 * opps + 4); test_complete(TEST_DVFS_INFO); /* Cycle through all of the OPPs. */ test_begin(TEST_DVFS_CTRL); for (uint8_t j = 0; j < opps; ++j) { /* Set DVFS domain i to OPP j. */ scpi_prepare_msg(&msg, SCPI_CMD_SET_DVFS); msg.size = 2; ((uint8_t *)msg.payload)[0] = i; ((uint8_t *)msg.payload)[1] = j; test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size == 0); /* Read back the OPP index and ensure it matches. */ scpi_prepare_msg(&msg, SCPI_CMD_GET_DVFS); msg.size = 1; ((uint8_t *)msg.payload)[0] = i; test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size == 1); test_assert(((uint8_t *)msg.payload)[0] == j); } test_complete(TEST_DVFS_CTRL); } /* Test the failure case of sending an invalid domain ID. */ test_begin(TEST_DVFS_INFO); scpi_prepare_msg(&msg, SCPI_CMD_GET_DVFS_INFO); msg.size = 1; ((uint8_t *)msg.payload)[0] = domains; test_send_request(&msg); test_assert(msg.status == SCPI_E_PARAM); test_complete(TEST_DVFS_INFO); } /* * Test: Power supplies. */ static void try_psus(void) { struct scpi_msg msg; uint16_t psus = 0; /* Skip this test if the required commands are not available. */ if (!scpi_has_command(SCPI_CMD_GET_PSU_CAP)) return; /* Get the number of clocks. */ test_begin(TEST_PSU_CAP); scpi_prepare_msg(&msg, SCPI_CMD_GET_PSU_CAP); test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size == 2); psus = ((uint16_t *)msg.payload)[0]; test_complete(TEST_PSU_CAP); /* If the firmware has clocks, it must fully support clock control. */ if (psus > 0) { test_begin(TEST_PSU_CMDS); test_assert(scpi_has_command(SCPI_CMD_GET_PSU_INFO)); test_assert(scpi_has_command(SCPI_CMD_SET_PSU)); test_assert(scpi_has_command(SCPI_CMD_GET_PSU)); test_complete(TEST_PSU_CMDS); } for (volatile uint16_t i = 0; i < psus; ++i) { uint8_t flags; uint32_t min_voltage, max_voltage, voltage; test_begin(TEST_PSU_INFO); scpi_prepare_msg(&msg, SCPI_CMD_GET_PSU_INFO); /* Send the PSU ID. */ msg.size = 2; ((uint16_t *)msg.payload)[0] = i; test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size >= 12); /* Assert that we got the same ID back. */ test_assert(((uint16_t *)msg.payload)[0] == i); /* Assert that there are no reserved flags. */ flags = ((uint16_t *)msg.payload)[1]; test_assert((flags & ~(FLAG_READABLE | FLAG_WRITABLE)) == 0); /* Assert that the minimum and maximum voltages make sense. */ min_voltage = ((uint32_t *)msg.payload)[1]; max_voltage = ((uint32_t *)msg.payload)[2]; test_assert(max_voltage >= min_voltage); test_complete(TEST_PSU_INFO); /* Set an invalid value (over the maximum). */ scpi_prepare_msg(&msg, SCPI_CMD_SET_PSU); msg.size = 8; ((uint32_t *)msg.payload)[0] = i; ((uint32_t *)msg.payload)[1] = max_voltage + 1000; test_send_request(&msg); /* Assert that the firmware respects its permission flags. */ if (flags & FLAG_WRITABLE) test_assert(msg.status == SCPI_E_RANGE); else test_assert(msg.status == SCPI_E_ACCESS); test_assert(msg.size == 0); /* Get the current value, for setting below. */ test_begin(TEST_PSU_CTRL); scpi_prepare_msg(&msg, SCPI_CMD_GET_PSU); msg.size = 2; ((uint16_t *)msg.payload)[0] = i; test_send_request(&msg); /* Assert that the firmware respects its permission flags. */ if (!(flags & FLAG_READABLE)) { test_assert(msg.status == SCPI_E_ACCESS); test_complete(TEST_PSU_CTRL); continue; } test_assert(msg.status == SCPI_OK); test_assert(msg.size == 4); voltage = ((uint32_t *)msg.payload)[0]; test_assert(voltage >= min_voltage); test_assert(voltage <= max_voltage); /* Setting arbitrary values is unsafe; set what was read. */ scpi_prepare_msg(&msg, SCPI_CMD_SET_PSU); msg.size = 8; ((uint32_t *)msg.payload)[0] = i; ((uint32_t *)msg.payload)[1] = voltage; test_send_request(&msg); /* Assert that the firmware respects its permission flags. */ if (flags & FLAG_WRITABLE) test_assert(msg.status == SCPI_OK); else test_assert(msg.status == SCPI_E_ACCESS); test_assert(msg.size == 0); test_complete(TEST_PSU_CTRL); } /* Test the failure case of sending an invalid PSU ID. */ test_begin(TEST_PSU_INFO); scpi_prepare_msg(&msg, SCPI_CMD_GET_PSU_INFO); msg.size = 2; ((uint16_t *)msg.payload)[0] = psus; test_send_request(&msg); test_assert(msg.status == SCPI_E_PARAM); test_complete(TEST_PSU_INFO); } /* * Test: Sensors. */ static void try_sensors(void) { struct scpi_msg msg; uint16_t sensors = 0; /* Skip this test if the required commands are not available. */ if (!scpi_has_command(SCPI_CMD_GET_SENSOR_CAP)) return; /* Get the number of sensors. */ test_begin(TEST_SENSOR_CAP); scpi_prepare_msg(&msg, SCPI_CMD_GET_SENSOR_CAP); test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size == 2); sensors = ((uint16_t *)msg.payload)[0]; test_complete(TEST_SENSOR_CAP); /* If the firmware has clocks, it must fully support clock control. */ if (sensors > 0) { test_begin(TEST_SENSOR_CMDS); test_assert(scpi_has_command(SCPI_CMD_GET_SENSOR_INFO)); test_assert(scpi_has_command(SCPI_CMD_GET_SENSOR)); test_complete(TEST_SENSOR_CMDS); } for (volatile uint16_t i = 0; i < sensors; ++i) { uint8_t class; uint32_t value; test_begin(TEST_SENSOR_INFO); scpi_prepare_msg(&msg, SCPI_CMD_GET_SENSOR_INFO); /* Send the sensor ID. */ msg.size = 2; ((uint16_t *)msg.payload)[0] = i; test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size >= 4); /* Assert that we got the same ID back. */ test_assert(((uint16_t *)msg.payload)[0] == i); /* Assert that the class is valid. */ class = ((uint8_t *)msg.payload)[2]; test_assert(class < SENSOR_CLASS_COUNT); /* Assert that the triggers are valid. */ test_assert((msg.payload[3] & ~(BIT(0) | BIT(1))) == 0); test_complete(TEST_SENSOR_INFO); /* Check the current value for sanity. */ test_begin(TEST_SENSOR_READ); scpi_prepare_msg(&msg, SCPI_CMD_GET_SENSOR); msg.size = 2; ((uint16_t *)msg.payload)[0] = i; test_send_request(&msg); test_assert(msg.status == SCPI_OK); test_assert(msg.size == 8); value = ((uint32_t *)msg.payload)[0]; /* Ensure temperature readings are below 100°C. */ if (class == SENSOR_CLASS_TEMPERATURE) test_assert(value < 100000); /* The firmware does not support more than 32-bit output. */ test_assert(((uint32_t *)msg.payload)[1] == 0); test_complete(TEST_SENSOR_READ); } } /* * Test: System power. */ static void try_sys_power(void) { struct scpi_msg msg; /* Positive tests would reset the machine; only test failure cases. */ test_begin(TEST_SYS_POWER); scpi_prepare_msg(&msg, SCPI_CMD_SET_SYS_POWER); msg.size = 1; /* 4 is not a valid system power state. */ ((uint8_t *)msg.payload)[0] = 4; test_send_request(&msg); test_assert(msg.status == SCPI_E_PARAM); scpi_prepare_msg(&msg, SCPI_CMD_SET_SYS_POWER); msg.size = 1; /* 20 is not a valid system power state. */ ((uint8_t *)msg.payload)[0] = 20; test_send_request(&msg); test_assert(msg.status == SCPI_E_PARAM); test_complete(TEST_SYS_POWER); } int main(int argc, char *argv[]) { void *mbox_map, *sram_map; int fd; static_assert(sizeof(struct scpi_msg) == SCPI_MESSAGE_SIZE, "struct scpi_msg does not have the correct size"); if (argc >= 2) { puts("ARISC firmware tester for " CONFIG_PLATFORM); printf("usage: %s [--help]\n", argv[0]); return strcmp("--help", argv[1]) ? EXIT_FAILURE : EXIT_SUCCESS; } /* Map the SCPI shared memory and the message box. */ fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd < 0) { perror("Failed to open /dev/mem"); return EXIT_FAILURE; } mbox_map = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PAGE_BASE(DEV_MSGBOX)); if (mbox_map == MAP_FAILED) { perror("Failed to mmap MSGBOX"); return EXIT_FAILURE; } sram_map = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PAGE_BASE(SRAM_A2_OFFSET + SCPI_MEM_BASE)); if (sram_map == MAP_FAILED) { perror("Failed to mmap SRAM A2"); return EXIT_FAILURE; } close(fd); /* Correct the addresses for mmap requiring page alignment. */ mbox = (uintptr_t)mbox_map + PAGE_OFFSET(DEV_MSGBOX); sram = (uintptr_t)sram_map + PAGE_OFFSET(SRAM_A2_OFFSET + SCPI_MEM_BASE); /* Set up the fatal error handler. */ if (sigsetjmp(main_buf, 0)) { test_summary(); return EXIT_FAILURE; } /* Run all tests. */ try_boot(); try_basic(); try_clocks(); try_css_power(); try_dvfs(); try_psus(); try_sensors(); try_sys_power(); /* Display a summary of the tests. */ test_summary(); /* Clean up. */ munmap(mbox_map, PAGESIZE); munmap(sram_map, PAGESIZE); return EXIT_SUCCESS; }