pax_global_header00006660000000000000000000000064150110643730014512gustar00rootroot0000000000000052 comment=58e5caa273752a88de064e9cd3ffde4e643baada raspi-utils-20250514/000077500000000000000000000000001501106437300142325ustar00rootroot00000000000000raspi-utils-20250514/.gitignore000066400000000000000000000010711501106437300162210ustar00rootroot00000000000000# # NOTE! Don't add files that are generated in specific # subdirectories here. Add them in the ".gitignore" file # in that subdirectory instead. # # NOTE! Please use 'git ls-files -i --exclude-standard' # command after changing this file, to see if there are # any tracked files which get ignored after the change. # # Normal rules (sorted alphabetically) # .* *.a *.bin *.bz2 *.c.[012]*.* *.dtb* *.dtb.S *.dwo *.elf *.exe *.gcno *.gz *.i *.ko *.ll *.lst *.lz4 *.lzma *.lzo *.mod.c *.o *.o.* *.order *.orig *.patch *.s *.so *.so.dbg *.su *.symtypes *.tar *.xz *~ \#*# raspi-utils-20250514/CMakeLists.txt000066400000000000000000000006271501106437300167770ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.1...3.27) project(utils) # List of subsidiary CMakeLists add_subdirectory(dtmerge) add_subdirectory(eeptools) add_subdirectory(kdtc) add_subdirectory(otpset) add_subdirectory(overlaycheck) add_subdirectory(ovmerge) add_subdirectory(pinctrl) add_subdirectory(piolib) add_subdirectory(raspinfo) add_subdirectory(vcgencmd) add_subdirectory(vclog) add_subdirectory(vcmailbox) raspi-utils-20250514/LICENCE000066400000000000000000000027651501106437300152310ustar00rootroot00000000000000Copyright (c) 2023, Raspberry Pi Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * 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. raspi-utils-20250514/README.md000066400000000000000000000034261501106437300155160ustar00rootroot00000000000000# utils A collection of scripts and simple applications * [dtmerge](dtmerge/) - A tool for applying compiled DT overlays (`*.dtbo`) to base Device Tree files (`*.dtb`). Also includes the `dtoverlay` and `dtparam` utilities. * [eeptools](eeptools/) - Tools for creating and managing EEPROMs for HAT+ and HAT board. * [kdtc](kdtc/) - A tool for compiling overlays with #includes, etc., as used in the kernel tree. * [otpset](otpset/) - A short script to help with reading and setting the customer OTP bits. * [overlaycheck](overlaycheck/) - A tool for validating the overlay files and README in a kernel source tree. * [ovmerge](ovmerge/) - A tool for merging DT overlay source files (`*-overlay.dts`), flattening and sorting `.dts` files for easy comparison, displaying the include tree, etc. * [pinctrl](pinctrl/) - A more powerful replacement for raspi-gpio, a tool for displaying and modifying the GPIO and pin muxing state of a system, bypassing the kernel. * [piolib](piolib/) - A library for accessing the Pi 5's PIO hardware. * [raspinfo](raspinfo/) - A short script to dump information about the Pi. Intended for the submission of bug reports. * [vclog](vclog/) - A tool to get VideoCore 'assert' or 'msg' logs with optional -f to wait for new logs to arrive. **Build Instructions** Install the prerequisites with "sudo apt install cmake device-tree-compiler libfdt-dev" - you need at least version 3.10 of cmake. Run the following commands to build and install everything, or see the README files in the subdirectories to just build utilities individually: - *cmake .* N.B. Use *cmake -DBUILD_SHARED_LIBS=1 .* to build the libraries in the subprojects (libdtovl, gpiolib and piolib) as shared (as opposed to static) libraries. - *make* - *sudo make install* raspi-utils-20250514/dtmerge/000077500000000000000000000000001501106437300156615ustar00rootroot00000000000000raspi-utils-20250514/dtmerge/CMakeLists.txt000066400000000000000000000023271501106437300204250ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) #set project name project(dtmerge) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror") if (CMAKE_COMPILER_IS_GNUCC) add_definitions (-ffunction-sections) endif () add_library (dtovl dtoverlay.c) target_link_libraries(dtovl fdt) set_target_properties(dtovl PROPERTIES PUBLIC_HEADER dtoverlay.h) set_target_properties(dtovl PROPERTIES SOVERSION 0) install(TARGETS dtovl ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) add_executable(dtmerge dtmerge.c) target_link_libraries(dtmerge dtovl) install(TARGETS dtmerge RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES dtmerge.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) add_executable(dtoverlay dtoverlay_main.c utils.c) target_link_libraries(dtoverlay dtovl) install(TARGETS dtoverlay RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES dtoverlay.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) add_custom_command(TARGET dtoverlay POST_BUILD COMMAND ln;-sf;dtoverlay;dtparam) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dtparam DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES dtparam.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) raspi-utils-20250514/dtmerge/README.md000066400000000000000000000061251501106437300171440ustar00rootroot00000000000000 # dtmerge, dtoverlay and dtparam dtmerge is a tool for applying pre-compiled overlays (`*.dtbo` files) to a base Device Tree file (`*.dtb`). dtoverlay is a tool for applying pre-compiled overlays to a live system. dtparam is a tool for applying paramaters of the base Device Tree of a live system. **Build Instructions** Install the prerequisites with "sudo apt install cmake libfdt-dev" - you need at least version 3.10 of cmake. Run the following commands here, or in the top-level directory to build and install all the utilities: - *cmake .* N.B. Use *cmake -DBUILD_SHARED_LIBS=1 .* to build libdtovl as a shared (as opposed to static) library. - *make* - *sudo make install* **Usage** ``` Usage: dtmerge [ - [param=value] ... to apply a parameter to the base dtb (like dtparam) dtmerge [ [param=value] ... to apply an overlay with parameters (like dtoverlay) where is any of: -d Enable debug output -h Show this help message ``` ``` Usage: dtoverlay [=...] Add an overlay (with parameters) dtoverlay -D Dry-run (prepare overlay, but don't apply - save it as dry-run.dtbo) dtoverlay -r [] Remove an overlay (by name, index or the last) dtoverlay -R [] Remove from an overlay (by name, index or all) dtoverlay -l List active overlays/params dtoverlay -a List all overlays (marking the active) dtoverlay -h Show this usage message dtoverlay -h Display help on an overlay dtoverlay -h .. Or its parameters where is the name of an overlay or 'dtparam' for dtparams Options applicable to most variants: -d Specify an alternate location for the overlays (defaults to /boot/overlays or /flash/overlays) -p Force a compatible string for the platform -v Verbose operation Adding or removing overlays and parameters requires root privileges. ``` ``` Usage: dtparam Display help on all parameters dtparam =... Add an overlay (with parameters) dtparam -D Dry-run (prepare overlay, but don't apply - save it as dry-run.dtbo) dtparam -r [] Remove an overlay (by index, or the last) dtparam -R [] Remove from an overlay (by index, or all) dtparam -l List active overlays/dtparams dtparam -a List all overlays/dtparams (marking the active) dtparam -h Show this usage message dtparam -h ... Display help on the listed parameters Options applicable to most variants: -d Specify an alternate location for the overlays (defaults to /boot/overlays or /flash/overlays) -p Force a compatible string for the platform -v Verbose operation Adding or removing overlays and parameters requires root privileges. ``` raspi-utils-20250514/dtmerge/dtmerge.1000066400000000000000000000027161501106437300174000ustar00rootroot00000000000000.TH DTMERGE 1 . .SH NAME dtmerge \- merge an overlay or parameters into a base device-tree . . .SH SYNOPSIS .SY dtmerge .OP \-d .I base-dtb .I merged-dtb .I overlay-dtb .RI [ param=val \|.\|.\|.] .YS . .SY dtmerge .B \-h .YS . . .SH DESCRIPTION .B dtmerge is a command line utility for merging overlays and/or device-tree parameters with a base device-tree. See .B [DTREE] for more information. . .PP The base device-tree (in dtb format) is always specified as the first (non-option) argument. The second argument is the output filename, which will also be in dtb format. The third argument provides the name of the overlay to merge into the base tree. If this is "-" then no overlay is used and the utility will simply customize the base tree with any parameters given. . . .SH OPTIONS . .TP .BR \-d Show debug output during operation. . .TP .BR \-h Displays help on the application. . . .SH EXAMPLES . .TP .B dtmerge /boot/bcm2711-rpi-4-b.dtb out.dtb - spi=on i2c=on Produce a device-tree for the Raspberry Pi 4 in "out.dtb" which has the SPI and I2C interfaces activated. . .TP .B dtmerge /boot/bcm2710-rpi-3-b-plus.dtb out.dtb /boot/overlays/gpio-shutdown.dtbo Produce a device-tree for the Raspberry Pi 3+ in "out.dtb" which includes the GPIO shutdown overlay (with all parameters set to their default). . . .SH SEE ALSO .BR dtoverlay (1), .BR dtparam (1), .B [DTREE] . . .SH REFERENCES .TP .B [DTREE] https://www.raspberrypi.com/documentation/computers/configuration.html#part5.2 raspi-utils-20250514/dtmerge/dtmerge.c000066400000000000000000000141001501106437300174500ustar00rootroot00000000000000/* Copyright (c) 2016 Raspberry Pi (Trading) Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * 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. */ #include #include #include #include "dtoverlay.h" static void usage(void) { printf("Usage:\n"); printf(" dtmerge [ - [param=value] ...\n"); printf(" to apply a parameter to the base dtb (like dtparam)\n"); printf(" dtmerge [ [param=value] ...\n"); printf(" to apply an overlay with parameters (like dtoverlay)\n"); printf(" where is any of:\n"); printf(" -d Enable debug output\n"); printf(" -h Show this help message\n"); exit(1); } int main(int argc, char **argv) { const char *base_file; const char *merged_file; const char *overlay_file; const char *compatible; char *overlay_dir; char *p; DTBLOB_T *base_dtb; DTBLOB_T *overlay_dtb; int err = 0; int argn = 1; int max_dtb_size = 200000; int compatible_len; while ((argn < argc) && (argv[argn][0] == '-')) { const char *arg = argv[argn++]; if ((strcmp(arg, "-d") == 0) || (strcmp(arg, "--debug") == 0)) dtoverlay_enable_debug(1); else if ((strcmp(arg, "-h") == 0) || (strcmp(arg, "--help") == 0)) usage(); else { printf("* Unknown option '%s'\n", arg); usage(); } } if (argc < (argn + 3)) { usage(); } base_file = argv[argn++]; merged_file = argv[argn++]; overlay_file = argv[argn++]; base_dtb = dtoverlay_load_dtb(base_file, max_dtb_size); if (!base_dtb) { printf("* failed to load '%s'\n", base_file); return -1; } if (strnlen(overlay_file, DTOVERLAY_MAX_PATH) == DTOVERLAY_MAX_PATH) { printf("* overlay filename too long\n"); return -1; } overlay_dir = strdup(overlay_file); p = strrchr(overlay_dir, '/'); if (p) *p = 0; else overlay_dir = "."; compatible = dtoverlay_get_property(base_dtb, dtoverlay_find_node(base_dtb, "/", 1), "compatible", &compatible_len); dtoverlay_init_map(overlay_dir, compatible, compatible_len); if (!dtoverlay_get_alias(base_dtb, "i2c")) { err = dtoverlay_set_synonym(base_dtb, "i2c_arm", "i2c0"); err = dtoverlay_set_synonym(base_dtb, "i2c_vc", "i2c1"); err = dtoverlay_set_synonym(base_dtb, "i2c_baudrate", "i2c0_baudrate"); err = dtoverlay_set_synonym(base_dtb, "i2c_arm_baudrate", "i2c0_baudrate"); err = dtoverlay_set_synonym(base_dtb, "i2c_vc_baudrate", "i2c1_baudrate"); }; if (strcmp(overlay_file, "-") == 0) { overlay_dtb = base_dtb; } else { char new_file[DTOVERLAY_MAX_PATH]; char *overlay_name; const char *new_name; char *p; int len; strcpy(new_file, overlay_file); overlay_name = strrchr(new_file, '/'); if (overlay_name) overlay_name++; else overlay_name = new_file; p = strrchr(overlay_name, '.'); if (p) *p = 0; new_name = dtoverlay_remap_overlay(overlay_name); if (new_name) { if (strcmp(overlay_name, new_name)) dtoverlay_debug("mapped overlay '%s' to '%s'", overlay_name, new_name); len = strlen(new_name); memmove(overlay_name, new_name, len); strcpy(overlay_name + len, ".dtbo"); overlay_dtb = dtoverlay_load_dtb(new_file, max_dtb_size); if (overlay_dtb) err = dtoverlay_fixup_overlay(base_dtb, overlay_dtb); else err = -1; } else { overlay_dtb = NULL; err = -2; } } while (!err && (argn < argc)) { char *param_name = argv[argn++]; char *param_value = param_name + strcspn(param_name, "="); const void *override_data; int data_len; if (*param_value == '=') { *(param_value++) = '\0'; } else { /* This isn't a well-formed parameter assignment, but it can be treated as an assignment of true. */ param_value = "true"; } override_data = dtoverlay_find_override(overlay_dtb, param_name, &data_len); if (override_data) { err = dtoverlay_apply_override(overlay_dtb, param_name, override_data, data_len, param_value); } else { override_data = dtoverlay_find_override(base_dtb, param_name, &data_len); if (override_data) { err = dtoverlay_apply_override(base_dtb, param_name, override_data, data_len, param_value); } else { printf("* unknown param '%s'\n", param_name); err = data_len; } } } if (!err && (overlay_dtb != base_dtb)) { err = dtoverlay_merge_overlay(base_dtb, overlay_dtb); dtoverlay_free_dtb(overlay_dtb); } if (!err) { dtoverlay_pack_dtb(base_dtb); err = dtoverlay_save_dtb(base_dtb, merged_file); } dtoverlay_free_dtb(base_dtb); return err; } raspi-utils-20250514/dtmerge/dtoverlay.1000066400000000000000000000066571501106437300177720ustar00rootroot00000000000000.TH DTOVERLAY 1 . .SH NAME dtoverlay \- load a device-tree overlay . . .SH SYNOPSIS .SY dtoverlay .OP \-d dir .OP \-p string .OP \-v .OP \-D .OP \-r .OP \-R .OP \-l .OP \-a .RI [ overlay " [" param=val \|.\|.\|.]] .YS . .SY dtoverlay .B \-h .RI [ overlay " [" param ]] .YS . . .SH DESCRIPTION .B dtoverlay is a command line utility for manipulating the system's runtime device-tree by adding or removing overlays. It can also customize the base device-tree or overlays by setting parameters within them. See .B [DTREE] for more information. . .PP By default, without the .B -r or .B -R options, the application adds the specified overlay. Manipulation of active device-tree overlays usually requires root privileges. . . .SH OPTIONS . .TP .BR \-a Lists all defined device-tree overlays, placing a "*" next to those that are currently loaded. . .TP .BR \-d " \fIdir\fR" Specifies an alternate location path from which to load overlays. The utility defaults to "/boot/overlays" or "/flash/overlays". . .TP .BR \-D Dry-run; prepares the specified overlay but doesn't apply it. The resulting prepared overlay is saved as "dry-run.dtbo". . .TP .BR \-h [\fIoverlay\fR] [\fIparam\fR] If given without .I overlay or .I param displays help on the application overall. If .I overlay is specified, prints help on that overlay. Finally, if .I param is also specified, prints help on that parameter of the overlay. To query parameters on the base overlay, specify "dtparam" as the .IR overlay . . .TP .BR \-l Lists all currently loaded device-tree overlays, in the order they were loaded. This also specifies the index of each overlay. . .TP .BR \-p " \fIstring\fR" Override the platform's "compatible" string, normally read from "/proc/device-tree/compatible". This is used with the overlay map to determine which platform-specific overlay to read (if necessary). . .TP .BR \-r Remove overlay. With this option, the utility will remove the specified overlay. If no overlay is given, the last overlay loaded will be removed. Overlays can be specified by name or by index. . .TP .BR \-R Remove the specified overlay, and all subsequent overlays that were loaded after it. If no overlay is given, all overlays will be removed. Overlays can be specified by name or by index. . .TP .BR \-v Verbose operation; the utility will produce more output. . . .SH EXAMPLES . .TP .B sudo dtoverlay w1-gpio Load the w1-gpio overlay (to enable Dallas 1-Wire on GPIO4). Note that root privileges are usually required for manipulating the device-tree overlays (hence, sudo in the example). . .TP .B dtoverlay -l Show the loaded overlays. . .TP .B sudo dtoverlay -r Remove the last loaded overlay. . .TP .B sudo dtoverlay gpio-shutdown gpio_pin=7 active_low=0 gpio_pull=down Load the gpio-shutdown overlay specifying that it should pull GPIO7 down and monitor it for a rising edge to initiate shutdown. . .TP .B dtoverlay -h gpio-shutdown Display help for the gpio-shutdown overlay . .TP .B sudo dtoverlay -r gpio-shutdown Remove the gpio-shutdown overlay, wherever it is in the load order. . . .SH HOOKS .B dtoverlay attempts to call two executables (shell scripts by default) during its operation: .B dtoverlay-pre prior to making device-tree modifications, and .B dtoverlay-post afterwards. Each executable is optional and will be ignored if not present. . . .SH SEE ALSO .BR dtparam (1), .BR dtmerge (1), .B [DTREE] . . .SH REFERENCES .TP .B [DTREE] https://www.raspberrypi.com/documentation/computers/configuration.html#part3.5.1 raspi-utils-20250514/dtmerge/dtoverlay.2000066400000000000000000000066571501106437300177730ustar00rootroot00000000000000.TH DTOVERLAY 1 . .SH NAME dtoverlay \- load a device-tree overlay . . .SH SYNOPSIS .SY dtoverlay .OP \-d dir .OP \-p string .OP \-v .OP \-D .OP \-r .OP \-R .OP \-l .OP \-a .RI [ overlay " [" param=val \|.\|.\|.]] .YS . .SY dtoverlay .B \-h .RI [ overlay " [" param ]] .YS . . .SH DESCRIPTION .B dtoverlay is a command line utility for manipulating the system's runtime device-tree by adding or removing overlays. It can also customize the base device-tree or overlays by setting parameters within them. See .B [DTREE] for more information. . .PP By default, without the .B -r or .B -R options, the application adds the specified overlay. Manipulation of active device-tree overlays usually requires root privileges. . . .SH OPTIONS . .TP .BR \-a Lists all defined device-tree overlays, placing a "*" next to those that are currently loaded. . .TP .BR \-d " \fIdir\fR" Specifies an alternate location path from which to load overlays. The utility defaults to "/boot/overlays" or "/flash/overlays". . .TP .BR \-D Dry-run; prepares the specified overlay but doesn't apply it. The resulting prepared overlay is saved as "dry-run.dtbo". . .TP .BR \-h [\fIoverlay\fR] [\fIparam\fR] If given without .I overlay or .I param displays help on the application overall. If .I overlay is specified, prints help on that overlay. Finally, if .I param is also specified, prints help on that parameter of the overlay. To query parameters on the base overlay, specify "dtparam" as the .IR overlay . . .TP .BR \-l Lists all currently loaded device-tree overlays, in the order they were loaded. This also specifies the index of each overlay. . .TP .BR \-p " \fIstring\fR" Override the platform's "compatible" string, normally read from "/proc/device-tree/compatible". This is used with the overlay map to determine which platform-specific overlay to read (if necessary). . .TP .BR \-r Remove overlay. With this option, the utility will remove the specified overlay. If no overlay is given, the last overlay loaded will be removed. Overlays can be specified by name or by index. . .TP .BR \-R Remove the specified overlay, and all subsequent overlays that were loaded after it. If no overlay is given, all overlays will be removed. Overlays can be specified by name or by index. . .TP .BR \-v Verbose operation; the utility will produce more output. . . .SH EXAMPLES . .TP .B sudo dtoverlay w1-gpio Load the w1-gpio overlay (to enable Dallas 1-Wire on GPIO4). Note that root privileges are usually required for manipulating the device-tree overlays (hence, sudo in the example). . .TP .B dtoverlay -l Show the loaded overlays. . .TP .B sudo dtoverlay -r Remove the last loaded overlay. . .TP .B sudo dtoverlay gpio-shutdown gpio_pin=7 active_low=0 gpio_pull=down Load the gpio-shutdown overlay specifying that it should pull GPIO7 down and monitor it for a rising edge to initiate shutdown. . .TP .B dtoverlay -h gpio-shutdown Display help for the gpio-shutdown overlay . .TP .B sudo dtoverlay -r gpio-shutdown Remove the gpio-shutdown overlay, wherever it is in the load order. . . .SH HOOKS .B dtoverlay attempts to call two executables (shell scripts by default) during its operation: .B dtoverlay-pre prior to making device-tree modifications, and .B dtoverlay-post afterwards. Each executable is optional and will be ignored if not present. . . .SH SEE ALSO .BR dtparam (1), .BR dtmerge (1), .B [DTREE] . . .SH REFERENCES .TP .B [DTREE] https://www.raspberrypi.com/documentation/computers/configuration.html#part3.5.1 raspi-utils-20250514/dtmerge/dtoverlay.c000066400000000000000000002776351501106437300200620ustar00rootroot00000000000000/* Copyright (c) 2016-2025 Raspberry Pi Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * 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. */ #include #include #include #include #include #include #include "dtoverlay.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define UNUSED(x) (void)(x) typedef enum { FIXUP_ABSOLUTE, FIXUP_RELATIVE } fixup_type_t; #define DTOVERRIDE_END 0 #define DTOVERRIDE_INTEGER 1 #define DTOVERRIDE_BOOLEAN 2 #define DTOVERRIDE_BOOLEAN_INV 3 #define DTOVERRIDE_STRING 4 #define DTOVERRIDE_OVERLAY 5 #define DTOVERRIDE_BYTE_STRING 6 static int dtoverlay_extract_override(const char *override_name, char *override_value, int value_size, int *phandle_ptr, const char **datap, const char *dataendp, const char **namep, int *namelenp, int *offp, int *sizep); static const char *dtoverlay_lookup_key(const char *lookup_string, const char *data_end, const char *key, char *buf, int buf_len); static int dtoverlay_set_node_name(DTBLOB_T *dtb, int node_off, const char *name); static void dtoverlay_stdio_logging(dtoverlay_logging_type_t type, const char *fmt, va_list args); #define phandle_debug if (0) dtoverlay_debug static DTOVERLAY_LOGGING_FUNC *dtoverlay_logging_func = dtoverlay_stdio_logging; static int dtoverlay_debug_enabled = 0; static DTBLOB_T *overlay_map; static const char *platform_name; static int platform_name_len; static void (*cell_changed_callback)(DTBLOB_T *, int, const char *, int, int); static void (*intra_fragment_merged_callback)(DTBLOB_T *, int, int); static const void *override_data_start; static const void *cell_source; static int strmemcmp(const char *mem, int mem_len, const char *str) { int ret = strncmp(mem, str, mem_len); if (ret == 0 && str[mem_len] != 0) ret = 1; return ret; } uint8_t dtoverlay_read_u8(const void *src, int off) { const unsigned char *p = src; return (p[off + 0] << 0); } uint16_t dtoverlay_read_u16(const void *src, int off) { const unsigned char *p = src; return (p[off + 0] << 8) | (p[off + 1] << 0); } uint32_t dtoverlay_read_u32(const void *src, int off) { const unsigned char *p = src; return (p[off + 0] << 24) | (p[off + 1] << 16) | (p[off + 2] << 8) | (p[off + 3] << 0); } uint64_t dtoverlay_read_u64(const void *src, int off) { const unsigned char *p = src; return ((uint64_t)p[off + 0] << 56) | ((uint64_t)p[off + 1] << 48) | ((uint64_t)p[off + 2] << 40) | ((uint64_t)p[off + 3] << 32) | (p[off + 4] << 24) | (p[off + 5] << 16) | (p[off + 6] << 8) | (p[off + 7] << 0); } void dtoverlay_write_u8(void *dst, int off, uint32_t val) { unsigned char *p = dst; p[off] = (val >> 0) & 0xff; } void dtoverlay_write_u16(void *dst, int off, uint32_t val) { unsigned char *p = dst; p[off + 0] = (val >> 8) & 0xff; p[off + 1] = (val >> 0) & 0xff; } void dtoverlay_write_u32(void *dst, int off, uint32_t val) { unsigned char *p = dst; p[off + 0] = (val >> 24) & 0xff; p[off + 1] = (val >> 16) & 0xff; p[off + 2] = (val >> 8) & 0xff; p[off + 3] = (val >> 0) & 0xff; } void dtoverlay_write_u64(void *dst, int off, uint64_t val) { unsigned char *p = dst; p[off + 0] = (val >> 56) & 0xff; p[off + 1] = (val >> 48) & 0xff; p[off + 2] = (val >> 40) & 0xff; p[off + 3] = (val >> 32) & 0xff; p[off + 4] = (val >> 24) & 0xff; p[off + 5] = (val >> 16) & 0xff; p[off + 6] = (val >> 8) & 0xff; p[off + 7] = (val >> 0) & 0xff; } // Returns the offset of the node indicated by the absolute path, creating // it and any intermediates as necessary, or a negative error code. int dtoverlay_create_node(DTBLOB_T *dtb, const char *node_path, int path_len) { const char *path_ptr; const char *path_end; int node_off = 0; if (!path_len) path_len = strlen(node_path); path_ptr = node_path; path_end = node_path + path_len; if ((path_len > 0) && (path_ptr[path_len - 1] == '/')) path_end--; while (path_ptr < path_end) { const char *path_next; int subnode_off; if (*path_ptr != '/') return -FDT_ERR_BADPATH; // find the next path separator (or the end of the string) path_ptr++; for (path_next = path_ptr; (path_next != path_end) && (*path_next != '/'); path_next++) continue; subnode_off = fdt_subnode_offset_namelen(dtb->fdt, node_off, path_ptr, path_next - path_ptr); if (subnode_off >= 0) node_off = subnode_off; else node_off = fdt_add_subnode_namelen(dtb->fdt, node_off, path_ptr, path_next - path_ptr); if (node_off < 0) break; path_ptr = path_next; } if ((node_off >= 0) && (path_ptr != path_end)) return -FDT_ERR_BADPATH; return node_off; } // Returns 0 on success, otherwise <0 error code int dtoverlay_delete_node(DTBLOB_T *dtb, const char *node_path, int path_len) { int node_off = 0; if (!path_len) path_len = strlen(node_path); dtoverlay_debug("delete_node(%.*s)", path_len, node_path); node_off = fdt_path_offset_namelen(dtb->fdt, node_path, path_len); if (node_off < 0) return node_off; return fdt_del_node(dtb->fdt, node_off); } // Returns the offset of the node indicated by the absolute path or a negative // error code. int dtoverlay_find_node(DTBLOB_T *dtb, const char *node_path, int path_len) { if (!path_len) path_len = strlen(node_path); return fdt_path_offset_namelen(dtb->fdt, node_path, path_len); } int dtoverlay_first_subnode(DTBLOB_T *dtb, int node_off) { return fdt_first_subnode(dtb->fdt, node_off); } int dtoverlay_next_subnode(DTBLOB_T *dtb, int subnode_off) { return fdt_next_subnode(dtb->fdt, subnode_off); } // Returns 0 on success, otherwise <0 error code int dtoverlay_set_node_properties(DTBLOB_T *dtb, const char *node_path, DTOVERLAY_PARAM_T *properties, unsigned int num_properties) { int err = 0; int node_off; node_off = fdt_path_offset(dtb->fdt, node_path); if (node_off < 0) node_off = dtoverlay_create_node(dtb, node_path, 0); if (node_off >= 0) { unsigned int i; for (i = 0; (i < num_properties) && (err == 0); i++) { DTOVERLAY_PARAM_T *p; p = properties + i; err = fdt_setprop(dtb->fdt, node_off, p->param, p->b, p->len); } } else err = node_off; return err; } struct dynstring { char *buf; int size; int len; }; static void dynstring_init(struct dynstring *ds) { ds->size = 0; ds->len = 0; ds->buf = NULL; } static int dynstring_init_size(struct dynstring *ds, int initial_size) { if (initial_size < 32) initial_size = 32; ds->size = 0; ds->len = 0; ds->buf = malloc(initial_size); if (!ds->buf) { dtoverlay_error(" out of memory"); return -FDT_ERR_NOSPACE; } ds->size = initial_size; return 0; } static int dynstring_set_size(struct dynstring *ds, int size) { if (size > ds->size) { size = (size * 5)/4; // Add a 25% headroom ds->buf = realloc(ds->buf, size); if (!ds->buf) { dtoverlay_error(" out of memory"); return -FDT_ERR_NOSPACE; } ds->size = size; } else if (size < 0) { return -FDT_ERR_BADVALUE; } return 0; } static int dynstring_dup(struct dynstring *ds, const char *src, int len) { int err = 0; if (!len) len = strlen(src); if (len < 0) return -FDT_ERR_BADVALUE; err = dynstring_set_size(ds, len + 1); if (!err) { memcpy(ds->buf, src, len + 1); ds->len = len; } return err; } static int dynstring_patch(struct dynstring *ds, int pos, int width, const char *src, int len) { int newlen = ds->len + (len - width); int err = dynstring_set_size(ds, newlen + 1); if (!err) { if (width != len) { // Move any data following the patch memmove(ds->buf + pos + len, ds->buf + pos + width, ds->len + 1 - (pos + width)); ds->len = newlen; } memcpy(ds->buf + pos, src, len); } return err; } static int dynstring_grow(struct dynstring *ds) { return dynstring_set_size(ds, (3*ds->size)/2); } static void dynstring_free(struct dynstring *ds) { free(ds->buf); dynstring_init(ds); } static int dtoverlay_set_node_name(DTBLOB_T *dtb, int node_off, const char *name) { struct dynstring path_buf; struct dynstring prop_buf; char *old_path; const char *old_name; const char *fixup_nodes[] = { "/__fixups__", "/__local_fixups__", // For old-style dtbos "/__symbols__" // Just in case the kernel cares }; int old_name_len; int old_path_len; // All of it int dir_len; // Excluding the node name, but with the trailling slash int name_len; int offset; unsigned int fixup_idx; int err = 0; // Fixups and local-fixups both use node names, so this // function must patch them up when a node is renamed // unless the fixups have already been applied. // Calculating a node's name is expensive, so only do it if // necessary. Since renaming a node can move things around, // don't use node_off afterwards. err = dynstring_init_size(&path_buf, 100); if (err) return err; if (!dtb->fixups_applied) { while (1) { err = fdt_get_path(dtb->fdt, node_off, path_buf.buf, path_buf.size); if (!err) break; if (err != -FDT_ERR_NOSPACE) return err; dynstring_grow(&path_buf); } } old_path = path_buf.buf; err = fdt_set_name(dtb->fdt, node_off, name); if (err || dtb->fixups_applied) goto clean_up; // Find the node name in old_path old_name = strrchr(old_path, '/'); assert(old_name); if (!old_name) return -FDT_ERR_INTERNAL; old_name++; old_name_len = strlen(old_name); dir_len = old_name - old_path; old_path_len = dir_len + old_name_len; // Short-circuit the case where the name isn't changing if (strcmp(name, old_name) == 0) goto clean_up; name_len = strlen(name); // Search the fixups and symbols for the old path (including as // a parent) and replace with the new name dynstring_init(&prop_buf); for (fixup_idx = 0; fixup_idx < ARRAY_SIZE(fixup_nodes); fixup_idx++) { int prop_off; offset = fdt_path_offset(dtb->fdt, fixup_nodes[fixup_idx]); if (offset > 0) { // Iterate through the properties for (prop_off = fdt_first_property_offset(dtb->fdt, offset); (prop_off >= 0) && (err == 0); prop_off = fdt_next_property_offset(dtb->fdt, prop_off)) { const char *prop_name; const char *prop_val; int prop_len; int pos; int changed = 0; prop_val = fdt_getprop_by_offset(dtb->fdt, prop_off, &prop_name, &prop_len); err = dynstring_dup(&prop_buf, prop_val, prop_len); if (err) break; // Scan each property for matching paths pos = 0; while (pos < prop_len) { if ((pos + old_path_len < prop_len) && (memcmp(prop_buf.buf + pos, old_path, old_path_len) == 0) && ((prop_buf.buf[pos + old_path_len] == ':') || (prop_buf.buf[pos + old_path_len] == '/') || (prop_buf.buf[pos + old_path_len] == '\0'))) { // Patch the string, replacing old name with new err = dynstring_patch(&prop_buf, pos + dir_len, old_name_len, name, name_len); if (err) break; prop_len += name_len - old_name_len; changed = 1; } pos += strlen(prop_buf.buf + pos) + 1; } if (!err && changed) { // Caution - may change offsets, but only by shuffling everything // afterwards, i.e. the offset to this node or property does not // change. err = fdt_setprop(dtb->fdt, offset, prop_name, prop_buf.buf, prop_len); } } } } dynstring_free(&prop_buf); if (err) goto clean_up; // Then look for a "/__local_fixups__" node, and rename // that as well. offset = fdt_path_offset(dtb->fdt, "/__local_fixups__"); if (offset > 0) { const char *p, *end; p = old_path; end = old_path + old_path_len; while (p < end) { const char *q; while (*p == '/') { p++; if (p == end) break; } q = memchr(p, '/', end - p); if (! q) q = end; offset = fdt_subnode_offset_namelen(dtb->fdt, offset, p, q-p); if (offset < 0) break; p = q; } if (offset > 0) err = fdt_set_name(dtb->fdt, offset, name); } // __overrides__ don't need patching because nodes are identified // using phandles, which are unaffected by renaming and resizing nodes. clean_up: dynstring_free(&path_buf); return err; } // Returns 0 on success, otherwise <0 error code int dtoverlay_create_prop_fragment(DTBLOB_T *dtb, int idx, int target_phandle, const char *prop_name, const void *prop_data, int prop_len) { char fragment_name[20]; int frag_off, ovl_off; int ret; snprintf(fragment_name, sizeof(fragment_name), "fragment-%u", idx); frag_off = fdt_add_subnode(dtb->fdt, 0, fragment_name); if (frag_off < 0) return frag_off; ret = fdt_setprop_u32(dtb->fdt, frag_off, "target", target_phandle); if (ret < 0) return ret; ovl_off = fdt_add_subnode(dtb->fdt, frag_off, "__overlay__"); if (ovl_off < 0) return ovl_off; return fdt_setprop(dtb->fdt, ovl_off, prop_name, prop_data, prop_len); } // Returns 0 on success, otherwise <0 error code int dtoverlay_merge_fragment(DTBLOB_T *base_dtb, int target_off, const DTBLOB_T *overlay_dtb, int overlay_off, int depth) { int prop_off, subnode_off; int err = 0; if (dtoverlay_debug_enabled) { char base_path[DTOVERLAY_MAX_PATH]; char overlay_path[DTOVERLAY_MAX_PATH]; fdt_get_path(base_dtb->fdt, target_off, base_path, sizeof(base_path)); fdt_get_path(overlay_dtb->fdt, overlay_off, overlay_path, sizeof(overlay_path)); dtoverlay_debug("merge_fragment(%s,%s)", base_path, overlay_path); } // Merge each property of the node for (prop_off = fdt_first_property_offset(overlay_dtb->fdt, overlay_off); (prop_off >= 0) && (err == 0); prop_off = fdt_next_property_offset(overlay_dtb->fdt, prop_off)) { const char *prop_name; const void *prop_val; int prop_len; struct fdt_property *target_prop; int target_len; prop_val = fdt_getprop_by_offset(overlay_dtb->fdt, prop_off, &prop_name, &prop_len); /* Skip these system properties (only phandles in the first level) */ if ((strcmp(prop_name, "name") == 0) || ((depth == 0) && ((strcmp(prop_name, "phandle") == 0) || (strcmp(prop_name, "linux,phandle") == 0)))) continue; dtoverlay_debug(" +prop(%s)", prop_name); if ((strcmp(prop_name, "bootargs") == 0) && ((target_prop = fdt_get_property_w(base_dtb->fdt, target_off, prop_name, &target_len)) != NULL) && (target_len > 0) && *target_prop->data) { target_prop->data[target_len - 1] = ' '; err = fdt_appendprop(base_dtb->fdt, target_off, prop_name, prop_val, prop_len); } else { err = fdt_setprop(base_dtb->fdt, target_off, prop_name, prop_val, prop_len); } } // Merge each subnode of the node for (subnode_off = fdt_first_subnode(overlay_dtb->fdt, overlay_off); (subnode_off >= 0) && (err == 0); subnode_off = fdt_next_subnode(overlay_dtb->fdt, subnode_off)) { const char *subnode_name; int name_len; int subtarget_off; subnode_name = fdt_get_name(overlay_dtb->fdt, subnode_off, &name_len); subtarget_off = fdt_subnode_offset_namelen(base_dtb->fdt, target_off, subnode_name, name_len); if (subtarget_off < 0) subtarget_off = fdt_add_subnode_namelen(base_dtb->fdt, target_off, subnode_name, name_len); if (subtarget_off >= 0) { err = dtoverlay_merge_fragment(base_dtb, subtarget_off, overlay_dtb, subnode_off, depth + 1); } else { err = subtarget_off; } } dtoverlay_debug("merge_fragment() end"); return err; } static int dtoverlay_phandle_relocate(DTBLOB_T *dtb, int node_off, const char *prop_name, uint32_t phandle_increment) { int len; const fdt32_t *prop_val = fdt_getprop(dtb->fdt, node_off, prop_name, &len); int err = 0; // The absence of the property is not an error if (prop_val) { uint32_t phandle; if (len < 4) { dtoverlay_error("%s property too small", prop_name); return -FDT_ERR_BADSTRUCTURE; } phandle = fdt32_to_cpu(*prop_val) + phandle_increment; phandle_debug(" phandle_relocate %d->%d", fdt32_to_cpu(*prop_val), phandle); err = fdt_setprop_inplace_u32(dtb->fdt, node_off, prop_name, phandle); } return err; } // Returns 0 on success, or an FDT error code static int dtoverlay_apply_fixups(DTBLOB_T *dtb, const char *fixups_stringlist, int fixups_len, uint32_t phandle, fixup_type_t type) { // The fixups arrive as a sequence of NUL-terminated strings, of the form: // "path:property:offset" // Use an empty string as an end marker, since: // 1) all tags begin 0x00 0x00 0x00, // 2) all string properties must be followed by a tag, // 3) an empty string is not a valid fixup, and // 4) the code is simpler as a result. const char *fixup = fixups_stringlist; const char *end = fixup + fixups_len; while (fixup < end && fixup[0]) { const char *prop_name, *offset_str; char *offset_end; const void *prop_ptr; int prop_len; int node_off; unsigned long offset; uint32_t patch; prop_name = strchr(fixup, ':'); if (!prop_name) return -FDT_ERR_BADSTRUCTURE; prop_name++; offset_str = strchr(prop_name, ':'); if (!offset_str) return -FDT_ERR_BADSTRUCTURE; offset_str++; offset = strtoul(offset_str, &offset_end, 10); if ((offset_end == offset_str) || (offset_end[0] != 0)) return -FDT_ERR_BADSTRUCTURE; node_off = fdt_path_offset_namelen(dtb->fdt, fixup, prop_name - 1 - fixup); if (node_off < 0) return node_off; prop_ptr = fdt_getprop_namelen(dtb->fdt, node_off, prop_name, offset_str - 1 - prop_name, &prop_len); if (!prop_ptr) return prop_len; if ((int)offset > (prop_len - 4)) return -FDT_ERR_BADSTRUCTURE; // Now apply the patch. Yes, prop_ptr is a const void *, but the // alternative (copying the whole property, patching, then updating as // a whole) is ridiculous. if (type == FIXUP_RELATIVE) { patch = phandle + dtoverlay_read_u32(prop_ptr, offset); phandle_debug(" phandle fixup %d+%d->%d", phandle, patch - phandle, patch); } else { patch = phandle; phandle_debug(" phandle ref '%s'->%d", prop_name, patch); } dtoverlay_write_u32((void *)prop_ptr, offset, patch); fixup = offset_end + 1; } return 0; } // Returns 0 on success, or an FDT error code static int dtoverlay_apply_fixups_node(DTBLOB_T *dtb, int fix_off, int target_off, uint32_t phandle_offset) { // The fixups are arranged as a subtree mirroring the structure of the // overall tree. Walk this tree in order. Each property is an array of cells // containing offsets to patch within the corresponding node/property of // the target tree. int err = 0; int prop_off; int subfix_off; // Merge each property of the node for (prop_off = fdt_first_property_offset(dtb->fdt, fix_off); (prop_off >= 0) && (err == 0); prop_off = fdt_next_property_offset(dtb->fdt, prop_off)) { const char *prop_name; const void *prop_val; int prop_len; void *target_ptr; int target_len; int off; prop_val = fdt_getprop_by_offset(dtb->fdt, prop_off, &prop_name, &prop_len); if (!prop_val) return -FDT_ERR_INTERNAL; target_ptr = fdt_getprop_w(dtb->fdt, target_off, prop_name, &target_len); if (!target_ptr) return -FDT_ERR_BADSTRUCTURE; for (off = 0; (off + 4) <= prop_len; off += 4) { uint32_t patch; int patch_offset = dtoverlay_read_u32(prop_val, off); if ((patch_offset + 4) > target_len) return -FDT_ERR_BADSTRUCTURE; patch = phandle_offset + dtoverlay_read_u32(target_ptr, patch_offset); phandle_debug(" phandle fixup %d+%d->%d", phandle_offset, patch - phandle_offset, patch); dtoverlay_write_u32(target_ptr, patch_offset, patch); } } // Merge each subnode of the node for (subfix_off = fdt_first_subnode(dtb->fdt, fix_off); (subfix_off >= 0) && (err == 0); subfix_off = fdt_next_subnode(dtb->fdt, subfix_off)) { const char *subnode_name; int name_len; int subtarget_off; subnode_name = fdt_get_name(dtb->fdt, subfix_off, &name_len); subtarget_off = fdt_subnode_offset_namelen(dtb->fdt, target_off, subnode_name, name_len); if (subtarget_off >= 0) { err = dtoverlay_apply_fixups_node(dtb, subfix_off, subtarget_off, phandle_offset); } else { err = subtarget_off; } } return err; } // Returns 0 on success, or a negative FDT error. static int dtoverlay_resolve_phandles(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) { int local_fixups_off; int node_off; int err = 0; // First find and update the phandles in the overlay for (node_off = 0; node_off >= 0; node_off = fdt_next_node(overlay_dtb->fdt, node_off, NULL)) { dtoverlay_phandle_relocate(overlay_dtb, node_off, "phandle", base_dtb->max_phandle); dtoverlay_phandle_relocate(overlay_dtb, node_off, "linux,phandle", base_dtb->max_phandle); } local_fixups_off = fdt_path_offset(overlay_dtb->fdt, "/__local_fixups__"); if (local_fixups_off >= 0) { const char *fixups_stringlist; // Update the references to local phandles using the local fixups. // The property name is "fixup". // The value is a NUL-separated stringlist of descriptors of the form: // path:property:offset fixups_stringlist = fdt_getprop(overlay_dtb->fdt, local_fixups_off, "fixup", &err); if (fixups_stringlist) { // Relocate the overlay phandle references err = dtoverlay_apply_fixups(overlay_dtb, fixups_stringlist, err, base_dtb->max_phandle, FIXUP_RELATIVE); } else { err = dtoverlay_apply_fixups_node(overlay_dtb, local_fixups_off, 0, base_dtb->max_phandle); } if (err < 0) { dtoverlay_error("error applying local fixups"); return err; } } overlay_dtb->max_phandle += base_dtb->max_phandle; phandle_debug(" +overlay max phandle +%d -> %d", base_dtb->max_phandle, overlay_dtb->max_phandle); return err; } // Returns 0 on success, or an FDT error code static int dtoverlay_resolve_fixups(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) { int fixups_off; int err = 0; fixups_off = fdt_path_offset(overlay_dtb->fdt, "/__fixups__"); if (fixups_off >= 0) { int fixup_off, symbols_off = -1; fixup_off = fdt_first_property_offset(overlay_dtb->fdt, fixups_off); if (fixup_off >= 0) { // Find the symbols, which will be needed to resolve the fixups symbols_off = fdt_path_offset(base_dtb->fdt, "/__symbols__"); if (symbols_off < 0) { dtoverlay_error("no symbols found"); return -FDT_ERR_NOTFOUND; } } for (; fixup_off >= 0; fixup_off = fdt_next_property_offset(overlay_dtb->fdt, fixup_off)) { const char *fixups_stringlist, *symbol_name, *target_path; const char *ref_type; int target_off, fixups_len; uint32_t target_phandle; // The property name identifies a symbol (or alias) in the base. // The value is a comma-separated list of descriptors of the form: // path:property:offset fixups_stringlist = fdt_getprop_by_offset(overlay_dtb->fdt, fixup_off, &symbol_name, &err); if (!fixups_stringlist) { dtoverlay_error("__fixups__ are borked"); break; } fixups_len = err; // 1) Find the target node. if (symbol_name[0] == '/') { /* This is a new-style path reference */ target_path = symbol_name; ref_type = "path"; } else { target_path = fdt_getprop(base_dtb->fdt, symbols_off, symbol_name, &err); if (!target_path) { dtoverlay_error("can't find symbol '%s'", symbol_name); break; } ref_type = "symbol"; } target_off = fdt_path_offset(base_dtb->fdt, target_path); if (target_off < 0) { dtoverlay_error("%s '%s' is invalid", ref_type, symbol_name); err = target_off; break; } // 2) Ensure that the target node has a phandle. target_phandle = fdt_get_phandle(base_dtb->fdt, target_off); if (!target_phandle) { // It doesn't, so give it one fdt32_t temp; target_phandle = ++base_dtb->max_phandle; temp = cpu_to_fdt32(target_phandle); err = fdt_setprop(base_dtb->fdt, target_off, "phandle", &temp, 4); if (err != 0) { dtoverlay_error("failed to add a phandle"); break; } phandle_debug(" phandle '%s'->%d", target_path, target_phandle); // The symbols may have moved, so recalculate symbols_off = fdt_path_offset(base_dtb->fdt, "/__symbols__"); } // Now apply the valid target_phandle to the items in the fixup string err = dtoverlay_apply_fixups(overlay_dtb, fixups_stringlist, fixups_len, target_phandle, FIXUP_ABSOLUTE); if (err) { dtoverlay_error("failed to apply fixups"); break; } } } return err; } static int dtoverlay_get_target_offset(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb, int frag_off) { const char *target_path; int target_off; int len; target_path = fdt_getprop(overlay_dtb->fdt, frag_off, "target-path", &len); if (target_path) { if (!base_dtb) return -FDT_ERR_NOTFOUND; if (len && (target_path[len - 1] == '\0')) len--; target_off = fdt_path_offset_namelen(base_dtb->fdt, target_path, len); if (target_off < 0) { dtoverlay_error("invalid target-path '%.*s'", len, target_path); return target_off; } } else { const void *target_prop; int phandle; target_prop = fdt_getprop(overlay_dtb->fdt, frag_off, "target", &len); if (!target_prop) { dtoverlay_error("no target or target-path"); return len; } if (len != 4) return -FDT_ERR_BADSTRUCTURE; phandle = fdt32_to_cpu(*(fdt32_t *)target_prop); if (!base_dtb) { if (phandle < 0 || (uint32_t)phandle > overlay_dtb->max_phandle) return -FDT_ERR_NOTFOUND; return fdt_node_offset_by_phandle(overlay_dtb->fdt, phandle); } target_off = fdt_node_offset_by_phandle(base_dtb->fdt, phandle); if (target_off < 0) { dtoverlay_error("invalid target (phandle %d)", phandle); return target_off; } } return target_off; } // Copy a node full of path strings (__symbols__, aliases) from an overlay to // the base dtb, rebasing any fragment-relative paths to make them relative // to the respective fragment target. Note that this should not be called for // intra-overlay fragments, and that overlay_dtb is not modified. static int dtoverlay_apply_overlay_paths(DTBLOB_T *base_dtb, int strings_off, DTBLOB_T *overlay_dtb, int frag_off, const char *type) { int sym_off; int err = 0; fdt_for_each_property_offset(sym_off, overlay_dtb->fdt, frag_off) { char target_path[DTOVERLAY_MAX_PATH]; const char *sym_name = NULL; const char *sym_path; const char *p; int sym_len; int sym_frag_off; int target_off; int target_path_len; int new_path_len; sym_path = fdt_getprop_by_offset(overlay_dtb->fdt, sym_off, &sym_name, &sym_len); if (!sym_path) break; /* Skip non-overlay symbols * Overlay symbol paths should be of the form: * //__overlay__/ * It doesn't actually matter what is. */ if (sym_path[0] != '/') goto copy_verbatim; p = strchr(sym_path + 1, '/'); if (!p || strncmp(p + 1, "__overlay__", 11) != 0 || (p[12] != '/' && p[12] != '\0')) goto copy_verbatim; /* Rebase the symbol path so that * /fragment@0/__overlay__/ * becomes * / */ /* Find the offset to the fragment */ sym_frag_off = dtoverlay_find_node(overlay_dtb, sym_path, p - sym_path); p += 12; /* p points to / */ /* Locate the path to the fragment target */ target_off = dtoverlay_get_target_offset(base_dtb, overlay_dtb, sym_frag_off); if (target_off < 0) return NON_FATAL(target_off); err = fdt_get_path(base_dtb->fdt, target_off, target_path, sizeof(target_path)); if (err) { dtoverlay_error("bad target path for %s", sym_path); break; } /* Append the fragment-relative path to the target path */ target_path_len = strlen(target_path); if (strcmp(target_path, "/") == 0) p++; // Avoid a '//' if the target is the root new_path_len = target_path_len + (sym_path + sym_len - p); if (new_path_len >= (int)sizeof(target_path)) { dtoverlay_error("exported symbol path too long for %s", sym_path); err = -FDT_ERR_NOSPACE; break; } strcpy(target_path + target_path_len, p); fdt_setprop(base_dtb->fdt, strings_off, sym_name, target_path, new_path_len); dtoverlay_debug("set %s '%s' path to '%s'", type, sym_name, target_path); continue; copy_verbatim: fdt_setprop(base_dtb->fdt, strings_off, sym_name, sym_path, sym_len); } return err; } // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) { // Merge each fragment node int frag_off; int frag_idx; int err = 0; int overlay_size = fdt_totalsize(overlay_dtb->fdt); void *overlay_copy = NULL; dtoverlay_filter_symbols(overlay_dtb); for (frag_off = fdt_first_subnode(overlay_dtb->fdt, 0), frag_idx = 0; frag_off >= 0; frag_off = fdt_next_subnode(overlay_dtb->fdt, frag_off), frag_idx++) { const char *node_name; const char *frag_name; int target_off, overlay_off; DTBLOB_T clone_dtb; int idx; node_name = fdt_get_name(overlay_dtb->fdt, frag_off, NULL); if (strncmp(node_name, "fragment@", 9) != 0 && strncmp(node_name, "fragment-", 9) != 0) continue; frag_name = node_name + 9; // Find the target and overlay nodes overlay_off = fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__overlay__"); if (overlay_off < 0) { if (fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__dormant__") >= 0) dtoverlay_debug("fragment %s disabled", frag_name); else dtoverlay_error("no overlay in fragment %s", frag_name); continue; } target_off = dtoverlay_get_target_offset(NULL, overlay_dtb, frag_off); if (target_off < 0) continue; // Merge the fragment with the overlay // We can't just call dtoverlay_merge_fragment with the overlay_dtb // as source and destination because the source is not expected to // change. Instead, clone the overlay, apply the fragment, then switch. if (intra_fragment_merged_callback) (*intra_fragment_merged_callback)(overlay_dtb, overlay_off, target_off); if (!overlay_copy) { overlay_copy = malloc(overlay_size); if (!overlay_copy) { err = -FDT_ERR_NOSPACE; break; } } memcpy(overlay_copy, overlay_dtb->fdt, overlay_size); memcpy(&clone_dtb, overlay_dtb, sizeof(DTBLOB_T)); clone_dtb.fdt = overlay_copy; err = dtoverlay_merge_fragment(&clone_dtb, target_off, overlay_dtb, overlay_off, 0); if (err) break; // Swap the buffers { void *temp = overlay_dtb->fdt; overlay_dtb->fdt = overlay_copy; overlay_copy = temp; } // Disable this fragment (and resync with the changed overlay) for (frag_off = fdt_first_subnode(overlay_dtb->fdt, 0), idx = 0; idx < frag_idx; frag_off = fdt_next_subnode(overlay_dtb->fdt, frag_off), idx++) continue; overlay_off = fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__overlay__"); if (overlay_off >= 0) dtoverlay_set_node_name(overlay_dtb, overlay_off, "__dormant__"); // As the new name is the same length, the offsets are still valid } if (overlay_copy) free(overlay_copy); if (err || !base_dtb) goto no_base_dtb; for (frag_off = fdt_first_subnode(overlay_dtb->fdt, 0), frag_idx = 0; frag_off >= 0; frag_off = fdt_next_subnode(overlay_dtb->fdt, frag_off), frag_idx++) { const char *node_name; const char *frag_name; int target_off, overlay_off; node_name = fdt_get_name(overlay_dtb->fdt, frag_off, NULL); if (strcmp(node_name, "__symbols__") == 0) { /* At this point, only exported symbols should remain */ int symbols_off = dtoverlay_find_node(base_dtb, "/__symbols__", 0); dtoverlay_apply_overlay_paths(base_dtb, symbols_off, overlay_dtb, frag_off, "label"); continue; } else if (strncmp(node_name, "fragment@", 9) != 0 && strncmp(node_name, "fragment-", 9) != 0) { continue; } frag_name = node_name + 9; // Find the target and overlay nodes overlay_off = fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__overlay__"); if (overlay_off < 0) { if (fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__dormant__") >= 0) dtoverlay_debug("fragment %s disabled", frag_name); else dtoverlay_error("no overlay in fragment %s", frag_name); continue; } target_off = dtoverlay_get_target_offset(base_dtb, overlay_dtb, frag_off); if (target_off < 0) { err = NON_FATAL(target_off); break; } // Now do the merge node_name = fdt_get_name(base_dtb->fdt, target_off, NULL); if (node_name && strcmp(node_name, "aliases") == 0) err = dtoverlay_apply_overlay_paths(base_dtb, target_off, overlay_dtb, overlay_off, "alias"); else err = dtoverlay_merge_fragment(base_dtb, target_off, overlay_dtb, overlay_off, 0); } if (err == 0) base_dtb->max_phandle = overlay_dtb->max_phandle; no_base_dtb: if (err) dtoverlay_error("merge failed"); return err; } // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors int dtoverlay_fixup_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) { int err; // To do: Check the "compatible" string? err = dtoverlay_resolve_fixups(base_dtb, overlay_dtb); if (err >= 0) err = dtoverlay_resolve_phandles(base_dtb, overlay_dtb); overlay_dtb->fixups_applied = 1; return NON_FATAL(err); } // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors int dtoverlay_merge_params(DTBLOB_T *dtb, const DTOVERLAY_PARAM_T *params, unsigned int num_params) { int err = 0; unsigned int i; for (i=0; (iparam; slash = strrchr(node_name, '/'); if (!slash) { err = NON_FATAL(FDT_ERR_BADPATH); break; } // Ensure that root properties ("/xxx") work if (slash == node_name) path_len = 1; else path_len = slash - node_name; // find node, create if it does not exist yet node_off = dtoverlay_create_node(dtb, node_name, path_len); if (node_off >= 0) { const char *prop_name = slash + 1; int prop_len; struct fdt_property *prop; if ((strcmp(prop_name, "bootargs") == 0) && ((prop = fdt_get_property_w(dtb->fdt, node_off, prop_name, &prop_len)) != NULL) && (prop_len > 0) && *prop->data) { prop->data[prop_len - 1] = ' '; err = fdt_appendprop(dtb->fdt, node_off, prop_name, p->b, p->len); } else err = fdt_setprop(dtb->fdt, node_off, prop_name, p->b, p->len); } else err = node_off; } return err; } int dtoverlay_filter_symbols(DTBLOB_T *dtb) { int symbols_off; int exports_off; struct str_item *exports = NULL; int prop_off; struct str_item { struct str_item *next; char str[1]; }; symbols_off = dtoverlay_find_node(dtb, "/__symbols__", 0); if (symbols_off < 0) return 0; exports_off = dtoverlay_find_node(dtb, "/__exports__", 0); if (exports_off < 0) { /* There are no exports, so keep all symbols private. */ fdt_del_node(dtb->fdt, symbols_off); return 0; } /* Internalise the names of the exported properties for speed * and to protect against the FDT contents moving. */ fdt_for_each_property_offset(prop_off, dtb->fdt, exports_off) { struct str_item *new_str; const char *name = NULL; fdt_getprop_by_offset(dtb->fdt, prop_off, &name, NULL); if (!name) break; new_str = malloc(sizeof(*new_str) + strlen(name)); if (!new_str) { /* Free all of the internalised exports */ while (exports) { struct str_item *str = exports; exports = str->next; free(str); } dtoverlay_error(" out of memory"); return -FDT_ERR_NOSPACE; } strcpy(new_str->str, name); new_str->next = exports; exports = new_str; } /* Iterate through the symbols, deleting any that aren't * exported. */ prop_off = fdt_first_property_offset(dtb->fdt, symbols_off); while (prop_off >= 0) { const char *name = NULL; struct str_item *str; (void)fdt_getprop_by_offset(dtb->fdt, prop_off, &name, NULL); if (!name) break; for (str = exports; str; str = str->next) { if (!strcmp(str->str, name)) break; } if (str) /* This symbol is exported */ prop_off = fdt_next_property_offset(dtb->fdt, prop_off); else fdt_delprop(dtb->fdt, symbols_off, name); } /* Free all of the internalised exports */ while (exports) { struct str_item *str = exports; exports = str->next; free(str); } return 0; } const char *dtoverlay_find_fixup(DTBLOB_T *dtb, const char *fixup_loc) { int fixups_off; fixups_off = fdt_path_offset(dtb->fdt, "/__fixups__"); if (fixups_off > 0) { int fixup_off; for (fixup_off = fdt_first_property_offset(dtb->fdt, fixups_off); fixup_off >= 0; fixup_off = fdt_next_property_offset(dtb->fdt, fixup_off)) { const char *fixups_stringlist; const char *symbol_name; int list_len; fixups_stringlist = fdt_getprop_by_offset(dtb->fdt, fixup_off, &symbol_name, &list_len); if (fdt_stringlist_contains(fixups_stringlist, list_len, fixup_loc) > 0) return symbol_name; } } return NULL; } int dtoverlay_add_fixup(DTBLOB_T *dtb, const char *symbol, const char *fixup_loc) { int loc_len = strlen(fixup_loc); int fixups_off; fixups_off = fdt_path_offset(dtb->fdt, "/__fixups__"); assert(fixups_off > 0); if (fixups_off < 0) return fixups_off; return fdt_appendprop(dtb->fdt, fixups_off, symbol, fixup_loc, loc_len + 1); } static const char *stringlist_find(const char *strlist, int listlen, const char *str, int len) { const char *p; while (listlen >= len) { if (memcmp(str, strlist, len + 1) == 0) return strlist; p = memchr(strlist, '\0', listlen); if (!p) return NULL; /* malformed strlist.. */ listlen -= (p-strlist) + 1; strlist = p + 1; } return NULL; } int dtoverlay_delete_fixup(DTBLOB_T *dtb, const char *fixup_loc) { int loc_len = strlen(fixup_loc); int fixups_off; fixups_off = fdt_path_offset(dtb->fdt, "/__fixups__"); if (fixups_off > 0) { int fixup_off; for (fixup_off = fdt_first_property_offset(dtb->fdt, fixups_off); fixup_off >= 0; fixup_off = fdt_next_property_offset(dtb->fdt, fixup_off)) { const char *fixups_stringlist; const char *symbol_name; const char *match; int list_len; fixups_stringlist = fdt_getprop_by_offset(dtb->fdt, fixup_off, &symbol_name, &list_len); match = stringlist_find(fixups_stringlist, list_len, fixup_loc, loc_len); if (match) { int match_len = loc_len + 1; if (match_len == list_len) { /* This was the only fixup - the symbol is no longer referenced */ return fdt_delprop(dtb->fdt, fixups_off, symbol_name); } else { char *buf = malloc(list_len - match_len); int match_off = match - fixups_stringlist; int after_match = list_len - (match_off + match_len); int err; if (match_off) memcpy(buf, fixups_stringlist, match_off); if (after_match) memcpy(buf + match_off, match + match_len, after_match); err = fdt_setprop(dtb->fdt, fixups_off, symbol_name, buf, list_len - match_len); free(buf); return err; } } } } return -FDT_ERR_NOTFOUND; } int dtoverlay_stringlist_replace(const char *src, int src_len, const char *src_prefix, int src_prefix_len, const char *dst_prefix, int dst_prefix_len, char *dst) { /* With a NULL dst, only returns the new length */ char *dst_p = dst; int replaced = 0; while (src_len) { const char *p; int copy_bytes; p = memchr(src, '\0', src_len); if (!p) return -1; /* malformed strlist.. */ if (src_prefix_len < src_len && memcmp(src, src_prefix, src_prefix_len) == 0) { if (dst) memcpy(dst_p, dst_prefix, dst_prefix_len); src_len -= src_prefix_len; src += src_prefix_len; dst_p += dst_prefix_len; replaced = 1; } copy_bytes = (p - src) + 1; if (dst) memcpy(dst_p, src, copy_bytes); dst_p += copy_bytes; src_len -= copy_bytes; src = p + 1; } if (!replaced) return -1; return dst_p - dst; } void dtoverlay_set_intra_fragment_merged_callback(void (*callback)(DTBLOB_T *, int, int)) { intra_fragment_merged_callback = callback; } void dtoverlay_set_cell_changed_callback(void (*callback)(DTBLOB_T *, int, const char *, int, int)) { cell_changed_callback = callback; } /* Returns a pointer to the override data and (through data_len) its length. On error, sets *data_len to be the error code. */ const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name, int *data_len) { int overrides_off; const char *data; int len; // Find the table of overrides overrides_off = fdt_path_offset(dtb->fdt, "/__overrides__"); if (overrides_off < 0) { dtoverlay_debug("/__overrides__ node not found"); *data_len = overrides_off; return NULL; } // Locate the property data = fdt_getprop(dtb->fdt, overrides_off, override_name, &len); *data_len = len; if (data) dtoverlay_debug("found override %s", override_name); else dtoverlay_debug("/__overrides__ has no %s property", override_name); return data; } static int hex_digit(char c) { if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'A' && c <= 'F') return 10 + c - 'A'; else if (c >= 'a' && c <= 'f') return 10 + c - 'a'; else return -1; } static int hex_byte(const char *p) { int nib1, nib2; nib1 = hex_digit(p[0]); if (nib1 < 0) return -1; nib2 = hex_digit(p[1]); if (nib2 < 0) return -1; return (nib1 << 4) | nib2; } int dtoverlay_override_one_target(int override_type, const char *override_value, DTBLOB_T *dtb, int node_off, const char *prop_name, int target_phandle, int target_off, int target_size, void *callback_state) { UNUSED(target_phandle); UNUSED(callback_state); int err = 0; if (override_type == DTOVERRIDE_STRING) { char unescaped_value[256]; char *prop_val, *q; const char *p; int prop_len; p = override_value; q = unescaped_value; while (*p) { int c = *(p++); if (c == '\\') { c = *(p++); if (c >= '0' && c <= '7') { c -= '0'; if (*p >= '0' && *p <= '7') { c = (c << 3) + *(p++) - '0'; if (*p >= '0' && *p <= '7') { c = (c << 3) + *(p++) - '0'; if (c > 255) c = -1; } } } else if (c == 'a') c = '\x07'; else if (c == 'b') c = '\b'; else if (c == 'f') c = '\f'; else if (c == 'n') c = '\n'; else if (c == 'r') c = '\r'; else if (c == 't') c = '\t'; else if (c == 'v') c = '\v'; else if (c == 'x') c = hex_byte(p), p += 2; else if (c != '\\' && c != '\'' && c != '"') c = -1; if (c < 0) { dtoverlay_error("invalid escape in '%s'", override_value); return NON_FATAL(FDT_ERR_BADVALUE); } } *(q++) = (char)c; } *(q++) = '\0'; /* Append to or replace the property string */ if ((strcmp(prop_name, "bootargs") == 0) && ((prop_val = fdt_getprop_w(dtb->fdt, node_off, prop_name, &prop_len)) != NULL) && (prop_len > 0) && prop_val[0]) { prop_val[prop_len - 1] = ' '; err = fdt_appendprop(dtb->fdt, node_off, prop_name, unescaped_value, q - unescaped_value); } else if (strcmp(prop_name, "name") == 0) // "name" is a pseudo-property { err = dtoverlay_set_node_name(dtb, node_off, unescaped_value); } else err = fdt_setprop(dtb->fdt, node_off, prop_name, unescaped_value, q - unescaped_value); } else if (override_type == DTOVERRIDE_BYTE_STRING) { /* Replace the whole property with the byte string */ uint8_t bytes_buf[32]; // For efficiency/laziness, place a limit on the length const char *p = override_value; int byte_count = 0; while (*p) { int byteval; // whitespace and colons are legal separators if (*p == ':' || *p == ' ' || *p == '\t') { p++; continue; } byteval = hex_byte(p); if (byteval < 0) { dtoverlay_error("invalid bytestring at '%s'", p); return NON_FATAL(FDT_ERR_BADVALUE); } if (byte_count == sizeof(bytes_buf)) { dtoverlay_error("bytestring '%s' too long", override_value); return NON_FATAL(FDT_ERR_BADVALUE); } bytes_buf[byte_count++] = byteval; p += 2; } err = fdt_setprop(dtb->fdt, node_off, prop_name, bytes_buf, byte_count); } else if (override_type != DTOVERRIDE_END) { const char *p; char *end; char *prop_val; void *prop_buf = NULL; int prop_len; int new_prop_len; uint64_t override_int; uint32_t frag_num; /* Parse as an integer */ override_int = strtoull(override_value, &end, 0); if (end[0] != '\0') { if ((strcmp(override_value, "y") == 0) || (strcmp(override_value, "yes") == 0) || (strcmp(override_value, "on") == 0) || (strcmp(override_value, "true") == 0) || (strcmp(override_value, "down") == 0)) override_int = 1; else if ((strcmp(override_value, "n") == 0) || (strcmp(override_value, "no") == 0) || (strcmp(override_value, "off") == 0) || (strcmp(override_value, "false") == 0)) override_int = 0; else if (strcmp(override_value, "up") == 0) override_int = 2; else { dtoverlay_error("invalid override value '%s' - ignored", override_value); return NON_FATAL(FDT_ERR_INTERNAL); } } switch (override_type) { case DTOVERRIDE_INTEGER: /* Patch a word within the property */ prop_val = (void *)fdt_getprop(dtb->fdt, node_off, prop_name, &prop_len); new_prop_len = target_off + target_size; if (prop_len < new_prop_len) { /* This property either doesn't exist or isn't long enough. Create a buffer containing any existing property data with zero padding, which will later be patched and written back. */ prop_buf = calloc(1, new_prop_len); if (!prop_buf) { dtoverlay_error(" out of memory"); return NON_FATAL(FDT_ERR_NOSPACE); } if (prop_len > 0) memcpy(prop_buf, prop_val, prop_len); prop_val = prop_buf; } switch (target_size) { case 1: dtoverlay_write_u8(prop_val, target_off, (uint32_t)override_int); break; case 2: dtoverlay_write_u16(prop_val, target_off, (uint32_t)override_int); break; case 4: dtoverlay_write_u32(prop_val, target_off, (uint32_t)override_int); break; case 8: dtoverlay_write_u64(prop_val, target_off, override_int); break; default: break; } if (prop_buf) { /* Add/extend the property by setting it */ if (strcmp(prop_name, "reg") != 0) // Don't create or extend "reg" - it must be a pseudo-property err = fdt_setprop(dtb->fdt, node_off, prop_name, prop_buf, new_prop_len); free(prop_buf); } if (strcmp(prop_name, "reg") == 0 && target_off == 0) { const char *old_name = fdt_get_name(dtb->fdt, node_off, NULL); const char *atpos = strchr(old_name, '@'); if (atpos) { int name_len = (atpos - old_name); char *new_name = malloc(name_len + 1 + 16 + 1); if (!new_name) return -FDT_ERR_NOSPACE; sprintf(new_name, "%.*s@%x", name_len, old_name, (uint32_t)override_int); err = dtoverlay_set_node_name(dtb, node_off, new_name); free(new_name); } } break; case DTOVERRIDE_BOOLEAN: case DTOVERRIDE_BOOLEAN_INV: /* This is a boolean property (present->true, absent->false) */ /* Either way, any fixups will need to be deleted */ fdt_getprop(dtb->fdt, node_off, prop_name, &prop_len); if (prop_len > 0) { char cell_target_loc[DTOVERLAY_MAX_PATH]; int path_len; fdt_get_path(dtb->fdt, node_off, cell_target_loc, sizeof(cell_target_loc)); path_len = strlen(cell_target_loc); snprintf(cell_target_loc + path_len, sizeof(cell_target_loc) - path_len, ":%s:%d", prop_name, target_off); dtoverlay_delete_fixup(dtb, cell_target_loc); } if (!!override_int ^ (override_type == DTOVERRIDE_BOOLEAN_INV)) err = fdt_setprop(dtb->fdt, node_off, prop_name, NULL, 0); else { err = fdt_delprop(dtb->fdt, node_off, prop_name); if (err == -FDT_ERR_NOTFOUND) err = 0; } break; case DTOVERRIDE_OVERLAY: /* Apply the overlay-wide override. The supported options are ( is a fragment number): + Enable a fragment - Disable a fragment = Enable/disable the fragment according to the override value ! Disable/enable the fragment according to the inverse of the override value */ p = prop_name; while (*p && !err) { char type = *p; int frag_off; switch (type) { case '+': case '-': case '=': case '!': frag_num = strtoul(p + 1, &end, 0); if (end != p) { /* Change fragment@/__overlay__<->__dormant__ as necessary */ const char *states[2] = { "__dormant__", "__overlay__" }; char node_name[24]; int active = (type == '+') || ((type == '=') && (override_int != 0)) || ((type == '!') && (override_int == 0)); snprintf(node_name, sizeof(node_name), "/fragment@%u", frag_num); frag_off = fdt_path_offset(dtb->fdt, node_name); if (frag_off < 0) { snprintf(node_name, sizeof(node_name), "/fragment-%u", frag_num); frag_off = fdt_path_offset(dtb->fdt, node_name); } if (frag_off >= 0) { frag_off = fdt_subnode_offset(dtb->fdt, frag_off, states[!active]); if (frag_off >= 0) (void)dtoverlay_set_node_name(dtb, frag_off, states[active]); } else { dtoverlay_error(" fragment %u not found", frag_num); err = NON_FATAL(frag_off); } p = end; } else { dtoverlay_error(" invalid overlay override '%s'", prop_name); err = NON_FATAL(FDT_ERR_BADVALUE); } break; default: err = NON_FATAL(FDT_ERR_BADVALUE); break; } } break; } } if (!err && cell_changed_callback && cell_source && override_type == DTOVERRIDE_INTEGER && target_size == 4) (*cell_changed_callback)(dtb, node_off, prop_name, target_off, (int)(cell_source - override_data_start)); return err; } /* The problem is the split between inline string values and inline cell values passed to the callback. For strings properties the returned data is strings; no conversion from cells is required. The special handling for "status" is performed before the callback. For all other property types the returned values are binary/opaque data. Any string data should have been converted to binary data already in the framework. Translation: 1. The override value (the value assigned to the parameter) is always a string. 2. Strings are converted according to type of the parameter at the point of use. 3. A single override value can result in multiple different values being assigned to properties as the result of type conversions and set lookups. 4. Cell literals have a binary value. 5. Lookups convert strings to either a string or a cell literal. 6. Cell literals are primarily (only?) useful for label references, which are really just integers. There is nothing stopping them (or other integers) being converted to strings. 7. Therefore dtoverlay_extract_override always returns a string value, either the input override value, a literal, or the result of a lookup. */ // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors // After calling this, assume all node offsets are no longer valid int dtoverlay_foreach_override_target(DTBLOB_T *dtb, const char *override_name, const char *override_data, int data_len, const char *override_value, override_callback_t callback, void *callback_state) { int err = 0; int target_phandle = 0; char *data_buf, *data, *data_end; /* Short-circuit the degenerate case of an empty parameter, avoiding an apparent memory allocation failure. */ if (!data_len) return 0; /* Copy the override data in case it moves */ data_buf = malloc(data_len); if (!data_buf) { dtoverlay_error(" out of memory"); return NON_FATAL(FDT_ERR_NOSPACE); } memcpy(data_buf, override_data, data_len); data = data_buf; data_end = data + data_len; override_data_start = data_buf; while (err == 0) { const char *target_prop = NULL; static char prop_name[256]; static char target_value[256]; int name_len = 0; int target_off = 0; int target_size = 0; int override_type; int node_off = 0; strcpy(target_value, override_value); override_type = dtoverlay_extract_override(override_name, target_value, sizeof(target_value), &target_phandle, (const char **)&data, data_end, &target_prop, &name_len, &target_off, &target_size); if (override_type < 0) { err = override_type; break; } if (target_phandle != 0) { node_off = fdt_node_offset_by_phandle(dtb->fdt, target_phandle); if (node_off < 0) { dtoverlay_error(" phandle %d not found", target_phandle); err = NON_FATAL(node_off); break; } } /* Sadly there are no '_namelen' setprop variants, so copies are required */ if (target_prop) { memcpy(prop_name, target_prop, name_len); prop_name[name_len] = '\0'; } /* Pass DTOVERRIDE_END to the callback, in case it is interested */ err = callback(override_type, target_value, dtb, node_off, prop_name, target_phandle, target_off, target_size, callback_state); if (override_type == DTOVERRIDE_END) break; } free(data_buf); return err; } // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors int dtoverlay_apply_override(DTBLOB_T *dtb, const char *override_name, const char *override_data, int data_len, const char *override_value) { return dtoverlay_foreach_override_target(dtb, override_name, override_data, data_len, override_value, dtoverlay_override_one_target, NULL); } /* Returns an override type (DTOVERRIDE_INTEGER, DTOVERRIDE_BOOLEAN, DTOVERRIDE_STRING, DTOVERRIDE_OVERLAY), DTOVERRIDE_END (0) at the end, or an error code (< 0) */ static int dtoverlay_extract_override(const char *override_name, char *override_value, int value_size, int *phandle_ptr, const char **datap, const char *data_end, const char **namep, int *namelenp, int *offp, int *sizep) { const char *data; const char *prop_name, *override_end; int len, override_len, name_len, target_len, phandle; const char *offset_seps = ".;:#?![{="; const char *literal_value = NULL; char literal_type = '?'; int type; cell_source = NULL; data = *datap; len = data_end - data; if (len <= 0) { if (len < 0) return len; *phandle_ptr = 0; *namep = NULL; return DTOVERRIDE_END; } // Check for space for a phandle, a terminating NUL and at least one char if (len < ((int)sizeof(fdt32_t) + 1 + 1)) { dtoverlay_error(" override %s: data is truncated or mangled", override_name); return -FDT_ERR_BADSTRUCTURE; } phandle = dtoverlay_read_u32(data, 0); *phandle_ptr = phandle; data += sizeof(fdt32_t); len -= sizeof(fdt32_t); override_end = memchr(data, 0, len); if (!override_end) { dtoverlay_error(" override %s: string is not NUL-terminated", override_name); return -FDT_ERR_BADSTRUCTURE; } prop_name = data; override_len = override_end - prop_name; data += (override_len + 1); *datap = data; if (phandle <= 0) { if (phandle < 0) return -FDT_ERR_BADPHANDLE; /* This is an "overlay" override, signalled using <0> as the phandle. */ *namep = prop_name; *namelenp = override_len; return DTOVERRIDE_OVERLAY; } target_len = strcspn(prop_name, "={"); name_len = strcspn(prop_name, offset_seps); *namep = prop_name; *namelenp = name_len; if (target_len < override_len) { /* Literal assignment or lookup table * Can't have '=' and '{' (or at least, don't need to support it. * = is an override value replacement * { is an override value transformation */ literal_type = prop_name[target_len]; literal_value = prop_name + target_len + 1; } if (name_len < target_len) { /* There is a separator specified */ char sep = prop_name[name_len]; if (sep == '?') { /* The target is a boolean parameter (present->true, absent->false) */ *offp = 0; *sizep = 0; type = DTOVERRIDE_BOOLEAN; dtoverlay_debug(" override %s: boolean target %.*s", override_name, name_len, prop_name); } else if (sep == '!') { /* The target is a boolean parameter (present->true, absent->false), * but the sense of the value is inverted */ *offp = 0; *sizep = 0; type = DTOVERRIDE_BOOLEAN_INV; dtoverlay_debug(" override %s: inverted boolean target %.*s", override_name, name_len, prop_name); } else if (sep == '[') { /* The target is a byte-string */ *offp = -1; *sizep = 0; type = DTOVERRIDE_BYTE_STRING; dtoverlay_debug(" override %s: byte-string target %.*s", override_name, name_len, prop_name); } else { /* The target is a cell/integer */ *offp = atoi(prop_name + name_len + 1); *sizep = 1 << (strchr(offset_seps, sep) - offset_seps); type = DTOVERRIDE_INTEGER; dtoverlay_debug(" override %s: cell target %.*s @ offset %d (size %d)", override_name, name_len, prop_name, *offp, *sizep); } } else { *offp = -1; *sizep = 0; type = DTOVERRIDE_STRING; dtoverlay_debug(" override %s: string target '%.*s'", override_name, name_len, prop_name); } if (literal_value) { if (literal_type == '=') { /* Immediate value */ if (type == DTOVERRIDE_STRING || type == DTOVERRIDE_BYTE_STRING || literal_value[0]) { /* String */ if (type == DTOVERRIDE_STRING && !literal_value[0]) { /* The empty string is a special case indicating that the literal * string follows the NUL. This could appear as * "foo=","bar"; * but the expecged use case is to support label paths: * "console=",&uart1; * which dtc will expand to something like: * "console=","/soc/serial@7e215040"; * Note that the corollary of this is that assigning an empty * string (not a likely scenario, and not one encountered at the * time of writing) requires an empty string to appear * immediately afterwards: * <&aliases>,"console=","",<&node>,"other:0"; * or that the empty string assignment is at the end: * <&aliases>,"console="; * although the same effect can be achieved with: * <&aliases>,"console[=00"; */ len = data_end - data; if (!len) { /* end-of-property case - treat as an empty string */ literal_value = data - 1; override_len = 1; } else { override_end = memchr(data, 0, len); if (!override_end) { dtoverlay_error(" override %s: string is not NUL-terminated", override_name); return -FDT_ERR_BADSTRUCTURE; } literal_value = data; data = override_end + 1; } *datap = data; } strcpy(override_value, literal_value); } else { /* Cell */ sprintf(override_value, "%u", dtoverlay_read_u32(data, 0)); cell_source = data; *datap = data + 4; } } else if (literal_type == '{') { /* Lookup */ data = dtoverlay_lookup_key(literal_value, data_end, override_value, override_value, value_size); *datap = data; if (!data) return -FDT_ERR_BADSTRUCTURE; } else { return -FDT_ERR_INTERNAL; } } if ((type == DTOVERRIDE_STRING) && (strmemcmp(prop_name, name_len, "status") == 0)) { /* Convert booleans to okay/disabled */ if ((strcmp(override_value, "y") == 0) || (strcmp(override_value, "yes") == 0) || (strcmp(override_value, "on") == 0) || (strcmp(override_value, "true") == 0) || (strcmp(override_value, "enable") == 0) || (strcmp(override_value, "1") == 0)) strcpy(override_value, "okay"); else if ((strcmp(override_value, "n") == 0) || (strcmp(override_value, "no") == 0) || (strcmp(override_value, "off") == 0) || (strcmp(override_value, "false") == 0) || (strcmp(override_value, "0") == 0)) strcpy(override_value, "disabled"); } return type; } /* Read the string or (if permitted) cell value, storing the result in buf. Returns a pointer to the first byte after the successfully parsed immediate, or NULL on error. */ static const char *dtoverlay_extract_immediate(const char *data, const char *data_end, char *buf, int buf_len) { if ((data + 1) < data_end && !data[0]) { uint32_t val; data++; if (data + 4 > data_end) { dtoverlay_error(" truncated cell immediate"); return NULL; } val = dtoverlay_read_u32(data, 0); if (buf) { cell_source = data; snprintf(buf, buf_len, "%d", val); } data += 4; } else if (data[0] == '\'') { // Continue to closing "'", error on end-of-string int len; data++; len = strcspn(data, "'"); if (!data[len]) { dtoverlay_error(" unterminated quoted string: '%s", data); return NULL; } if (len >= buf_len) { dtoverlay_error(" immediate string too long: '%s", data); return NULL; } if (buf) { memcpy(buf, data, len); buf[len] = '\0'; } data += len + 1; if (*data == ',') // Skip a comma, preserve a brace data++; } else { // Continue to a comma, right brace or end-of-string NUL int len = strcspn(data, ",}"); if (len >= buf_len) { dtoverlay_error(" immediate string too long: '%s", data); return NULL; } if (buf) { memcpy(buf, data, len); buf[len] = '\0'; } data += len; if (*data == ',') // Skip a comma, preserve a brace data++; } return data; } static const char *dtoverlay_lookup_key(const char *lookup_string, const char *data_end, const char *key, char *buf, int buf_len) { const char *p = lookup_string; int found = 0; while (p < data_end && *p && *p != '}') { int key_len = strcspn(p, "=,}"); char *q = NULL; char sep = p[key_len]; if (!key_len) { if (sep) // default value { if (!found) { q = buf; found = 2; } } } else { if (found != 1 && strmemcmp(p, key_len, key) == 0) { q = buf; found = 1; } } p += key_len; if (sep == '=') { p = dtoverlay_extract_immediate(p + 1, data_end, q, buf_len); } else { if (q && q != key) { strncpy(q, key, buf_len); q[buf_len - 1] = 0; } if (sep == ',') p++; } } if (!found) { dtoverlay_error("lookup -> no match for '%s'", key); return NULL; } if (p == data_end) return p; if (!*p) { dtoverlay_error(" malformed lookup"); return NULL; } assert(p[0] != 0 && p[1] == 0); return p + 2; } int dtoverlay_set_synonym(DTBLOB_T *dtb, const char *dst, const char *src) { /* Add/update all aliases, symbols and overrides named dst to be equivalent to those named src. An absent src is ignored. */ int err; err = dtoverlay_dup_property(dtb, "/aliases", dst, src); if (err == 0) err = dtoverlay_dup_property(dtb, "/__symbols__", dst, src); if (err == 0) dtoverlay_dup_property(dtb, "/__overrides__", dst, src); return err; } int dtoverlay_dup_property(DTBLOB_T *dtb, const char *node_name, const char *dst, const char *src) { /* Find the node and src property */ const DTBLOB_T *src_prop; int node_off; int prop_len = 0; int err = 0; node_off = fdt_path_offset(dtb->fdt, node_name); if (node_off < 0) return 0; src_prop = fdt_getprop(dtb->fdt, node_off, src, &prop_len); if (!src_prop) return 0; err = fdt_setprop_inplace(dtb->fdt, node_off, dst, src_prop, prop_len); if (err != 0) { void *prop_data; /* Copy the src property, just in case things move */ prop_data = malloc(prop_len); memcpy(prop_data, src_prop, prop_len); err = fdt_setprop(dtb->fdt, node_off, dst, prop_data, prop_len); free(prop_data); } if (err == 0) dtoverlay_debug("%s:%s=%s", node_name, dst, src); return err; } int dtoverlay_find_pins_for_device(DTBLOB_T *dtb, const char *symbol, PIN_ITER_T *iter) { int pos = dtoverlay_find_symbol(dtb, symbol); memset(iter, 0, sizeof(*iter)); if (pos < 0) return pos; iter->dtb = dtb; if (dtoverlay_node_is_enabled(dtb, pos)) iter->pinctrl = dtoverlay_get_property(dtb, pos, "pinctrl-0", &iter->pinctrl_len); return 0; } int dtoverlay_next_pin(PIN_ITER_T *iter, int *pin, int *func, int *pull) { if (pin) *pin = -1; if (func) *func = -1; if (pull) *pull = -1; while (1) { int phandle, pos; if ((iter->pin_off) + 4 <= iter->pins_len) { int off = iter->pin_off; if (pin) *pin = GETBE4(iter->pins, off); if (func && iter->funcs_len) *func = GETBE4(iter->funcs, (iter->funcs_len > 4) ? off : 0); if (pull && iter->pulls_len) *pull = GETBE4(iter->pulls, (iter->pulls_len > 4) ? off : 0); iter->pin_off = off + 4; return 1; } if ((iter->pinctrl_off + 4) > iter->pinctrl_len) break; phandle = GETBE4(iter->pinctrl, iter->pinctrl_off); iter->pinctrl_off += 4; pos = dtoverlay_find_phandle(iter->dtb, phandle); iter->pins = dtoverlay_get_property(iter->dtb, pos, "brcm,pins", &iter->pins_len); iter->funcs = dtoverlay_get_property(iter->dtb, pos, "brcm,function", &iter->funcs_len); iter->pulls = dtoverlay_get_property(iter->dtb, pos, "brcm,pull", &iter->pulls_len); iter->pin_off = 0; } return 0; } DTBLOB_T *dtoverlay_create_dtb(int max_size) { DTBLOB_T *dtb = NULL; void *fdt = NULL; fdt = malloc(max_size); if (!fdt) { dtoverlay_error("out of memory"); goto error_exit; } if (fdt_create_empty_tree(fdt, max_size) != 0) { dtoverlay_error("failed to create empty dtb"); goto error_exit; } dtb = calloc(1, sizeof(DTBLOB_T)); if (!dtb) { dtoverlay_error("out of memory"); goto error_exit; } dtb->fdt = fdt; dtb->max_phandle = 0; // Not a valid phandle return dtb; error_exit: free(fdt); if (dtb) free(dtb->trailer); free(dtb); return NULL; } DTBLOB_T *dtoverlay_load_dtb_from_fp(FILE *fp, int max_size) { DTBLOB_T *dtb = NULL; void *fdt = NULL; if (fp) { long len; long bytes_read; int dtb_len; fseek(fp, 0, SEEK_END); len = ftell(fp); fseek(fp, 0, SEEK_SET); if (max_size > 0) { if (max_size < len) { dtoverlay_error("file too large (%d bytes) for max_size", len); goto error_exit; } } else if (max_size < 0) { max_size = len - max_size; } else { max_size = len; } fdt = malloc(max_size); if (!fdt) { dtoverlay_error("out of memory"); goto error_exit; } bytes_read = fread(fdt, 1, len, fp); fclose(fp); if (bytes_read != len) { dtoverlay_error("fread failed"); goto error_exit; } // Record the total size before any expansion dtb_len = fdt_totalsize(fdt); dtb = dtoverlay_import_fdt(fdt, max_size); if (!dtb) goto error_exit; dtb->fdt_is_malloced = 1; if (len > dtb_len) { /* Load the trailer */ dtb->trailer_len = len - dtb_len; dtb->trailer = malloc(dtb->trailer_len); if (!dtb->trailer) { dtoverlay_error("out of memory"); goto error_exit; } dtb->trailer_is_malloced = 1; memcpy(dtb->trailer, (char *)fdt + dtb_len, dtb->trailer_len); } } return dtb; error_exit: free(fdt); if (dtb) free(dtb->trailer); free(dtb); return NULL; } DTBLOB_T *dtoverlay_load_dtb(const char *filename, int max_size) { FILE *fp = fopen(filename, "rb"); if (fp) return dtoverlay_load_dtb_from_fp(fp, max_size); dtoverlay_error("failed to open '%s'", filename); return NULL; } void dtoverlay_init_map_from_fp(FILE *fp, const char *compatible, int compatible_len) { if (!compatible) return; while (compatible_len > 0) { const char *p; int len; // Look for a string containing a comma p = memchr(compatible, ',', compatible_len); if (p) { p++; len = compatible + compatible_len - p; } else { // Otherwise treat it as a simple string p = compatible; len = compatible_len; } /* Group the members of the BCM2835 family */ if (strncmp(p, "bcm2708", len) == 0 || strncmp(p, "bcm2709", len) == 0 || strncmp(p, "bcm2710", len) == 0 || strncmp(p, "bcm2835", len) == 0 || strncmp(p, "bcm2836", len) == 0 || strncmp(p, "bcm2837", len) == 0) { platform_name = "bcm2835"; break; } else if (strncmp(p, "bcm2711", len) == 0) { platform_name = "bcm2711"; break; } else if (strncmp(p, "bcm2712", len) == 0) { platform_name = "bcm2712"; break; } compatible_len -= (p - compatible); compatible = p; len = strnlen(compatible, compatible_len) + 1; compatible += len; compatible_len -= len; } if (platform_name) { dtoverlay_debug("using platform '%s'", platform_name); platform_name_len = strlen(platform_name); if (fp) overlay_map = dtoverlay_load_dtb_from_fp(fp, 0); } else { dtoverlay_warn("no matching platform found"); } dtoverlay_debug("overlay map %sloaded", overlay_map ? "" : "not "); } void dtoverlay_init_map(const char *overlay_dir, const char *compatible, int compatible_len) { char map_file[DTOVERLAY_MAX_PATH]; int dir_len = strlen(overlay_dir); FILE *fp; static int tried; if (tried) return; tried = 1; if (!compatible) return; /* Handle the possibility that the supplied directory may or may not end with a slash */ sprintf(map_file, "%s%soverlay_map.dtb", overlay_dir, (!dir_len || overlay_dir[dir_len - 1] != '/') ? "/" : ""); fp = fopen(map_file, "rb"); dtoverlay_init_map_from_fp(fp, compatible, compatible_len); } const char *dtoverlay_remap_overlay(const char *overlay) { while (overlay_map) { const char *deprecated_msg; const char *new_name; int root_off; int overlay_off; int prop_len; root_off = fdt_path_offset(overlay_map->fdt, "/"); overlay_off = fdt_subnode_offset(overlay_map->fdt, root_off, overlay); if (overlay_off < 0) break; new_name = fdt_getprop_namelen(overlay_map->fdt, overlay_off, platform_name, platform_name_len, &prop_len); if (new_name) { if (new_name[0]) overlay = new_name; break; } // Has it been renamed or deprecated? new_name = fdt_getprop_namelen(overlay_map->fdt, overlay_off, "renamed", 7, &prop_len); if (new_name) { dtoverlay_warn("overlay '%s' has been renamed '%s'", overlay, new_name); overlay = new_name; continue; } deprecated_msg = fdt_getprop_namelen(overlay_map->fdt, overlay_off, "deprecated", 10, &prop_len); if (deprecated_msg) dtoverlay_error("overlay '%s' is deprecated: %s", overlay, deprecated_msg); else dtoverlay_error("overlay '%s' is not supported on the '%s' platform", overlay, platform_name); return NULL; } return overlay; } DTBLOB_T *dtoverlay_import_fdt(void *fdt, int buf_size) { DTBLOB_T *dtb = NULL; int node_off; int dtb_len; int err; err = fdt_check_header(fdt); if (err != 0) { dtoverlay_error("not a valid FDT - err %d", err); goto error_exit; } dtb_len = fdt_totalsize(fdt); if (buf_size < dtb_len) { dtoverlay_error("fdt is too large"); goto error_exit; } if (buf_size > dtb_len) fdt_set_totalsize(fdt, buf_size); dtb = calloc(1, sizeof(DTBLOB_T)); if (!dtb) { dtoverlay_error("out of memory"); goto error_exit; } dtb->fdt = fdt; dtb->max_phandle = 0; // Not a valid phandle // Find the minimum and maximum phandles, in case it is necessary to // relocate existing ones or create new ones. for (node_off = 0; node_off >= 0; node_off = fdt_next_node(fdt, node_off, NULL)) { uint32_t phandle = fdt_get_phandle(fdt, node_off); if (phandle > dtb->max_phandle) dtb->max_phandle = phandle; } error_exit: return dtb; } int dtoverlay_save_dtb(const DTBLOB_T *dtb, const char *filename) { FILE *fp = fopen(filename, "wb"); int err = 0; if (fp) { int len = fdt_totalsize(dtb->fdt); if (fwrite(dtb->fdt, len, 1, fp) != 1) { dtoverlay_error("fwrite failed"); err = -2; goto error_exit; } if (dtb->trailer_len && (fwrite(dtb->trailer, dtb->trailer_len, 1, fp) != 1)) { dtoverlay_error("fwrite failed"); err = -2; goto error_exit; } dtoverlay_debug("wrote %ld bytes to '%s'", len, filename); fclose(fp); } else { dtoverlay_debug("failed to create '%s'", filename); err = -1; } error_exit: return err; } int dtoverlay_extend_dtb(DTBLOB_T *dtb, int new_size) { int size = fdt_totalsize(dtb->fdt); int err = 0; if (new_size < 0) new_size = size - new_size; if (new_size > size) { void *fdt; fdt = malloc(new_size); if (fdt) { memcpy(fdt, dtb->fdt, size); fdt_set_totalsize(fdt, new_size); if (dtb->fdt_is_malloced) free(dtb->fdt); dtb->fdt = fdt; dtb->fdt_is_malloced = 1; } else { err = -FDT_ERR_NOSPACE; } } else if (new_size < size) { /* Can't shrink it */ err = -FDT_ERR_NOSPACE; } return err; } int dtoverlay_dtb_totalsize(DTBLOB_T *dtb) { return fdt_totalsize(dtb->fdt); } void dtoverlay_pack_dtb(DTBLOB_T *dtb) { fdt_pack(dtb->fdt); } void dtoverlay_free_dtb(DTBLOB_T *dtb) { if (dtb) { if (dtb->fdt_is_malloced) free(dtb->fdt); if (dtb->trailer_is_malloced) free(dtb->trailer); free(dtb); } } int dtoverlay_find_phandle(DTBLOB_T *dtb, int phandle) { return fdt_node_offset_by_phandle(dtb->fdt, phandle); } int dtoverlay_find_symbol(DTBLOB_T *dtb, const char *symbol_name) { int symbols_off, path_len; const char *node_path; node_path = dtoverlay_get_alias(dtb, symbol_name); if (node_path) { path_len = strlen(node_path); } else { symbols_off = fdt_path_offset(dtb->fdt, "/__symbols__"); if (symbols_off < 0) { dtoverlay_error("no symbols found"); return -FDT_ERR_NOTFOUND; } node_path = fdt_getprop(dtb->fdt, symbols_off, symbol_name, &path_len); if (path_len < 0) return -FDT_ERR_NOTFOUND; //Ensure we don't have trailing NULLs if (path_len > (int)strnlen(node_path, path_len)) path_len = (int)strnlen(node_path, path_len); } return fdt_path_offset_namelen(dtb->fdt, node_path, path_len); } int dtoverlay_find_matching_node(DTBLOB_T *dtb, const char **node_names, int pos) { while (1) { const char *node_name; pos = fdt_next_node(dtb->fdt, pos, NULL); if (pos < 0) break; node_name = fdt_get_name(dtb->fdt, pos, NULL); if (node_name) { int i; for (i = 0; node_names[i]; i++) { const char *node = node_names[i]; int matchlen = strlen(node); if ((strncmp(node_name, node, matchlen) == 0) && ((node[matchlen] == '\0') || (node[matchlen] == '@'))) return pos; } } } return -1; } int dtoverlay_node_is_enabled(DTBLOB_T *dtb, int pos) { if (pos >= 0) { const void *prop = dtoverlay_get_property(dtb, pos, "status", NULL); if (prop && ((strcmp((const char *)prop, "okay") == 0) || (strcmp((const char *)prop, "ok") == 0))) return 1; } return 0; } const void *dtoverlay_get_property(DTBLOB_T *dtb, int pos, const char *prop_name, int *prop_len) { return fdt_getprop(dtb->fdt, pos, prop_name, prop_len); } int dtoverlay_set_property(DTBLOB_T *dtb, int pos, const char *prop_name, const void *prop, int prop_len) { int err = fdt_setprop(dtb->fdt, pos, prop_name, prop, prop_len); if (err < 0) dtoverlay_error("failed to set property '%s'", prop_name); return err; } const char *dtoverlay_get_alias(DTBLOB_T *dtb, const char *alias_name) { int node_off; int prop_len; const char *alias; node_off = fdt_path_offset(dtb->fdt, "/aliases"); alias = fdt_getprop(dtb->fdt, node_off, alias_name, &prop_len); if (alias && !prop_len) alias = ""; return alias; } int dtoverlay_set_alias(DTBLOB_T *dtb, const char *alias_name, const char *value) { int node_off; node_off = fdt_path_offset(dtb->fdt, "/aliases"); if (node_off < 0) node_off = fdt_add_subnode(dtb->fdt, 0, "aliases"); return fdt_setprop_string(dtb->fdt, node_off, alias_name, value); } void dtoverlay_set_logging_func(DTOVERLAY_LOGGING_FUNC *func) { dtoverlay_logging_func = func; } void dtoverlay_enable_debug(int enable) { dtoverlay_debug_enabled = enable; } void dtoverlay_error(const char *fmt, ...) { va_list args; va_start(args, fmt); (*dtoverlay_logging_func)(DTOVERLAY_ERROR, fmt, args); va_end(args); } void dtoverlay_warn(const char *fmt, ...) { va_list args; va_start(args, fmt); (*dtoverlay_logging_func)(DTOVERLAY_WARN, fmt, args); va_end(args); } void dtoverlay_debug(const char *fmt, ...) { va_list args; if (dtoverlay_debug_enabled) { va_start(args, fmt); (*dtoverlay_logging_func)(DTOVERLAY_DEBUG, fmt, args); va_end(args); } } static void dtoverlay_stdio_logging(dtoverlay_logging_type_t type, const char *fmt, va_list args) { const char *type_str; switch (type) { case DTOVERLAY_ERROR: type_str = "error"; break; case DTOVERLAY_WARN: type_str = "warn"; break; case DTOVERLAY_DEBUG: type_str = "debug"; break; default: type_str = "?"; } fprintf(stderr, "DTOVERLAY[%s]: ", type_str); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); } raspi-utils-20250514/dtmerge/dtoverlay.h000066400000000000000000000231311501106437300200430ustar00rootroot00000000000000/* Copyright (c) 2016-2023 Raspberry Pi Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * 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. */ #ifndef DTOVERLAY_H #define DTOVERLAY_H #include #define BE4(x) ((x)>>24)&0xff, ((x)>>16)&0xff, ((x)>>8)&0xff, ((x)>>0)&0xff #define GETBE4(p, off) ((((unsigned char *)p)[off + 0]<<24) + (((unsigned char *)p)[off + 1]<<16) + \ (((unsigned char *)p)[off + 2]<<8) + (((unsigned char *)p)[off + 3]<<0)) #define SETBE4(p, off, x) do { \ ((unsigned char *)p)[off + 0] = ((x>>24) & 0xff); \ ((unsigned char *)p)[off + 1] = ((x>>16) & 0xff); \ ((unsigned char *)p)[off + 2] = ((x>>8) & 0xff); \ ((unsigned char *)p)[off + 3] = ((x>>0) & 0xff); \ } while (0) #define NON_FATAL(err) (((err) < 0) ? -(err) : (err)) #define IS_FATAL(err) ((err) < 0) #define ONLY_FATAL(err) (IS_FATAL(err) ? (err) : 0) #define DTOVERLAY_PADDING(size) (-(size)) #define DTOVERLAY_MAX_PATH 256 typedef enum { DTOVERLAY_ERROR, DTOVERLAY_DEBUG, DTOVERLAY_WARN, // Append to preserve backwards compatibility } dtoverlay_logging_type_t; typedef struct dtoverlay_struct { const char *param; int len; const char *b; } DTOVERLAY_PARAM_T; typedef struct dtblob_struct { void *fdt; char fdt_is_malloced; char trailer_is_malloced; char fixups_applied; uint32_t min_phandle; uint32_t max_phandle; void *trailer; int trailer_len; } DTBLOB_T; typedef struct pin_iter_struct { DTBLOB_T *dtb; const void *pinctrl; int pinctrl_len; int pinctrl_off; const void *pins; const void *funcs; const void *pulls; int pins_len; int pin_off; int funcs_len; int pulls_len; } PIN_ITER_T; typedef void DTOVERLAY_LOGGING_FUNC(dtoverlay_logging_type_t type, const char *fmt, va_list args); typedef int (*override_callback_t)(int override_type, const char *override_value, DTBLOB_T *dtb, int node_off, const char *prop_name, int target_phandle, int target_off, int target_size, void *callback_state); uint8_t dtoverlay_read_u8(const void *src, int off); uint16_t dtoverlay_read_u16(const void *src, int off); uint32_t dtoverlay_read_u32(const void *src, int off); uint64_t dtoverlay_read_u64(const void *src, int off); void dtoverlay_write_u8(void *dst, int off, uint32_t val); void dtoverlay_write_u16(void *dst, int off, uint32_t val); void dtoverlay_write_u32(void *dst, int off, uint32_t val); void dtoverlay_write_u64(void *dst, int off, uint64_t val); /* Return values: -ve = fatal error, positive = non-fatal error */ int dtoverlay_create_node(DTBLOB_T *dtb, const char *node_name, int path_len); int dtoverlay_delete_node(DTBLOB_T *dtb, const char *node_name, int path_len); int dtoverlay_find_node(DTBLOB_T *dtb, const char *node_path, int path_len); int dtoverlay_first_subnode(DTBLOB_T *dtb, int node_off); int dtoverlay_next_subnode(DTBLOB_T *dtb, int subnode_off); int dtoverlay_set_node_properties(DTBLOB_T *dtb, const char *node_path, DTOVERLAY_PARAM_T *properties, unsigned int num_properties); int dtoverlay_create_prop_fragment(DTBLOB_T *dtb, int idx, int target_phandle, const char *prop_name, const void *prop_data, int prop_len); int dtoverlay_merge_fragment(DTBLOB_T *base_dtb, int target_off, const DTBLOB_T *overlay_dtb, int overlay_off, int depth); int dtoverlay_fixup_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb); int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb); int dtoverlay_merge_params(DTBLOB_T *dtb, const DTOVERLAY_PARAM_T *params, unsigned int num_params); int dtoverlay_filter_symbols(DTBLOB_T *dtb); const char *dtoverlay_find_fixup(DTBLOB_T *dtb, const char *fixup_loc); int dtoverlay_add_fixup(DTBLOB_T *dtb, const char *symbol, const char *fixup_loc); int dtoverlay_delete_fixup(DTBLOB_T *dtb, const char *fixup_loc); int dtoverlay_stringlist_replace(const char *src, int src_len, const char *src_prefix, int src_prefix_len, const char *dst_prefix, int dst_prefix_len, char *dst); void dtoverlay_set_intra_fragment_merged_callback(void (*callback)(DTBLOB_T *, int, int)); void dtoverlay_set_cell_changed_callback(void (*callback)(DTBLOB_T *, int, const char *, int, int)); const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name, int *data_len); int dtoverlay_override_one_target(int override_type, const char *override_value, DTBLOB_T *dtb, int node_off, const char *prop_name, int target_phandle, int target_off, int target_size, void *callback_state); int dtoverlay_foreach_override_target(DTBLOB_T *dtb, const char *override_name, const char *override_data, int data_len, const char *override_value, override_callback_t callback, void *callback_value); int dtoverlay_apply_override(DTBLOB_T *dtb, const char *override_name, const char *override_data, int data_len, const char *override_value); int dtoverlay_set_synonym(DTBLOB_T *dtb, const char *dst, const char *src); int dtoverlay_dup_property(DTBLOB_T *dtb, const char *node_name, const char *dst, const char *src); DTBLOB_T *dtoverlay_create_dtb(int max_size); DTBLOB_T *dtoverlay_load_dtb_from_fp(FILE *fp, int max_size); DTBLOB_T *dtoverlay_load_dtb(const char *filename, int max_size); void dtoverlay_init_map_from_fp(FILE *fp, const char *compatible, int compatible_len); void dtoverlay_init_map(const char *overlay_dir, const char *compatible, int compatible_len); const char *dtoverlay_remap_overlay(const char *overlay); DTBLOB_T *dtoverlay_import_fdt(void *fdt, int max_size); int dtoverlay_save_dtb(const DTBLOB_T *dtb, const char *filename); int dtoverlay_extend_dtb(DTBLOB_T *dtb, int new_size); int dtoverlay_dtb_totalsize(DTBLOB_T *dtb); void dtoverlay_pack_dtb(DTBLOB_T *dtb); void dtoverlay_free_dtb(DTBLOB_T *dtb); static inline void *dtoverlay_dtb_trailer(DTBLOB_T *dtb) { return dtb->trailer; } static inline int dtoverlay_dtb_trailer_len(DTBLOB_T *dtb) { return dtb->trailer_len; } static inline void dtoverlay_dtb_set_trailer(DTBLOB_T *dtb, void *trailer, int trailer_len) { dtb->trailer = trailer; dtb->trailer_len = trailer_len; dtb->trailer_is_malloced = 0; } int dtoverlay_find_pins_for_device(DTBLOB_T *dtb, const char *symbol, PIN_ITER_T *iter); int dtoverlay_next_pin(PIN_ITER_T *iter, int *pin, int *func, int *pull); int dtoverlay_find_phandle(DTBLOB_T *dtb, int phandle); int dtoverlay_find_symbol(DTBLOB_T *dtb, const char *symbol_name); int dtoverlay_find_matching_node(DTBLOB_T *dtb, const char **node_names, int pos); int dtoverlay_node_is_enabled(DTBLOB_T *dtb, int pos); const void *dtoverlay_get_property(DTBLOB_T *dtb, int pos, const char *prop_name, int *prop_len); int dtoverlay_set_property(DTBLOB_T *dtb, int pos, const char *prop_name, const void *prop, int prop_len); const char *dtoverlay_get_alias(DTBLOB_T *dtb, const char *alias_name); int dtoverlay_set_alias(DTBLOB_T *dtb, const char *alias_name, const char *value); void dtoverlay_set_logging_func(DTOVERLAY_LOGGING_FUNC *func); void dtoverlay_enable_debug(int enable); void dtoverlay_error(const char *fmt, ...); void dtoverlay_warn(const char *fmt, ...); void dtoverlay_debug(const char *fmt, ...); #endif raspi-utils-20250514/dtmerge/dtoverlay_main.c000066400000000000000000001166331501106437300210540ustar00rootroot00000000000000/* Copyright (c) 2016-2023 Raspberry Pi Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * 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. */ #include #include #include #include #include #include #include #include #include #include "dtoverlay.h" #include "utils.h" #define CFG_DIR_1 "/sys/kernel/config" #define CFG_DIR_2 "/config" #define DT_SUBDIR "/device-tree" #define WORK_DIR "/tmp/.dtoverlays" #define OVERLAY_SRC_SUBDIR "overlays" #define README_FILE "README" #define DT_OVERLAYS_SUBDIR "overlays" #define DTOVERLAY_PATH_MAX 128 #define DIR_MODE 0755 enum { OPT_ADD, OPT_REMOVE, OPT_REMOVE_FROM, OPT_LIST, OPT_LIST_ALL, OPT_HELP }; static const char *boot_dirs[] = { #ifdef FORCE_BOOT_DIR FORCE_BOOT_DIR, #else "/boot/firmware", "/boot", "/flash", #ifdef OTHER_BOOT_DIR OTHER_BOOT_DIR, #endif #endif NULL /* Terminator */ }; typedef struct state_struct { int count; struct dirent **namelist; } STATE_T; static int dtoverlay_add(STATE_T *state, const char *overlay, int argc, const char **argv); static int dtoverlay_remove(STATE_T *state, const char *overlay, int and_later); static int dtoverlay_list(STATE_T *state); static int dtoverlay_list_all(STATE_T *state); static void usage(void); static void root_check(void); static void overlay_help(const char *overlay, const char **params); static int apply_overlay(const char *overlay_file, const char *overlay); static int overlay_applied(const char *overlay_dir); static STATE_T *read_state(const char *dir); static void free_state(STATE_T *state); const char *cmd_name; const char *work_dir = WORK_DIR; const char *overlay_src_dir; const char *dt_overlays_dir; const char *error_file = NULL; const char *platform_string; int platform_string_len; int dry_run = 0; char cell_source_loc[DTOVERLAY_MAX_PATH]; int cell_source_loc_len; int main(int argc, const char **argv) { int argn = 1; int opt = OPT_ADD; int is_dtparam; const char *overlay = NULL; const char **params = NULL; int ret = 0; STATE_T *state = NULL; const char *cfg_dir; cmd_name = argv[0]; if (strrchr(cmd_name, '/')) cmd_name = strrchr(cmd_name, '/') + 1; is_dtparam = (strcmp(cmd_name, "dtparam") == 0); while ((argn < argc) && (argv[argn][0] == '-')) { const char *arg = argv[argn++]; if (strcmp(arg, "-r") == 0) { if (opt != OPT_ADD) usage(); opt = OPT_REMOVE; } else if (strcmp(arg, "-R") == 0) { if (opt != OPT_ADD) usage(); opt = OPT_REMOVE_FROM; } else if (strcmp(arg, "-D") == 0) { if (opt != OPT_ADD) usage(); dry_run = 1; work_dir = "."; } else if ((strcmp(arg, "-l") == 0) || (strcmp(arg, "--list") == 0)) { if (opt != OPT_ADD) usage(); opt = OPT_LIST; } else if ((strcmp(arg, "-a") == 0) || (strcmp(arg, "--listall") == 0) || (strcmp(arg, "--all") == 0)) { if (opt != OPT_ADD) usage(); opt = OPT_LIST_ALL; } else if (strcmp(arg, "-d") == 0) { if (argn == argc) usage(); overlay_src_dir = argv[argn++]; } else if (strcmp(arg, "-p") == 0) { if (argn == argc) usage(); platform_string = argv[argn++]; platform_string_len = strlen(platform_string) + 1; } else if (strcmp(arg, "-v") == 0) { opt_verbose = 1; } else if (strcmp(arg, "-h") == 0) { opt = OPT_HELP; } else { fprintf(stderr, "* unknown option '%s'\n", arg); usage(); } } if ((opt == OPT_ADD) || (opt == OPT_REMOVE) || (opt == OPT_REMOVE_FROM) || (opt == OPT_HELP)) { if ((argn == argc) && ((!is_dtparam && ((opt == OPT_ADD) || (opt == OPT_HELP))) || (is_dtparam && (opt == OPT_HELP)))) usage(); if (is_dtparam && (opt == OPT_ADD) && (argn == argc)) opt = OPT_HELP; if (is_dtparam && ((opt == OPT_ADD) || (opt == OPT_HELP))) overlay = "dtparam"; else if (argn < argc) overlay = argv[argn++]; } if ((opt == OPT_HELP) && (argn < argc)) { params = &argv[argn]; argn = argc; } if ((opt != OPT_ADD) && (argn != argc)) usage(); dtoverlay_enable_debug(opt_verbose); if (!overlay_src_dir) { /* Find the overlays and README */ const char *os_prefix; int i; /* Check for an os_prefix value */ os_prefix = read_file_as_string("/proc/device-tree/chosen/os_prefix", NULL); if (!os_prefix) os_prefix = ""; for (i = 0; boot_dirs[i]; i++) { overlay_src_dir = sprintf_dup("%s/%s" OVERLAY_SRC_SUBDIR, boot_dirs[i], os_prefix); if (dir_exists(overlay_src_dir)) break; free_string(overlay_src_dir); overlay_src_dir = NULL; } if (!overlay_src_dir) fatal_error("Failed to find overlays directory"); } if (opt == OPT_HELP) { overlay_help(overlay, params); goto orderly_exit; } if (!dir_exists(work_dir)) { if (mkdir(work_dir, DIR_MODE) != 0) fatal_error("Failed to create '%s' - %d", work_dir, errno); } error_file = sprintf_dup("%s/%s", work_dir, "error.dtb"); cfg_dir = CFG_DIR_1 DT_SUBDIR; if (!dry_run && !dir_exists(cfg_dir)) { root_check(); cfg_dir = CFG_DIR_2; if (!dir_exists(cfg_dir)) { if (mkdir(cfg_dir, DIR_MODE) != 0) fatal_error("Failed to create '%s' - %d", cfg_dir, errno); } cfg_dir = CFG_DIR_2 DT_SUBDIR; if (!dir_exists(cfg_dir) && (run_cmd("mount -t configfs none '%s'", cfg_dir) != 0)) fatal_error("Failed to mount configfs - %d", errno); } if (!platform_string) platform_string = read_file_as_string("/proc/device-tree/compatible", &platform_string_len); if (platform_string) dtoverlay_init_map(overlay_src_dir, platform_string, platform_string_len); if (!dry_run) { dt_overlays_dir = sprintf_dup("%s/%s", cfg_dir, DT_OVERLAYS_SUBDIR); if (!dir_exists(dt_overlays_dir)) fatal_error("configfs overlays folder not found - incompatible kernel"); state = read_state(work_dir); if (!state) fatal_error("Failed to read state"); } switch (opt) { case OPT_ADD: case OPT_REMOVE: case OPT_REMOVE_FROM: if (!dry_run) { root_check(); run_cmd("which dtoverlay-pre >/dev/null 2>&1 && dtoverlay-pre"); } break; default: break; } switch (opt) { case OPT_ADD: ret = dtoverlay_add(state, overlay, argc - argn, argv + argn); break; case OPT_REMOVE: ret = dtoverlay_remove(state, overlay, 0); break; case OPT_REMOVE_FROM: ret = dtoverlay_remove(state, overlay, 1); break; case OPT_LIST: ret = dtoverlay_list(state); break; case OPT_LIST_ALL: ret = dtoverlay_list_all(state); break; default: ret = 1; break; } switch (opt) { case OPT_ADD: case OPT_REMOVE: case OPT_REMOVE_FROM: if (!dry_run) run_cmd("which dtoverlay-post >/dev/null 2>&1 && dtoverlay-post"); break; default: break; } orderly_exit: if (state) free_state(state); free_strings(); if ((ret == 0) && error_file) unlink(error_file); return ret; } static int dtparam_callback(int override_type, const char *override_value, DTBLOB_T *dtb, int node_off, const char *prop_name, int target_phandle, int target_off, int target_size, void *callback_state) { STRING_VEC_T *used_props = callback_state; char prop_id[80]; int err; err = dtoverlay_override_one_target(override_type, override_value, dtb, node_off, prop_name, target_phandle, target_off, target_size, callback_state); if ((err == 0) && (target_phandle != 0)) { if (snprintf(prop_id, sizeof(prop_id), "%08x%s", target_phandle, prop_name) < 0) err = FDT_ERR_INTERNAL; else if (string_vec_find(used_props, prop_id, 0) < 0) string_vec_add(used_props, prop_id, 0); } return err; } // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors static int dtparam_apply(DTBLOB_T *dtb, const char *override_name, const char *override_data, int data_len, const char *override_value, STRING_VEC_T *used_props) { void *data; int err; /* Copy the override data in case it moves */ data = malloc(data_len); if (data) { memcpy(data, override_data, data_len); err = dtoverlay_foreach_override_target(dtb, override_name, data, data_len, override_value, dtparam_callback, used_props); free(data); } else { dtoverlay_error("out of memory"); err = NON_FATAL(FDT_ERR_NOSPACE); } return err; } static void intra_fragment_merged_callback(DTBLOB_T *dtb, int fragment_off, int target_off) { // look for all fixups that refer to the indicated fragment, and rebase them // onto the target char fragment_path[DTOVERLAY_MAX_PATH]; char target_path[DTOVERLAY_MAX_PATH]; char *src_path; char *dst_path; DTBLOB_T clone_dtb; DTBLOB_T *src_dtb; int fragment_path_len; int target_path_len; int fixups_off; int fixup_off; int src_off; int dst_off; fdt_get_path(dtb->fdt, fragment_off, fragment_path, sizeof(fragment_path)); fragment_path_len = strlen(fragment_path); fragment_path[fragment_path_len++] = ':'; fragment_path[fragment_path_len] = '\0'; fdt_get_path(dtb->fdt, target_off, target_path, sizeof(target_path)); target_path_len = strlen(target_path); target_path[target_path_len++] = ':'; target_path[target_path_len] = '\0'; fixups_off = fdt_path_offset(dtb->fdt, "/__fixups__"); if (fixups_off < 0) goto try_local_fixups; for (fixup_off = fdt_first_property_offset(dtb->fdt, fixups_off); fixup_off >= 0; fixup_off = fdt_next_property_offset(dtb->fdt, fixup_off)) { const char *fixups_stringlist; const char *symbol_name; char *new_list = NULL; int list_len; int new_len; int err; int i; fixups_stringlist = fdt_getprop_by_offset(dtb->fdt, fixup_off, &symbol_name, &list_len); for (i = 0; i < 2; i++) { new_len = dtoverlay_stringlist_replace(fixups_stringlist, list_len, fragment_path, fragment_path_len, target_path, target_path_len, new_list); if (new_len < 0) break; if (!new_list) new_list = malloc(new_len); } if (!new_list) continue; err = fdt_setprop(dtb->fdt, fixups_off, symbol_name, new_list, new_len); free(new_list); if (err < 0) break; } try_local_fixups: // Strip off the trailing ':'s fragment_path[--fragment_path_len] = '\0'; target_path[--target_path_len] = '\0'; src_path = sprintf_dup("/__local_fixups__%s", fragment_path); dst_path = sprintf_dup("/__local_fixups__%s", target_path); src_dtb = dtb; src_off = fdt_path_offset(src_dtb->fdt, src_path); if (src_off < 0) // No phandles in the fragment payload return; dst_off = dtoverlay_create_node(dtb, dst_path, 0); if (dst_off < 0) fatal_error("failed to copy local fixups"); if (dst_off < src_off) { // Make a copy of the src, so that it doesn't move int overlay_size = fdt_totalsize(dtb->fdt); void *overlay_copy = malloc(overlay_size); if (!overlay_copy) fatal_error("out of memory"); memcpy(overlay_copy, dtb->fdt, overlay_size); memcpy(&clone_dtb, dtb, sizeof(DTBLOB_T)); clone_dtb.fdt = overlay_copy; src_dtb = &clone_dtb; src_off = fdt_path_offset(src_dtb->fdt, src_path); } if (dtoverlay_merge_fragment(dtb, dst_off, src_dtb, src_off, 1)) fatal_error("failed to copy local fixups"); if (src_dtb != dtb) free(src_dtb->fdt); } static void cell_changed_callback(DTBLOB_T *dtb, int node_off, const char *prop_name, int target_off, int cell_data_offset) { char cell_target_loc[DTOVERLAY_MAX_PATH]; const char *symbol; int path_len; sprintf(cell_source_loc + cell_source_loc_len, "%d", cell_data_offset); // XXX fdt_get_path(dtb->fdt, node_off, cell_target_loc, sizeof(cell_target_loc)); path_len = strlen(cell_target_loc); sprintf(cell_target_loc + path_len, ":%s:%d", prop_name, target_off); // XXX dtoverlay_delete_fixup(dtb, cell_target_loc); symbol = dtoverlay_find_fixup(dtb, cell_source_loc); if (symbol) dtoverlay_add_fixup(dtb, symbol, cell_target_loc); } // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors static int apply_override(DTBLOB_T *dtb, const char *override_name, const char *override_data, int data_len, const char *override_value) { cell_source_loc_len = sprintf(cell_source_loc, "/__overrides__:%s:", override_name); return dtoverlay_foreach_override_target(dtb, override_name, override_data, data_len, override_value, dtoverlay_override_one_target, NULL); } static int apply_parameter(DTBLOB_T *overlay_dtb, const char *arg, int is_dtparam, STRING_VEC_T *used_props, char **param_string) { const char *param_val = strchr(arg, '='); const char *param, *override; char *p = NULL; int override_len; int err = 0; if (param_val) { int len = (param_val - arg); p = sprintf_dup("%.*s", len, arg); param = p; param_val++; } else { /* Use the default parameter value - true */ param = arg; param_val = "true"; } override = dtoverlay_find_override(overlay_dtb, param, &override_len); if (!override) return error("Unknown parameter '%s'", param); if (is_dtparam) err = dtparam_apply(overlay_dtb, param, override, override_len, param_val, used_props); else err = apply_override(overlay_dtb, param, override, override_len, param_val); if (err != 0) return error("Failed to set %s=%s", param, param_val); *param_string = sprintf_dup("%s %s=%s", *param_string ? *param_string : "", param, param_val); free_string(p); return 0; } static int dtoverlay_add(STATE_T *state, const char *overlay, int argc, const char **argv) { const char *overlay_name; const char *overlay_file; char *overrides = NULL; char *param_string = NULL; int is_dtparam; DTBLOB_T *base_dtb = NULL; DTBLOB_T *overlay_dtb; STRING_VEC_T used_props; int err; int len; int i; len = strlen(overlay) - 5; is_dtparam = (strcmp(overlay, "dtparam") == 0); if (is_dtparam) { /* Convert /proc/device-tree to a .dtb and load it */ overlay_file = sprintf_dup("%s/%s", work_dir, "base.dtb"); if (run_cmd("dtc -I fs -O dtb -o '%s' /proc/device-tree 1>/dev/null 2>&1", overlay_file) != 0) return error("Failed to read active DTB"); } else if ((len > 0) && (strcmp(overlay + len, ".dtbo") == 0)) { const char *p; overlay_file = overlay; p = strrchr(overlay, '/'); if (p) { overlay = p + 1; len = strlen(overlay) - 5; } overlay = sprintf_dup("%.*s", len, overlay); } else { /* Allow for comma-separated parameters */ const char *in_params = strchr(overlay, ','); const char *remapped; int name_len; if (in_params && in_params[1]) { int len = in_params - overlay; overlay = sprintf_dup("%.*s", len, overlay); if (!overlay) return error("Out of memory"); in_params++; } else { in_params = ""; } remapped = dtoverlay_remap_overlay(overlay); if (!remapped) return error("Failed to load '%s'", overlay); if (strcmp(overlay, remapped)) dtoverlay_debug("mapped overlay '%s' to '%s'", overlay, remapped); overlay = remapped; name_len = strcspn(overlay, ","); if (overlay[name_len] && overlay[name_len + 1]) { /* There are parameters */ overrides = sprintf_dup("%s,%s", overlay + name_len + 1, in_params); } else { overrides = strdup(in_params); } overlay = sprintf_dup("%.*s", name_len, overlay); overlay_file = sprintf_dup("%s/%s.dtbo", overlay_src_dir, overlay); } if (dry_run) overlay_name = "dry_run"; else overlay_name = sprintf_dup("%d_%s", state->count, overlay); dtoverlay_debug("loading file '%s'", overlay_file); overlay_dtb = dtoverlay_load_dtb(overlay_file, DTOVERLAY_PADDING(4096)); if (!overlay_dtb) return error("Failed to read '%s'", overlay_file); if (is_dtparam) { base_dtb = overlay_dtb; string_vec_init(&used_props); } dtoverlay_set_cell_changed_callback(&cell_changed_callback); /* Apply any parameters that came from the overlay map, along with any supplied on the command line separated by commas */ while (overrides && *overrides) { int override_len = strcspn(overrides, ","); int last = !overrides[override_len]; overrides[override_len] = '\0'; err = apply_parameter(overlay_dtb, overrides, is_dtparam, &used_props, ¶m_string); if (err) return err; if (last) break; overrides += override_len + 1; } /* Then any others supplied on the command line */ for (i = 0; i < argc; i++) { err = apply_parameter(overlay_dtb, argv[i], is_dtparam, &used_props, ¶m_string); if (err) return err; } dtoverlay_set_cell_changed_callback(NULL); /* Apply any intra-overlay fragments, filtering the symbols */ dtoverlay_set_intra_fragment_merged_callback(intra_fragment_merged_callback); err = dtoverlay_merge_overlay(NULL, overlay_dtb); if (err != 0) return error("Failed to apply intra-overlay fragments"); dtoverlay_set_intra_fragment_merged_callback(NULL); if (is_dtparam) { /* Build an overlay DTB */ overlay_dtb = dtoverlay_create_dtb(2048 + 256 * used_props.num_strings); for (i = 0; i < used_props.num_strings; i++) { int phandle, node_off, prop_len; const char *str, *prop_name; const void *prop_data; str = used_props.strings[i]; sscanf(str, "%8x", &phandle); prop_name = str + 8; node_off = dtoverlay_find_phandle(base_dtb, phandle); prop_data = dtoverlay_get_property(base_dtb, node_off, prop_name, &prop_len); err = dtoverlay_create_prop_fragment(overlay_dtb, i, phandle, prop_name, prop_data, prop_len); } dtoverlay_free_dtb(base_dtb); } if (param_string) dtoverlay_dtb_set_trailer(overlay_dtb, param_string, strlen(param_string) + 1); /* Create a filename with the sequence number */ overlay_file = sprintf_dup("%s/%s.dtbo", work_dir, overlay_name); /* then write the overlay to the file */ dtoverlay_pack_dtb(overlay_dtb); dtoverlay_save_dtb(overlay_dtb, overlay_file); dtoverlay_free_dtb(overlay_dtb); if (!dry_run && !apply_overlay(overlay_file, overlay_name)) { if (error_file) { rename(overlay_file, error_file); free_string(error_file); } return 1; } return 0; } static int dtoverlay_remove(STATE_T *state, const char *overlay, int and_later) { const char *overlay_dir; const char *dir_name = NULL; char *end; int overlay_len; int count = state->count; int rmpos; int i; if (chdir(work_dir) != 0) fatal_error("Failed to chdir to '%s'", work_dir); if (overlay) { overlay_len = strlen(overlay); rmpos = strtoul(overlay, &end, 10); if (end && (*end == '\0')) { if (rmpos >= count) return error("Overlay index (%d) too large", rmpos); dir_name = state->namelist[rmpos]->d_name; } /* Locate the most recent reference to the overlay */ else for (rmpos = count - 1; rmpos >= 0; rmpos--) { const char *left, *right; dir_name = state->namelist[rmpos]->d_name; left = strchr(dir_name, '_'); if (!left) return error("Internal error"); left++; right = strchr(left, '.'); if (!right) return error("Internal error"); if (((right - left) == overlay_len) && (memcmp(overlay, left, overlay_len) == 0)) break; dir_name = NULL; } if (rmpos < 0) return error("Overlay '%s' is not loaded", overlay); } else { if (!count) return error("No overlays loaded"); rmpos = and_later ? 0 : (count - 1); dir_name = state->namelist[rmpos]->d_name; } if (rmpos < count) { /* Unload it and all subsequent overlays in reverse order */ for (i = count - 1; i >= rmpos; i--) { const char *left, *right; left = state->namelist[i]->d_name; right = strrchr(left, '.'); if (!right) return error("Internal error"); overlay_dir = sprintf_dup("%s/%.*s", dt_overlays_dir, right - left, left); if (rmdir(overlay_dir) != 0) return error("Failed to remove directory '%s'", overlay_dir); free_string(overlay_dir); } /* Replay the sequence, deleting files for the specified overlay, and renumbering and reloading all other overlays. */ for (i = rmpos, state->count = rmpos; i < count; i++) { const char *left, *right; const char *filename = state->namelist[i]->d_name; left = strchr(filename, '_'); if (!left) return error("Internal error"); left++; right = strchr(left, '.'); if (!right) return error("Internal error"); if (and_later || (i == rmpos)) { /* This one is being deleted */ unlink(filename); } else { /* Keep this one - renumber and reload */ int len = right - left; char *new_name = sprintf_dup("%d_%.*s", state->count, len, left); char *new_file = sprintf_dup("%s.dtbo", new_name); int ret = 0; if ((len == 7) && (memcmp(left, "dtparam", 7) == 0)) { /* Regenerate the overlay in case multiple overlays target different parts of the same property. */ DTBLOB_T *dtb; char *params; const char **paramv; int paramc; int j; char *p; /* Extract the parameters */ dtb = dtoverlay_load_dtb(filename, 0); unlink(filename); if (!dtb) { error("Failed to re-apply dtparam"); continue; } params = (char *)dtoverlay_dtb_trailer(dtb); if (!params) { error("Failed to re-apply dtparam"); dtoverlay_free_dtb(dtb); continue; } /* Count and NUL-separate the params */ p = params; paramc = 0; while (*p) { int paramlen; *(p++) = '\0'; paramlen = strcspn(p, " "); paramc++; p += paramlen; } paramv = malloc((paramc + 1) * sizeof(const char *)); if (!paramv) { error("out of memory re-applying dtparam"); dtoverlay_free_dtb(dtb); continue; } for (j = 0, p = params + 1; j < paramc; j++) { paramv[j] = p; p += strlen(p) + 1; } paramv[j] = NULL; /* Create the new overlay */ ret = dtoverlay_add(state, "dtparam", paramc, paramv); free(paramv); dtoverlay_free_dtb(dtb); } else { rename(filename, new_file); ret = !apply_overlay(new_file, new_name); } if (ret != 0) { error("Failed to re-apply dtparam"); continue; } state->count++; } } } return 0; } static int dtoverlay_list(STATE_T *state) { if (state->count == 0) { printf("No overlays loaded\n"); } else { int i; printf("Overlays (in load order):\n"); for (i = 0; i < state->count; i++) { const char *name, *left, *right; const char *saved_overlay; DTBLOB_T *dtb; name = state->namelist[i]->d_name; left = strchr(name, '_'); if (!left) return error("Internal error"); left++; right = strchr(left, '.'); if (!right) return error("Internal error"); saved_overlay = sprintf_dup("%s/%s", work_dir, name); dtb = dtoverlay_load_dtb(saved_overlay, 0); if (dtoverlay_dtb_trailer(dtb)) printf("%d: %.*s %.*s\n", i, (int)(right - left), left, dtoverlay_dtb_trailer_len(dtb), (char *)dtoverlay_dtb_trailer(dtb)); else printf("%d: %.*s\n", i, (int)(right - left), left); dtoverlay_free_dtb(dtb); } } return 0; } static int dtoverlay_list_all(STATE_T *state) { int i; DIR *dh; struct dirent *de; STRING_VEC_T strings; string_vec_init(&strings); /* Enumerate .dtbo files in the /boot/overlays directory */ dh = opendir(overlay_src_dir); while ((de = readdir(dh)) != NULL) { int len = strlen(de->d_name) - 5; if ((len >= 0) && strcmp(de->d_name + len, ".dtbo") == 0) { char *str = string_vec_add(&strings, de->d_name, len + 2); str[len] = '\0'; str[len + 1] = ' '; } } closedir(dh); /* Merge in active overlays, marking them */ for (i = 0; i < state->count; i++) { const char *left, *right; char *str; int len, idx; left = strchr(state->namelist[i]->d_name, '_'); if (!left) return error("Internal error"); left++; right = strchr(left, '.'); if (!right) return error("Internal error"); len = right - left; if ((len == 7) && (memcmp(left, "dtparam", 7) == 0)) continue; idx = string_vec_find(&strings, left, len); if (idx >= 0) { str = strings.strings[idx]; len = strlen(str); } else { str = string_vec_add(&strings, left, len + 2); str[len] = '\0'; } str[len + 1] = '*'; } if (strings.num_strings == 0) { printf("No overlays found\n"); } else { /* Sort */ string_vec_sort(&strings); /* Display */ printf("All overlays (* = loaded):\n"); for (i = 0; i < strings.num_strings; i++) { const char *str = strings.strings[i]; printf("%c %s\n", str[strlen(str)+1], str); } } string_vec_uninit(&strings); return 0; } static void usage(void) { printf("Usage:\n"); if (strcmp(cmd_name, "dtparam") == 0) { printf(" %s Display help on all parameters\n", cmd_name); printf(" %s =...\n", cmd_name); printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), ""); printf(" %s -D Dry-run (prepare overlay, but don't apply -\n", cmd_name); printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), ""); printf(" %s -r [] Remove an overlay (by index, or the last)\n", cmd_name); printf(" %s -R [] Remove from an overlay (by index, or all)\n", cmd_name); printf(" %s -l List active overlays/dtparams\n", cmd_name); printf(" %s -a List all overlays/dtparams (marking the active)\n", cmd_name); printf(" %s -h Show this usage message\n", cmd_name); printf(" %s -h ... Display help on the listed parameters\n", cmd_name); } else { printf(" %s [=...]\n", cmd_name); printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), ""); printf(" %s -D Dry-run (prepare overlay, but don't apply -\n", cmd_name); printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), ""); printf(" %s -r [] Remove an overlay (by name, index or the last)\n", cmd_name); printf(" %s -R [] Remove from an overlay (by name, index or all)\n", cmd_name); printf(" %s -l List active overlays/params\n", cmd_name); printf(" %s -a List all overlays (marking the active)\n", cmd_name); printf(" %s -h Show this usage message\n", cmd_name); printf(" %s -h Display help on an overlay\n", cmd_name); printf(" %s -h .. Or its parameters\n", cmd_name); printf(" where is the name of an overlay or 'dtparam' for dtparams\n"); } printf("Options applicable to most variants:\n"); printf(" -d Specify an alternate location for the overlays\n"); printf(" (defaults to /boot/overlays or /flash/overlays)\n"); printf(" -p Force a compatible string for the platform\n"); printf(" -v Verbose operation\n"); printf("\n"); printf("Adding or removing overlays and parameters requires root privileges.\n"); exit(1); } static void root_check(void) { if (getuid() != 0) fatal_error("Must be run as root - try 'sudo %s ...'", cmd_name); } static void overlay_help(const char *overlay, const char **params) { OVERLAY_HELP_STATE_T *state; const char *readme_path = sprintf_dup("%s/%s", overlay_src_dir, README_FILE); state = overlay_help_open(readme_path); free_string(readme_path); if (state) { if (strcmp(overlay, "dtparam") == 0) overlay = ""; if (overlay_help_find(state, overlay)) { if (params && overlay_help_find_field(state, "Params")) { int in_param = 0; while (1) { const char *line = overlay_help_field_data(state); if (!line) break; if (line[0] == '\0') continue; if (line[0] != ' ') { /* This is a parameter name */ size_t param_len = strcspn(line, " "); const char **p = params; const char **q = p; in_param = 0; while (*p) { if ((param_len == strlen(*p)) && (memcmp(line, *p, param_len) == 0)) in_param = 1; else *(q++) = *p; p++; } *(q++) = 0; } if (in_param) printf("%s\n", line); } /* This only shows the first unknown parameter, but * that is enough. */ if (*params) fatal_error("Unknown parameter '%s'", *params); } else { printf("Name: %s\n\n", overlay); overlay_help_print_field(state, "Info", "Info:", 8, 0); overlay_help_print_field(state, "Load", "Usage:", 8, 0); overlay_help_print_field(state, "Params", "Params:", 8, 0); } } else { fatal_error("No help found for overlay '%s'", overlay); } overlay_help_close(state); } else { fatal_error("Help file not found"); } } static int apply_overlay(const char *overlay_file, const char *overlay) { const char *overlay_dir = sprintf_dup("%s/%s", dt_overlays_dir, overlay); int ret = 0; if (dir_exists(overlay_dir)) { error("Overlay '%s' is already loaded", overlay); } else if (mkdir(overlay_dir, DIR_MODE) == 0) { DTBLOB_T *dtb = dtoverlay_load_dtb(overlay_file, 0); if (!dtb) { error("Failed to apply overlay '%s' (load)", overlay); } else { const char *dest_file = sprintf_dup("%s/dtbo", overlay_dir); /* then write the overlay to the file */ if (dtoverlay_save_dtb(dtb, dest_file) != 0) error("Failed to apply overlay '%s' (save)", overlay); else if (!overlay_applied(overlay_dir)) error("Failed to apply overlay '%s' (kernel)", overlay); else ret = 1; free_string(dest_file); dtoverlay_free_dtb(dtb); } if (!ret) rmdir(overlay_dir); } else { error("Failed to create overlay directory"); } return ret; } static int overlay_applied(const char *overlay_dir) { char status[7] = { '\0' }; const char *status_path = sprintf_dup("%s/status", overlay_dir); FILE *fp = fopen(status_path, "r"); int bytes = 0; if (fp) { bytes = fread(status, 1, sizeof(status), fp); fclose(fp); } free_string(status_path); return (bytes == sizeof(status)) && (memcmp(status, "applied", sizeof(status)) == 0); } int seq_filter(const struct dirent *de) { int num; return (sscanf(de->d_name, "%d_", &num) == 1); } int seq_compare(const struct dirent **de1, const struct dirent **de2) { int num1 = atoi((*de1)->d_name); int num2 = atoi((*de2)->d_name); if (num1 < num2) return -1; else if (num1 == num2) return 0; else return 1; } static STATE_T *read_state(const char *dir) { STATE_T *state = malloc(sizeof(STATE_T)); int i; if (state) { state->count = scandir(dir, &state->namelist, seq_filter, seq_compare); for (i = 0; i < state->count; i++) { int num = atoi(state->namelist[i]->d_name); if (i != num) error("Overlay sequence error"); } } return state; } static void free_state(STATE_T *state) { int i; for (i = 0; i < state->count; i++) { free(state->namelist[i]); } free(state->namelist); free(state); } raspi-utils-20250514/dtmerge/dtparam.1000066400000000000000000000022711501106437300173750ustar00rootroot00000000000000.TH DTPARAM 1 . .SH NAME dtparam \- mainpulate parameters of the base device-tree . . .SH SYNOPSIS .SY dtparam .RI [ param=val \|.\|.\|.] .YS . .SY dtparam .B \-h .RI [ param ] .YS . . .SH DESCRIPTION .B dtparam is a command line utility for manipulating the base device-tree's parameters. For example, it can be used to enable the SPI or I2C interfaces at runtime without rebooting. If .B dtparam is run without any argument, it prints the names of all base device-tree parameters and their description. . . .SH OPTIONS . .TP .BR \-h " [\fIparam\fR]" If given without .I param displays help on the application overall. If .I param is specified, prints help about that parameter in the base device-tree. . . .SH EXAMPLES . .TP .B sudo dtparam spi=on Enable the SPI interface on the GPIO header. Note that root privileges are usually required for manipulating the base device-tree (hence, sudo in the example). . .TP .B dtparam -h spi Print help about the "spi" parameter. . .TP .B sudo dtparam audio=off Disable the audio output. . . .SH SEE ALSO .BR dtoverlay (1), .BR dtmerge (1), .B [DTREE] . . .SH REFERENCES .TP .B [DTREE] https://www.raspberrypi.com/documentation/computers/configuration.html#part3.5.2 raspi-utils-20250514/dtmerge/utils.c000066400000000000000000000255141501106437300171740ustar00rootroot00000000000000/* Copyright (c) 2016 Raspberry Pi (Trading) Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * 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. */ #include #include #include #include #include #include #include #include "utils.h" #define OVERLAY_HELP_INDENT 8 int opt_verbose; int opt_dry_run; static STRING_T *allocated_strings; struct overlay_help_state_struct { FILE *fp; long rec_pos; int line_len; int line_pos; int blank_count; int end_of_field; char line_buf[82]; }; static int overlay_help_get_line(OVERLAY_HELP_STATE_T *state); OVERLAY_HELP_STATE_T *overlay_help_open(const char *helpfile) { OVERLAY_HELP_STATE_T *state = NULL; FILE *fp = fopen(helpfile, "r"); if (fp) { state = calloc(1, sizeof(OVERLAY_HELP_STATE_T)); if (!state) fatal_error("Out of memory"); state->fp = fp; state->line_pos = -1; state->rec_pos = -1; } return state; } void overlay_help_close(OVERLAY_HELP_STATE_T *state) { fclose(state->fp); free(state); } int overlay_help_find(OVERLAY_HELP_STATE_T *state, const char *name) { state->line_pos = -1; state->rec_pos = -1; state->blank_count = 0; fseek(state->fp, 0, SEEK_SET); while (overlay_help_find_field(state, "Name")) { const char *overlay = overlay_help_field_data(state); if (overlay && (strcmp(overlay, name) == 0)) { state->rec_pos = (long)ftell(state->fp); return 1; } } return 0; } int overlay_help_find_field(OVERLAY_HELP_STATE_T *state, const char *field) { int field_len = strlen(field); int found = 0; if (state->rec_pos >= 0) fseek(state->fp, state->rec_pos, SEEK_SET); while (!found) { int line_len = overlay_help_get_line(state); if (line_len < 0) break; /* Check for the ":" prefix */ if ((line_len >= (field_len + 1)) && (state->line_buf[field_len] == ':') && (memcmp(state->line_buf, field, field_len) == 0)) { /* Found it If this initial line has no content then skip it */ if (line_len > OVERLAY_HELP_INDENT) state->line_pos = OVERLAY_HELP_INDENT; else state->line_pos = -1; state->end_of_field = 0; found = 1; } else { state->line_pos = -1; } } return found; } const char *overlay_help_field_data(OVERLAY_HELP_STATE_T *state) { int line_len, pos; if (state->end_of_field) return NULL; line_len = state->line_len; if ((state->line_pos < 0) || (state->line_pos >= line_len)) { line_len = overlay_help_get_line(state); /* Fields end at the start of the next field or the end of the record */ if ((line_len < 0) || (state->line_buf[0] != ' ')) { state->end_of_field = 1; return NULL; } if (line_len == 0) return ""; } /* Return field data starting at OVERLAY_HELP_INDENT, if there is any */ pos = line_len; if (pos > OVERLAY_HELP_INDENT) pos = OVERLAY_HELP_INDENT; state->line_pos = -1; return &state->line_buf[pos]; } void overlay_help_print_field(OVERLAY_HELP_STATE_T *state, const char *field, const char *label, int indent, int strip_blanks) { if (!overlay_help_find_field(state, field)) return; while (1) { const char *line = overlay_help_field_data(state); if (!line) break; if (label) { int spaces = indent - strlen(label); if (spaces < 0) spaces = 0; printf("%s%*s%s\n", label, spaces, "", line); label = NULL; } else if (line[0]) { printf("%*s%s\n", indent, "", line); } else if (!strip_blanks) { printf("\n"); } } if (!strip_blanks) printf("\n"); } /* Returns the length of the line, or -1 on end of file or record */ static int overlay_help_get_line(OVERLAY_HELP_STATE_T *state) { int line_len; if (state->line_pos >= 0) return state->line_len; get_next_line: state->line_buf[sizeof(state->line_buf) - 1] = ' '; line_len = -1; if (fgets(state->line_buf, sizeof(state->line_buf), state->fp)) { // Check for overflow // Strip the newline line_len = strlen(state->line_buf); if (line_len && (state->line_buf[line_len - 1] == '\n')) { line_len--; state->line_buf[line_len] = '\0'; } } if (state->rec_pos >= 0) { if (line_len == 0) { state->blank_count++; if (state->blank_count >= 2) return -1; state->line_pos = 0; goto get_next_line; } else if (state->blank_count) { /* Return a single blank line now - the non-empty line will be returned next time */ state->blank_count = 0; return 0; } } state->line_len = line_len; state->line_pos = (line_len >= 0) ? 0 : -1; return line_len; } int run_cmd(const char *fmt, ...) { va_list ap; char *cmd; int ret; va_start(ap, fmt); cmd = vsprintf_dup(fmt, ap); va_end(ap); if (opt_dry_run || opt_verbose) fprintf(stderr, "run_cmd: %s\n", cmd); ret = opt_dry_run ? 0 : system(cmd); free_string(cmd); return ret; } /* Not thread safe */ void free_string(const char *string) { STRING_T *str; if (!string) return; str = (STRING_T *)(string - sizeof(STRING_T)); if (str == allocated_strings) { allocated_strings = str->next; if (allocated_strings == str) allocated_strings = NULL; } str->prev->next = str->next; str->next->prev = str->prev; free(str); } /* Not thread safe */ void free_strings(void) { if (allocated_strings) { STRING_T *str = allocated_strings; do { STRING_T *t = str; str = t->next; free(t); } while (str != allocated_strings); allocated_strings = NULL; } } /* Not thread safe */ char *sprintf_dup(const char *fmt, ...) { va_list ap; char *str; va_start(ap, fmt); str = vsprintf_dup(fmt, ap); va_end(ap); return str; } /* Not thread safe */ char *vsprintf_dup(const char *fmt, va_list ap) { char scratch[512]; size_t len; STRING_T *str; len = vsnprintf(scratch, sizeof(scratch), fmt, ap) + 1; if (len > sizeof(scratch)) fatal_error("Maximum string length exceeded"); str = malloc(sizeof(STRING_T) + len); if (!str) fatal_error("Out of memory"); memcpy(str->data, scratch, len); if (allocated_strings) { str->next = allocated_strings; str->prev = allocated_strings->prev; str->next->prev = str; str->prev->next = str; } else { str->next = str; str->prev = str; allocated_strings = str; } return str->data; } int dir_exists(const char *dirname) { struct stat finfo; return (stat(dirname, &finfo) == 0) && S_ISDIR(finfo.st_mode); } int file_exists(const char *dirname) { struct stat finfo; return (stat(dirname, &finfo) == 0) && S_ISREG(finfo.st_mode); } void string_vec_init(STRING_VEC_T *vec) { vec->num_strings = 0; vec->max_strings = 0; vec->strings = NULL; } char *string_vec_add(STRING_VEC_T *vec, const char *str, int len) { char *copy; if (vec->num_strings == vec->max_strings) { if (vec->max_strings) vec->max_strings *= 2; else vec->max_strings = 16; vec->strings = realloc(vec->strings, vec->max_strings * sizeof(const char *)); if (!vec->strings) fatal_error("Out of memory"); } if (len) { copy = malloc(len + 1); strncpy(copy, str, len); copy[len] = '\0'; } else copy = strdup(str); if (!copy) fatal_error("Out of memory"); vec->strings[vec->num_strings++] = copy; return copy; } int string_vec_find(STRING_VEC_T *vec, const char *str, int len) { int i; for (i = 0; i < vec->num_strings; i++) { if (len) { if ((strncmp(vec->strings[i], str, len) == 0) && (vec->strings[i][len] == '\0')) return i; } else if (strcmp(vec->strings[i], str) == 0) return i; } return -1; } int string_vec_compare(const void *a, const void *b) { return strcmp(*(const char **)a, *(const char **)b); } void string_vec_sort(STRING_VEC_T *vec) { qsort(vec->strings, vec->num_strings, sizeof(char *), &string_vec_compare); } void string_vec_uninit(STRING_VEC_T *vec) { int i; for (i = 0; i < vec->num_strings; i++) free(vec->strings[i]); free(vec->strings); } int error(const char *fmt, ...) { va_list ap; fprintf(stderr, "* "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); return 1; } void fatal_error(const char *fmt, ...) { va_list ap; fprintf(stderr, "* "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); exit(1); } char *read_file_as_string(const char *path, int *plen) { FILE *fp = fopen(path, "r"); char *string = NULL; long len = 0; if (fp) { long bytes_read; fseek(fp, 0, SEEK_END); len = ftell(fp); fseek(fp, 0, SEEK_SET); /* Ensure NUL-termination */ string = malloc(len + 1); if (!string) fatal_error("Out of memory", path); string[len] = 0; bytes_read = fread(string, 1, len, fp); if (bytes_read != len) fatal_error("Failed to read file '%s'", path); fclose(fp); } if (plen) *plen = (int)len; return string; } raspi-utils-20250514/dtmerge/utils.h000066400000000000000000000061151501106437300171750ustar00rootroot00000000000000/* Copyright (c) 2016 Raspberry Pi (Trading) Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * 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. */ #ifndef UTILS_H #define UTILS_H typedef struct string_struct { struct string_struct *prev; struct string_struct *next; char data[0]; } STRING_T; typedef struct string_vec_struct { int num_strings; int max_strings; char **strings; } STRING_VEC_T; typedef struct overlay_help_state_struct OVERLAY_HELP_STATE_T; extern int opt_verbose; extern int opt_dry_run; OVERLAY_HELP_STATE_T *overlay_help_open(const char *helpfile); void overlay_help_close(OVERLAY_HELP_STATE_T *state); int overlay_help_find(OVERLAY_HELP_STATE_T *state, const char *name); int overlay_help_find_field(OVERLAY_HELP_STATE_T *state, const char *field); const char *overlay_help_field_data(OVERLAY_HELP_STATE_T *state); void overlay_help_print_field(OVERLAY_HELP_STATE_T *state, const char *field, const char *label, int indent, int strip_blanks); int run_cmd(const char *fmt, ...); void free_string(const char *string); /* Not thread safe */ void free_strings(void); /* Not thread safe */ char *sprintf_dup(const char *fmt, ...); /* Not thread safe */ char *vsprintf_dup(const char *fmt, va_list ap); /* Not thread safe */ int dir_exists(const char *dirname); int file_exists(const char *dirname); void string_vec_init(STRING_VEC_T *vec); char *string_vec_add(STRING_VEC_T *vec, const char *str, int len); int string_vec_find(STRING_VEC_T *vec, const char *str, int len); void string_vec_sort(STRING_VEC_T *vec); void string_vec_uninit(STRING_VEC_T *vec); int error(const char *fmt, ...); void fatal_error(const char *fmt, ...); char *read_file_as_string(const char *path, int *plen); #endif raspi-utils-20250514/eeptools/000077500000000000000000000000001501106437300160645ustar00rootroot00000000000000raspi-utils-20250514/eeptools/CMakeLists.txt000066400000000000000000000010341501106437300206220ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) #set project name project(eeptools) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror") if (CMAKE_COMPILER_IS_GNUCC) add_definitions (-ffunction-sections) endif () add_executable(eepmake eepmake.c eeplib.c) install(TARGETS eepmake RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) add_executable(eepdump eepdump.c eeplib.c) install(TARGETS eepdump RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(PROGRAMS eepflash.sh DESTINATION ${CMAKE_INSTALL_BINDIR}) raspi-utils-20250514/eeptools/README.md000066400000000000000000000073061501106437300173510ustar00rootroot00000000000000# Utilities to create, flash and dump HAT EEPROM images **Build Instructions** Install the prerequisites with "sudo apt install cmake" - you need at least version 3.10 of cmake. Run the following commands here, or in the top-level directory to build and install all the utilities: - *cmake .* - *make* - *sudo make install* **Usage** 1. Copy `eeprom_settings.txt` (or `eeprom_v1_settings.txt` for a V1 HAT) to `myhat_eeprom.txt`. 2. Edit `myhat_eeprom.txt` to suit your specific HAT. 3. Run `eepmake myhat_eeprom.txt myhat.eep` (or `eepmake -v1 myhat_eeprom.txt myhat.eep` for a V1 HAT) to create the .eep binary. 4. If `eepmake` has generated a product UUID for you, it is a good idea to patch your myhat_eeprom.txt to include and hence preserve it (or use `eepdump` to regenerate it). **Flashing the EEPROM image** Follow these steps on the Raspberry Pi after installing the tools and building the .eep file: 1. Disable EEPROM write protection * Sometimes this requires a jumper on the board * Sometimes this is a GPIO * Check your schematics 2. Make sure you can talk to the EEPROM * In the HAT specification, the HAT EEPROM is connected to pins that can be driven by I2C0. However, this is the same interface as used by the camera and displays, so use of it by the ARMs is discouraged. The `eepflash.sh` script gets around this problem by instantiating a software driven I2C interface using those pins as GPIOs, calling it `i2c-9`: ``` sudo dtoverlay i2c-gpio i2c_gpio_sda=0 i2c_gpio_scl=1 bus=9 ``` * Install i2cdetect `sudo apt install i2c-tools` * As explained in the HAT+ specification, HAT EEPROMs can have a variety of (hexadecimal) I2C addresses: + 50: standard HAT+ (or legacy HAT) + 51: stackable HAT+ (e.g. Raspberry Pi M.2 M Key HAT) + 52: stackable Power HAT+ (MODE0) + 53: stackable Power HAT+ (MODE1) The examples below assume a standard HAT+/HAT, so be prepared to substitute the correct address for another type. * Check with `i2cdetect -y 9` (should be at address 0x50) ```bash i2cdetect -y 9 0x50 0x50 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: 10: 20: 30: 40: 50: 50 60: 70: ``` Normally, you can skip this step, and assume things are working. 3. Flash eep file `sudo ./eepflash.sh -w -t=24c32 -a=0x50 -f=eeprom.eep` 4. Enable EEPROM write protection, by undoing step 1 (putting back jumper, or resetting GPIO) ## String data formatting The tools support two string representations: 1. A single, simple string with no CR/NL, no NUL-termination, and no embedded double-quotes. 2. A multiline string using minimal escaping: * String begins with a single `"` followed by CR/NL (carriage return/newline) * NULs are escaped as \0, and followed by an extra NL (because they end a string) * Backslashes are escaped (`\\`) * NLs and TABs are included verbatim * CRs are escaped (`\r`) * The multiline string is terminated by `\"` * Any literal, unescaped CRs found in the string are ignored `eepmake` attempts to display dt_blob and custom_data blocks as simple strings, falling back to multiline strings, ultimately resorting to hexadecimal data. If in doubt, put the text in a file, add it using the `-c` option in `eepmake`, and use `eepdump` to see what it looks like. To confirm that blobs data is encoded and preserved correctly, use the `-b ` option to `eepdump` to save the blob data as separate files. Examples: ``` dt_blob "rpi-dacpro" custom_data " This is the start of a long string. End this line with a carriage return\r NUL-terminated\0 \" custom_data " NL and NUL-terminated \0 \" # This one could have been a simple string custom_data " End text with no NL\" custom_data " End text with NL \" ``` raspi-utils-20250514/eeptools/eepdump.c000066400000000000000000000174001501106437300176710ustar00rootroot00000000000000#include #include #include #include #include #include "eeplib.h" static struct var_blob_t dt_atom, custom_atom; static struct vendor_info_d vinf; static struct gpio_map_d gpiomap; static struct power_supply_d supply; static const char *blob_prefix; static const char *atom_type_names[] ={ [ATOM_INVALID_TYPE] = "invalid", [ATOM_VENDOR_TYPE] = "vendor", [ATOM_GPIO_TYPE] = "GPIO map", [ATOM_DT_TYPE] = "DT overlay", [ATOM_CUSTOM_TYPE] = "manufacturer custom data", [ATOM_GPIO_BANK1_TYPE] = "GPIO map bank 1", [ATOM_POWER_SUPPLY_TYPE] = "power supply", }; static void dump_data(FILE *out, const uint8_t *data, int len) { bool is_string = true, is_simple_string = true; int j; for (j = 0; j < len; j++) { int c = data[j]; if (!c || c == '"' || !isprint(c)) is_simple_string = false; if (c && !isprint(c) && !isspace(c)) { is_string = false; break; } } if (is_simple_string) { fputs(" \"", out); for (j = 0; j < len; j++) fputc(data[j], out); fputs("\"\n", out); } else if (is_string) { fputs(" \"\n", out); for (j = 0; j < len; j++) { int c = data[j]; if (c == '\\') fputs("\\\\", out); else if (c == '\r') fputs("\\r", out); else if (!c) fputs("\\0\n", out); else fputc(c, out); } fputs("\\\"\n", out); } else { for (j = 0; j < len; j++) { if (j % 16 == 0) fprintf(out, "\n"); fprintf(out, "%02X ", data[j]); } fprintf(out, "\n"); } } static void dump_blob(const char *filename, const struct var_blob_t *blob) { FILE *fp = fopen(filename, "wb"); if (fp) { fwrite(blob->data, blob->dlen, 1, fp); fclose(fp); } } static int read_bin(const char *in, const char *outf) { enum file_ver_t hat_format; int numatoms; int custom_count = 0; const char *atom_name; FILE *fp, *out; int i; uint32_t j; fp = fopen(in, "rb"); if (!fp) { printf("Error reading file %s\n", in); return -1; } if (outf) out = fopen(outf, "w"); else out = stdout; if (!out) { printf("Error writing file %s\n", outf); return -1; } fprintf(out, "# ---------- Dump generated by eepdump handling format version 0x%02x ----------\n#\n", EEP_VERSION); eepio_start(&hat_format, &numatoms, EEPIO_READ, fp); if (EEP_VERSION_HATPLUS != hat_format && EEP_VERSION_HATV1 != hat_format) { fprintf(out, "# WARNING: format version mismatch!!!\n"); goto err; } if (EEP_VERSION != eep_header.ver && HEADER_SIGN != eep_header.signature) { printf("header version and signature mismatch, maybe wrong file?\n"); goto err; } fprintf(out, "# --Header--\n# signature=0x%08x\n# version=0x%02x\n# reserved=%u\n# numatoms=%u\n# eeplen=%u\n# ----------\n\n\n", eep_header.signature, eep_header.ver, eep_header.res, eep_header.numatoms, eep_header.eeplen); for (i = 0; i < numatoms && !eepio_got_error(); i++) { unsigned int gpio_count; enum atom_type_t type = ATOM_INVALID_TYPE; bool bank1 = false; uint32_t dlen; if (!eepio_atom_start(&type, &dlen)) goto err; atom_name = (type < count_of(atom_type_names)) ? atom_type_names[type] : "unknown"; printf("Reading atom %d (type = 0x%04x (%s), length = %i bytes)...\n", i, type, atom_name, eep_atom_header.dlen); fprintf(out, "# Start of atom #%u of type 0x%04x and length %u\n", eep_atom_header.count, type, eep_atom_header.dlen); switch (type) { case ATOM_VENDOR_TYPE: if (!eepio_atom_vinf(&vinf)) goto atom_read_err; fprintf(out, "# Vendor info\n"); fprintf(out, "product_uuid %08x-%04x-%04x-%04x-%04x%08x\n", vinf.serial[3], vinf.serial[2] >> 16, vinf.serial[2] & 0xffff, vinf.serial[1] >> 16, vinf.serial[1] & 0xffff, vinf.serial[0]); fprintf(out, "product_id 0x%04x\n", vinf.pid); fprintf(out, "product_ver 0x%04x\n", vinf.pver); fprintf(out, "vendor \"%s\" # length=%u\n", vinf.vstr, vinf.vslen); fprintf(out, "product \"%s\" # length=%u\n", vinf.pstr, vinf.pslen); break; case ATOM_POWER_SUPPLY_TYPE: if (!eepio_atom_power_supply(&supply)) goto atom_read_err; fprintf(out, "# power supply\n"); fprintf(out, "current_supply %u\n", supply.current_supply); break; case ATOM_GPIO_BANK1_TYPE: bank1 = true; /* fall through... */ case ATOM_GPIO_TYPE: if (!eepio_atom_gpio(&gpiomap)) goto atom_read_err; fprintf(out, "# GPIO "); if (bank1) fprintf(out, "bank 1 "); fprintf(out, "map info\n"); if (bank1) fprintf(out, "bank1_"); fprintf(out, "gpio_drive %d\n", gpiomap.flags & 15); // 1111 if (bank1) fprintf(out, "bank1_"); fprintf(out, "gpio_slew %d\n", (gpiomap.flags & 48) >> 4); // 110000 if (bank1) fprintf(out, "bank1_"); fprintf(out, "gpio_hysteresis %d\n", (gpiomap.flags & 192) >> 6); // 11000000 if (!bank1) fprintf(out, "back_power %d\n", gpiomap.power); fprintf(out, "# GPIO FUNCTION PULL\n# ---- -------- ----\n"); gpio_count = bank1 ? GPIO_COUNT_BANK1 : GPIO_COUNT; for (j = 0; j < gpio_count; j++) { if (gpiomap.pins[j] & (1 << 7)) { // board uses this pin char *pull_str = "INVALID"; char *func_str = "INVALID"; switch ((gpiomap.pins[j] & 96) >> 5) { // 1100000 case 0: pull_str = "DEFAULT"; break; case 1: pull_str = "UP"; break; case 2: pull_str = "DOWN"; break; case 3: pull_str = "NONE"; break; } switch ((gpiomap.pins[j] & 7)) { // 111 case 0: func_str = "INPUT"; break; case 1: func_str = "OUTPUT"; break; case 4: func_str = "ALT0"; break; case 5: func_str = "ALT1"; break; case 6: func_str = "ALT2"; break; case 7: func_str = "ALT3"; break; case 3: func_str = "ALT4"; break; case 2: func_str = "ALT5"; break; } fprintf(out, "setgpio %d %s %s\n", bank1 ? j + GPIO_COUNT : j, func_str, pull_str); } } break; case ATOM_DT_TYPE: dt_atom.dlen = dlen; if (!eepio_atom_var(&dt_atom)) goto atom_read_err; fprintf(out, "dt_blob"); dump_data(out, dt_atom.data, dt_atom.dlen); if (blob_prefix) { char filename[FILENAME_MAX]; sprintf(filename, "%s_dt_blob", blob_prefix); dump_blob(filename, &dt_atom); } break; case ATOM_CUSTOM_TYPE: custom_atom.dlen = dlen; if (!eepio_atom_var(&custom_atom)) goto atom_read_err; fprintf(out, "custom_data"); dump_data(out, custom_atom.data, custom_atom.dlen); if (blob_prefix) { char filename[FILENAME_MAX]; sprintf(filename, "%s_custom_data_%d", blob_prefix, custom_count); dump_blob(filename, &custom_atom); } custom_count++; break; default: printf("Error: unrecognised atom type\n"); fprintf(out, "# Error: unrecognised atom type\n"); goto err; } eepio_atom_end(); fprintf(out, "# End of atom. CRC16=0x%04x\n", eep_atom_crc); fprintf(out, "\n"); } eepio_end(); printf("Done.\n"); fclose(fp); if (outf) fclose(out); return 0; atom_read_err: printf("Error reading %s atom\n", atom_name); err: printf("Unexpected EOF or error occurred\n"); fclose(fp); fclose(out); return -1; } int usage(void) { printf("Usage: eepdump input_file [-b blob_prefix] [output_file]\n"); printf(" where\n"); printf(" blob_prefix is prefix string used to generate file names for any\n"); printf(" data blobs\n"); return 1; } int main(int argc, const char *argv[]) { int argn; for (argn = 1; argn < argc && argv[argn][0] == '-'; argn++) { const char *arg = argv[argn]; if (!strcmp(arg, "-b")) { if (argn == argc) return usage(); blob_prefix = argv[++argn]; } else { printf("Unknown option '%s'\n", arg); return usage(); } } if (argc <= argn) return usage(); return read_bin(argv[argn], argv[argn + 1]); } raspi-utils-20250514/eeptools/eepflash.sh000077500000000000000000000105051501106437300202130ustar00rootroot00000000000000#!/bin/sh me=$(basename "$0") MODE="NOT_SET" FILE="NOT_SET" TYPE="NOT_SET" BUS="NOT_SET" ADDR="NOT_SET" BATCH="NOT_SET" usage() { echo "eepflash: Writes or reads .eep binary image to/from HAT EEPROM on a Raspberry Pi" echo "" echo "$me" echo " -h --help: display this help message" echo " -r --read: read .eep from the EEPROM" echo " -w --write: write .eep to the EEPROM" echo " -y --yes: disable interactive mode. By default eepflash.sh will wait" echo " for confirmation from the user. When this flag is used, it will" echo " perform the operation directly. This is mainly meant to be used" echo " in scripts" echo " -f=file_name --file=file_name: binary .eep file to read to/from" echo " N.B. -f file_name and --file file_name (without =) also accepted" echo " -d= --device= i2c bus number (ex if the eeprom is on i2c-0 set -d=0)" echo ' -a= --address= i2c EEPROM address (0x50 for standard HAT+/HAT, 0x51' echo " for stackable HAT+, 0x52 for MODE0 Power HAT+, 0x53 for MODE1" echo ' Power HAT+)' echo " -t=eeprom_type --type=eeprom_type: EEPROM type to use" echo " The following EEPROM types are supported:" echo " 24c32" echo " 24c64" echo " 24c128" echo " 24c256" echo " 24c512" echo " 24c1024" echo "" echo "Example:" echo "$me -w -f=crex0.1.eep -t=24c32 -d=1 -a=57" echo "$me -r -f dump.eep -t=24c32 -d=1 -a=57" echo "" } while [ "$1" != "" ]; do PARAM=$(echo "$1" | awk -F= '{print $1}') VALUE=$(echo "$1" | awk -F= '{print $2}') case $PARAM in -h | --help) usage exit ;; -r | --read) MODE="read" ;; -w | --write) MODE="write" ;; -y | --yes) BATCH="yes" ;; -t | --type) if [ "$VALUE" = "24c32" ] || [ "$VALUE" = "24c64" ] || [ "$VALUE" = "24c128" ] || [ "$VALUE" = "24c256" ] || [ "$VALUE" = "24c512" ] || [ "$VALUE" = "24c1024" ]; then TYPE=$VALUE else echo "ERROR: Unrecognised eeprom type. Try -h for help" exit 1 fi ;; -d | --device) BUS=$VALUE ;; -a | --address) ADDR=$VALUE ;; -f | --file) FILE=$VALUE if [ "$1" = "-f" ] || [ "$1" = "--file" ]; then shift FILE=$1 fi ;; *) echo "ERROR: unknown parameter \"$PARAM\"" usage exit 1 ;; esac shift done if [ "$MODE" = "NOT_SET" ]; then echo 'You need to set mode (read or write). Try -h for help.' exit 1 elif [ "$FILE" = "NOT_SET" ]; then echo "You need to set binary .eep file to read to/from. Try -h for help." exit 1 elif [ "$TYPE" = "NOT_SET" ]; then echo "You need to set eeprom type. Try -h for help." exit 1 fi if [ "$ADDR" = "NOT_SET" ]; then ADDR=50 fi echo "This will attempt to talk to an eeprom at i2c address 0x$ADDR. Make sure there is an eeprom at this address." echo "This script comes with ABSOLUTELY no warranty. Continue only if you know what you are doing." if [ "$(id -u)" != "0" ]; then echo "This script must be run as root" 1>&2 exit 1 fi if [ "$BATCH" = "NOT_SET" ]; then while true; do printf 'Do you wish to continue? (yes/no): ' read -r yn case $yn in yes | Yes ) break;; no | No ) exit;; * ) echo "Please type yes or no.";; esac done fi modprobe i2c_dev if [ "$BUS" = "NOT_SET" ]; then if [ -e "/dev/i2c-0" ]; then BUS=0 elif [ -e "/dev/i2c-9" ]; then BUS=9 else dtoverlay i2c-gpio i2c_gpio_sda=0 i2c_gpio_scl=1 bus=9 rc=$? if [ $rc != 0 ]; then echo "Loading of i2c-gpio dtoverlay failed." exit $rc fi sleep 1 if [ -e "/dev/i2c-9" ]; then BUS=9 else echo 'Expected I2C bus (i2c-9) not found.' fi fi fi modprobe at24 rc=$? if [ $rc != 0 ]; then echo "Modprobe of at24 failed." exit $rc fi SYS=/sys/class/i2c-dev/i2c-$BUS/device if [ ! -d "$SYS/$BUS-00$ADDR" ]; then echo "$TYPE 0x$ADDR" > $SYS/new_device fi DD_VERSION=$(dd --version 2>&1 | grep coreutils | sed -e 's/\.//' | cut -d' ' -f 3) if [ -z "$DD_VERSION" ]; then # probably busybox's dd DD_STATUS="" else if [ "$DD_VERSION" -ge 824 ]; then DD_STATUS="status=progress" else DD_STATUS="status=none" fi fi if [ "$MODE" = "write" ] then echo "Writing..." dd if="$FILE" of=$SYS/$BUS-00$ADDR/eeprom $DD_STATUS rc=$? elif [ "$MODE" = "read" ] then echo "Reading..." dd if=$SYS/$BUS-00$ADDR/eeprom of="$FILE" $DD_STATUS rc=$? fi echo "Closing EEPROM Device." echo "0x$ADDR" > $SYS/delete_device if [ $rc != 0 ]; then echo "Error doing I/O operation." exit $rc else echo "Done." fi raspi-utils-20250514/eeptools/eeplib.c000066400000000000000000000151731501106437300174770ustar00rootroot00000000000000#include #include #include #include "eeplib.h" struct header_t eep_header; struct atom_t eep_atom_header; uint16_t eep_atom_crc; unsigned int eep_atom_num; static enum eepio_dir_t eepio_dir; static FILE *eepio_fp; static uint16_t crc_state; static long eepio_pos_start; static long eepio_atom_data_start; static bool eepio_error_flag; static bool eepio_buffer_writes; static void *eepio_write_buf; static int eepio_write_buf_pos; static int eepio_write_buf_size; static log_callback_t eep_error_callback; static log_callback_t eep_warning_callback; void eepio_clear_error(void) { eepio_error_flag = false; } bool eepio_error(const char *msg, ...) { va_list ap; va_start(ap, msg); eepio_error_flag = true; if (eep_error_callback) (*eep_error_callback)(msg, ap); else { printf("ERROR: "); vprintf(msg, ap); printf("\n"); } va_end(ap); return true; } void eepio_fatal_error(const char *msg, ...) { va_list ap; va_start(ap, msg); if (eep_error_callback) (*eep_error_callback)(msg, ap); else { printf("ERROR: "); vprintf(msg, ap); printf("\n"); } va_end(ap); exit(1); } void eepio_warning(const char *msg, ...) { va_list ap; va_start(ap, msg); if (eep_warning_callback) (*eep_warning_callback)(msg, ap); else { printf("WARNING: "); vprintf(msg, ap); printf("\n"); } va_end(ap); } bool eepio_got_error(void) { return eepio_error_flag; } void crc_init(void) { crc_state = 0; } void crc_add(uint8_t *data, unsigned int size) { int bits_read = 0, bit_flag; while (size > 0) { bit_flag = crc_state >> 15; /* Get next bit: */ crc_state <<= 1; /* work from the least significant bits */ crc_state |= (*data >> bits_read) & 1; /* Increment bit counter: */ bits_read++; if (bits_read > 7) { bits_read = 0; data++; size--; } /* Cycle check: */ if (bit_flag) crc_state ^= CRC16; } } uint16_t crc_get(void) { uint16_t crc; int i, j; /* "push out" the last 16 bits */ for (i = 0; i < 16; ++i) { int bit_flag = crc_state >> 15; crc_state <<= 1; if (bit_flag) crc_state ^= CRC16; } /* reverse the bits */ crc = 0; i = 0x8000; j = 0x0001; for (; i != 0; i >>= 1, j <<= 1) { if (i & crc_state) crc |= j; } return crc; } static void *eepio_write_buf_space(int len) { void *space; if ((eepio_write_buf_pos + len) > eepio_write_buf_size) { eepio_write_buf_size += eepio_write_buf_size + len * 2; eepio_write_buf = realloc(eepio_write_buf, eepio_write_buf_size); if (!eepio_write_buf) eepio_fatal_error("Out of memory"); } space = (void *)((uint8_t *)eepio_write_buf + eepio_write_buf_pos); eepio_write_buf_pos += len; return space; } void eepio_blob(void *blob, int len) { if (eepio_buffer_writes) { memcpy(eepio_write_buf_space(len), blob, len); return; } if (eepio_dir == EEPIO_READ) { if (!fread(blob, len, 1, eepio_fp)) eepio_error("Failed to read from file"); } else { if (!fwrite(blob, len, 1, eepio_fp)) eepio_error("Failed to write to file"); } crc_add(blob, len); } void eepio_string(char **pstr, int len) { if (eepio_dir == EEPIO_READ) *pstr = (char *)malloc(len + 1); eepio_blob(*pstr, len); (*pstr)[len] = 0; } void eepio_start(enum file_ver_t *pver, int *pnumatoms, enum eepio_dir_t dir, FILE *fp) { if (dir == EEPIO_WRITE) { eep_header.signature = HEADER_SIGN; eep_header.ver = *pver; eep_header.res = 0; eep_header.numatoms = 0; } eepio_dir = dir; eepio_fp = fp; eepio_pos_start = ftell(eepio_fp); eepio_blob(&eep_header, sizeof(eep_header)); if (dir == EEPIO_READ) { *pver = eep_header.ver; if (eep_header.signature != HEADER_SIGN) eepio_warning("Format signature mismatch"); if (pnumatoms) *pnumatoms = eep_header.numatoms; } eep_atom_num = 0; } bool eepio_end(void) { // Patch/check header with atom count and total length long pos, pos_end; pos = ftell(eepio_fp); if (pos == -1) return eepio_error("ftell failed"); if (eepio_dir == EEPIO_WRITE) { eep_header.eeplen = pos - eepio_pos_start; eep_header.numatoms = eep_atom_num; fseek(eepio_fp, eepio_pos_start, SEEK_SET); eepio_blob(&eep_header, sizeof(eep_header)); } else { fseek(eepio_fp, 0L, SEEK_END); pos_end = ftell(eepio_fp); if (pos_end == -1) return eepio_error("ftell failed"); if (pos != pos_end) eepio_warning("Dump finished before EOF"); if (pos != (long)eep_header.eeplen) eepio_warning("Dump finished before length specified in header"); if (pos_end != (long)eep_header.eeplen) eepio_warning("EOF does not match length specified in header"); if (pos_end != (long)eep_header.eeplen) eepio_warning("%i bytes of file not processed"); } return !eepio_got_error(); } bool eepio_atom_start(enum atom_type_t *type, uint32_t *pdlen) { if (eepio_dir == EEPIO_WRITE) { eep_atom_header.type = *type; eep_atom_header.count = eep_atom_num; eepio_write_buf_pos = 0; eepio_buffer_writes = true; } crc_init(); if (eepio_dir == EEPIO_READ) { eepio_blob(&eep_atom_header, ATOM_HDR_SIZE); *type = eep_atom_header.type; if (eep_atom_num != eep_atom_header.count) eepio_error("Atom count mismatch (expected %u)", eep_atom_num); } eepio_atom_data_start = ftell(eepio_fp); if (pdlen) *pdlen = eep_atom_header.dlen - CRC_SIZE; return !eepio_got_error(); } void eepio_atom_end(void) { uint16_t crc_actual; if (eepio_dir == EEPIO_WRITE) { eep_atom_header.dlen = eepio_write_buf_pos + 2; eepio_buffer_writes = false; eepio_blob(&eep_atom_header, ATOM_HDR_SIZE); eepio_blob(eepio_write_buf, eepio_write_buf_pos); } crc_actual = crc_get(); eep_atom_crc = crc_actual; eepio_blob(&eep_atom_crc, 2); if (eepio_dir == EEPIO_READ) { long pos = ftell(eepio_fp); if (pos - eepio_atom_data_start != (long)eep_atom_header.dlen) eepio_warning("atom data length mismatch"); if (crc_actual != eep_atom_crc) eepio_warning("atom CRC16 mismatch. Calculated CRC16=0x%02x", crc_actual); } eep_atom_num++; } bool eepio_atom_var(struct var_blob_t *var) { if (eepio_dir == EEPIO_READ) { var->data = malloc(var->dlen); if (!var->data) eepio_fatal_error("out of memory"); } eepio_blob(var->data, var->dlen); return !eepio_got_error(); } bool eepio_atom_vinf(struct vendor_info_d *vinf) { eepio_blob(vinf, VENDOR_SIZE); eepio_string(&vinf->vstr, vinf->vslen); eepio_string(&vinf->pstr, vinf->pslen); return !eepio_got_error(); } bool eepio_atom_power_supply(struct power_supply_d *power) { eepio_blob(power, POWER_SUPPLY_SIZE); return !eepio_got_error(); } bool eepio_atom_gpio(struct gpio_map_d *map) { eepio_blob(map, GPIO_SIZE); return !eepio_got_error(); } bool eepio_atom_gpio_bank1(struct gpio_map_d *map) { eepio_blob(map, GPIO_BANK1_SIZE); return !eepio_got_error(); } raspi-utils-20250514/eeptools/eeplib.h000066400000000000000000000054751501106437300175100ustar00rootroot00000000000000#ifndef _EEPLIB_H #define _EEPLIB_H #include #include #include #include #include // minimal sizes of data structures #define HEADER_SIZE 12 #define ATOM_SIZE 10 #define ATOM_HDR_SIZE 8 #define VENDOR_SIZE 22 #define GPIO_SIZE 30 #define GPIO_BANK1_SIZE 20 #define POWER_SUPPLY_SIZE 4 #define CRC_SIZE 2 #define GPIO_MIN 2 #define GPIO_COUNT 28 #define GPIO_COUNT_BANK1 18 #define GPIO_COUNT_TOTAL (GPIO_COUNT + GPIO_COUNT_BANK1) #define CRC16 0x8005 // Signature is "R-Pi" in ASCII. It is required to reversed (little endian) on disk. #define HEADER_SIGN (uint32_t)be32toh((((char)'R' << 24) | ((char)'-' << 16) | ((char)'P' << 8) | ((char)'i'))) #define count_of(x) ((sizeof(x) / sizeof(x[0]))) #define max(x, y) ((x) > (y) ? (x) : (y)) typedef void (*log_callback_t)(const char *, va_list); /* Atom types */ enum atom_type_t { ATOM_INVALID_TYPE = 0x0000, ATOM_VENDOR_TYPE = 0x0001, ATOM_GPIO_TYPE = 0x0002, ATOM_DT_TYPE = 0x0003, ATOM_CUSTOM_TYPE = 0x0004, ATOM_GPIO_BANK1_TYPE = 0x0005, ATOM_POWER_SUPPLY_TYPE = 0x0006, ATOM_HINVALID_TYPE = 0xffff }; enum file_ver_t { EEP_VERSION_HATV1 = 0x01, EEP_VERSION_HATPLUS = 0x02, EEP_VERSION = EEP_VERSION_HATPLUS, }; enum eepio_dir_t { EEPIO_READ, EEPIO_WRITE, }; /* EEPROM header structure */ struct header_t { uint32_t signature; unsigned char ver; unsigned char res; uint16_t numatoms; uint32_t eeplen; }; /* Atom structure */ struct atom_t { uint16_t type; uint16_t count; uint32_t dlen; char *data; }; struct var_blob_t { uint8_t *data; uint32_t dlen; }; /* Vendor info atom data */ struct vendor_info_d { uint32_t serial[4]; // 0 = least significant, 3 = most significant uint16_t pid; uint16_t pver; unsigned char vslen; unsigned char pslen; char *vstr; char *pstr; }; /* GPIO map atom data */ struct gpio_map_d { unsigned char flags; unsigned char power; unsigned char pins[GPIO_COUNT]; }; /* Power supply atom data */ struct power_supply_d { uint32_t current_supply; /* In milliamps */ }; extern struct header_t eep_header; extern struct atom_t eep_atom_header; extern uint16_t eep_atom_crc; void eepio_start(enum file_ver_t *pver, int *pnumatoms, enum eepio_dir_t dir, FILE *fp); bool eepio_end(void); bool eepio_atom_start(enum atom_type_t *ptype, uint32_t *pdlen); bool eepio_atom_vinf(struct vendor_info_d *vinf); bool eepio_atom_gpio(struct gpio_map_d *map); bool eepio_atom_gpio_bank1(struct gpio_map_d *map); bool eepio_atom_power_supply(struct power_supply_d *power); bool eepio_atom_var(struct var_blob_t *var); void eepio_atom_end(void); void eepio_clear_error(void); bool eepio_error(const char *msg, ...); void eepio_warning(const char *msg, ...); bool eepio_got_error(void); void eepio_set_error_callback(log_callback_t callback); void eepio_set_warning_callback(log_callback_t callback); #endif raspi-utils-20250514/eeptools/eepmake.c000066400000000000000000000367641501106437300176570ustar00rootroot00000000000000/* * Parses EEPROM text file and createds binary .eep file * Usage: eepmake input_file output_file */ #include #include #include #include #include #include #include "eeplib.h" static struct vendor_info_d vinf; static struct var_blob_t dt_blob; struct var_blob_t *custom_blobs; static struct var_blob_t *data_blob; bool in_string; /* Common features */ static bool product_serial_set, product_id_set, product_ver_set, vendor_set, product_set; static bool has_dt; /* Legacy V1 features */ static struct gpio_map_d gpiomap_bank0, gpiomap_bank1; static bool has_gpio_bank0, has_gpio_bank1; static bool gpio_drive_set, gpio_slew_set, gpio_hysteresis_set, gpio_power_set, bank1_gpio_drive_set, bank1_gpio_slew_set, bank1_gpio_hysteresis_set; /* HAT+ features */ static struct power_supply_d power_supply; static bool current_supply_set, has_power_supply; static unsigned int custom_ct, data_cap, custom_cap; static enum file_ver_t hat_format = EEP_VERSION_HATPLUS; static void fatal_error(const char *msg, ...) { va_list ap; va_start(ap, msg); printf("FATAL: "); vprintf(msg, ap); printf("\n"); exit(1); } static void hatplus_required(const char *cmd) { if (hat_format >= EEP_VERSION_HATPLUS) return; printf("'%s' not supported on V1 HAT\n", cmd); exit(1); } static void hatplus_unsupported(const char *cmd) { if (hat_format == EEP_VERSION_HATV1) return; printf("'%s' not supported on HAT+\n", cmd); exit(1); } static int write_binary(const char *out) { FILE *fp; unsigned int i; enum eepio_dir_t dir = EEPIO_WRITE; enum atom_type_t type; fp = fopen(out, "wb"); if (!fp) { printf("Error writing file %s\n", out); return -1; } eepio_start(&hat_format, NULL, dir, fp); type = ATOM_VENDOR_TYPE; eepio_atom_start(&type, NULL); eepio_atom_vinf(&vinf); eepio_atom_end(); if (has_gpio_bank0) { type = ATOM_GPIO_TYPE; eepio_atom_start(&type, NULL); eepio_atom_gpio(&gpiomap_bank0); eepio_atom_end(); } if (has_dt) { printf("Writing out DT...\n"); type = ATOM_DT_TYPE; eepio_atom_start(&type, NULL); eepio_atom_var(&dt_blob); eepio_atom_end(); } for (i = 0; i < custom_ct; i++) { type = ATOM_CUSTOM_TYPE; eepio_atom_start(&type, NULL); eepio_atom_var(&custom_blobs[i]); eepio_atom_end(); } if (has_gpio_bank1) { type = ATOM_GPIO_BANK1_TYPE; eepio_atom_start(&type, NULL); eepio_atom_gpio_bank1(&gpiomap_bank1); eepio_atom_end(); } if (has_power_supply) { type = ATOM_POWER_SUPPLY_TYPE; eepio_atom_start(&type, NULL); eepio_atom_power_supply(&power_supply); eepio_atom_end(); } eepio_end(); fclose(fp); return 0; } static void add_data_byte(int byte) { if (data_blob->dlen >= data_cap) { data_cap = max(data_cap * 2, 64); data_blob->data = realloc(data_blob->data, data_cap); } data_blob->data[data_blob->dlen++] = byte; } static void finish_data(void) { if (data_blob) { data_blob->data = realloc(data_blob->data, data_blob->dlen); data_blob = NULL; data_cap = 0; } } static int parse_string(const char *p) { /* Read string data, stopping at escaped-doublequote */ int c; while ((c = *(p++)) != 0) { if (c == '\\') { int c2 = *(p++); if (c2 == 'r') c = '\r'; else if (c2 == '\\') c = '\\'; // Technically a no-op else if (c2 == '0') { add_data_byte(0); break; // Ignore the rest of the line } else if (c2 == '"') { in_string = false; finish_data(); break; } else fatal_error("Bad escape sequence '\\%c'", c2); } else if (c == '\r') // Real CRs are escaped, so ignore this as MS droppings continue; add_data_byte(c); } return 0; } static int parse_data(char *c) { char *i = c; char *j = c; int len; int k; while (isspace(*j)) j++; if (j[0] == '"') { if (j[1] == '\n' || j[1] == '\r') { // Ignore anything else in_string = true; return 0; } // Simple in-line string while (1) { int byte = *(++j); if (byte == '"') { finish_data(); return 0; } if (!isprint(byte)) fatal_error("Bad character 0x%02x in simple string '%s'", byte, c); add_data_byte(byte); } return 0; } /* Filter out the non-hex-digits */ while (*j != '\0') { *i = *j++; if (isxdigit(*i)) i++; } *i = '\0'; len = strlen(c); if (len % 2 != 0) { printf("Error: data must have an even number of hex digits\n"); return -1; } else { for (k = 0; k < len / 2; k++) { int byte; sscanf(c, "%2x", &byte); add_data_byte(byte); c += 2; } } return 0; } static void init_blob(struct var_blob_t *blob) { blob->data = NULL; blob->dlen = 0; } struct var_blob_t *add_custom_blob(void) { struct var_blob_t *blob; if (custom_cap == custom_ct) { custom_cap = max(custom_cap * 2, 1); custom_blobs = realloc(custom_blobs, custom_cap * sizeof(struct var_blob_t)); } blob = &custom_blobs[custom_ct++]; init_blob(blob); return blob; } static int parse_command(char *cmd, char *c) { int val; uint32_t high1, high2; char *fn, *pull; char pin; bool valid; /* Vendor info related part */ if (strcmp(cmd, "product_uuid") == 0) { product_serial_set = true; // required field high1 = 0; high2 = 0; sscanf(c, "%100s %08x-%04x-%04x-%04x-%04x%08x\n", cmd, &vinf.serial[3], &high1, &vinf.serial[2], &high2, &vinf.serial[1], &vinf.serial[0]); vinf.serial[2] |= high1 << 16; vinf.serial[1] |= high2 << 16; if ((vinf.serial[3] == 0) && (vinf.serial[2] == 0) && (vinf.serial[1] == 0) && (vinf.serial[0] == 0)) { // read 128 random bits from /dev/urandom int random_file = open("/dev/urandom", O_RDONLY); ssize_t result = read(random_file, vinf.serial, 16); close(random_file); if (result <= 0) { printf("Unable to read from /dev/urandom to set up UUID"); return -1; } else { // put in the version vinf.serial[2] = (vinf.serial[2] & 0xffff0fff) | 0x00004000; // put in the variant vinf.serial[1] = (vinf.serial[1] & 0x3fffffff) | 0x80000000; printf("UUID=%08x-%04x-%04x-%04x-%04x%08x\n", vinf.serial[3], vinf.serial[2] >> 16, vinf.serial[2] & 0xffff, vinf.serial[1] >> 16, vinf.serial[1] & 0xffff, vinf.serial[0]); } } } else if (strcmp(cmd, "product_id") == 0) { product_id_set = true; // required field sscanf(c, "%100s %hx", cmd, &vinf.pid); } else if (strcmp(cmd, "product_ver") == 0) { product_ver_set = true; // required field sscanf(c, "%100s %hx", cmd, &vinf.pver); } else if (strcmp(cmd, "vendor") == 0) { vendor_set = true; // required field vinf.vstr = malloc(256); sscanf(c, "%100s \"%255[^\"]\"", cmd, vinf.vstr); vinf.vslen = strlen(vinf.vstr); } else if (strcmp(cmd, "product") == 0) { product_set = true; // required field vinf.pstr = malloc(256); sscanf(c, "%100s \"%255[^\"]\"", cmd, vinf.pstr); vinf.pslen = strlen(vinf.pstr); } /* HAT+ features */ else if (strcmp(cmd, "current_supply") == 0) { hatplus_required(cmd); sscanf(c, "%100s %u", cmd, &power_supply.current_supply); if (power_supply.current_supply) { current_supply_set = true; has_power_supply = true; } } /* GPIO map related part */ else if (strcmp(cmd, "gpio_drive") == 0) { hatplus_unsupported(cmd); gpio_drive_set = true; // required field has_gpio_bank0 = true; sscanf(c, "%100s %1x", cmd, &val); if (val > 8 || val < 0) printf("Warning: gpio_drive property in invalid region, using default value instead\n"); else gpiomap_bank0.flags |= val; } else if (strcmp(cmd, "gpio_slew") == 0) { hatplus_unsupported(cmd); gpio_slew_set = true; // required field has_gpio_bank0 = true; sscanf(c, "%100s %1x", cmd, &val); if (val > 2 || val < 0) printf("Warning: gpio_slew property in invalid region, using default value instead\n"); else gpiomap_bank0.flags |= val << 4; } else if (strcmp(cmd, "gpio_hysteresis") == 0) { hatplus_unsupported(cmd); gpio_hysteresis_set = true; // required field has_gpio_bank0 = true; sscanf(c, "%100s %1x", cmd, &val); if (val > 2 || val < 0) printf("Warning: gpio_hysteresis property in invalid region, using default value instead\n"); else gpiomap_bank0.flags |= val << 6; } else if (strcmp(cmd, "back_power") == 0) { hatplus_unsupported(cmd); gpio_power_set = true; // required field has_gpio_bank0 = true; sscanf(c, "%100s %1x", cmd, &val); if (val > 2 || val < 0) printf("Warning: back_power property in invalid region, using default value instead\n"); else gpiomap_bank0.power = val; } else if (strcmp(cmd, "bank1_gpio_drive") == 0) { hatplus_unsupported(cmd); bank1_gpio_drive_set = true; // required field if bank 1 is used has_gpio_bank1 = true; sscanf(c, "%100s %1x", cmd, &val); if (val > 8 || val < 0) printf("Warning: bank1 gpio_drive property in invalid region, using default value instead\n"); else gpiomap_bank1.flags |= val; } else if (strcmp(cmd, "bank1_gpio_slew") == 0) { hatplus_unsupported(cmd); bank1_gpio_slew_set = true; // required field if bank 1 is used has_gpio_bank1 = true; sscanf(c, "%100s %1x", cmd, &val); if (val > 2 || val < 0) printf("Warning: bank1 gpio_slew property in invalid region, using default value instead\n"); else gpiomap_bank1.flags |= val << 4; } else if (strcmp(cmd, "bank1_gpio_hysteresis") == 0) { hatplus_unsupported(cmd); bank1_gpio_hysteresis_set = true; // required field if bank 1 is used has_gpio_bank1 = true; sscanf(c, "%100s %1x", cmd, &val); if (val > 2 || val < 0) printf("Warning: bank1 gpio_hysteresis property in invalid region, using default value instead\n"); else gpiomap_bank1.flags |= val << 6; } else if (strcmp(cmd, "setgpio") == 0) { hatplus_unsupported(cmd); fn = malloc(101); pull = malloc(101); sscanf(c, "%100s %d %100s %100s", cmd, &val, fn, pull); if (val < GPIO_MIN || val >= GPIO_COUNT_TOTAL) printf("Error: GPIO number out of bounds\n"); else { struct gpio_map_d *gpiomap = &gpiomap_bank0; if (val >= GPIO_COUNT) { gpiomap = &gpiomap_bank1; val -= GPIO_COUNT; has_gpio_bank1 = true; } valid = true; pin = 0; if (strcmp(fn, "INPUT") == 0) { // no action } else if (strcmp(fn, "OUTPUT") == 0) { pin |= 1; } else if (strcmp(fn, "ALT0") == 0) { pin |= 4; } else if (strcmp(fn, "ALT1") == 0) { pin |= 5; } else if (strcmp(fn, "ALT2") == 0) { pin |= 6; } else if (strcmp(fn, "ALT3") == 0) { pin |= 7; } else if (strcmp(fn, "ALT4") == 0) { pin |= 3; } else if (strcmp(fn, "ALT5") == 0) { pin |= 2; } else { printf("Error at setgpio: function type not recognised\n"); valid = false; } if (strcmp(pull, "DEFAULT") == 0) { // no action } else if (strcmp(pull, "UP") == 0) { pin |= 1 << 5; } else if (strcmp(pull, "DOWN") == 0) { pin |= 2 << 5; } else if (strcmp(pull, "NONE") == 0) { pin |= 3 << 5; } else { printf("Error at setgpio: pull type not recognised\n"); valid = false; } pin |= 1 << 7; // board uses this pin if (valid) gpiomap->pins[val] = pin; } } else if (strcmp(cmd, "dt_blob") == 0) { finish_data(); if (has_dt) fatal_error("Only one dt_blob allowed"); has_dt = true; data_blob = &dt_blob; c += strlen("dt_blob"); return parse_data(c); } else if (strcmp(cmd, "custom_data") == 0) { finish_data(); data_blob = add_custom_blob(); c += strlen("custom_data"); return parse_data(c); } else if (strcmp(cmd, "end") == 0) { // close last data atom finish_data(); } else if (data_blob) { return parse_data(c); } return 0; } static int read_text(const char *in) { FILE *fp; char *line = NULL; char *c = NULL; size_t len = 0; ssize_t read; int linect = 0; char *command = (char *)malloc(101); int i; has_dt = false; printf("Opening file '%s' for read\n", in); fp = fopen(in, "r"); if (fp == NULL) { printf("Error opening input file '%s'\n", in); return -1; } // allocating memory and setting up required atoms custom_cap = 1; custom_blobs = malloc(sizeof(struct atom_t) * custom_cap); in_string = false; while ((read = getline(&line, &len, fp)) != -1) { linect++; c = line; if (in_string) { if (parse_string(c)) return -1; continue; } for (i = 0; i < read; i++) { if (c[i] == '#') c[i] = '\0'; } while (isspace(*c)) ++c; if (*c == '\0' || *c == '\n' || *c == '\r') { // empty line, do nothing } else if (isalnum(*c)) { sscanf(c, "%100s", command); if (parse_command(command, c)) return -1; } else printf("Can't parse line %u: %s", linect, c); } finish_data(); if (!product_serial_set || !product_id_set || !product_ver_set || !vendor_set || !product_set) printf("Warning: required fields missing in vendor information, using default values\n"); if (hat_format == EEP_VERSION_HATV1 && !has_gpio_bank0) fatal_error("GPIO bank 0 is required for HAT V1"); if (has_gpio_bank0 && (!gpio_drive_set || !gpio_slew_set || !gpio_hysteresis_set || !gpio_power_set)) printf("Warning: required fields missing in GPIO map, using default values\n"); if (has_gpio_bank1 && (!bank1_gpio_drive_set || !bank1_gpio_slew_set || !bank1_gpio_hysteresis_set)) printf("Warning: required fields missing in GPIO map of bank 1, using default values\n"); if (hat_format != EEP_VERSION_HATV1 && dt_blob.data && !isalnum(dt_blob.data[0])) fatal_error("Only embed the name of the overlay"); printf("Done reading\n"); return 0; } static int read_blob_file(const char *in, const char *type, struct var_blob_t *blob) { FILE *fp; unsigned long size = 0; printf("Opening %s file '%s' for read\n", type, in); fp = fopen(in, "rb"); if (fp == NULL) fatal_error("Error opening input file '%s'", in); fseek(fp, 0L, SEEK_END); size = ftell(fp); fseek(fp, 0L, SEEK_SET); printf("Adding %lu bytes of %s data\n", size, type); blob->dlen = size; blob->data = malloc(size); if (!fread(blob->data, size, 1, fp)) goto err; fclose(fp); return 0; err: printf("Unexpected EOF or error occurred\n"); fclose(fp); return -1; } int main(int argc, char *argv[]) { const char *output_file; int argn = 1; int ret; while (argn < argc && argv[argn][0] == '-') { const char *arg = argv[argn++]; if (!strcmp(arg, "-v1")) { printf("[ Reverting to legacy HAT V1 format ]\n"); hat_format = EEP_VERSION_HATV1; } else { printf("Unrecognised option '%s'\n", arg); return 1; } } if ((argc - argn) < 2) { printf("Wrong input format.\n"); printf("Try 'eepmake [-v1] input_file output_file [dt_file] [-c custom_file_1 ... custom_file_n]'\n"); return 1; } ret = read_text(argv[argn++]); if (ret) fatal_error("Error reading and parsing input, aborting"); output_file = argv[argn++]; if (argn < argc && argv[argn][0] != '-') { // DT file specified if (hat_format == EEP_VERSION_HATPLUS) fatal_error("For HAT+ EEPROMs, specify the name of the DT overlay in the text file"); ret = read_blob_file(argv[argn++], "DT", &dt_blob); if (ret) fatal_error("Error reading DT file, aborting"); has_dt = true; } if (argn < argc && argv[argn][0] == '-') { if (strcmp(argv[argn], "-c") != 0) fatal_error("Unknown option - expected '-c'"); argn++; } while (argn < argc) { struct var_blob_t *blob = add_custom_blob(); // new custom data file ret = read_blob_file(argv[argn++], "custom data", blob); if (ret) fatal_error("Error reading custom file, aborting"); } printf("Writing out...\n"); ret = write_binary(output_file); if (ret) fatal_error("Error writing output"); printf("Done.\n"); return 0; } raspi-utils-20250514/eeptools/eeprom_settings.txt000077500000000000000000000030371501106437300220420ustar00rootroot00000000000000######################################################################## # EEPROM settings text file # # Edit this file for your particular board and run through eepmake tool, # then use eepflash tool to write to attached HAT ID EEPROM # # Tools available: # eepmake Parses EEPROM text file and creates binary .eep file # eepdump Dumps a binary .eep file as human readable text (for debug) # eepflash Write or read .eep binary image to/from HAT EEPROM # ######################################################################## # 128 bit UUID. If left at zero eepmake tool will auto-generate # RFC 4122 compliant UUID product_uuid 00000000-0000-0000-0000-000000000000 # 16 bit product id product_id 0x0000 # 16 bit product version product_ver 0x0000 # ASCII vendor string (max 255 characters) vendor "ACME Technology Company" # ASCII product string (max 255 characters) product "Special Sensor Board" # How much current the HAT+ can supply, in milliamps current_supply 0 # Which Device Tree overlay to load dt_blob "acme-sensor" # Custom binary data custom_data deadbeef c00 1c0d e end custom_data " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. \" raspi-utils-20250514/eeptools/eeprom_v1_settings.txt000077500000000000000000000077061501106437300224570ustar00rootroot00000000000000######################################################################## # EEPROM settings text file # # Edit this file for your particular board and run through eepmake tool, # then use eepflash tool to write to attached HAT ID EEPROM # # Tools available: # eepmake Parses EEPROM text file and creates binary .eep file # eepdump Dumps a binary .eep file as human readable text (for debug) # eepflash Write or read .eep binary image to/from HAT EEPROM # ######################################################################## ######################################################################## # Vendor info # 128 bit UUID. If left at zero eepmake tool will auto-generate # RFC 4122 compliant UUID product_uuid 00000000-0000-0000-0000-000000000000 # 16 bit product id product_id 0x0000 # 16 bit product version product_ver 0x0000 # ASCII vendor string (max 255 characters) vendor "ACME Technology Company" # ASCII product string (max 255 characters) product "Special Sensor Board" # Custom binary data custom_data deadbeef c00 1c0d e end ######################################################################## # GPIO bank settings, set to nonzero to change from the default. # NOTE these setting can only be set per BANK, uncommenting any of # these will force the bank to use the custom setting. # drive strength, 0=default, 1-8=2,4,6,8,10,12,14,16mA, 9-15=reserved gpio_drive 0 # 0=default, 1=slew rate limiting, 2=no slew limiting, 3=reserved gpio_slew 0 # 0=default, 1=hysteresis disabled, 2=hysteresis enabled, 3=reserved gpio_hysteresis 0 # If board back-powers Pi via 5V GPIO header pins: # 0 = board does not back-power # 1 = board back-powers and can supply the Pi with a minimum of 1.3A # 2 = board back-powers and can supply the Pi with a minimum of 2A # 3 = reserved # If back_power=2 then USB high current mode will be automatically # enabled on the Pi back_power 0 ######################################################################## # GPIO pins, uncomment for GPIOs used on board # Options for FUNCTION: INPUT, OUTPUT, ALT0-ALT5 # Options for PULL: DEFAULT, UP, DOWN, NONE # NB GPIO0 and GPIO1 are reserved for ID EEPROM so cannot be set # GPIO FUNCTION PULL # ---- -------- ---- #setgpio 2 INPUT DEFAULT #setgpio 3 INPUT DEFAULT #setgpio 4 INPUT DEFAULT #setgpio 5 INPUT DEFAULT #setgpio 6 INPUT DEFAULT #setgpio 7 INPUT DEFAULT #setgpio 8 INPUT DEFAULT #setgpio 9 INPUT DEFAULT #setgpio 10 INPUT DEFAULT #setgpio 11 INPUT DEFAULT #setgpio 12 INPUT DEFAULT #setgpio 13 INPUT DEFAULT #setgpio 14 INPUT DEFAULT #setgpio 15 INPUT DEFAULT #setgpio 16 INPUT DEFAULT #setgpio 17 INPUT DEFAULT #setgpio 18 INPUT DEFAULT #setgpio 19 INPUT DEFAULT #setgpio 20 INPUT DEFAULT #setgpio 21 INPUT DEFAULT #setgpio 22 INPUT DEFAULT #setgpio 23 INPUT DEFAULT #setgpio 24 INPUT DEFAULT #setgpio 25 INPUT DEFAULT #setgpio 26 INPUT DEFAULT #setgpio 27 INPUT DEFAULT ######################################################################## # Settings for bank 1 (only valid for CM1/3/3+/4S). Setting one or more of # these GPIOs requires setting of drive, slew and hysteresis for bank 1. # bank1_gpio_drive 0 # bank1_gpio_slew 0 # bank1_gpio_hysteresis 0 #setgpio 28 INPUT DEFAULT #setgpio 29 INPUT DEFAULT #setgpio 30 INPUT DEFAULT #setgpio 31 INPUT DEFAULT #setgpio 32 INPUT DEFAULT #setgpio 33 INPUT DEFAULT #setgpio 34 INPUT DEFAULT #setgpio 35 INPUT DEFAULT #setgpio 36 INPUT DEFAULT #setgpio 37 INPUT DEFAULT #setgpio 38 INPUT DEFAULT #setgpio 39 INPUT DEFAULT #setgpio 40 INPUT DEFAULT #setgpio 41 INPUT DEFAULT #setgpio 42 INPUT DEFAULT #setgpio 43 INPUT DEFAULT #setgpio 44 INPUT DEFAULT #setgpio 45 INPUT DEFAULT raspi-utils-20250514/kdtc/000077500000000000000000000000001501106437300151575ustar00rootroot00000000000000raspi-utils-20250514/kdtc/CMakeLists.txt000066400000000000000000000002631501106437300177200ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) #set project name project(kdtc) #add executables install(PROGRAMS kdtc DESTINATION ${CMAKE_INSTALL_BINDIR}) raspi-utils-20250514/kdtc/README.md000066400000000000000000000035441501106437300164440ustar00rootroot00000000000000# kdtc `kdtc` is a wrapper around `dtc` (the Device Tree compiler) that passes input files through `cpp` (the C preprocessor) first, in order to resolve any #include directives or expand any #define macros. When run without any `dtc` options, `kdtc` attempts to detect the input format and do the right thing. **Build Instructions** Install the prerequisites with "sudo apt install cmake device-tree-compiler" - you need at least version 3.10 of cmake. Run the following commands, either here or in the top-level directory to build and install everything: - *cmake .* - *sudo make install* Alternatively, to avoid fetching the entire `utils` repository and its history, you can just download it with: ``` $ wget https://raw.githubusercontent.com/raspberrypi/utils/refs/heads/master/kdtc/kdtc $ chmod +x kdtc ``` **Usage** ``` Usage: kdtc [] [ []] where can be any of: -h|--help Show this help message -i|--include Add a path to search for include files -k|--kerndir The path to the kernel tree -n|--just-print Just show the command that would be executed -w|--warnings Don't suppress common dtc warnings or any dtc options (see 'dtc -h') When run with no dtc options, kdtc detects the input format and attempts to do the right thing. ``` Assuming `kdtc` is in your PATH, compiling an overlay without `#include`s is simple: ``` $ kdtc widget-overlay.dts widget.dtbo ``` For an overlay written as if it were in the kernel tree with `#include`s, use either: ``` $ kdtc -k /path/to/kernel doofer-overlay.dts doofer.dtbo ``` Or, running from a directory within the kernel tree: ``` $ kdtc /path/to/whatsit-overlay.dts /path/to/whatsit.dtbo ``` (When run within a git kernel source tree, the `--kerndir` path is inferred.) To decompile an overlay to `stdout`: ``` $ kdtc mystery.dtbo ``` raspi-utils-20250514/kdtc/kdtc000077500000000000000000000127011501106437300160330ustar00rootroot00000000000000#!/bin/env perl use strict; use Getopt::Long; my @warnings_to_suppress = ( 'unit_address_vs_reg', 'simple_bus_reg', 'unit_address_format', 'interrupts_property', 'gpios_property', 'label_is_string', 'unique_unit_address', 'avoid_unnecessary_addr_size', 'pci_device_reg', 'pci_device_bus_num', 'reg_format', 'interrupt_provider', 'dma_ranges_format', 'avoid_default_addr_size', ); my @cpp_files = ('arm-linux-gnueabihf-cpp', 'aarch64-linux-gnu-cpp', 'cpp'); my $cpp; foreach my $cmd (@cpp_files) { if (system("command -v $cmd >/dev/null 2>&1") == 0) { $cpp = $cmd; last; } } die "* no suitable cpp found\n" if (!$cpp); my $dtc_opt; my $in_fmt; my $kerndir; my $out_file; my $help; my @include_paths = ('.'); my $justprint; my $show_warnings; my @args = (@ARGV); Getopt::Long::Configure(qw( require_order gnu_getopt )); my @kdtc_opts = ( 'kerndir|k=s' => \$kerndir, 'just-print|n' => \$justprint, 'warnings|w' => \$show_warnings, ); Getopt::Long::GetOptions( 'in-format|I=s' => \$in_fmt, 'quiet|q' => \$dtc_opt, 'out|o=s' => \$out_file, 'out-format|O=s' => \$dtc_opt, 'out-version|V=s' => \$dtc_opt, 'out-dependency|d=s' => \$dtc_opt, 'reserve|R=i' => \$dtc_opt, 'space|S=i' => \$dtc_opt, 'pad|p=i' => \$dtc_opt, 'align|a=i' => \$dtc_opt, 'boot-cpu|b=i' => \$dtc_opt, 'force|f' => \$dtc_opt, 'include|i=s' => \@include_paths, 'sort|s' => \$dtc_opt, 'phandle|H=s' => \$dtc_opt, 'warning|W=s' => \$dtc_opt, 'error|E=s' => \$dtc_opt, 'symbols|@' => \$dtc_opt, 'auto-alias|A' => \$dtc_opt, 'annotate|T' => \$dtc_opt, 'help|h' => \$help, 'version|v' => \$dtc_opt, @kdtc_opts ) || usage(); usage() if ($help); # Filter out the arguments which are only for kdtc filter_args(\@args, \@kdtc_opts); # Remove any remaining parameter(s) - the filename, if there is one splice(@args, @args - @ARGV); my $simple_mode = !($dtc_opt || $in_fmt || $out_file); my $in_file = $ARGV[0]; if (!$out_file) { $out_file = $ARGV[1]; $out_file = "$1.dtbo" if (!$out_file && $in_file =~ /^(.+)-overlay.dts$/); push @args, '-o', $out_file if ($out_file); } my $dts_in = 1; if ($in_fmt =~ /^(dtb|fs|yaml)$/) { $dts_in = 0; } elsif ($in_file =~ /\.(dtb|dtbo|yaml)$/) { $dts_in = 0; } if (!$kerndir) { $kerndir = `git rev-parse --show-toplevel 2>/dev/null`; chomp($kerndir); #fatal_error("This isn't a Linux repository") if (!-d "$kerndir/kernel"); } push @include_paths, "$kerndir/include", "$kerndir/arch/arm/boot/dts/overlays" if ($kerndir); if ($simple_mode) { if ($dts_in) { push @args, '-@', '-H', 'epapr', '-I', 'dts', '-O', 'dtb'; } else { push @args, '-I', 'dtb', '-O', 'dts'; } } if ($dts_in) { foreach my $path (@include_paths) { escape(\$path); $path = "-I$path"; } escape(\$in_file); foreach my $arg (@args) { escape(\$arg); } if (!$show_warnings) { # And add the warning suppression foreach my $warn (@warnings_to_suppress) { push @args, '-W', "no-".$warn; } } my $cmd = "$cpp -nostdinc -undef -D__DTS__ -x assembler-with-cpp " . join(" ", @include_paths) . " $in_file | dtc " . join(" ", @args); if ($justprint) { print($cmd, "\n"); } else { exec($cmd); } } else { my @cmd = ('dtc', @args, $in_file); if ($justprint) { foreach my $arg (@cmd) { escape(\$arg); } print(join(" ", @cmd), "\n"); } else { exec(@cmd); } } sub escape { ${$_[0]} = "'" . ${$_[0]} . "'" if (${$_[0]} =~ /^[^'].*\s/); } sub usage { print("Usage: kdtc [] [ []]\n"); print(" where can be any of:\n"); print("\n"); print(" -h|--help Show this help message\n"); print(" -i|--include Add a path to search for include files\n"); print(" -k|--kerndir The path to the kernel tree\n"); print(" -n|--just-print Just show the command that would be executed\n"); print(" -w|--warnings Don't suppress common dtc warnings\n"); print("\n"); print(" or any dtc options (see 'dtc -h')\n"); print("\n"); print("When run with no dtc options, kdtc detects the input format and attempts\n"); print("to do the right thing. With no , kdtc will infer 'x.dtbo' from an\n"); print(" of 'x-overlay.dts'.\n"); print("\n"); print("If run within a git kernel source tree, the kerndir path is inferred.\n"); exit(0); } sub filter_args { my ($args, $remove) = @_; my %filters; my $i; for ($i = 0; $i < @$remove; $i += 2) { my $opt = $remove->[$i]; my ($long, $short, $type) = ($opt =~ /^([-a-z]+)\|([a-zA-Z])(?:=([is]))?$/); my $len = $type ? 2 : 1; $filters{"--$long"} = $len; $filters{"-$short"} = $len; } $i = 0; while ($i < @$args) { my $filter = $filters{$args->[$i]}; if ($filter) { splice(@$args, $i, $filter); } else { $i++; } } } raspi-utils-20250514/otpset/000077500000000000000000000000001501106437300155505ustar00rootroot00000000000000raspi-utils-20250514/otpset/CMakeLists.txt000066400000000000000000000002671501106437300203150ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) #set project name project(otpset) #add executables install(PROGRAMS otpset DESTINATION ${CMAKE_INSTALL_BINDIR}) raspi-utils-20250514/otpset/README.md000066400000000000000000000010621501106437300170260ustar00rootroot00000000000000# otpset Wrapper script for programming the OTP on Raspberry Pi devices. OTP setting is done via `vcmailbox` calls, and are not particularly easy to use. This wrapper makes it easier to read and set customer OTP bits, and provides some level of error checking to the process. Once OTP bits are set, they CANNOT be unset, so this wrapper requests confirmation before performing destructive/irreversible operations. See official docs on the subject here: https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#industrial-use-of-the-raspberry-pi raspi-utils-20250514/otpset/otpset000077500000000000000000000134561501106437300170250ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (c) 2022 Raspberry Pi Ltd. # # SPDX-License-Identifier: BSD-3-Clause # import argparse import subprocess import sys customer_otp_offset = 36 legacy_offset = 8 def ParseCommandLine(): parser = argparse.ArgumentParser(description='A helper application for reading and setting Raspberry Pi OTP (One Time Programmable) bits.\n Take care when using this application, changes to OTP are irreversible.', epilog="Script provided by Raspberry Pi Ltd under a BSD-3-Clause license") parser.add_argument("data", nargs="*", type=str, help="Customer OTP data to set. 1 to 8 32 bit values. Can be decimal, hex (0x...), or binary (0b...)") parser.add_argument("-r", "--row", type=int, help="Customer OTP start row. Valid values are 0 to 7", default=-1, choices=range(0,8), metavar="0-7") parser.add_argument("-c", "--customer", action='store_true', help="Read customer OTP bits") parser.add_argument("-p", "--pi", action='store_true', help="Read Raspberry Pi OTP bits") parser.add_argument("-v", "--verbose", action='store_true', help="Extra detailed messages") parser.add_argument("--yes", action='store_true', help="Make changes to OTP (without this a dry run is done). WARNING: Changes to OTP are irreversible") parser.add_argument("--lock", action='store_true', help="Permanently disable writing to customer OTP. WARNING: This operation is irreversible") parser.add_argument("-f", "--fake", action='store_true', help="Use fake data for testing") if len(sys.argv) == 1: parser.print_help(sys.stderr) sys.exit(1) return parser.parse_args() def ReadOTP(args): # Map key is actually the value from the docs, so we do need to subtract/add the legacy_offset from it where necessary. row_info = { 17: "Bootmode register", 18: "Bootmode register copy", 28: "Serial number", 29: "One's compliment of serial number", 30: "Revision code", 33: "Extended board revision", 36: "Customer OTP Row 0", 37: "Customer OTP Row 1", 38: "Customer OTP Row 2", 39: "Customer OTP Row 3", 40: "Customer OTP Row 4", 41: "Customer OTP Row 5", 42: "Customer OTP Row 6", 43: "Customer OTP Row 7", 46: "MPEG2 decode key", 47: "WVC1 decode key", 64: "MAC address", 65: "MAC address", 66: "Advanced boot mode", } if args.fake: result = b'08:00000000\n09:00000000\n10:00000000\n11:00000000\n12:00000000\n13:00000000\n14:00000000\n15:00000000\n16:00280000\n17:000008b0\n18:000008b0\n19:ffffffff\n20:ffffffff\n21:ffffffff\n22:ffffffff\n23:ffffffff\n24:ffffffff\n25:ffffffff\n26:ffffffff\n27:00005d5d\n28:58d2eb5c\n29:a72d14a3\n30:00c03111\n31:00000000\n32:00000000\n33:00000000\n34:00000000\n35:00000000\n36:00000000\n37:00000000\n38:00000000\n39:00000000\n40:00000000\n41:00000000\n42:00000000\n43:00000000\n44:00000000\n45:00000000\n46:00000000\n47:00000000\n48:00000000\n49:00000000\n50:00000000\n51:00000000\n52:00000000\n53:00000000\n54:00000000\n55:00000000\n56:00000000\n57:00000000\n58:00000000\n59:00000000\n60:00000000\n61:00000000\n62:00000000\n63:00000000\n64:00650000\n65:00000000\n66:00000000\n' else: result = subprocess.check_output(['vcgencmd', 'otp_dump']) result = result.split(b"\n") result.pop() # don't need the last entry, it's fluff for i, val in enumerate(result): result[i] = int(val[3:], base=16) if args.customer: if i >= customer_otp_offset - legacy_offset and i < 44 - legacy_offset: print(i - (customer_otp_offset - legacy_offset), "0x{:08x} b'{:032b}".format(result[i], result[i])) else : print(i + legacy_offset, "0x{:08x} b'{:032b} {}".format(result[i], result[i], row_info.get(i + legacy_offset, ""))) def WriteOTP(args): # At this stage we know the args.rows are valid as argparser sorted that out, and we have an number of position parameters in args.data which contain the 32 bit values to write # We also have the flag to say defo do this, rather than dry run in args.yes # Format of the mailbox call is # vcmailbox [8 + number * 4] [8 + number * 4] [start_num] [number] [value] [value] [value] ... # write command is 0x00038021 write_cmd = 0x00038021 num_rows = len(args.data) first_row = 0 if args.row == -1 else args.row if (num_rows < 1 or num_rows > 8 - first_row): print("Invalid combination of start row ({}) and number of words to write to OTP ({})". format(first_row, num_rows)) sys.exit(-1) num_bytes = (num_rows * 4) + 8 cmd = ['vcmailbox', hex(write_cmd), str(num_bytes), str(num_bytes), str(first_row), str(num_rows) ] for i, val in enumerate(args.data): cmd.append(hex(int(val, 0))) if args.verbose : print('Setting {} rows of customer OTP from row {} onwards'.format(num_rows, first_row)) print('Sending VC mailbox call: ',' '.join(cmd)) if args.yes: print("Are you ABSOLUTELY sure you want to make changes to the customer OTP?. This operation is IRREVERSIBLE. Type YES to confirm") confirm = input() if confirm == 'YES': result = subprocess.check_output(cmd) def LockOTP(args): cmd = ['vcmailbox', '0x00038021', '8', '8', '0xffffffff', '0xaffe0000' ] if args.verbose : print('Sending VC mailbox call to lock OTP: ',' '.join(cmd)) print("Are you ABSOLUTELY sure you want to permamently disable changes to the customer OTP?. This operation is IRREVERSIBLE. Type YES to confirm") confirm = input() if confirm == 'YES': result = subprocess.check_output(cmd) ################################################################################### # main execution starteth here args = ParseCommandLine() if args.customer or args.pi : ReadOTP(args) sys.exit(0) if args.lock : LockOTP(args) sys.exit(0) WriteOTP(args) raspi-utils-20250514/overlaycheck/000077500000000000000000000000001501106437300167115ustar00rootroot00000000000000raspi-utils-20250514/overlaycheck/CMakeLists.txt000066400000000000000000000004221501106437300214470ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) #set project name project(overlaycheck) #add executables install(PROGRAMS overlaycheck DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES overlaycheck_exclusions.txt DESTINATION ${CMAKE_INSTALL_BINDIR}) raspi-utils-20250514/overlaycheck/README.md000066400000000000000000000012511501106437300201670ustar00rootroot00000000000000 # overlaycheck overlaycheck is a tool for validating the overlay files and README in a kernel source tree. Note that overlaycheck makes use of ovmerge, dtmerge, dtc, dtdiff and fdtget, and therefore requires them all to be installed and on the path. **Build Instructions** Install the prerequisites with "sudo apt install cmake device-tree-compiler" - you need at least version 3.10 of cmake. Run the following commands, either here or in the top-level directory to build and install everything: - *cmake .* - *make* - *sudo make install* **Usage** * overlaycheck * overlaycheck -v (for verbose output) * overlaycheck -s (to see all the strict warnings from dtc) raspi-utils-20250514/overlaycheck/overlaycheck000077500000000000000000000617461501106437300213340ustar00rootroot00000000000000#!/usr/bin/env perl use strict; use warnings; use File::Temp qw/ tempdir /; my $DTMERGE = "dtmerge"; my $TMPDIR = tempdir( CLEANUP => 1); my $MERGED_DTB = "$TMPDIR/merged.dtb"; my @base_files = ( "bcm2712-rpi-5-b", "bcm2712-rpi-cm5-cm5io", "bcm2711-rpi-4-b", "bcm2711-rpi-cm4", "bcm2711-rpi-cm4s", "bcm2708-rpi-b", "bcm2708-rpi-b-rev1", "bcm2708-rpi-b-plus", "bcm2708-rpi-zero", "bcm2708-rpi-zero-w", "bcm2708-rpi-cm", "bcm2709-rpi-2-b", "bcm2710-rpi-3-b", "bcm2710-rpi-3-b-plus", "bcm2710-rpi-cm3", "bcm2710-rpi-zero-2-w", ); my @extra_base_files = ( "bcm2709-rpi-cm2", "bcm2710-rpi-cm0", "bcm2710-rpi-2-b", "bcm2711-rpi-400", "bcm2712-rpi-500", "bcm2712-rpi-cm5-cm4io", "bcm2712-rpi-cm5l-cm5io", "bcm2712-rpi-cm5l-cm4io", ); my %platform_reps = ( 'bcm2835' => 'bcm2710-rpi-3-b', 'bcm2711' => 'bcm2711-rpi-4-b', 'bcm2712' => 'bcm2712-rpi-5-b', ); $ENV{'LD_LIBRARY_PATH'} = "$ENV{'HOME'}/lib"; my %overlay_checkers = ( # With the current overlays, the exclusions are no longer necessary, but an # example for i2c-sensor would be: # '^(addr|lm75addr|int_pin|i2c\d|i2c_csi_dsi|no_timeout)$' 'i2c-rtc' => [ \&container_checker, undef ], 'i2c-fan' => [ \&container_checker, undef ], 'i2c-sensor' => [ \&container_checker, undef ], ); my $word_pattern = '[0-9a-zA-Z0-9][-_a-zA-Z0-9]*'; my $readme_param_pattern = '([-_a-zA-Z0-9]|<[a-z]>|<[a-z]-[a-z]>)*'; my $verbose; my $strict_dtc; my $try_all; my $fail = 0; while (@ARGV && $ARGV[0] =~ /^-/) { my $arg = shift @ARGV; if ($arg eq '-v') { $verbose = 1; } elsif ($arg eq '-s') { $strict_dtc = 1; } elsif ($arg eq '-t') { $try_all = 1; } else { fatal_error("Unknown option '$arg'"); } } # Aims: # * Check that each overlay has an entry in the README # * Check that the parameters of each overlay are in the README # * Check that the parameters of the base DTBs are in the README # * Check that each overlay has an entry in the Makefile # Process: # 1) Parse the base dts's and overlays # 2) Parse the README # 3) Compare the two # 4) Parse the Makefile, and compare with the others my $kerndir = `git rev-parse --show-toplevel 2>/dev/null`; chomp($kerndir); fatal_error("This isn't a Linux repository") if (!-d "$kerndir/kernel"); # 32-bit first, to avoid the #include "arm/broadcom..." versions in the arm64 tree my $dtspath = (-f $kerndir."/arch/arm/boot/dts/broadcom/Makefile") ? $kerndir."/arch/arm/boot/dts/broadcom" : $kerndir."/arch/arm/boot/dts"; my $dts64path = (-f $kerndir."/arch/arm64/boot/dts/broadcom/Makefile") ? $kerndir."/arch/arm64/boot/dts/broadcom" : $kerndir."/arch/arm64/boot/dts"; my (@base32_files, @base64_files, @extra_base32_files, @extra_base64_files); foreach my $base (@base_files) { if (-f $dtspath."/".$base.".dts") { push @base32_files, $base; } elsif (-f $dts64path."/".$base.".dts") { push @base64_files, $base; } } foreach my $base (@extra_base_files) { if (-f $dtspath."/".$base.".dts") { push @extra_base32_files, $base; } elsif (-f $dts64path."/".$base.".dts") { push @extra_base64_files, $base; } } my @dtsgroups = ( [ $dts64path, \@base64_files ], [ $dtspath, \@base32_files ], ); my @extra_dtsgroups = ( [ $dts64path, \@extra_base64_files ], [ $dtspath, \@extra_base32_files ], ); my $overlaysdir = $kerndir."/arch/arm/boot/dts/overlays"; my @cpp_files = ('arm-linux-gnueabihf-cpp', 'aarch64-linux-gnu-cpp', 'cpp'); my $cpp; foreach my $cmd (@cpp_files) { if (system("command -v $cmd >/dev/null 2>&1") == 0) { $cpp = $cmd; last; } } die "* no suitable cpp found\n" if (!$cpp); my @cpp_cmd = ( $cpp, '-nostdinc', '-I.', "-I$kerndir/include", '-Ioverlays', '-undef', '-D__DTS__', '-x', 'assembler-with-cpp' ); my $DTC = "$kerndir/scripts/dtc/dtc"; my $exclusions_file = $0 . "_exclusions.txt"; my @warnings_to_suppress = ( 'unit_address_vs_reg', 'simple_bus_reg', 'unit_address_format', 'interrupts_property', 'gpios_property', 'label_is_string', 'unique_unit_address', 'avoid_unnecessary_addr_size' ); push @warnings_to_suppress, ( 'pci_device_reg', 'pci_device_bus_num', 'reg_format', 'interrupt_provider', 'dma_ranges_format', 'avoid_default_addr_size' ) if (!$strict_dtc); my @dtc_opts; foreach my $warn (@warnings_to_suppress) { push @dtc_opts, '-W', "no-$warn" if (system("$DTC -W no-$warn -v >/dev/null 2>&1") == 0); } my $dtc_opts = join(' ', @dtc_opts); # Parse the exclusions my ($ignore_missing, $ignore_vestigial) = parse_exclusions($exclusions_file); my $documentation = {}; # Parse the README chdir($overlaysdir); my $readme = parse_readme("README"); # Parse the base dts files and overlays my $source = parse_source_files($overlaysdir, @dtsgroups, @extra_dtsgroups); # Compare the two my ($left_only, $common, $right_only) = compare_sets([keys(%$source)], [keys(%$readme)]); ($left_only) = compare_sets($left_only, [keys(%$ignore_missing)]); ($right_only) = compare_sets($right_only, [keys(%$ignore_vestigial)]); list_print("Overlays without documentation", $left_only); list_print("Vestigial overlay documentation", $right_only); $fail ||= (@$left_only || @$right_only); foreach my $overlay (@$common) { for (my $i = 0; $i < @{$readme->{$overlay}}; $i++) { my $param = ${$readme->{$overlay}}[$i]; if ($documentation->{$param}) { my $expansion = $readme->{$param}; splice(@{$readme->{$overlay}}, $i, 1, @$expansion); $i += @$expansion - 1; } } my ($l, $b, $r) = compare_sets($source->{$overlay}, $readme->{$overlay}); my $rpos = 0; while ($rpos < @$r) { my $rv = $r->[$rpos]; if (($rv =~ s/<[i-z]>/[0-9a-fA-F]+/g) || ($rv =~ s/<[a-h]>/[a-z]/g)) { my $lpos = 0; my $found = 0; while ($lpos < @$l) { if ($l->[$lpos] =~ /^$rv$/) { splice(@$l, $lpos, 1); $found = 1; } else { $lpos++; } } splice(@$r, $rpos, 1) if ($found); $rpos++ if (!$found); } else { $rpos++; } } ($l) = compare_sets($l, $ignore_missing->{$overlay}); ($r) = compare_sets($r, $ignore_vestigial->{$overlay}); list_print("$overlay undocumented parameters", $l); list_print("$overlay vestigial parameter documentation", $r); $fail ||= (@$l || @$r); } # Parse the Makefile chdir($overlaysdir); my $makefile = parse_makefile("Makefile"); ($left_only, $common, $right_only) = compare_sets([keys(%$source)], $makefile); list_print("Overlays missing from the Makefile", $left_only); list_print("Vestigial overlay Makefile entries", $right_only); $fail ||= (@$left_only || @$right_only); # Now some build/runtime checks foreach my $group (@dtsgroups, @extra_dtsgroups) { chdir($group->[0]); foreach my $base (@{$group->[1]}) { dtc_cpp("$base.dts", "$TMPDIR/$base.dtb"); if (system("ovmerge -q $base.dts") >> 8) { error(" Error ^ in base file $base.dts\n"); } } } chdir($overlaysdir); dtc_cpp("overlay_map.dts", "$TMPDIR/overlay_map.dtb") if (-r "overlay_map.dts"); foreach my $overlay (sort(keys(%$source))) { next if ($overlay =~ /^> 8) { error(" Error ^ in overlay $overlay\n"); } dtc_cpp("${overlay}-overlay.dts", "$TMPDIR/$overlay.dtbo"); my $overlay_props = $source->{$overlay}[0]; my $base; while (my ($plat, $dts) = each(%platform_reps)) { if ($overlay_props->{$plat}) { $base = $dts; last; } } if ($base && (system($DTMERGE, "$TMPDIR/$base.dtb", $MERGED_DTB, "$TMPDIR/$overlay.dtbo") >> 8)) { error(" Error ^ in overlay $overlay\n"); } dormant_checker($overlay); my $checker = $overlay_checkers{$overlay}; ($checker->[0])->($overlay, $checker->[1], $source->{$overlay}) if ($checker); } if ($try_all) { foreach my $overlay (sort(keys(%$source))) { next if ($overlay =~ /^{$overlay}[0]; foreach my $base (@base_files) { next if (!-f "$TMPDIR/$base.dtb"); next if (($overlay =~ /(wifi|bt)/) && ($base !~ /^(bcm2708-rpi-zero-w|bcm2709-rpi-zero-2|bcm2710-rpi-3-|bcm2711-rpi-(4|cm4$)|bcm2712-rpi-(5|cm5))/)); next if ($overlay_props->{'bcm2711'} && $base !~ /^bcm2711/); next if ($overlay_props->{'bcm2712'} && $base !~ /^bcm2712/); next if (system("$DTMERGE $TMPDIR/$base.dtb $MERGED_DTB $TMPDIR/$overlay.dtbo >/dev/null 2>&1") == ((-2 & 0xff) << 8)); error("Failed to merge $overlay with $base") if (system($DTMERGE, $verbose ? ('-d') : (), "$TMPDIR/$base.dtb", $MERGED_DTB, "$TMPDIR/$overlay.dtbo") != 0); } } } rmdir($TMPDIR); printf("%s\n", $fail ? "Failed" : "OK"); exit($fail); sub compare_sets { my ($l, $r) = @_; my (@lonly, @both, @ronly); my @l = ($l ? sort(not_ref(@$l)) : ()); my @r = ($r ? sort(not_ref(@$r)) : ()); my $lidx = 0; my $ridx = 0; while (($lidx < @l) && ($ridx < @r)) { my $l = $l[$lidx]; my $r = $r[$ridx]; my $cmp = $l cmp $r; if ($cmp == -1) { push @lonly, $l; $lidx++; } elsif ($cmp == 1) { push @ronly, $r; $ridx++; } else { push @both, $l; $lidx++; $ridx++; } } push @lonly, @l[$lidx..$#l]; push @ronly, @r[$ridx..$#r]; return (\@lonly, \@both, \@ronly); } sub not_ref { my @res; foreach my $i (@_) { push @res, $i if (!ref $i); } return @res; } sub list_print { my ($label, $list) = @_; if (@$list) { print ("$label:\n"); foreach my $x (@$list) { print (" $x\n"); } } } sub parse_source_files { my ($overlaydir, @dtsgroups) = @_; my $overlays = {}; my %params; foreach my $group (@dtsgroups) { chdir($group->[0]); foreach my $file (@{$group->[1]}) { foreach my $param (get_params($file.".dts")) { next if (ref $param); $params{$param} = 1; } } } chdir($overlaydir); $overlays->{''} = [ sort(keys(%params)) ]; foreach my $file (glob("*-overlay.dts")) { my $overlay = ($file =~ /^(.*)-overlay.dts$/)[0]; my $redo_cmd = `grep '// redo: ' $file`; if ($redo_cmd =~ /\/\/ redo: ([^\r\n]+)/) { system("cp $file overlaycheck.dts"); system("ovmerge -r overlaycheck.dts > $overlay-overlay.dts"); system("rm overlaycheck.dts"); if (system("git diff --quiet $file") >> 8) { print("* '$overlay' overlay changed after redo\n"); } } $overlays->{ $overlay } = [ get_params($file) ]; } return $overlays; } sub parse_readme { my ($file) = @_; my $overlays = {}; my $fh; error("Failed to open '$file'") if (!open($fh, '<', $file)); my $overlay; my $last_overlay = ''; my $params; my $has_params; my $in_params; my $blank_count; my $linenum = 0; my $descr_column = 32; while (my $line = <$fh>) { $linenum++; chomp($line); error("TABs in README ($linenum)") if ($line =~ /\t/); error("Trailing whitespace in README ($linenum)") if ($line =~ /\s$/); error("Line too long in README ($linenum)") if (length($line) > 80 && $line !~ /^\s+[^\s]+$/); if ($overlay && !$line) { $blank_count++; if ($blank_count == 2) { error("Missing params for overlay $overlay ($linenum)") if (!$params); if (ref $params) { $overlays->{ $overlay } = [ sort(@{$params}) ]; } elsif ($params) { $overlays->{ $overlay } = $params; } else { $overlays->{ $overlay } = [ ]; } $overlay = undef; $params = undef; $in_params = 0; } next; } $blank_count = 0; if (($line =~ /^\w+:\s/) && ($line !~ /^(Name|Info|Load|Params):/)) { error("Bad label ($linenum)"); } if ($line =~ /^Name:(\s*)(.*)\s*$/) { error("Bad formatting in README ($linenum)") if ($1 ne ' '); error("Missing blank lines after overlay? ($linenum)") if ($params); $overlay = $2; $params = undef; $in_params = 0; if ($overlay !~ /^$word_pattern$/ && $overlay ne '') { error("Illegal overlay name '$overlay' in README ($linenum)"); } error("Overlay '$overlay' - order violation in README ($linenum)") if (($overlay cmp $last_overlay) != 1); $last_overlay = ($overlay ne '') ? $overlay : ' '; } elsif ($line =~ /^Info:(\s*)(.*)\s*$/) { error("Bad formatting in README ($linenum)") if ($1 ne ' '); error("Info label with no Name? ($linenum)") if (!$overlay); my $info = $2; if ($2 =~ /^See ($word_pattern)(?:\s+\(.*)?$/) { $params = $1; } } elsif ($line =~ /^Load:(\s*)(.*)\s*$/) { error("Bad formatting in README ($linenum)") if ($1 ne ' '); error("Load label with no Name? ($linenum)") if (!$overlay); my $cmd = $2; $has_params = 0; if ($overlay ne '') { if ($cmd eq '') { # Ignore this overlay $ignore_missing->{$overlay} = 1; $ignore_vestigial->{$overlay} = 1; $overlay = undef; } elsif ($cmd eq '') { # Not a real overlay - a placeholder for common documentation $documentation->{$overlay} = 1; $ignore_missing->{$overlay} = 1; $ignore_vestigial->{$overlay} = 1; $has_params = 1; } else { if ($cmd !~ /^dtoverlay=($word_pattern)(,(?:=|\[=\])?)?$/) { error("Invalid Load example ($linenum)"); } error("Wrong overlay name in Load example ($linenum)") if ($1 ne $overlay); $has_params = 1 if ($2); } } } elsif (defined($overlay) && ($line =~ /^Params:(.*)$/)) { $blank_count = 0; my $rol = $1; $params = []; $in_params = 1; if ($rol) { if ($rol eq ' ') { error("Parameter presence mismatch ($linenum)") if ($has_params); next; } error("Bad formatting in README ($linenum)") if ($rol !~ /^ ([^ ]+)( *)/); error("Parameter presence mismatch ($linenum)") if (!$has_params); my ($param, $indent2) = ($1, length($2)); if ($param !~ /^$readme_param_pattern$/) { error("Invalid parameter name '$param' in README ($linenum)"); } my $descr_indent = 8 + length($param) + $indent2; if (($descr_indent != $descr_column) && ($indent2 != 0)) { error("Bad formatting in README ($linenum)"); } push @$params, $param; } } elsif ($in_params && ($line =~ /^( +)([^ ]+)( *)/)) { my ($indent, $param, $indent2) = (length($1), $2, length($3)); if ($indent == 8) { my $descr_indent = 8 + length($2) + $indent2; # Try to spot a comment if ((($indent2 == 1) || ($indent2 == 0 && $param =~ /:$/)) && ($descr_indent < $descr_column)) { $in_params = 0; next; } if ($param !~ /^$readme_param_pattern$/) { error("Invalid parameter name '$param' in README ($linenum)"); } if (($descr_indent != $descr_column) && ($indent2 != 0)) { error("Bad formatting in README ($linenum)"); } push @$params, $param; } else { error("Bad formatting in README ($linenum)") if (($indent < 8) || (($indent > 8) && ($indent < $descr_column))); } } } # Resolve inter-overlay ("See") references foreach my $overlay (keys(%$overlays)) { my $src = $overlays->{$overlay}; if (!ref $src) { $src = $overlays->{$src}; if (ref $src) { $overlays->{$overlay} = $src; } else { error("Chained 'See' link from $overlay to $src") if (!ref $src) } } } return $overlays; } sub parse_makefile { my ($file) = @_; my $overlays = []; my $fh; error("Failed to open '$file'") if (!open($fh, '<', $file)); push @$overlays, ''; my $last_overlay = ''; my $linenum = 0; my $in_multiline = 0; while (my $line = <$fh>) { $linenum++; chomp($line); error("Trailing whitespace in Makefile ($linenum)") if ($line =~ /\s$/); my $overlay; if ($in_multiline) { if ($line =~ /^\t(.+)\.dtbo( \\)?$/) { $overlay = $1; $in_multiline = 0 if (!$2); } else { error("Syntax error in Makefile ($linenum)"); } } elsif ($line =~ /^dtbo-\$\(RPI_DT_OVERLAYS\) \+= (.+)\.dtbo$/) { $overlay = $1; } elsif ($line =~ /^dtbo-\$\(CONFIG_ARCH_BCM2835\) \+= \\$/) { $in_multiline = 1; } if ($overlay) { error("Overlay '$overlay' - order violation in Makefile ($linenum)") if (($overlay cmp $last_overlay) != 1); push @$overlays, $overlay; $last_overlay = $overlay; } } return $overlays; } sub parse_exclusions { my ($file) = @_; my $missing = {}; my $vestigial = {}; my $fh; error("Failed to open '$file'") if (!open($fh, '<', $file)); my $overlay; my $linenum = 0; while (my $line = <$fh>) { $linenum++; chomp($line); if ($line =~ /^=\s*(.+)$/) { $overlay = $1; $missing->{$overlay} = []; $vestigial->{$overlay} = []; } elsif ($line =~ /^-\s*(.+)$/) { push @{$missing->{$overlay}}, $1; } elsif ($line =~ /^\+\s*(.+)$/) { push @{$vestigial->{$overlay}}, $1; } } return ($missing, $vestigial); } sub get_params { my ($file) = @_; my @params; my $props = {}; print ("[ get_params $file ]\n") if ($verbose); # Run the file through DTS to expand and clean up my $cpp_cmd = join(" ", @cpp_cmd); my @lines = `$cpp_cmd $file | $DTC $dtc_opts -i overlays -@ -I dts -O dts`; my $is_overlay = ($file =~ /-overlay\.dts$/); for (my $i = 0; $i < @lines; $i++) { my $line = $lines[$i]; if ($line =~ /^\s*(__overrides__ \{|target-path\s*=\s*"\/__overrides__"\s*;)\s*$/) { # Support parameters pushed into the base DTB if (0 && $1 =~ /^target-path/) { while ($lines[++$i] !~ /^\s*__overlay__\s*{\s*$/) { } } while ($lines[++$i] =~ /^\s*([^ \}]+)(?: = |;)/) { my $param = $1; my $linenum = $i + 1; error("Invalid parameter name '$param' in '$file'") if ($param !~ /^$word_pattern$/); push @params, $param; if ($lines[$i] =~ /(deadbeef|de ad be ef)/) { printf("$file: $param\n"); $fail = 1; } } } elsif ($is_overlay && $line =~ /^\s*\/\s*{\s*$/) { while ($line !~ /^\s*compatible\s*=/) { $line = $lines[++$i]; if (!$line || $line =~ /{/) { error("Missing overlay compatible string in '$file'"); last; } } my ($comp) = ($line =~ /^\s*compatible\s*=\s*(.*)?\s*;\s*$/); if ($comp) { $comp =~ s/\s+//g; if ($comp !~ /^\"brcm,(bcm2835|bcm2711|bcm2712)\"$/) { error("Invalid overlay compatible string '$comp' in '$file'"); } $props->{$1} = 1; } } elsif ($line =~ /^\s+(?:__symbols__|__fixups__|__local_fixups__)/) { last; } } return ($props, sort(@params)); } sub dtc_cpp { my ($infile, $outfile) = @_; my @dtc_cmd = ( $DTC, @dtc_opts, '-i', 'overlays', '-@', '-I', 'dts', '-O', 'dtb', '-o' ); my $tmpfile = $outfile.".tmp"; error("Failed to CPP '$infile'") if (system(@cpp_cmd, '-o', $tmpfile, $infile) != 0); error("Failed to compile '$infile'") if (system(@dtc_cmd, $outfile, $tmpfile) != 0); unlink($tmpfile); } sub container_checker { my ($name, $exclude, $params) = @_; my $basename = $base_files[0]; my $base = "$TMPDIR/$basename.dtb"; foreach my $param (@$params) { next if (ref $param || ($exclude && $param =~ $exclude)); my $target = `fdtget -t bx "$TMPDIR/$name.dtbo" /__overrides__ $param`; chomp($target); next if ($target !~ /^0 0 0 0 /); error("Failed to merge $name with $basename") if (system($DTMERGE, $verbose ? ('-d') : (), $base, $MERGED_DTB, "$TMPDIR/$name.dtbo", $param) != 0); my $diffs = `dtdiff $base $MERGED_DTB`; if ($diffs !~ /^\+\s*$param@/m) { error("container_checker($name): parameter '$param' doesn't enable matching node"); } } } sub dormant_checker { my ($overlay) = @_; my $ph; my $fragnum; my %is_dormant; my %is_wakeable; my $in_overrides = 0; die if (!open($ph, '-|', "ovmerge -N ${overlay}-overlay.dts")); while (my $line = <$ph>) { chomp($line); if ($in_overrides) { if ($line =~ /}/) { $in_overrides = 0; } elsif ($line =~ /^\s*([-\w]+) = (.*?;)$/) { my ($param, $rol) = ($1,$2); while ($rol =~ /\G<(&\w+|0)>, "([^"]+)"(?:, |;)/cg) { my ($ref, $override) = ($1, $2); if ($ref eq '0') { while ($override =~ /\G([-+=!])(\d+)/cg) { my ($type, $num) = ($1, $2); $is_wakeable{$num} = 1 if ($type ne '-'); } } elsif ($override =~ /=$/) { $rol =~ /\G[^,;]+(?:, |;)/cg; } } } } elsif ($line =~ /\bfragment@(\d+)\s\{/) { $fragnum = $1; } elsif ($line =~ /\b__dormant__\b/) { $is_dormant{$fragnum} = 1; } elsif ($line =~ /\b__overrides__\b/) { $in_overrides = 1; } } foreach my $frag (sort { $a <=> $b} keys(%is_dormant)) { error("$overlay: fragment $frag is dormant and cannot be activated") if (!$is_wakeable{$frag}); } } sub error { print("* $_[0]\n"); $fail = 1; } sub fatal_error { error(@_); exit(1); } raspi-utils-20250514/overlaycheck/overlaycheck_exclusions.txt000066400000000000000000000014011501106437300244010ustar00rootroot00000000000000= - arm_freq - cache_line_size - core_freq - i2c0 - i2c0_baudrate - i2c1 - i2c1_baudrate - i2c2_baudrate - i2c2_iknowwhatimdoing - pwr_led_activelow - pwr_led_gpio - pwr_led_trigger - uart0_clkrate - cam0-led - cam0-led-ctrl - cam0-pwdn - cam0-pwdn-ctrl + i2c + i2c_arm + i2c_arm_baudrate + i2c_baudrate + i2c_vc + i2c_vc_baudrate =ads1015 - chb_cfg - chb_datarate - chb_enable - chb_gain - chc_cfg - chc_datarate - chc_enable - chc_gain - chd_cfg - chd_datarate - chd_enable - chd_gain =ads1115 - chb_cfg - chb_datarate - chb_enable - chb_gain - chc_cfg - chc_datarate - chc_enable - chc_gain - chd_cfg - chd_datarate - chd_enable - chd_gain =ov5647 - cam0-led - cam0-led-ctrl - cam0-pwdn - cam0-pwdn-ctrl =rotary-encoder - rotary0_pin_a - rotary0_pin_b raspi-utils-20250514/ovmerge/000077500000000000000000000000001501106437300156765ustar00rootroot00000000000000raspi-utils-20250514/ovmerge/CMakeLists.txt000066400000000000000000000002711501106437300204360ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) #set project name project(ovmerge) #add executables install(PROGRAMS ovmerge DESTINATION ${CMAKE_INSTALL_BINDIR}) raspi-utils-20250514/ovmerge/README.md000066400000000000000000000026721501106437300171640ustar00rootroot00000000000000 # ovmerge ovmerge is tool for merging DT overlay source files (`*-overlay.dts`), flattening and sorting `.dts` files for easy comparison, displaying the include tree, etc. **Build Instructions** Install the prerequisites with "sudo apt install cmake" - you need at least version 3.10 of cmake. Run the following commands here, or in the top-level directory to build and install all the utilities: - *cmake .* - *make* - *sudo make install* **Usage** ``` Usage: ovmerge where is the name of an overlay, optionally followed by a comma-separated list of parameters, each with optional '=' assignments. The presence of any parameters, or a comma followed by no parameters, removes the parameter declarations from the merged overlay to avoid a potential name clash. and are any of: -b Read files from specified git branch -c Include 'redo' comment with command line (c.f. '-r') -e Expand mode - list non-skipped lines in order of inclusion -h Display this help info -i Show include hierarchy for each file -l Like expand mode, but labels each line with source file -n No .dts file header (just parsing .dtsi files) -p Emulate Pi firmware manipulation -r Redo command comment in named files (c.f. '-c') -s Sort nodes and properties (for easy comparison) -t Trace -w Show warnings ``` raspi-utils-20250514/ovmerge/ovmerge000077500000000000000000001345171501106437300173030ustar00rootroot00000000000000#!/usr/bin/perl # Author: Phil Elwell # Copyright (c) 2018-2021, Raspberry Pi (Trading) Ltd. # # 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, # without modification. # 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. The names of the above-listed copyright holders may not be used # to endorse or promote products derived from this software without # specific prior written permission. # # ALTERNATIVELY, this software may be distributed under the terms of the # GNU General Public License ("GPL") version 2, as published by the Free # Software Foundation. # # 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 OWNER 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. # To Do: # * Combine duplicate labels on the same node # * Consider using hashes for properties and node names # * &{/path} syntax # * 'blame' mode that prepends filename to each line - good for grep. # Fundamental types # '' - string # '#' - 64-bit # ':' - 32-bit integer # ';' - 16-bit integer # '.' - 8-bit integer (byte) # '?' - boolean # '!' - inverted boolean # '[' - byte array // Byte array syntax accepts but doesn't require colons between bytes # 'prop[' // Interpret value as byte array and assign to prop # 'prop[=00:01:02' // property = literal byte array # # Operations on a fundamental type: # // N.B. The type must go before the list so we know how to interpret it. # # 'reg:0' // set reg and unit address to value # 'reg:0=0' // set reg and unit address to the supplied literal # // The reg property is only set if it already exists. # # 'name' // Assigning to name property automatically sets the node name # // The name property is only set if it already exists. # # '=' - literal assignment (if a string, the literal after the =), if an integer, # either the in-band integer or the next cell (useful for phandles). # "prop=foo" // String literal assignment # "prop=", &spi // String path literal assignment (the path to the node is substituted, used for aliases) # "prop:0=0" // Integer literal assignment # "prop:0=", <&spi>; // Integer cell assignment # # '{...}' - use the value as the key to an element in the set. The usual type indicators apply. # 'prop{a='alpha',b='bravo',c='charlie'}"; # 'prop:0{0=",<&i2c0>,"1=",<&i2c1>,"3=0x2a}"; use strict; use integer; use warnings; no warnings 'portable'; use POSIX qw(strftime); my %elem_sizes = ( '"' => 0, # string '.' => 1, # byte ';' => 2, # 16-bit int ':' => 4, # 32-bit int '#' => 8, # 64-bit int ); my $branch; my $comment = 0; my $expand = 0; my $expand_label = 0; my $show_includes = 0; my $pi_extras = 0; my $redo = 0; my $sort = 0; my $warnings = 0; my $no_dts = 0; my $no_renumber = 0; my $cur_dt; my $retcode = 0; my $query = 0; my $force = 0; my $trace_parse = 0; my $trace_tree = 0; my $trace_prop = ''; my $trace_label = ''; my $indent_str = "\t"; my @redo_comments; my @cmdline; while ($ARGV[0] =~ /^-/) { my $arg = shift @ARGV; if ($arg eq '-b') { $branch = shift @ARGV; if (!defined $branch) { print STDERR ("* Branch parameter missing\n"); usage(); } push @cmdline, $arg, $branch; } elsif ($arg eq '-c') { $comment = 1; } elsif ($arg eq '-e') { $expand = 1; } elsif ($arg eq '-h') { usage(); } elsif ($arg eq '-i') { $show_includes = 1; } elsif ($arg eq '-l') { $expand = 1; $expand_label = 1; } elsif ($arg eq '-n') { $no_dts = 1; } elsif ($arg eq '-p') { $pi_extras = 1; push @cmdline, $arg; } elsif ($arg eq '-r') { my $firstline = <>; if ($firstline !~ /^\/\/ redo: ovmerge (.*)/) { print STDERR ("* Redo but input has no 'redo:' comment\n"); usage(); } my $cmdline = $1; @ARGV = split(/\s/, $cmdline); while (my $line = <>) { chomp($line); last if ($line =~ /^\/dts-v1\//); push @redo_comments, $line; } } elsif ($arg eq '-s') { $sort = 1; push @cmdline, $arg; } elsif ($arg eq '-t') { $trace_tree = 1; } elsif ($arg eq '-T') { $trace_parse = 1; } elsif ($arg eq '-w') { $warnings = 1; } elsif ($arg eq '-q') { $query = 1; } elsif ($arg eq '-f') { $force = 1; } elsif ($arg eq '-N') { $no_renumber = 1; } elsif ($arg eq '-S') { my $indent_spaces = shift @ARGV; if (!defined $indent_spaces) { print STDERR ("* Spaces count parameter missing\n"); usage(); } elsif ($indent_spaces !~ /^\d+$/) { print STDERR ("* Invalid spaces count parameter. Expected an integer.\n"); usage(); } $indent_str = ' ' x $indent_spaces; push @cmdline, $arg, $indent_spaces; } else { print STDERR ("* Unknown option '$arg'\n"); usage(); } } usage() if (!@ARGV); push @cmdline, @ARGV; my @overlays; foreach my $overlay (@ARGV) { printf("[ overlay %s ]\n", $overlay) if ($trace_tree); $overlay =~ s/^([^,:]+)//; my $ovname = $1; my $dt = dtparse($ovname, $no_dts); my $apply_params = ($overlay =~ /^,/); next if ($show_includes || $expand); my $model = get_prop_string($dt->{'root'}, 'model'); $cur_dt = $dt; if ($model && $model =~ /^Raspberry Pi/ && $pi_extras) { # Pi firmware adds some labels and aliases that overlays # also require. my $aliases = get_child($dt->{'root'}, 'aliases'); my $i2c = get_prop($aliases, 'i2c1')->[1]; set_prop($aliases, 'i2c', $i2c); set_prop($aliases, 'i2c_arm', $i2c); $i2c = resolve_label($dt, $i2c->[1]); add_label($dt, $i2c, 'i2c_arm'); $i2c = get_prop($aliases, 'i2c0')->[1]; set_prop($aliases, 'i2c_vc', $i2c); $i2c = resolve_label($dt, $i2c->[1]); add_label($dt, $i2c, 'i2c_vc'); my $overrides = get_child($dt->{'root'}, '__overrides__'); $i2c = get_prop($overrides, 'i2c1'); my @prop = @$i2c[1..$#$i2c]; set_prop($overrides, 'i2c', @prop); set_prop($overrides, 'i2c_arm', @prop); $i2c = get_prop($overrides, 'i2c0'); @prop = @$i2c[1..$#$i2c]; set_prop($overrides, 'i2c_vc', @prop); } if ($apply_params) { while ($overlay =~ /\G[,:]([^=,]+)(?:=([^,]+))?/g) { dtparam($dt, $1, defined($2) ? $2 : ''); } ovapply1($dt) if ($dt->{'plugin'}); } my $exports = get_node($dt, '/__exports__'); if ($exports) { foreach my $symbol (@{$exports->[1]}) { # Only the name is required my $expname = $symbol->[0]; # Increment the reference count of the named label adj_ref(1, $expname); } } if ($apply_params) { delete_node(get_node($dt, '/__overrides__')); foreach my $fragment (get_fragments($dt)) { delete_node($fragment) if (get_child($fragment, '__dormant__')); } } $cur_dt = undef; ovstrip($dt) if ($dt->{'plugin'}); push @overlays, $dt; } exit(0) if (!@overlays); if ($overlays[0]->{'plugin'}) { # Count and renumber the fragments in the base renumber_fragments($overlays[0], 0) if (!$no_renumber); for (my $i = 1; $i < @overlays; $i++) { ovmerge($overlays[0], $overlays[$i]); } } else { my $base = $overlays[0]; if (@overlays > 1) { # A real Pi base tree will have a __symbols__ node # Some overlays rely on one being present, so ensure one is my $symbols = get_child($base->{'root'}, '__symbols__'); $symbols = add_node($base->{'root'}, '__symbols__') if (!$symbols); # Count and renumber the fragments in the first overlay renumber_fragments($overlays[1], 0); for (my $i = 2; $i < @overlays; $i++) { ovmerge($overlays[1], $overlays[$i]); } ovapply2($base, $overlays[1]); delete_node($symbols) if (is_node_empty($symbols)); } } if ($comment) { print('// redo: ovmerge -c'); foreach my $opt (@cmdline) { if ($opt =~ /\s/) { print(" '$opt'"); } else { print(" $opt"); } } print("\n"); if (@redo_comments) { print(join("\n", @redo_comments)); } print("\n"); } dtdump($overlays[0]) if (!$query); exit($retcode); sub dtparse { # DT = hash of: # 'root' => '/' node # 'plugin' => boolean true if /plugin/ tag is present. # 'labels' => hash of labels used in tree # 'refcount' => hash of reference count of labels in tree (excluding the definition) # 'includes' => array of included headers (array to preserve order) # 'memreserves' => array of memreseve [base,length] pairs # 'defines' => array of memreseve [base,length] pairs my ($filename, $got_header) = @_; my $defines = {}; my $state = [ read_tokens($filename, 0, $defines), 0, undef ]; my $labels = {}; my $dt = { 'labels'=>$labels, 'includes'=>[], 'memreserves'=>[], 'refcount'=>{}, 'defines'=>$defines }; my $next = get_head($state); while ($next =~ /^(\/.+\/|#include)$/) { my $type = $next; $next = match($state, $next); if ($type eq '#include') { set_add($dt->{'includes'}, $next); $next = match($state, $next); } else { if (!$got_header) { die "* File missing /dts-v1/ tag\n" if ($type ne '/dts-v1/'); $got_header = 1; } elsif ($type eq '/dts-v1/') { print("* Ignoring duplicate /dts-v1/ tag\n") if ($warnings); } elsif ($type eq '/plugin/') { $dt->{'plugin'} = 1; } elsif ($type eq '/memreserve/') { my $start = get_int($state); my $length = get_int($state); set_add($dt->{'memreserves'}, [ $start, $length ]); } else { die "* Unexpected token '$type'\n"; } $next = match($state, ';'); } } $cur_dt = $dt; while (defined $next) { if ($next eq '/') { match($state, '/'); $next = parse_node($state, undef, 0, '/'); } else { my @newlabels; while ($next =~ /^(\w+):$/) { push @newlabels, $1; print("[Label: $1]\n") if ($trace_parse); $next = match($state, $next); } if ($next =~ /^&(\w+)$/) { my $subnode = $labels->{$1}; match($state, $next); if (defined $subnode) { $next = parse_node($state, $subnode->[4], $subnode->[5], $subnode, @newlabels); } else { print STDERR ("* Unknown label '$1'\n"); $next = parse_node($state, undef, 0, '/', @newlabels); } } elsif ($next eq '/delete-node/') { $next = match($state, $next); if ($next =~ /^&(\w+)$/) { my $label = $1; my $subnode = $labels->{$label}; match($state, $next); if (defined $subnode) { delete_node($subnode); } else { print STDERR ("* Unknown label '$1'\n"); } $next = match($state, ';'); } } elsif ($next eq '#include') { $next = match($state, $next); set_add($dt->{'includes'}, $next); $next = match($state, $next); } else { die "* Unexpected token '$next'\n"; } } } $cur_dt = undef; if ($state->[1] != @{$state->[0]}) { # For now printf("* Junk at the end - %s ...\n", get_head($state)); } # Check reference counts while (my ($key, $value) = each (%{$dt->{'refcount'}})) { if ($value < 0) { die "* Internal error - negative refcount on '$key'\n"; } elsif ($value > 0 && !defined $dt->{'labels'}->{$key} && !$dt->{'plugin'}) { print STDERR ("* symbol '$key' is undefined in '$filename'\n"); $retcode = 1; } } # Check the fragment order ordercheck($dt, {}) if ($dt->{'plugin'}); # Check the overrides my $overrides = get_node($dt, '/__overrides__'); foreach my $param (@{$overrides->[1]}) { dtparam($dt, $param->[0], undef); } return $dt; } sub dtdump { my ($dt) = @_; print("/dts-v1/;\n"); print("/plugin/;\n") if ($dt->{'plugin'}); print("\n"); if (!set_empty($dt->{'includes'})) { foreach my $inc (set_vals($dt->{'includes'})) { print("#include $inc\n"); } print("\n"); } if (!set_empty($dt->{'memreserves'})) { foreach my $res (set_vals($dt->{'memreserves'})) { print('/memreserve/ ', $res->[0], ' ', $res->[1], ";\n"); } print("\n"); } dump_node($dt->{'root'}, 0); } sub get_vector { my ($p, $size, $len) = @_; if (($p->[0] eq '<' || $p->[0] eq '[') && (($p->[1] == $size) || ($p->[1] == 4 && $size == 8)) && (!defined $len || $len == @{$p->[2]})) { return $p->[2]; } else { return undef; } } sub get_label_ref { my ($p) = @_; my $vector = get_vector($p, 4, 1); if ($vector && ($vector->[0] =~ /^(?:&.*|0)$/)) { return $vector->[0]; } else { return undef; } } sub parse_lookup_table { my ($table, $ovr, $ppos, $value) = @_; my $val; # "prop{a=alpha,b='bravo !',c,=default}"; # Non-match -> 'default' # "prop:0{0=",<&i2c0>,"1=",<&i2c1>,"3=0x2a,}"; # Non-match copies # "not:0{0=1,1=0}"; # Non-match -> error while ($table =~ /(?:'([^']*)'|([^=,\}]*))(?:=(?:'([^']*)'|([^,\}]*)))?([,\}])?/cg) { my ($key, $sub, $sep) = (defined($1) ? $1 : $2, defined($3) ? $3 : $4, $5); if (!defined $sep) { my $p = $ovr->[$$ppos++]; $sub = get_vector($p, 4, 1)->[0]; $table = $ovr->[$$ppos++]; if (!defined $table) { $sep = '}'; } else { die "* Expected string in lookup table\n" if ($table->[0] ne '"'); $table = $table->[1]; $sep = $1 if ($table =~ /^(\})/); } } if (defined $value) { if ($key eq '') { if (!defined $val) { $val = (defined $sub) ? $sub : $value; } } elsif ($key eq $value) { $val = (defined $sub) ? $sub : $key; } } last if (($sep || '') eq '}'); } die "* No match for '$value'\n" if (defined $value && !defined $val); return $val; } sub dtparam { my ($dt, $param, $value) = @_; my $overrides = get_node($dt, '/__overrides__'); die "* No overrides found\n" if (!$overrides); my $ovr = get_prop($overrides, $param); die "* dtparam '$param' not found\n" if (!$ovr); for (my $pos = 1; $pos < @$ovr;) { my $p = $ovr->[$pos++]; my $label = get_label_ref($p); die "* Invalid override 1: $param\n" if (!defined $label); $p = $ovr->[$pos++]; die "* Invalid override 2: $param\n" if ($p->[0] ne '"'); my $decl = $p->[1]; if ($label =~ /^&(.*)/) { my $node = resolve_label($dt, $1); die "* Override '$param' targets unknown label '$1'\n" if (!$node); if ($decl =~ /^([-a-zA-Z0-9_,]+)([.;:#])(\d+)(?:(=|\{)(.*))?$/) { # Integer parameter my ($prop, $type, $offset, $op, $opdata) = ($1, $2, $3, $4 || '', $5); my $size = $elem_sizes{$type}; my $val = $value; if ($op eq '=') { if ($opdata ne '') { $val = $opdata; } else { my $vector = get_vector($ovr->[$pos++], 4, 1); die "* Expected cell value in parameter '$param'\n" if (!defined $vector); $val = $vector->[0]; } } elsif ($op eq '{') { $val = parse_lookup_table($opdata, $ovr, \$pos, $value); } my $intval = integer_value($val, $size); if (defined $value && $prop eq 'reg') { my $regval = sprintf("%x", $intval); $node->[0] =~ s/@[0-9a-fA-F]*$/\@$regval/; } # Locate the offset within the property my ($chunk, $chunk_idx) = find_prop_chunk($node, $prop, $offset, $size, $param, defined($value) && $prop ne 'reg'); if ($chunk) { # Check the override type matches the property type my $vector = get_vector($chunk, $size); die "* Probably incorrect override property type for '$prop'\n" if (!$vector); if (defined $value) { # Apply the override for (my $i = @$vector; $i < $chunk_idx; $i++) { $vector->[$i] = 0; } $vector->[$chunk_idx] = $intval; } } } elsif ($decl =~ /^([-a-zA-Z0-9_,]+)([\?\!])(?:(=|\{)(.*))?$/) { # boolean my ($prop, $sense, $op, $opdata) = ($1, $2, $3 || '', $4); my $val = $value; if ($op eq '=') { if ($opdata ne '') { $val = $opdata; } else { my $vector = get_vector($ovr->[$pos++], 4, 1); die "* Expected cell value in parameter '$param'\n" if (!defined $vector); $val = $vector->[0]; } } elsif ($op eq '{') { $val = parse_lookup_table($opdata, $ovr, \$pos, $value); } if (defined $value) { my $bool = boolean_value($val); $bool = !$bool if ($sense eq '!'); if ($bool) { set_prop($node, $prop); } else { delete_prop($node, $prop); } } } elsif ($decl =~ /^([-a-zA-Z0-9_,]+)\[(?:(=|\{)(.*))?$/) { # byte array my ($prop, $op, $opdata) = ($1, $2 || '', $3); my $val = $value; if ($op eq '=') { $val = $opdata; } elsif ($op eq '{') { $val = parse_lookup_table($opdata, $ovr, \$pos, $value); } if (defined $value) { apply_prop($node, $prop, ['[', 1, byte_array_value($val)]); } } elsif ($decl =~ /^([-a-zA-Z0-9_,]+)(?:(=|\{)(.*))?$/) { # string my ($prop, $op, $opdata) = ($1, $2 || '', $3); my $val = $value; if ($op eq '=') { if ($opdata ne '') { $val = $opdata; } elsif ($pos == @$ovr) { $val = ''; } else { $val = $ovr->[$pos++]; die "* Expected a string or label reference in parameter '$param'\n" if (($val->[0] ne '"') && ($val->[0] ne '&')); } } elsif ($op eq '{') { $val = parse_lookup_table($opdata, $ovr, \$pos, $value); } if (defined $value) { if ($prop eq 'name') { $node->[0] = $val; } else { apply_prop($node, $prop, ref($val) ? $val : [ '"', $val ]); } } } else { die "* Invalid parameter declaration '$decl'\n"; } } else { while ($decl =~ /\G([=!+-])(\d+)/g) { my ($op, $num) = ($1, $2); my $frag = get_node($dt, '/fragment-'.$num) || get_node($dt, '/fragment@'.$num); die "* Param $param: no fragment $num\n" if (!$frag); if (defined $value) { # Enable or disable the fragment as needed my $bool = boolean_value($value); if ($op eq '!') { $bool = !$bool; } elsif ($op eq '+') { $bool = 1; } elsif ($op eq '-') { $bool = 0; } $frag->[2]->[0]->[0] = ($bool ? '__overlay__' : '__dormant__'); } } die "* Invalid override 3:$param\n" if (defined(pos($decl))); } } } # Combine two (possibly partially overridden) overlays sub ovmerge { my ($base, $ov) = @_; die "* Cannot merge a non-overlay\n" if (!$base->{'plugin'} || !$ov->{'plugin'}); # Combine the list of includes, removing any duplicates foreach my $inc (set_vals($ov->{'includes'})) { set_add($base->{'includes'}, $inc); } # Count and renumber the fragments in the overlay renumber_fragments($ov, $base->{'frag_count'}); # Uniquify and merge the overlay labels my %transform; my $base_labels = $base->{'labels'}; my $ov_labels = $ov->{'labels'}; foreach my $l (keys(%$ov_labels)) { my $nl = $l; my $n = $ov_labels->{$l}; if ($base_labels->{$l}) { my $i; for ($i = 1; ; $i++) { $nl = "${l}_$i"; last if (!$base_labels->{$nl}); } $transform{$l} = $nl; # Don't use get_labels here because it returns a copy # and we need to modify the original foreach my $ol (@{$n->[3]}) { $ol = $nl if ($ol eq $l); } } $base_labels->{$nl} = $n; } relabel_node($ov->{'root'}, \%transform, 0); my $base_overrides = get_node($base, '/__overrides__'); my $ov_overrides = get_node($ov, '/__overrides__'); remove_node($base_overrides) if ($base_overrides); # Merge the fragments foreach my $child (get_fragments($ov)) { add_node($base->{'root'}, $child); $base->{'frag_count'}++; } # Merge the overrides if ($ov_overrides) { $base_overrides ||= new_node('__overrides__'); foreach my $ovr (@{$ov_overrides->[1]}) { die "* Duplicate parameter '$ovr->[0]'\n" if (get_prop($base_overrides, $ovr->[0])); set_prop($base_overrides, @$ovr); } } add_node($base->{'root'}, $base_overrides) if ($base_overrides); } # Remove the dormant fragments and unused labels from an overlay sub ovstrip { my ($dt) = @_; my @unused; print("[ ovstrip ]\n") if ($trace_tree); $cur_dt = $dt; while (my ($key, $value) = each (%{$dt->{'labels'}})) { push @unused, $key if (!$dt->{'refcount'}->{$key}); } foreach my $label (@unused) { my $node = $dt->{'labels'}->{$label}; map_del($dt->{'labels'}, $label); my $node_labels = $node->[3]; for (my $i = 0; $i < @$node_labels; $i++) { if ($node_labels->[$i] eq $label) { splice(@$node_labels, $i, 1); last; } } } $cur_dt = undef; } # Apply fragments that target other fragments within the overlay sub ovapply1 { my ($ov) = @_; die "* Cannot apply a non-overlay\n" if (!$ov->{'plugin'}); foreach my $fragment (get_fragments($ov)) { my $overlay = get_child($fragment, '__overlay__'); next if (!$overlay); print("[ apply fragment $fragment->[0] ]\n") if ($trace_tree); my $target_node; my $target = get_prop($fragment, 'target'); if ($target) { my $label = get_label_ref($target->[1]); die "* Invalid target reference\n" if ($label !~ /^&(.*)/); $target_node = $ov->{'labels'}->{$1}; if ($target_node) { # Merge properties and subnodes apply_node($ov, $target_node, $overlay); $overlay->[0] = '__dormant__'; } } } } # Apply an overlay to a base tree sub ovapply2 { my ($base, $ov) = @_; die "* Cannot apply a non-overlay\n" if (!$ov->{'plugin'}); die "* Cannot apply an overlay to an overlay\n" if ($base->{'plugin'}); # Combine the list of includes, removing any duplicates foreach my $inc (set_vals($ov->{'includes'})) { set_add($base->{'includes'}, $inc); } my $base_overrides = get_node($base, '/__overrides__'); foreach my $fragment (get_fragments($ov)) { my $overlay = get_child($fragment, '__overlay__'); next if (!$overlay); print("[ apply fragment $fragment->[0] to base tree]\n") if ($trace_tree); my $target_node; my $target = get_prop($fragment, 'target'); if ($target) { my $label = get_label_ref($target->[1]); die "* Invalid target reference\n" if ($label !~ /^&(.*)/); $target_node = $base->{'labels'}->{$1}; die "* Label '$1' not found in base\n" if (!$target_node); } else { $target = get_prop($fragment, 'target-path'); die "* Invalid target-path\n" if ($target->[1]->[0] ne '"'); $target_node = get_node($base, $target->[1]->[1]); die "* Path '$target->[1]->[1]' not found in base\n" if (!$target_node); } # Merge properties and subnodes apply_node($base, $target_node, $overlay); } } sub ordercheck { my ($ov, $applied) = @_; # Try to apply fragments that target other fragments within the overlay foreach my $fragment (get_fragments($ov)) { my $overlay = get_child($fragment, '__overlay__') || get_child($fragment, '__dormant__'); next if (!$overlay); my $target_node; my $target = get_prop($fragment, 'target'); if ($target) { my $label = get_label_ref($target->[1]); die "* Invalid target reference\n" if ($label !~ /^&(.*)/); $target_node = $ov->{'labels'}->{$1}; if ($target_node) { my $target_fragment = fragment_of($target_node); die "* $fragment->[0] should precede " . ($target_fragment ? $target_fragment->[0] : "fragment@?") . "\n" if ($applied->{$target_node}); set_applied($overlay, $applied); } } } } sub set_applied { my ($node, $applied) = @_; $applied->{$node} = 1; foreach my $subnode (get_children($node)) { set_applied($subnode, $applied); } } sub parse_node { my ($state, $parent, $depth, $node, @newlabels) = @_; # scalar name # array properties # array children # array labels # ref parent # scalar depth my $next = match($state, '{'); $node = (get_child($parent, $node) || add_node($parent, $node)) if (!ref $node); printf("parse_node(%s, %d ...)\n", $node->[0], $depth) if ($trace_parse); # Parse the properties first # Properties are "name=value;" while ($next ne '}') { my @childlabels; if ($next eq '/delete-node/') { $next = match($state, $next); if ($next =~ /^[-a-zA-Z0-9,._+#@]+$/) { delete_node(get_child($node, $next)); match($state, $next); $next = match($state, ';'); } next; } elsif ($next eq '/delete-property/') { $next = match($state, $next); if ($next =~ /^[-a-zA-Z0-9,._+#@]+$/) { delete_prop($node, $next); match($state, $next); $next = match($state, ';'); } next; } while ($next =~ /^(\w+):$/) { push @childlabels, $1; print("[Label: $1]\n") if ($trace_parse); $next = match($state, $next); } if ($next =~ /^[-a-zA-Z0-9,._+#@]+$/) { my $name = $next; print("* Leading zero in node name '$name'\n") if ($name =~ /\@0[0-9a-fA-F]/); $next = match($state, $next); if ($next eq '{') { $next = parse_node($state, $node, $depth + 1, $name, @childlabels); } elsif ($next eq '=') { my @prop; print("* Ignoring label on property '$name'\n") if (@childlabels && $warnings); do { $next = match($state, $next); if ($next =~ /^"(.*)"$/) { # string push @prop, [ '"', $1 ]; $next = match($state, $next); } elsif ($next =~ /^&(.*)/) { # noderef string push @prop, [ '&', $1 ]; $next = match($state, $next); } elsif (($next eq '<') || ($next eq '/bits/')) { my $elemsize = 4; if ($next eq '/bits/') { $next = match($state, $next); if (($next != 8) && ($next != 16) && ($next != 32) && ($next != 64)) { die "* Invalid /bits/ value '$next'.\n"; } $elemsize = $next/8; match($state, $next); } $next = match($state, '<'); # vector my $vals = []; while ($next ne '>') { push @$vals, $next; $next = match($state, $next); } push @prop, [ '<', $elemsize, $vals ]; $next = match($state, '>'); } else { # bytestring my $vals = []; $next = match($state, '['); while ($next ne ']') { push @$vals, @{byte_array_value($next)}; $next = match($state, $next); } $next = match($state, ']'); push @prop, [ '[', 1, $vals ]; } } while ($next eq ','); $next = match($state, ';'); set_prop($node, $name, @prop); } else { print("* Ignoring label on property '$name'\n") if (@childlabels && $warnings); $next = match($state, ';'); set_prop($node, $name); } } else { die "* Unexpected token '$next'\n"; } } my $labels = $cur_dt->{'labels'}; foreach my $newlabel (@newlabels) { my $labelled_node = map_find($labels, $newlabel); if ($labelled_node) { if ($labelled_node != $node) { print STDERR ("* Duplicated label '$newlabel' - '", $labelled_node->[0], "' and '", $node->[0], "'\n"); } else { print("* Replicated label '$newlabel' (on the same node)\n") if ($warnings); } } add_label($cur_dt, $node, $newlabel); } match($state, '}'); return match($state, ';'); } sub add_label { my ($dt, $node, $label, $move_from) = @_; printf("[ add_label %s -> %s ]\n", $label, $node->[0]) if ($trace_tree); my $old_value = map_find($dt->{'labels'}, $label); if (defined $old_value) { return if ($old_value == $node); die "* Label '$label' redefined\n" if ($move_from && ($old_value != $move_from)); @{$move_from->[3]} = grep $_ ne $label, @{$move_from->[3]}; } map_add($dt->{'labels'}, $label, $node); push @{$node->[3]}, $label; print("* Multiple labels on '" . node_path($node) . "'\n") if ($warnings && @{$node->[3]} > 1); } sub resolve_label { my ($dt, $label) = @_; return $dt->{'labels'}->{$label}; } sub resolve_alias { my ($dt, $alias) = @_; my $aliases = get_node($dt, '/aliases'); $alias = get_prop($aliases, $alias); return undef if (!$alias); if ($alias->[1][0] eq '&') { return resolve_label($dt, $alias->[1][1]); } else { return get_node($dt, $alias->[1][1]); } } sub fragment_of { my ($node) = @_; return undef if (!$node); return $node if ($node->[0] =~ /^fragment@/); return fragment_of($node->[4]); } sub dump_node { my ($node, $depth) = @_; my $indent = $indent_str x $depth; print($indent, join(': ', get_labels($node), $node->[0]), " {\n"); # Properties foreach my $prop (get_props($node)) { my @terms; print($indent, $indent_str, $prop->[0]); for (my $i = 1; $i < @$prop; $i++) { my $chunk = $prop->[$i]; if ($chunk->[0] eq '"') { push @terms, '"'.$chunk->[1].'"'; } elsif ($chunk->[0] eq '&') { push @terms, '&'.$chunk->[1]; } elsif ($chunk->[0] =~ '<') { push @terms, (($chunk->[1] != 4) ? sprintf("/bits/ %d ", $chunk->[1] * 8) : '') . '<'.join(' ', @{$chunk->[2]}).'>'; } elsif ($chunk->[0] eq '[') { push @terms, '['.byte_array_string($chunk->[2]).']'; } else { push @terms, '?'; } } print(' = ', join(', ', @terms)) if (@terms); print(";\n"); } # Sub-nodes foreach my $subnode (get_children($node)) { dump_node($subnode, $depth + 1); } print($indent, "};\n"); } sub read_tokens { my ($filename, $depth, $defines) = @_; my $linenum = 0; my $fh; my $tokens = [ ['/file/', $filename] ]; my $in_comment = 0; my $if_count = 0; my $hidden_count = 0; my $expr; my $expr_level = 0; my $filepath = $filename; $filepath =~ s/\/?[^\/]*$//; $filepath .= '/' if ($filepath); print(" " x $depth, $filename, "\n") if ($show_includes); print("[read_tokens '$filename']\n") if ($trace_parse); print("#### Start of '$filename'\n") if ($expand && !$expand_label); die "* Failed to open '$filename'\n" if ($branch ? !open($fh, '-|', "git show $branch:./$filename") : !open($fh, '<', $filename)); while (my $line = <$fh>) { $linenum++; if ($in_comment) { next if ($line !~ s/^.*?\*\///); $in_comment = 0; } if ($line =~ /^\s*#if(def|ndef)?\s+(\w+)/) { my $mode = $1; my $defined = defined($defines->{$2}); $if_count++; $hidden_count++ if ($hidden_count || !$mode || ($mode eq 'def' && !$defined) || ($mode eq 'ndef' && $defined)); next; } elsif ($line =~ /^\s*#else/) { if ($hidden_count == 0) { $hidden_count = 1; } elsif ($hidden_count == 1) { $hidden_count = 0; } next; } elsif ($line =~ /^\s*#endif/) { die "* Unmatched #endif ($filename:$linenum)\n" if (!$if_count); $if_count--; $hidden_count-- if ($hidden_count); next; } next if ($hidden_count); if ($line =~ /^\s*(?:#include|\/include\/)\s+(["<][^">]+[">])\s*$/) { my $incfile = $1; if ($incfile =~ /\.h.$/) { push @$tokens, '#include', $incfile; } elsif ($incfile =~ /\.dtsi?.$/) { my $dtsfile = search_path($filepath.substr($incfile, 1, -1)); die "* Failed to find include file '$incfile'" if (!$dtsfile); my $inc_tokens = read_tokens($dtsfile, $depth + 1, $defines); push @$tokens, @$inc_tokens; push @$tokens, ['/file/', $filename]; print("#### Continue '$filename'\n") if ($expand && !$expand_label); } else { die "* Invalid include file '$incfile'\n"; } next; } elsif ($line =~ /^\s*#define\s+(\w+)(?:\s+([^\r\n]+))?/) { my $symbol = $1; my $val = $2 || ''; $val =~ s/\/\/.*//; $val =~ s/\s+$//; $defines->{$symbol} = $val; next; } elsif ($line =~ /^\s*#undef\s+(\w+)/) { delete $defines->{$1}; next; } elsif ($line =~ /^\s*#bkpt/) { $DB::single = 1; next; } print("$filename:$linenum: ") if ($expand_label); print($line) if ($expand); # Split the line into tokens $line =~ /^\s*/g; if ($expr_level) { while ($expr_level && $line =~ /\G(.*?)([\(\)])(\s*)/cg) { $expr_level += ($2 eq '(') ? 1 : -1; $expr .= $1 . $2; $expr .= $3 if ($expr_level); } if ($expr_level) { $expr .= $1 if ($line =~ /\G(.*?)\s*$/cg); next; } push @$tokens, $expr; } while ($line =~ /\G((?:\/(?:dts-v1|plugin|memreserve|bits|delete-node|delete-property)\/)|&[a-zA-Z_][a-zA-Z0-9_]*|[a-zA-Z_][a-zA-Z0-9_]*:|[-a-zA-Z0-9,._+#@]+|\(|"(?:[^\\"]|\\.)*"|'(?:[^']|\\.)*'|\/\/|\/\*|[\/{};=<>,\[\]])\s*/cg) { my $tok = $1; if ($tok eq '//') { $line = ''; last; } elsif ($tok eq '/*') { if ($line !~ /\G.*?\*\/\s*/cg) { $in_comment = 1; $line = ''; last; } next; } elsif ($tok eq '(') { $expr_level = 1; $expr = '('; while ($expr_level && $line =~ /\G(.*?)([\(\)])(\s*)/cg) { $expr_level += ($2 eq '(') ? 1 : -1; $expr .= $1 . $2; $expr .= $3 if ($expr_level); } if ($expr_level) { $expr .= $1 if ($line =~ /\G(.*?)\s*$/cg); last; } $tok = $expr; } if ($tok =~ /\b(\w+)\b/) { my $sym = $1; my $newsym = $defines->{$sym}; if (defined($newsym)) { printf("['%s' -> '%s']\n", $sym, $newsym) if ($trace_parse); $tok =~ s/\b$sym\b/$newsym/; } } push @$tokens, $tok; } if ($line !~ /\G[\r\n]*$/cg) { $line = substr($line, pos($line)); die "* Bad token at '$line'\n"; } } close($fh); print("#### End of '$filename'\n") if ($expand && !$expand_label); return $tokens; } sub match { my ($state, $match) = @_; my $next = get_head($state); print("[match '$match' @ $state->[1]]\n") if ($trace_parse); die "* Unexpected token '$next' - expected '$match'\n" if ($next ne $match); return get_next($state); } sub get_next { my ($state) = @_; $state->[1]++; return get_head($state); } sub get_head { my ($state) = @_; my $head = ${$state->[0]}[$state->[1]]; while (ref $head) { if ($head->[0] eq '/file/') { my $file = $head->[1]; $state->[2] = $file; print("[file $file]\n") if ($trace_parse); } else { die "* Unknown metadata '$head->[0]'\n"; } $head = get_next($state); } return $head; } sub remove_node { my ($node) = @_; my $parent = $node->[4]; printf("[ remove_node %s ]\n", $node->[0]) if ($trace_tree); return if (!$parent); $node->[4] = undef; # Find the node in the parent my $found; for (my $i = 0; $i < @{$parent->[2]}; $i++) { if ($parent->[2]->[$i] == $node) { $found = $i; last; } } die "* Internal error - wrong parent/missing child\n" if (!defined $found); # Remove from the parent splice(@{$parent->[2]}, $found, 1); } sub delete_node { my ($node) = @_; my $found; return if (!$node); printf("[ delete node %s ]\n", $node->[0]) if ($trace_tree); remove_node($node); # Delete all labels attached to the node foreach my $label (get_labels($node)) { map_del($cur_dt->{'labels'}, $label); } # Delete all label references from the properties foreach my $prop (get_props($node)) { adj_val_refs(-1, @$prop[1..$#$prop]); } # Delete all subnodes while (@{$node->[2]}) { delete_node($node->[2]->[0]); } return 1; } sub relabel_node { my ($node, $transform, $depth) = @_; # Properties foreach my $prop (get_props($node)) { if ($depth > 0) { for (my $i = 1; $i < @$prop; $i++) { my $chunk = $prop->[$i]; if ($chunk->[0] eq '<') { foreach my $term (@{$chunk->[2]}) { if ($term =~ /^&(.*)/) { my $newlabel = $transform->{$1}; if ($newlabel) { adj_ref(-1, $1); adj_ref(1, $newlabel); $term = '&'.$newlabel; } } } } elsif ($chunk->[0] eq '&') { my $newlabel = $transform->{$chunk->[1]}; if ($newlabel) { adj_ref(-1, $1); adj_ref(1, $newlabel); $chunk->[1] = $newlabel; } } } } } # Sub-nodes foreach my $subnode (get_children($node)) { relabel_node($subnode, $transform, $depth + 1); } } sub apply_node { my ($base, $dst, $src) = @_; # Properties foreach my $prop (get_props($src)) { apply_prop($dst, @$prop); } # Labels foreach my $label (get_labels($src)) { add_label($base, $dst, $label, $src); # Move, not copy } # Sub-nodes foreach my $subsrc (get_children($src)) { my $subdst = get_child($dst, $subsrc->[0]); if ($subdst && !$force) { die "* Subnode $subsrc->[0] already exists\n"; } else { $subdst = add_node($dst, $subsrc->[0]); } apply_node($base, $subdst, $subsrc); } } sub search_path { my ($fname) = @_; return $fname if ($branch && system("git cat-file -e $branch:./$fname") == 0); return $fname if (-r $fname); return undef; } sub new_node { my ($name) = @_; return [ $name, [], [], [] ]; } sub add_node { my ($parent, $name) = @_; my $node = (ref $name) ? $name : new_node($name); printf("[ add_node %s -> %s ]\n", $node->[0] || "?", $parent ? $parent->[0] : "-") if ($trace_tree); $node->[4] = $parent; if ($parent) { $node->[5] = $parent->[5] + 1; push @{$parent->[2]}, $node; } else { die "* Invalid root node '$name'\n" if ($name ne '/'); $node->[5] = 0; $cur_dt->{'root'} = $node; } return $node; } sub get_node { my ($dt, $path) = @_; my $node = $dt->{'root'}; if ($path =~ s/^([^\/]+)(\/|$)/\//) { $node = resolve_alias($dt, $1); } return $node if ($path eq '/'); while ($node && $path =~ /\G\/([-a-zA-Z0-9,._+#@]+)/g) { my $name = $1; $node = get_child($node, $name); } return $node; } sub is_node_empty { my ($node) = @_; return !get_children($node) && !get_props($node); } sub get_child { my ($node, $name) = @_; if ($node) { foreach my $child (@{$node->[2]}) { return $child if (($child->[0] eq $name) || ($name !~ /@/ && $child->[0] =~ /^$name@/)); } } else { return $cur_dt->{'root'} if ($name eq '/'); } return undef; } sub by_addr { my $a_addr = ($a->[0] =~ /@(.*)$/) ? hex($1) : undef; my $b_addr = ($b->[0] =~ /@(.*)$/) ? hex($1) : undef; return $a_addr <=> $b_addr if (defined $a_addr && defined $b_addr); return -1 if (defined $a_addr); return 1 if (defined $b_addr); return $a->[0] cmp $b->[0]; } sub get_children { my ($node) = @_; return sort by_addr (@{$node->[2]}) if ($sort); return (@{$node->[2]}); } sub get_fragments { my ($ov) = @_; my @fragments; foreach my $child (get_children($ov->{'root'})) { push @fragments, $child if ($child->[0] =~ /^fragment[@-](\d+)$/); } return @fragments; } sub renumber_fragments { my ($ov, $offset) = @_; my @fragments; my @remap; my $count = 0; my $overrides; foreach my $child (get_children($ov->{'root'})) { if ($child->[0] =~ /^fragment([@-])(\d+)$/) { my ($sep,$num) = ($1,$2); $remap[$num] = $count + $offset; $child->[0] = sprintf('fragment%s%d', $sep, $count + $offset); push @fragments, $child; $count++; } elsif ($child->[0] eq '__overrides__') { $overrides = $child; } } $ov->{'frag_count'} = $count; return if (!$overrides); foreach my $ovr (@{$overrides->[1]}) { for (my $pos = 1; ($pos + 1) < @$ovr; $pos++) { if ((get_label_ref($ovr->[$pos]) // '') eq '0') { $pos++; while ($ovr->[$pos]->[1] =~ /\G[=!+-](\d+)/g) { die ("* override '$ovr->[0]}' references missing fragment $1\n") if (!defined $remap[$1]); } $ovr->[$pos]->[1] =~ s/\G([=!+-])(\d+)/$1.$remap[$2]/eg; } } } } sub node_path { my ($node) = @_; return '/' if ($node->[0] eq '/'); my $parent_path = node_path($node->[4]); $parent_path = '' if ($parent_path eq "/"); return $parent_path.'/'.$node->[0]; } sub get_prop_string { my ($node, $name) = @_; my $prop = get_prop($node, $name); return undef if (!$prop); return undef if (@$prop != 2); return undef if ($prop->[1]->[0] ne '"'); return $prop->[1]->[1]; } sub get_prop { my ($node, $name) = @_; foreach my $prop (@{$node->[1]}) { return $prop if ($prop->[0] eq $name); } return undef; } sub get_props { my ($node) = @_; return sort { $a->[0] cmp $b->[0] } (@{$node->[1]}) if ($sort); return (@{$node->[1]}); } sub add_prop { my ($node, $name, @vals) = @_; my $new = [ $name, @vals ]; push @{$node->[1]}, $new; return $new; } sub set_prop { my ($node, $name, @vals) = @_; $DB::single = 1 if ($name eq $trace_prop); adj_val_refs(1, @vals); foreach my $prop (@{$node->[1]}) { if ($prop->[0] eq $name) { adj_val_refs(-1, @$prop[1..$#$prop]); splice(@$prop, 1, @$prop - 1, @vals); return $prop; } } return add_prop($node, $name, @vals); } sub apply_prop { my ($node, $name, @vals) = @_; if ($name eq 'status') { @vals = (['"', boolean_value($vals[0][1]) ? 'okay' : 'disabled']); } elsif ($name eq 'bootargs') { # Concatenate bootargs @vals = (['"', get_prop($node, $name)->[1][1] . ' ' . $vals[0][1]]); } return set_prop($node, $name, @vals); } sub delete_prop { my ($node, $name) = @_; $DB::single = 1 if ($name eq $trace_prop); for (my $i = 0; $i < @{$node->[1]}; $i++) { my $prop = $node->[1]->[$i]; if ($prop->[0] eq $name) { adj_val_refs(-1, @$prop[1..$#$prop]); return splice(@{$node->[1]}, $i, 1); } } return undef; } sub find_prop_chunk { my ($node, $propname, $offset, $size, $ovrname, $create) = @_; my $chunk; my $prop = get_prop($node, $propname); if (!$prop && $create) { $prop = set_prop($node, $propname, [ '<', $size, [] ]); } return (undef, 0) if (!$prop); my $pos = 0; for (my $i = 1; $i < @$prop; $i++) { $chunk = $prop->[$i]; my $type = $chunk->[0]; my $end; if ($type eq '"') { $end = $pos + length($chunk->[1]) + 1; } elsif ($type eq '[') { $end = $pos + @{$chunk->[2]}; } else { $end = $pos + $chunk->[1] * @{$chunk->[2]}; } last if ($offset < $end); $pos = $end; } if (!$chunk && $create) { $chunk = [ '<', $size, [] ]; push @$prop, $chunk; } $offset -= $pos; die "* Unaligned override '$ovrname', property $prop\n" if ($offset % $size); return ($chunk, $offset / $size); } sub get_labels { my ($node) = @_; return sort { $a cmp $b } (@{$node->[3]}) if ($sort); return (@{$node->[3]}); } sub integer_value { my ($value, $size) = @_; # The following line should say '8=>0xffffffffffffffff', but this upsets # builds of Perl with ivsize=4. This won't give the correct result for # large values but Perl will already have rejected them due to integer # overflow. my %masks = (1=>0xff, 2=>0xffff, 4=>0xffffffff, 8=>-1); return undef if (!defined $value); if ($value =~ /^(y|yes|on|true|down)?$/) { return 1; } elsif ($value =~ /^(n|no|off|false|none)$/) { return 0; } elsif ($value =~ /^up$/) { return 2; } elsif ($value =~ /^&/) { die "* Label '$value' used as non-32-bit integer\n" if ($size != 4); return $value; } elsif ($value =~ /^[0-9]/) { my $mask = $masks{$size}; die "* Bad size '$size' for integer\n" if (!$mask); return eval($value) & $mask; } elsif ($value =~ /^[A-Z][A-Z0-9_]+$/) { # Assume it's a valid define, e.g. from a dt-bindings file return $value; } elsif ($value =~ /^\(.+\)$/) { # Assume it's a valid expression return $value; } die "* Bad integer value '$value'\n"; } sub boolean_value { my ($value) = @_; if ($value =~ /^(y|yes|on|true|okay)?$/) { return 1; } elsif ($value =~ /^(n|no|off|false|disabled)$/) { return 0; } elsif ($value !~ /^[0-9]/) { die "* Bad boolean value '$value'\n"; } return $value != 0; } sub byte_array_value { my ($value) = @_; my $arr = []; foreach my $val (split(/[: ]/, $value)) { die "* invalid bytestring at '$val'\n" if ($val !~ /^([0-9a-f][0-9a-f])*$/i); while ($val =~ m/(..)/g) { push @$arr, hex($1); } } return $arr; } sub byte_array_string { my ($arr) = @_; my $str = ""; foreach my $val (@$arr) { $str .= ' ' if ($str); $str .= sprintf("%02x", $val); } return $str; } sub set_add { my ($set, $val) = @_; for (my $i = 0; $i < @$set; $i++) { return if ((ref $val && $set->[$i] == $val) || ($set->[$i] eq $val)); } push @$set, $val; } sub set_vals { my ($set) = @_; return @$set; } sub set_empty { my ($set) = @_; return @$set == 0; } sub map_add { my ($map, $name, $val) = @_; $map->{$name} = $val; } sub map_del { my ($map, $name) = @_; delete $map->{$name}; } sub map_find { my ($map, $name) = @_; return $map->{$name}; } sub get_int { my ($state) = @_; my $head = get_head($state); return undef if ($head !~ /^[0-9]/); get_next($state); return $head; } sub adj_val_refs { my ($inc, @vals) = @_; foreach my $val (@vals) { if ($val->[0] eq '&') { adj_ref($inc, $val->[1]); } elsif ($val->[0] eq '<') { foreach my $elem (@{$val->[2]}) { adj_ref($inc, $1) if ($elem =~ /^&(.*)/); } } } } sub adj_ref { my ($inc, $label) = @_; return if (!$cur_dt); $DB::single = 1 if ($inc < 0 && $cur_dt->{'refcount'}->{$label} == 0); $cur_dt->{'refcount'}->{$label} += $inc; printf ("[ ref %s -> %s ]\n", $label, $cur_dt->{'refcount'}->{$label}) if ($label eq $trace_label); } sub usage { print STDERR ("Usage: ovmerge \n"); print STDERR (" where is the name of an overlay, optionally followed by\n"); print STDERR (" a comma-separated list of parameters, each with optional '='\n"); print STDERR (" assignments. The presence of any parameters, or a comma followed by\n"); print STDERR (" no parameters, removes the parameter declarations from the merged\n"); print STDERR (" overlay to avoid a potential name clash.\n"); print STDERR (" and are any of:\n"); print STDERR (" -b Read files from specified git branch\n"); print STDERR (" -c Include 'redo' comment with command line (c.f. '-r')\n"); print STDERR (" -e Expand mode - list non-skipped lines in order of inclusion\n"); print STDERR (" -f Force some errors to be ignored\n"); print STDERR (" -h Display this help info\n"); print STDERR (" -i Show include hierarchy for each file\n"); print STDERR (" -l Like expand mode, but labels each line with source file\n"); print STDERR (" -n No .dts file header (just parsing .dtsi files)\n"); print STDERR (" -N Don't renumber overlay fragments (not guaranteed to work)\n"); print STDERR (" -p Emulate Pi firmware manipulation\n"); print STDERR (" -q Query mode (no output, just the success/failure return code)\n"); print STDERR (" -r Redo command comment in named files (c.f. '-c')\n"); print STDERR (" -s Sort nodes and properties (for easy comparison)\n"); print STDERR (" -S Instead of tabs, use 'n' spaces for indentation\n"); print STDERR (" -t Trace the tree changes\n"); print STDERR (" -T Trace the parsing process\n"); print STDERR (" -w Show warnings\n"); exit(1); } raspi-utils-20250514/pinctrl/000077500000000000000000000000001501106437300157055ustar00rootroot00000000000000raspi-utils-20250514/pinctrl/CMakeLists.txt000066400000000000000000000016221501106437300204460ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -pedantic") #set project name project(pinctrl) add_compile_definitions(LIBRARY_BUILD=1) add_library(gpiolib gpiolib.c util.c library_gpiochips.c gpiochip_bcm2835.c gpiochip_bcm2712.c gpiochip_rp1.c) target_sources(gpiolib PUBLIC gpiolib.h) set_target_properties(gpiolib PROPERTIES PUBLIC_HEADER gpiolib.h) set_target_properties(gpiolib PROPERTIES SOVERSION 0) #add executables add_executable(pinctrl pinctrl.c) target_link_libraries(pinctrl gpiolib) install(TARGETS pinctrl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS gpiolib ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(FILES pinctrl-completion.bash RENAME pinctrl DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/bash-completion/completions") raspi-utils-20250514/pinctrl/README.md000066400000000000000000000033031501106437300171630ustar00rootroot00000000000000 # pinctrl pinctrl is a more powerful replacement for raspi-gpio, a tool for displaying and modifying the GPIO and pin muxing state of a system. It accesses the hardware directly, bypassing the kernel drivers, and as such requires root privilege (run with "sudo"). The improvements over raspi-gpio include: * GPIO controllers are detected at runtime from Device Tree. * GPIOs can be referred to by name or number. * Pin mode (-p) switches the UI to be in terms of 40-way header pin numbers. * The "poll" command causes it to constantly monitor the specified pins, displaying any level changes it sees. For slow signals (up to a few hundred kHz) it can act as a basic logic analyser. * The "get" and "set" keywords are optional in most cases. * Splitting into a general gpiolib library and a separate client application allows new applications to be added easily. **Build Instructions** Install the prerequisites with "sudo apt install cmake" - you need at least version 3.10 of cmake. Run the following commands, either here or in the top-level directory to build and install everything: - *cmake .* N.B. Use *cmake -DBUILD_SHARED_LIBS=1 .* to build gpiolib as a shared (as opposed to static) library. - *make* - *sudo make install* **Usage** * `sudo pinctrl` (Display the state of all recognised GPIOs) * `sudo pinctrl -p` (Show the state of the 40-way header pins) * `sudo pinctrl 4,6 op dl` (Make GPIOs 4 and 6 outputs, driving low) * `sudo pinctrl poll BT_CTS,BT_RTS` (Monitor the levels of the Bluetooth flow control signals) * `pinctrl funcs 9-11` (List the available alternate functions on GPIOs 9, 10 and 11) * `pinctrl help` (Show the full usage guide) raspi-utils-20250514/pinctrl/gpiochip.h000066400000000000000000000037041501106437300176640ustar00rootroot00000000000000#ifndef GPIOCHIP_H #define GPIOCHIP_H #include "gpiolib.h" #if LIBRARY_BUILD #define DECLARE_GPIO_CHIP(name, compatible, iface, size, data) \ const GPIO_CHIP_T name ## _chip = { #name, compatible, iface, size, data } #else #define DECLARE_GPIO_CHIP(name, compatible, iface, size, data) \ const GPIO_CHIP_T name ## _chip = { #name, compatible, iface, size, data }; \ const GPIO_CHIP_T *name ## _chip_ptr __attribute__ ((section ("gpiochips"))) __attribute__ ((used)) = &(name ## _chip) #endif typedef struct GPIO_CHIP_INTERFACE_ GPIO_CHIP_INTERFACE_T; typedef struct GPIO_CHIP_ { const char *name; const char *compatible; const GPIO_CHIP_INTERFACE_T *interface; int size; uintptr_t data; } GPIO_CHIP_T; struct GPIO_CHIP_INTERFACE_ { void * (*gpio_create_instance)(const GPIO_CHIP_T *chip, const char *dtnode); int (*gpio_count)(void *priv); void * (*gpio_probe_instance)(void *priv, volatile uint32_t *base); GPIO_FSEL_T (*gpio_get_fsel)(void *priv, uint32_t gpio); void (*gpio_set_fsel)(void *priv, uint32_t gpio, const GPIO_FSEL_T func); void (*gpio_set_drive)(void *priv, uint32_t gpio, GPIO_DRIVE_T drv); void (*gpio_set_dir)(void *priv, uint32_t gpio, GPIO_DIR_T dir); GPIO_DIR_T (*gpio_get_dir)(void *priv, uint32_t gpio); int (*gpio_get_level)(void *priv, uint32_t gpio); /* The actual level observed */ GPIO_DRIVE_T (*gpio_get_drive)(void *priv, uint32_t gpio); /* What it is being driven as */ GPIO_PULL_T (*gpio_get_pull)(void *priv, uint32_t gpio); void (*gpio_set_pull)(void *priv, uint32_t gpio, GPIO_PULL_T pull); const char * (*gpio_get_name)(void *priv, uint32_t gpio); const char * (*gpio_get_fsel_name)(void *priv, uint32_t gpio, GPIO_FSEL_T fsel); }; #if LIBRARY_BUILD extern const GPIO_CHIP_T *const library_gpiochips[]; extern const int library_gpiochips_count; #else extern const GPIO_CHIP_T *__start_gpiochips; extern const GPIO_CHIP_T *__stop_gpiochips; #endif #endif raspi-utils-20250514/pinctrl/gpiochip_bcm2712.c000066400000000000000000001120561501106437300210150ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "gpiochip.h" #include "util.h" /* 2712 definitions */ #define BCM2712_GIO_DATA 0x04 #define BCM2712_GIO_IODIR 0x08 #define BCM2712_PAD_PULL_OFF 0 #define BCM2712_PAD_PULL_DOWN 1 #define BCM2712_PAD_PULL_UP 2 #define BCM2712_MAX_INSTANCES 2 #define BCM2712_FSEL_COUNT 9 #define FLAGS_AON 1 #define FLAGS_C0 2 #define FLAGS_D0 4 #define FLAGS_GPIO 8 #define FLAGS_PINCTRL 16 struct bcm2712_inst { volatile uint32_t *gpio_base; volatile uint32_t *pinmux_base; unsigned pad_offset; uint32_t *bank_widths; unsigned flags; unsigned num_gpios; unsigned num_banks; }; static unsigned num_instances; static struct bcm2712_inst bcm2712_instances[BCM2712_MAX_INSTANCES] = { 0 }; static unsigned shared_flags; static const char *bcm2712_c0_gpio_alt_names[][BCM2712_FSEL_COUNT - 1] = { { "BSC_M3_SDA" , "VC_SDA0" , "GPCLK0" , "ENET0_LINK" , "VC_PWM1_0" , "VC_SPI0_CE1_N" , "IR_IN" , }, // 0 { "BSC_M3_SCL" , "VC_SCL0" , "GPCLK1" , "ENET0_ACTIVITY" , "VC_PWM1_1" , "SR_EDM_SENSE" , "VC_SPI0_CE0_N" , "VC_TXD3" , }, // 1 { "PDM_CLK" , "I2S_CLK0_IN" , "GPCLK2" , "VC_SPI4_CE1_N" , "PKT_CLK0" , "VC_SPI0_MISO" , "VC_RXD3" , }, // 2 { "PDM_DATA0" , "I2S_LR0_IN" , "VC_SPI4_CE0_N" , "PKT_SYNC0" , "VC_SPI0_MOSI" , "VC_CTS3" , }, // 3 { "PDM_DATA1" , "I2S_DATA0_IN" , "ARM_RTCK" , "VC_SPI4_MISO" , "PKT_DATA0" , "VC_SPI0_SCLK" , "VC_RTS3" , }, // 4 { "PDM_DATA2" , "VC_SCL3" , "ARM_TRST" , "SD_CARD_LED_E" , "VC_SPI4_MOSI" , "PKT_CLK1" , "VC_PCM_CLK" , "VC_SDA5" , }, // 5 { "PDM_DATA3" , "VC_SDA3" , "ARM_TCK" , "SD_CARD_WPROT_E" , "VC_SPI4_SCLK" , "PKT_SYNC1" , "VC_PCM_FS" , "VC_SCL5" , }, // 6 { "I2S_CLK0_OUT" , "SPDIF_OUT" , "ARM_TDI" , "SD_CARD_PRES_E" , "VC_SDA3" , "ENET0_RGMII_START_STOP", "VC_PCM_DIN" , "VC_SPI4_CE1_N" , }, // 7 { "I2S_LR0_OUT" , "AUD_FS_CLK0" , "ARM_TMS" , "SD_CARD_VOLT_E" , "VC_SCL3" , "ENET0_MII_TX_ERR" , "VC_PCM_DOUT" , "VC_SPI4_CE0_N" , }, // 8 { "I2S_DATA0_OUT" , "AUD_FS_CLK0" , "ARM_TDO" , "SD_CARD_PWR0_E" , "ENET0_MII_RX_ERR" , "SD_CARD_VOLT_C" , "VC_SPI4_SCLK" , }, // 9 { "BSC_M3_SCL" , "MTSIF_DATA4_ALT1" , "I2S_CLK0_IN" , "I2S_CLK0_OUT" , "VC_SPI5_CE1_N" , "ENET0_MII_CRS" , "SD_CARD_PWR0_C" , "VC_SPI4_MOSI" , }, // 10 { "BSC_M3_SDA" , "MTSIF_DATA5_ALT1" , "I2S_LR0_IN" , "I2S_LR0_OUT" , "VC_SPI5_CE0_N" , "ENET0_MII_COL" , "SD_CARD_PRES_C" , "VC_SPI4_MISO" , }, // 11 { "SPI_S_SS0B" , "MTSIF_DATA6_ALT1" , "I2S_DATA0_IN" , "I2S_DATA0_OUT" , "VC_SPI5_MISO" , "VC_I2CSL_MOSI" , "SD0_CLK" , "SD_CARD_VOLT_D", }, // 12 { "SPI_S_MISO" , "MTSIF_DATA7_ALT1" , "I2S_DATA1_OUT" , "USB_VBUS_PRESENT" , "VC_SPI5_MOSI" , "VC_I2CSL_CE_N" , "SD0_CMD" , "SD_CARD_PWR0_D", }, // 13 { "SPI_S_MOSI_OR_BSC_S_SDA", "VC_I2CSL_SCL_SCLK", "ENET0_RGMII_RX_OK", "ARM_TCK" , "VC_SPI5_SCLK" , "VC_PWM0_0" , "VC_SDA4" , "SD_CARD_PRES_D", }, // 14 { "SPI_S_SCK_OR_BSC_S_SCL" , "VC_I2CSL_SDA_MISO", "VC_SPI3_CE1_N" , "ARM_TMS" , "VC_PWM0_1" , "VC_SCL4" , "GPCLK0" , }, // 15 { "SD_CARD_PRES_B" , "I2S_CLK0_OUT" , "VC_SPI3_CE0_N" , "I2S_CLK0_IN" , "SD0_DAT0" , "ENET0_RGMII_MDIO" , "GPCLK1" , }, // 16 { "SD_CARD_WPROT_B" , "I2S_LR0_OUT" , "VC_SPI3_MISO" , "I2S_LR0_IN" , "EXT_SC_CLK" , "SD0_DAT1" , "ENET0_RGMII_MDC", "GPCLK2" , }, // 17 { "SD_CARD_LED_B" , "I2S_DATA0_OUT" , "VC_SPI3_MOSI" , "I2S_DATA0_IN" , "SD0_DAT2" , "ENET0_RGMII_IRQ" , "VC_PWM1_0" , }, // 18 { "SD_CARD_VOLT_B" , "USB_PWRFLT" , "VC_SPI3_SCLK" , "PKT_DATA1" , "SPDIF_OUT" , "SD0_DAT3" , "IR_IN" , "VC_PWM1_1" , }, // 19 { "SD_CARD_PWR0_B" , "UUI_TXD" , "VC_TXD0" , "ARM_TMS" , "UART_TXD_2" , "USB_PWRON" , "VC_PCM_CLK" , "VC_TXD4" , }, // 20 { "USB_PWRFLT" , "UUI_RXD" , "VC_RXD0" , "ARM_TCK" , "UART_RXD_2" , "SD_CARD_VOLT_B" , "VC_PCM_FS" , "VC_RXD4" , }, // 21 { "USB_PWRON" , "ENET0_LINK" , "VC_CTS0" , "MTSIF_ATS_RST" , "UART_RTS_2" , "USB_VBUS_PRESENT" , "VC_PCM_DIN" , "VC_SDA5" , }, // 22 { "USB_VBUS_PRESENT" , "ENET0_ACTIVITY" , "VC_RTS0" , "MTSIF_ATS_INC" , "UART_CTS_2" , "I2S_DATA2_OUT" , "VC_PCM_DOUT" , "VC_SCL5" , }, // 23 { "MTSIF_ATS_RST" , "PKT_CLK0" , "UART_RTS_0" , "ENET0_RGMII_RX_CLK", "ENET0_RGMII_START_STOP", "VC_SDA4" , "VC_TXD3" , }, // 24 { "MTSIF_ATS_INC" , "PKT_SYNC0" , "SC0_CLK" , "UART_CTS_0" , "ENET0_RGMII_RX_EN_CTL" , "ENET0_RGMII_RX_OK" , "VC_SCL4" , "VC_RXD3" , }, // 25 { "MTSIF_DATA1" , "PKT_DATA0" , "SC0_IO" , "UART_TXD_0" , "ENET0_RGMII_RXD_00" , "VC_TXD4" , "VC_SPI5_CE0_N" , }, // 26 { "MTSIF_DATA2" , "PKT_CLK1" , "SC0_AUX1" , "UART_RXD_0" , "ENET0_RGMII_RXD_01" , "VC_RXD4" , "VC_SPI5_SCLK" , }, // 27 { "MTSIF_CLK" , "PKT_SYNC1" , "SC0_AUX2" , "ENET0_RGMII_RXD_02", "VC_CTS4" , "VC_SPI5_MOSI" , }, // 28 { "MTSIF_DATA0" , "PKT_DATA1" , "SC0_PRES" , "ENET0_RGMII_RXD_03", "VC_RTS4" , "VC_SPI5_MISO" , }, // 29 { "MTSIF_SYNC" , "PKT_CLK2" , "SC0_RST" , "SD2_CLK" , "ENET0_RGMII_TX_CLK" , "GPCLK0" , "VC_PWM0_0" , }, // 30 { "MTSIF_DATA3" , "PKT_SYNC2" , "SC0_VCC" , "SD2_CMD" , "ENET0_RGMII_TX_EN_CTL" , "VC_SPI3_CE1_N" , "VC_PWM0_1" , }, // 31 { "MTSIF_DATA4" , "PKT_DATA2" , "SC0_VPP" , "SD2_DAT0" , "ENET0_RGMII_TXD_00" , "VC_SPI3_CE0_N" , "VC_TXD3" , }, // 32 { "MTSIF_DATA5" , "PKT_CLK3" , "SD2_DAT1" , "ENET0_RGMII_TXD_01", "VC_SPI3_SCLK" , "VC_RXD3" , }, // 33 { "MTSIF_DATA6" , "PKT_SYNC3" , "EXT_SC_CLK" , "SD2_DAT2" , "ENET0_RGMII_TXD_02" , "VC_SPI3_MOSI" , "VC_SDA5" , }, // 34 { "MTSIF_DATA7" , "PKT_DATA3" , "SD2_DAT3" , "ENET0_RGMII_TXD_03", "VC_SPI3_MISO" , "VC_SCL5" , }, // 35 { "SD0_CLK" , "MTSIF_ATS_RST" , "SC0_RST" , "I2S_DATA1_IN" , "VC_TXD3" , "VC_TXD2" , }, // 36 { "SD0_CMD" , "MTSIF_ATS_INC" , "SC0_VCC" , "VC_SPI0_CE1_N" , "I2S_DATA2_IN" , "VC_RXD3" , "VC_RXD2" , }, // 37 { "SD0_DAT0" , "MTSIF_DATA4_ALT" , "SC0_VPP" , "VC_SPI0_CE0_N" , "I2S_DATA3_IN" , "VC_CTS3" , "VC_RTS2" , }, // 38 { "SD0_DAT1" , "MTSIF_DATA5_ALT" , "SC0_CLK" , "VC_SPI0_MISO" , "VC_RTS3" , "VC_CTS2" , }, // 39 { "SD0_DAT2" , "MTSIF_DATA6_ALT" , "SC0_IO" , "VC_SPI0_MOSI" , "BSC_M3_SDA" , }, // 40 { "SD0_DAT3" , "MTSIF_DATA7_ALT" , "SC0_PRES" , "VC_SPI0_SCLK" , "BSC_M3_SCL" , }, // 41 { "VC_SPI0_CE1_N" , "MTSIF_CLK_ALT" , "VC_SDA0" , "SD_CARD_PRES_A" , "MTSIF_CLK_ALT1" , "ARM_TRST" , "PDM_CLK" , "SPI_M_SS1B" , }, // 42 { "VC_SPI0_CE0_N" , "MTSIF_SYNC_ALT" , "VC_SCL0" , "SD_CARD_PWR0_A" , "MTSIF_SYNC_ALT1" , "ARM_RTCK" , "PDM_DATA0" , "SPI_M_SS0B" , }, // 43 { "VC_SPI0_MISO" , "MTSIF_DATA0_ALT" , "ENET0_LINK" , "SD_CARD_LED_A" , "MTSIF_DATA0_ALT1" , "ARM_TDO" , "PDM_DATA1" , "SPI_M_MISO" , }, // 44 { "VC_SPI0_MOSI" , "MTSIF_DATA1_ALT" , "ENET0_ACTIVITY" , "SD_CARD_VOLT_A" , "MTSIF_DATA1_ALT1" , "ARM_TCK" , "PDM_DATA2" , "SPI_M_MOSI" , }, // 45 { "VC_SPI0_SCLK" , "MTSIF_DATA2_ALT" , "SD_CARD_WPROT_A" , "MTSIF_DATA2_ALT1" , "ARM_TDI" , "PDM_DATA3" , "SPI_M_SCK" , }, // 46 { "ENET0_ACTIVITY" , "MTSIF_DATA3_ALT" , "I2S_DATA3_OUT" , "MTSIF_DATA3_ALT1" , "ARM_TMS" , }, // 47 { "SC0_RST" , "USB_PWRFLT" , "SPDIF_OUT" , "MTSIF_ATS_RST" , }, // 48 { "SC0_VCC" , "USB_PWRON" , "AUD_FS_CLK0" , "MTSIF_ATS_INC" , }, // 49 { "SC0_VPP" , "USB_VBUS_PRESENT" , "SC0_AUX1" , }, // 50 { "SC0_CLK" , "ENET0_LINK" , "SC0_AUX2" , "SR_EDM_SENSE" , }, // 51 { "SC0_IO" , "ENET0_ACTIVITY" , "VC_PWM1_1" , }, // 52 { "SC0_PRES" , "ENET0_RGMII_RX_OK", "EXT_SC_CLK" , }, // 53 }; static const char *bcm2712_d0_gpio_alt_names[][BCM2712_FSEL_COUNT - 1] = { { "" }, // 0 { "VC_SCL0" , "USB_PWRFLT" , "GPCLK0" , "SD_CARD_LED_E" , "VC_SPI3_CE1_N" , "SR_EDM_SENSE" , "VC_SPI0_CE0_N" , "VC_TXD0" , }, // 1 { "VC_SDA0" , "USB_PWRON" , "GPCLK1" , "SD_CARD_WPROT_E" , "VC_SPI3_CE0_N" , "CLK_OBSERVE" , "VC_SPI0_MISO" , "VC_RXD0" , }, // 2 { "VC_SCL3" , "USB_VBUS_PRESENT" , "GPCLK2" , "SD_CARD_PRES_E" , "VC_SPI3_MISO" , "VC_SPI0_MOSI" , "VC_CTS0" , }, // 3 { "VC_SDA3" , "VC_PWM1_1" , "VC_SPI3_CE0_N" , "SD_CARD_VOLT_E" , "VC_SPI3_MOSI" , "VC_SPI0_SCLK" , "VC_RTS0" , }, // 4 { "" }, // 5 { "" }, // 6 { "" }, // 7 { "" }, // 8 { "" }, // 9 { "BSC_M3_SCL" , "VC_PWM1_0" , "VC_SPI3_CE1_N" , "SD_CARD_PWR0_E" , "VC_SPI3_SCLK" , "GPCLK0" , }, // 10 { "BSC_M3_SDA" , "VC_SPI3_MISO" , "CLK_OBSERVE" , "SD_CARD_PRES_C" , "GPCLK1" , }, // 11 { "SPI_S_SS0B" , "VC_SPI3_MOSI" , "SD_CARD_PWR0_C" , "SD_CARD_VOLT_D" , }, // 12 { "SPI_S_MISO" , "VC_SPI3_SCLK" , "SD_CARD_PRES_C" , "SD_CARD_PWR0_D" , }, // 13 { "SPI_S_MOSI_OR_BSC_S_SDA", "UUI_TXD" , "ARM_TCK" , "VC_PWM0_0" , "VC_SDA0" , "SD_CARD_PRES_D" , }, // 14 { "SPI_S_SCK_OR_BSC_S_SCL" , "UUI_RXD" , "ARM_TMS" , "VC_PWM0_1" , "VC_SCL0" , "GPCLK0" , }, // 15 { "" }, // 16 { "" }, // 17 { "SD_CARD_PRES_F" , "VC_PWM1_0" , }, // 18 { "SD_CARD_PWR0_F" , "USB_PWRFLT" , "VC_PWM1_1" , }, // 19 { "VC_SDA3" , "UUI_TXD" , "VC_TXD0" , "ARM_TMS" , "VC_TXD2" , }, // 20 { "VC_SCL3" , "UUI_RXD" , "VC_RXD0" , "ARM_TCK" , "VC_RXD2" , }, // 21 { "SD_CARD_PRES_F" , "VC_CTS0" , "VC_SDA3" , }, // 22 { "VC_RTS0" , "VC_SCL3" , }, // 23 { "SD_CARD_PRES_B" , "VC_SPI0_CE1_N" , "ARM_TRST" , "UART_RTS_0" , "USB_PWRFLT" , "VC_RTS2" , "VC_TXD0" , }, // 24 { "SD_CARD_WPROT_B" , "VC_SPI0_CE0_N" , "ARM_TCK" , "UART_CTS_0" , "USB_PWRON" , "VC_CTS2" , "VC_RXD0" , }, // 25 { "SD_CARD_LED_B" , "VC_SPI0_MISO" , "ARM_TDI" , "UART_TXD_0" , "USB_VBUS_PRESENT" , "VC_TXD2" , "VC_SPI0_CE0_N" , }, // 26 { "SD_CARD_VOLT_B" , "VC_SPI0_MOSI" , "ARM_TMS" , "UART_RXD_0" , "VC_RXD2" , "VC_SPI0_SCLK" , }, // 27 { "SD_CARD_PWR0_B" , "VC_SPI0_SCLK" , "ARM_TDO" , "VC_SDA0" , "VC_SPI0_MOSI" , }, // 28 { "ARM_RTCK" , "VC_SCL0" , "VC_SPI0_MISO" , }, // 29 { "SD2_CLK" , "GPCLK0" , "VC_PWM0_0" , }, // 30 { "SD2_CMD" , "VC_SPI3_CE1_N" , "VC_PWM0_1" , }, // 31 { "SD2_DAT0" , "VC_SPI3_CE0_N" , "VC_TXD3" , }, // 32 { "SD2_DAT1" , "VC_SPI3_SCLK" , "VC_RXD3" , }, // 33 { "SD2_DAT2" , "VC_SPI3_MOSI" , "VC_SDA5" , }, // 34 { "SD2_DAT3" , "VC_SPI3_MISO" , "VC_SCL5" , }, // 35 }; static const char *bcm2712_c0_aon_gpio_alt_names[][BCM2712_FSEL_COUNT - 1] = { { "IR_IN" , "VC_SPI0_CE1_N" , "VC_TXD3" , "VC_SDA3" , "TE0" , "VC_SDA0" , }, // 0 { "VC_PWM0_0" , "VC_SPI0_CE0_N" , "VC_RXD3" , "VC_SCL3" , "TE1" , "AON_PWM0" , "VC_SCL0" , "VC_PWM1_0" , }, // 1 { "VC_PWM0_1" , "VC_SPI0_MISO" , "VC_CTS3" , "CTL_HDMI_5V" , "FL0" , "AON_PWM1" , "IR_IN" , "VC_PWM1_1" , }, // 2 { "IR_IN" , "VC_SPI0_MOSI" , "VC_RTS3" , "AON_FP_4SEC_RESETB", "FL1" , "SD_CARD_VOLT_G" , "AON_GPCLK" , }, // 3 { "GPCLK0" , "VC_SPI0_SCLK" , "VC_I2CSL_SCL_SCLK", "AON_GPCLK" , "PM_LED_OUT" , "AON_PWM0" , "SD_CARD_PWR0_G", "VC_PWM0_0" , }, // 4 { "GPCLK1" , "IR_IN" , "VC_I2CSL_SDA_MISO", "CLK_OBSERVE" , "AON_PWM1" , "SD_CARD_PRES_G" , "VC_PWM0_1" , }, // 5 { "UART_TXD_1" , "VC_TXD4" , "GPCLK2" , "CTL_HDMI_5V" , "VC_TXD0" , "VC_SPI3_CE0_N" , }, // 6 { "UART_RXD_1" , "VC_RXD4" , "GPCLK0" , "AON_PWM0" , "VC_RXD0" , "VC_SPI3_SCLK" , }, // 7 { "UART_RTS_1" , "VC_RTS4" , "VC_I2CSL_MOSI" , "CTL_HDMI_5V" , "VC_RTS0" , "VC_SPI3_MOSI" , }, // 8 { "UART_CTS_1" , "VC_CTS4" , "VC_I2CSL_CE_N" , "AON_PWM1" , "VC_CTS0" , "VC_SPI3_MISO" , }, // 9 { "TSIO_CLK_OUT" , "CTL_HDMI_5V" , "SC0_AUX1" , "SPDIF_OUT" , "VC_SPI5_CE1_N", "USB_PWRFLT" , "AON_GPCLK" , "SD_CARD_VOLT_F", }, // 10 { "TSIO_DATA_IN" , "UART_RTS_0" , "SC0_AUX2" , "AUD_FS_CLK0" , "VC_SPI5_CE0_N", "USB_VBUS_PRESENT", "VC_RTS2" , "SD_CARD_PWR0_F", }, // 11 { "TSIO_DATA_OUT" , "UART_CTS_0" , "VC_RTS0" , "TSIO_VCTRL" , "VC_SPI5_MISO" , "USB_PWRON" , "VC_CTS2" , "SD_CARD_PRES_F", }, // 12 { "BSC_M1_SDA" , "UART_TXD_0" , "VC_TXD0" , "UUI_TXD" , "VC_SPI5_MOSI" , "ARM_TMS" , "VC_TXD2" , "VC_SDA3" , }, // 13 { "BSC_M1_SCL" , "UART_RXD_0" , "VC_RXD0" , "UUI_RXD" , "VC_SPI5_SCLK" , "ARM_TCK" , "VC_RXD2" , "VC_SCL3" , }, // 14 { "IR_IN" , "AON_FP_4SEC_RESETB", "VC_CTS0" , "PM_LED_OUT" , "CTL_HDMI_5V" , "AON_PWM0" , "AON_GPCLK" , }, // 15 { "AON_CPU_STANDBYB", "GPCLK0" , "PM_LED_OUT" , "CTL_HDMI_5V" , "VC_PWM0_0" , "USB_PWRON" , "AUD_FS_CLK0" , }, // 16 // Pad out the bank to 32 entries { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, // 17-23 { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, // 24-31 { "HDMI_TX0_BSC_SCL", "HDMI_TX0_AUTO_I2C_SCL", "BSC_M0_SCL", "VC_SCL0", }, // sgpio 0 { "HDMI_TX0_BSC_SDA", "HDMI_TX0_AUTO_I2C_SDA", "BSC_M0_SDA", "VC_SDA0", }, // sgpio 1 { "HDMI_TX1_BSC_SCL", "HDMI_TX1_AUTO_I2C_SCL", "BSC_M1_SCL", "VC_SCL4", "CTL_HDMI_5V", }, // sgpio 2 { "HDMI_TX1_BSC_SDA", "HDMI_TX1_AUTO_I2C_SDA", "BSC_M1_SDA", "VC_SDA4", }, // sgpio 3 { "AVS_PMU_BSC_SCL", "BSC_M2_SCL", "VC_SCL5", "CTL_HDMI_5V", }, // sgpio 4 { "AVS_PMU_BSC_SDA", "BSC_M2_SDA", "VC_SDA5", }, // sgpio 5 }; static const char *bcm2712_d0_aon_gpio_alt_names[][BCM2712_FSEL_COUNT - 1] = { { "IR_IN" , "VC_SPI0_CE1_N" , "VC_TXD0" , "VC_SDA3" , "UART_TXD_0" , "VC_SDA0" , }, // 0 { "VC_PWM0_0" , "VC_SPI0_CE0_N" , "VC_RXD0" , "VC_SCL3" , "UART_RXD_0" , "AON_PWM0" , "VC_SCL0" , "VC_PWM1_0" , }, // 1 { "VC_PWM0_1" , "VC_SPI0_MISO" , "VC_CTS0" , "CTL_HDMI_5V" , "UART_CTS_0" , "AON_PWM1" , "IR_IN" , "VC_PWM1_1" , }, // 2 { "IR_IN" , "VC_SPI0_MOSI" , "VC_RTS0" , "UART_RTS_0" , "SD_CARD_VOLT_G" , "AON_GPCLK" , }, // 3 { "GPCLK0" , "VC_SPI0_SCLK" , "PM_LED_OUT" , "AON_PWM0" , "SD_CARD_PWR0_G" , "VC_PWM0_0" , }, // 4 { "GPCLK1" , "IR_IN" , "AON_PWM1" , "SD_CARD_PRES_G" , "VC_PWM0_1" , }, // 5 { "UART_TXD_1" , "VC_TXD2" , "CTL_HDMI_5V" , "GPCLK2" , "VC_SPI3_CE0_N" , }, // 6 { "" }, // 7 { "UART_RTS_1" , "VC_RTS2" , "CTL_HDMI_5V" , "VC_SPI0_CE1_N" , "VC_SPI3_SCLK" , }, // 8 { "UART_CTS_1" , "VC_CTS2" , "VC_CTS0" , "AON_PWM1" , "VC_SPI0_CE0_N" , "VC_RTS2" , "VC_SPI3_MOSI" , }, // 9 { "" }, // 10 { "" }, // 11 { "UART_RXD_1" , "VC_RXD2" , "VC_RTS0" , "VC_SPI0_MISO" , "USB_PWRON" , "VC_CTS2" , "VC_SPI3_MISO" , }, // 12 { "BSC_M1_SDA" , "VC_TXD0" , "UUI_TXD" , "VC_SPI0_MOSI" , "ARM_TMS" , "VC_TXD2" , "VC_SDA3" , }, // 13 { "BSC_M1_SCL" , "AON_GPCLK" , "VC_RXD0" , "UUI_RXD" , "VC_SPI0_SCLK" , "ARM_TCK" , "VC_RXD2" , "VC_SCL3" , }, // 14 // Pad out the bank to 32 entries { "" }, // 15 { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, // 16-23 { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, // 24-31 { "HDMI_TX0_BSC_SCL", "HDMI_TX0_AUTO_I2C_SCL", "BSC_M0_SCL", "VC_SCL0", }, // sgpio 0 { "HDMI_TX0_BSC_SDA", "HDMI_TX0_AUTO_I2C_SDA", "BSC_M0_SDA", "VC_SDA0", }, // sgpio 1 { "HDMI_TX1_BSC_SCL", "HDMI_TX1_AUTO_I2C_SCL", "BSC_M1_SCL", "VC_SCL0", "CTL_HDMI_5V", }, // sgpio 2 { "HDMI_TX1_BSC_SDA", "HDMI_TX1_AUTO_I2C_SDA", "BSC_M1_SDA", "VC_SDA0", }, // sgpio 3 { "AVS_PMU_BSC_SCL", "BSC_M2_SCL", "VC_SCL3", "CTL_HDMI_5V", }, // sgpio 4 { "AVS_PMU_BSC_SDA", "BSC_M2_SDA", "VC_SDA3", }, // sgpio 5 }; static const int bcm2712_gpio_d0_to_c0[] = { -1, 0, 1, 2, 3, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 }; static const int bcm2712_gpio_aon_d0_to_c0[] = { 0, 1, 2, 3, 4, 5, 6, -1, 7, 8, -1, -1, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 32, 33, 34, 35, 36, 37 }; static volatile uint32_t *bcm2712_gpio_base(struct bcm2712_inst *inst, unsigned gpio, unsigned int *bit) { unsigned bank = gpio / 32; gpio %= 32; if ((bank >= inst->num_banks) || (gpio >= inst->bank_widths[bank]) || !inst->gpio_base) return NULL; *bit = gpio; return inst->gpio_base + bank * (0x20 / 4); } static volatile uint32_t *bcm2712_pinmux_base(struct bcm2712_inst *inst, unsigned gpio, unsigned int *bit) { unsigned bank, gpio_offset; if (gpio >= inst->num_gpios || !inst->pinmux_base) return NULL; if (inst->flags & FLAGS_D0) { if (inst->flags & FLAGS_AON) gpio = bcm2712_gpio_aon_d0_to_c0[gpio]; else gpio = bcm2712_gpio_d0_to_c0[gpio]; if ((int)gpio < 0) return NULL; } bank = gpio / 32; gpio_offset = gpio % 32; if ((bank >= inst->num_banks) || (gpio_offset >= inst->bank_widths[bank])) return NULL; if (inst->flags & FLAGS_AON) { if (bank == 1) { if (gpio_offset == 4) { *bit = 0; return inst->pinmux_base + 1; } else if (gpio_offset == 5) { *bit = 0; return inst->pinmux_base + 2; } else { *bit = gpio_offset * 4; return inst->pinmux_base; } } *bit = (gpio_offset % 8) * 4; return inst->pinmux_base + 3 + (gpio_offset / 8); } *bit = (gpio_offset % 8) * 4; return inst->pinmux_base + (bank * 4) + (gpio_offset / 8); } static volatile uint32_t *bcm2712_pad_base(struct bcm2712_inst *inst, unsigned gpio, unsigned int *bit) { unsigned bank, gpio_offset; if (gpio >= inst->num_gpios || !inst->pinmux_base) return NULL; if (inst->flags & FLAGS_D0) { if (inst->flags & FLAGS_AON) gpio = bcm2712_gpio_aon_d0_to_c0[gpio]; else gpio = bcm2712_gpio_d0_to_c0[gpio]; if ((int)gpio < 0) return NULL; } bank = gpio / 32; gpio_offset = gpio % 32; if ((bank >= inst->num_banks) || (gpio_offset >= inst->bank_widths[bank])) return NULL; if ((inst->flags & FLAGS_AON) && (bank > 0)) { /* There is no SGPIO pad control (that I know of) */ return NULL; } gpio += inst->pad_offset; *bit = (gpio % 15) * 2; return inst->pinmux_base + (gpio / 15); } static int bcm2712_gpio_get_level(void *priv, unsigned gpio) { struct bcm2712_inst *inst = priv; unsigned int bit; volatile uint32_t *gpio_base = bcm2712_gpio_base(inst, gpio, &bit); if (!gpio_base) return -1; return !!(gpio_base[BCM2712_GIO_DATA / 4] & (1 << bit)); } static void bcm2712_gpio_set_drive(void *priv, unsigned gpio, GPIO_DRIVE_T drv) { struct bcm2712_inst *inst = priv; unsigned int bit; volatile uint32_t *gpio_base = bcm2712_gpio_base(inst, gpio, &bit); uint32_t gpio_val; if (!gpio_base) return; gpio_val = gpio_base[BCM2712_GIO_DATA / 4]; gpio_val = (gpio_val & ~(1U << bit)) | (drv << bit); gpio_base[BCM2712_GIO_DATA / 4] = gpio_val; } static GPIO_DRIVE_T bcm2712_gpio_get_drive(void *priv, unsigned gpio) { struct bcm2712_inst *inst = priv; unsigned int bit; volatile uint32_t *gpio_base = bcm2712_gpio_base(inst, gpio, &bit); uint32_t gpio_val; if (!gpio_base) return DRIVE_MAX; gpio_val = gpio_base[BCM2712_GIO_DATA / 4]; return (gpio_val & (1U << bit)) ? DRIVE_HIGH : DRIVE_LOW; } static void bcm2712_gpio_set_dir(void *priv, unsigned gpio, GPIO_DIR_T dir) { struct bcm2712_inst *inst = priv; unsigned int bit; volatile uint32_t *gpio_base = bcm2712_gpio_base(inst, gpio, &bit); uint32_t gpio_val; if (!gpio_base) return; gpio_val = gpio_base[BCM2712_GIO_IODIR / 4]; gpio_val &= ~(1U << bit); gpio_val |= ((dir == DIR_INPUT) << bit); gpio_base[BCM2712_GIO_IODIR / 4] = gpio_val; } static GPIO_DIR_T bcm2712_gpio_get_dir(void *priv, unsigned gpio) { struct bcm2712_inst *inst = priv; unsigned int bit; volatile uint32_t *gpio_base = bcm2712_gpio_base(inst, gpio, &bit); uint32_t gpio_val; if (!gpio_base) return DIR_MAX; gpio_val = gpio_base[BCM2712_GIO_IODIR / 4]; return (gpio_val & (1U << bit)) ? DIR_INPUT : DIR_OUTPUT; } static GPIO_FSEL_T bcm2712_pinctrl_get_fsel(void *priv, unsigned gpio) { struct bcm2712_inst *inst = priv; unsigned int pinmux_bit; volatile uint32_t *pinmux_base = bcm2712_pinmux_base(inst, gpio, &pinmux_bit); int fsel; if (!pinmux_base) return -1; fsel = ((*pinmux_base >> pinmux_bit) & 0xf); if (fsel == 0) return GPIO_FSEL_GPIO; else if (fsel < BCM2712_FSEL_COUNT) return GPIO_FSEL_FUNC1 + (fsel - 1); else if (fsel == 0xf) // Choose one value as a considered NONE return GPIO_FSEL_NONE; /* Unknown FSEL */ return -1; } static void bcm2712_pinctrl_set_fsel(void *priv, unsigned gpio, const GPIO_FSEL_T func) { struct bcm2712_inst *inst = priv; unsigned int pinmux_bit; volatile uint32_t *pinmux_base = bcm2712_pinmux_base(inst, gpio, &pinmux_bit); uint32_t pinmux_val; int fsel; if (!pinmux_base) return; if (func == GPIO_FSEL_INPUT || func == GPIO_FSEL_OUTPUT || func == GPIO_FSEL_GPIO) { // Set direction before switching // N.B. We explicitly interpret a request for FUNC_A0/GPIO as "last/current GPIO dir" fsel = 0; if (func == GPIO_FSEL_INPUT) bcm2712_gpio_set_dir(priv, gpio, DIR_INPUT); else if (func == GPIO_FSEL_OUTPUT) bcm2712_gpio_set_dir(priv, gpio, DIR_OUTPUT); } else if (func >= GPIO_FSEL_FUNC0 && func <= GPIO_FSEL_FUNC8) { fsel = func - GPIO_FSEL_FUNC0; } else { return; } pinmux_val = *pinmux_base; pinmux_val &= ~(0xf << pinmux_bit); pinmux_val |= (fsel << pinmux_bit); *pinmux_base = pinmux_val; } static GPIO_PULL_T bcm2712_pinctrl_get_pull(void *priv, unsigned gpio) { struct bcm2712_inst *inst = priv; unsigned int bit; volatile uint32_t *pad_base = bcm2712_pad_base(inst, gpio, &bit); uint32_t pad_val; if (!pad_base) return PULL_MAX; pad_val = (*pad_base >> bit) & 0x3; switch (pad_val) { case BCM2712_PAD_PULL_OFF: return PULL_NONE; case BCM2712_PAD_PULL_DOWN: return PULL_DOWN; case BCM2712_PAD_PULL_UP: return PULL_UP; default: return PULL_MAX; /* This is an error */ } } static void bcm2712_pinctrl_set_pull(void *priv, unsigned gpio, GPIO_PULL_T pull) { struct bcm2712_inst *inst = priv; unsigned int bit = 0; volatile uint32_t *pad_base = bcm2712_pad_base(inst, gpio, &bit); uint32_t padval; int val; if (!pad_base) return; switch (pull) { case PULL_NONE: val = BCM2712_PAD_PULL_OFF; break; case PULL_DOWN: val = BCM2712_PAD_PULL_DOWN; break; case PULL_UP: val = BCM2712_PAD_PULL_UP; break; default: assert(0); return; } padval = *pad_base; padval &= ~(3 << bit); padval |= (val << bit); *pad_base = padval; } static void *bcm2712_gpio_create_instance(const GPIO_CHIP_T *chip, const char *dtnode) { struct bcm2712_inst *inst = NULL; uint32_t *widths; unsigned num_banks, inst_gpios; unsigned flags = FLAGS_GPIO | chip->data; unsigned bank; unsigned i; widths = dt_read_cells(dtnode, "brcm,gpio-bank-widths", &num_banks); if (!widths) return NULL; inst_gpios = 0; for (bank = 0; bank < num_banks; bank++) { inst_gpios = ROUND_UP(inst_gpios, 32) + widths[bank]; } flags |= shared_flags; if (widths[0] < 32) { flags |= FLAGS_AON; if (widths[0] == 15) flags |= FLAGS_D0; else flags |= FLAGS_C0; } else if (!(flags & (FLAGS_C0 | FLAGS_D0))) { size_t names_len; char *names = dt_read_prop(dtnode, "gpio-line-names", &names_len); if (!names[0]) flags |= FLAGS_D0; dt_free(names); } shared_flags |= (flags & (FLAGS_C0 | FLAGS_D0)); /* look for a corresponding pinctrl instance */ for (i = 0; i < num_instances; i++) { struct bcm2712_inst *pinctrl_inst = &bcm2712_instances[i]; pinctrl_inst->flags |= shared_flags; if (!((pinctrl_inst->flags ^ flags) & FLAGS_AON)) { if (pinctrl_inst->flags & FLAGS_GPIO) { assert(!"duplicate gpio nodes?"); return NULL; } inst = pinctrl_inst; break; } } if (!inst) { if (num_instances == BCM2712_MAX_INSTANCES) return NULL; inst = &bcm2712_instances[num_instances++]; } inst->num_gpios = inst_gpios; inst->num_banks = num_banks; inst->bank_widths = widths; inst->flags |= flags; return (void *)inst; } static int bcm2712_gpio_count(void *priv) { struct bcm2712_inst *inst = priv; return inst->num_gpios; } static void *bcm2712_gpio_probe_instance(void *priv, volatile uint32_t *base) { struct bcm2712_inst *inst = priv; inst->gpio_base = base; return inst; } static void *bcm2712_pinctrl_create_instance(const GPIO_CHIP_T *chip, const char *dtnode) { struct bcm2712_inst *inst = NULL; unsigned flags = FLAGS_PINCTRL | chip->data; unsigned reg_cells, reg_size; uint32_t *reg; unsigned i; if (dtnode) { reg = dt_read_cells(dtnode, "reg", ®_cells); if (!reg || reg_cells < 2) return NULL; reg_size = (reg_cells > 1) ? reg[reg_cells - 1] : 0; dt_free(reg); switch (reg_size) { case 0x1c: assert((flags & FLAGS_AON) && (flags & FLAGS_D0)); break; case 0x20: assert(((flags & FLAGS_AON) && !(flags & FLAGS_D0)) || (!(flags & FLAGS_AON) && (flags & FLAGS_D0))); break; case 0x30: assert(!(flags & FLAGS_AON) && !(flags & FLAGS_D0)); break; default: assert(0); } } shared_flags |= (flags & (FLAGS_C0 | FLAGS_D0)); /* look for a corresponding gpio instance */ for (i = 0; i < num_instances; i++) { struct bcm2712_inst *gpio_inst = &bcm2712_instances[i]; gpio_inst->flags |= shared_flags; if (!((gpio_inst->flags ^ flags) & FLAGS_AON)) { if (gpio_inst->flags & FLAGS_PINCTRL) { assert(!"duplicate pinctrl nodes?"); return NULL; } inst = gpio_inst; break; } } if (!inst) { if (num_instances == BCM2712_MAX_INSTANCES) return NULL; inst = &bcm2712_instances[num_instances++]; } inst->flags |= flags; return (void *)inst; } static int bcm2712_pinctrl_count(void *priv) { struct bcm2712_inst *inst = priv; if (inst->flags & FLAGS_GPIO) return 0; /* Don't occupy any GPIO space */ if (!inst->num_gpios) { switch (inst->flags & (FLAGS_AON | FLAGS_C0 | FLAGS_D0)) { case 0: case FLAGS_C0: inst->num_gpios = 54; break; case FLAGS_D0: inst->num_gpios = 36; break; case FLAGS_AON: case FLAGS_AON | FLAGS_D0: case FLAGS_AON | FLAGS_C0: inst->num_gpios = 38; break; default: break; } } return inst->num_gpios; } static void *bcm2712_pinctrl_probe_instance(void *priv, volatile uint32_t *base) { struct bcm2712_inst *inst = priv; unsigned pad_offset; inst->pinmux_base = base; switch (inst->flags & (FLAGS_D0 | FLAGS_C0 | FLAGS_AON)) { case FLAGS_C0: default: pad_offset = 112; break; case FLAGS_D0: pad_offset = 65; break; case FLAGS_AON: case FLAGS_C0 | FLAGS_AON: pad_offset = 100; break; case FLAGS_D0 | FLAGS_AON: pad_offset = 84; break; } inst->pad_offset = pad_offset; return inst; } static const char *bcm2712_pinctrl_get_fsel_name(void *priv, unsigned gpio, GPIO_FSEL_T fsel) { struct bcm2712_inst *inst = priv; const char *name = NULL; switch (fsel) { case GPIO_FSEL_GPIO: case GPIO_FSEL_FUNC0: name = "gpio"; break; case GPIO_FSEL_INPUT: name = "input"; break; case GPIO_FSEL_OUTPUT: name = "output"; break; case GPIO_FSEL_NONE: name = "none"; break; case GPIO_FSEL_FUNC1: case GPIO_FSEL_FUNC2: case GPIO_FSEL_FUNC3: case GPIO_FSEL_FUNC4: case GPIO_FSEL_FUNC5: case GPIO_FSEL_FUNC6: case GPIO_FSEL_FUNC7: case GPIO_FSEL_FUNC8: if (gpio < inst->num_gpios) { switch (inst->flags & (FLAGS_AON | FLAGS_C0 | FLAGS_D0)) { case FLAGS_C0 | FLAGS_AON: case FLAGS_AON: name = bcm2712_c0_aon_gpio_alt_names[gpio][fsel - 1]; break; case FLAGS_C0: case 0: name = bcm2712_c0_gpio_alt_names[gpio][fsel - 1]; break; case FLAGS_D0 | FLAGS_AON: name = bcm2712_d0_aon_gpio_alt_names[gpio][fsel - 1]; break; case FLAGS_D0: name = bcm2712_d0_gpio_alt_names[gpio][fsel - 1]; break; } if (!name) name = "-"; } break; default: break; } return name; } static const char *bcm2712_gpio_get_name(void *priv, unsigned gpio) { struct bcm2712_inst *inst = priv; const char *fsel_name; static char name_buf[16]; unsigned gpio_offset; unsigned bank; fsel_name = bcm2712_pinctrl_get_fsel_name(priv, gpio, GPIO_FSEL_FUNC1); if (!fsel_name || !fsel_name[0]) return NULL; bank = gpio / 32; gpio_offset = gpio % 32; if ((inst->flags & FLAGS_GPIO) && ((bank >= inst->num_banks) || (gpio_offset >= inst->bank_widths[bank]))) return NULL; if (inst->flags & FLAGS_AON) { if (bank == 1) sprintf(name_buf, "AON_SGPIO%d", gpio_offset); else sprintf(name_buf, "AON_GPIO%d", gpio_offset); } else { sprintf(name_buf, "GPIO%d", gpio); } return name_buf; } static const GPIO_CHIP_INTERFACE_T bcm2712_gpio_interface = { .gpio_create_instance = bcm2712_gpio_create_instance, .gpio_count = bcm2712_gpio_count, .gpio_probe_instance = bcm2712_gpio_probe_instance, .gpio_get_fsel = bcm2712_pinctrl_get_fsel, .gpio_set_fsel = bcm2712_pinctrl_set_fsel, .gpio_set_drive = bcm2712_gpio_set_drive, .gpio_set_dir = bcm2712_gpio_set_dir, .gpio_get_dir = bcm2712_gpio_get_dir, .gpio_get_level = bcm2712_gpio_get_level, .gpio_get_drive = bcm2712_gpio_get_drive, .gpio_get_pull = bcm2712_pinctrl_get_pull, .gpio_set_pull = bcm2712_pinctrl_set_pull, .gpio_get_name = bcm2712_gpio_get_name, .gpio_get_fsel_name = bcm2712_pinctrl_get_fsel_name, }; DECLARE_GPIO_CHIP(brcmstb, "brcm,brcmstb-gpio", &bcm2712_gpio_interface, 0x40, 0); static const GPIO_CHIP_INTERFACE_T bcm2712_pinctrl_interface = { .gpio_create_instance = bcm2712_pinctrl_create_instance, .gpio_count = bcm2712_pinctrl_count, .gpio_probe_instance = bcm2712_pinctrl_probe_instance, .gpio_get_fsel = bcm2712_pinctrl_get_fsel, .gpio_set_fsel = bcm2712_pinctrl_set_fsel, .gpio_set_drive = bcm2712_gpio_set_drive, .gpio_set_dir = bcm2712_gpio_set_dir, .gpio_get_dir = bcm2712_gpio_get_dir, .gpio_get_level = bcm2712_gpio_get_level, .gpio_get_drive = bcm2712_gpio_get_drive, .gpio_get_pull = bcm2712_pinctrl_get_pull, .gpio_set_pull = bcm2712_pinctrl_set_pull, .gpio_get_name = bcm2712_gpio_get_name, .gpio_get_fsel_name = bcm2712_pinctrl_get_fsel_name, }; DECLARE_GPIO_CHIP(bcm2712, "brcm,bcm2712-pinctrl", &bcm2712_pinctrl_interface, 0x30, 0); DECLARE_GPIO_CHIP(bcm2712_aon, "brcm,bcm2712-aon-pinctrl", &bcm2712_pinctrl_interface, 0x20, FLAGS_AON); DECLARE_GPIO_CHIP(bcm2712c0, "brcm,bcm2712c0-pinctrl", &bcm2712_pinctrl_interface, 0x30, FLAGS_C0); DECLARE_GPIO_CHIP(bcm2712c0_aon, "brcm,bcm2712c0-aon-pinctrl", &bcm2712_pinctrl_interface, 0x20, FLAGS_C0 | FLAGS_AON); DECLARE_GPIO_CHIP(bcm2712d0, "brcm,bcm2712d0-pinctrl", &bcm2712_pinctrl_interface, 0x20, FLAGS_D0); DECLARE_GPIO_CHIP(bcm2712d0_aon, "brcm,bcm2712d0-aon-pinctrl", &bcm2712_pinctrl_interface, 0x1c, FLAGS_D0 | FLAGS_AON); raspi-utils-20250514/pinctrl/gpiochip_bcm2835.c000066400000000000000000000520011501106437300210140ustar00rootroot00000000000000#include #include #include #include #include #include #include "gpiochip.h" #include "util.h" #define BCM2835_NUM_GPIOS 54 #define BCM2835_ALT_COUNT 6 #define BCM2711_NUM_GPIOS 58 #define BCM2711_ALT_COUNT 6 /* 2835 register offsets */ #define GPFSEL0 0 #define GPFSEL1 1 #define GPFSEL2 2 #define GPFSEL3 3 #define GPFSEL4 4 #define GPFSEL5 5 #define GPSET0 7 #define GPSET1 8 #define GPCLR0 10 #define GPCLR1 11 #define GPLEV0 13 #define GPLEV1 14 #define GPPUD 37 #define GPPUDCLK0 38 #define GPPUDCLK1 39 /* 2711 has a different mechanism for pin pull-up/down/enable */ #define GPPUPPDN0 57 /* Pin pull-up/down for pins 15:0 */ #define GPPUPPDN1 58 /* Pin pull-up/down for pins 31:16 */ #define GPPUPPDN2 59 /* Pin pull-up/down for pins 47:32 */ #define GPPUPPDN3 60 /* Pin pull-up/down for pins 57:48 */ struct bcm2835_inst { unsigned num_gpios; volatile uint32_t *base; }; static struct bcm2835_inst bcm2835_instance = { .num_gpios = BCM2835_NUM_GPIOS }; static struct bcm2835_inst bcm2711_instance = { .num_gpios = BCM2711_NUM_GPIOS }; static const char *bcm2835_gpio_alt_names[BCM2835_NUM_GPIOS][BCM2835_ALT_COUNT] = { { "SDA0" , "SA5" , "PCLK" , "AVEOUT_VCLK" , "AVEIN_VCLK" , 0 , }, { "SCL0" , "SA4" , "DE" , "AVEOUT_DSYNC" , "AVEIN_DSYNC", 0 , }, { "SDA1" , "SA3" , "LCD_VSYNC" , "AVEOUT_VSYNC" , "AVEIN_VSYNC", 0 , }, { "SCL1" , "SA2" , "LCD_HSYNC" , "AVEOUT_HSYNC" , "AVEIN_HSYNC", 0 , }, { "GPCLK0" , "SA1" , "DPI_D0" , "AVEOUT_VID0" , "AVEIN_VID0" , "ARM_TDI" , }, { "GPCLK1" , "SA0" , "DPI_D1" , "AVEOUT_VID1" , "AVEIN_VID1" , "ARM_TDO" , }, { "GPCLK2" , "SOE_N_SE" , "DPI_D2" , "AVEOUT_VID2" , "AVEIN_VID2" , "ARM_RTCK" , }, { "SPI0_CE1_N", "SWE_N_SRW_N", "DPI_D3" , "AVEOUT_VID3" , "AVEIN_VID3" , 0 , }, { "SPI0_CE0_N", "SD0" , "DPI_D4" , "AVEOUT_VID4" , "AVEIN_VID4" , 0 , }, { "SPI0_MISO" , "SD1" , "DPI_D5" , "AVEOUT_VID5" , "AVEIN_VID5" , 0 , }, { "SPI0_MOSI" , "SD2" , "DPI_D6" , "AVEOUT_VID6" , "AVEIN_VID6" , 0 , }, { "SPI0_SCLK" , "SD3" , "DPI_D7" , "AVEOUT_VID7" , "AVEIN_VID7" , 0 , }, { "PWM0" , "SD4" , "DPI_D8" , "AVEOUT_VID8" , "AVEIN_VID8" , "ARM_TMS" , }, { "PWM1" , "SD5" , "DPI_D9" , "AVEOUT_VID9" , "AVEIN_VID9" , "ARM_TCK" , }, { "TXD0" , "SD6" , "DPI_D10" , "AVEOUT_VID10" , "AVEIN_VID10", "TXD1" , }, { "RXD0" , "SD7" , "DPI_D11" , "AVEOUT_VID11" , "AVEIN_VID11", "RXD1" , }, { "FL0" , "SD8" , "DPI_D12" , "CTS0" , "SPI1_CE2_N" , "CTS1" , }, { "FL1" , "SD9" , "DPI_D13" , "RTS0" , "SPI1_CE1_N" , "RTS1" , }, { "PCM_CLK" , "SD10" , "DPI_D14" , "I2CSL_SDA_MOSI", "SPI1_CE0_N" , "PWM0" , }, { "PCM_FS" , "SD11" , "DPI_D15" , "I2CSL_SCL_SCLK", "SPI1_MISO" , "PWM1" , }, { "PCM_DIN" , "SD12" , "DPI_D16" , "I2CSL_MISO" , "SPI1_MOSI" , "GPCLK0" , }, { "PCM_DOUT" , "SD13" , "DPI_D17" , "I2CSL_CE_N" , "SPI1_SCLK" , "GPCLK1" , }, { "SD0_CLK" , "SD14" , "DPI_D18" , "SD1_CLK" , "ARM_TRST" , 0 , }, { "SD0_CMD" , "SD15" , "DPI_D19" , "SD1_CMD" , "ARM_RTCK" , 0 , }, { "SD0_DAT0" , "SD16" , "DPI_D20" , "SD1_DAT0" , "ARM_TDO" , 0 , }, { "SD0_DAT1" , "SD17" , "DPI_D21" , "SD1_DAT1" , "ARM_TCK" , 0 , }, { "SD0_DAT2" , "TE0" , "DPI_D22" , "SD1_DAT2" , "ARM_TDI" , 0 , }, { "SD0_DAT3" , "TE1" , "DPI_D23" , "SD1_DAT3" , "ARM_TMS" , 0 , }, { "SDA0" , "SA5" , "PCM_CLK" , "FL0" , 0 , 0 , }, { "SCL0" , "SA4" , "PCM_FS" , "FL1" , 0 , 0 , }, { "TE0" , "SA3" , "PCM_DIN" , "CTS0" , 0 , "CTS1" , }, { "FL0" , "SA2" , "PCM_DOUT" , "RTS0" , 0 , "RTS1" , }, { "GPCLK0" , "SA1" , "RING_OCLK" , "TXD0" , 0 , "TXD1" , }, { "FL1" , "SA0" , "TE1" , "RXD0" , 0 , "RXD1" , }, { "GPCLK0" , "SOE_N_SE" , "TE2" , "SD1_CLK" , 0 , 0 , }, { "SPI0_CE1_N", "SWE_N_SRW_N", 0 , "SD1_CMD" , 0 , 0 , }, { "SPI0_CE0_N", "SD0" , "TXD0" , "SD1_DAT0" , 0 , 0 , }, { "SPI0_MISO" , "SD1" , "RXD0" , "SD1_DAT1" , 0 , 0 , }, { "SPI0_MOSI" , "SD2" , "RTS0" , "SD1_DAT2" , 0 , 0 , }, { "SPI0_SCLK" , "SD3" , "CTS0" , "SD1_DAT3" , 0 , 0 , }, { "PWM0" , "SD4" , 0 , "SD1_DAT4" , "SPI2_MISO" , "TXD1" , }, { "PWM1" , "SD5" , "TE0" , "SD1_DAT5" , "SPI2_MOSI" , "RXD1" , }, { "GPCLK1" , "SD6" , "TE1" , "SD1_DAT6" , "SPI2_SCLK" , "RTS1" , }, { "GPCLK2" , "SD7" , "TE2" , "SD1_DAT7" , "SPI2_CE0_N" , "CTS1" , }, { "GPCLK1" , "SDA0" , "SDA1" , "TE0" , "SPI2_CE1_N" , 0 , }, { "PWM1" , "SCL0" , "SCL1" , "TE1" , "SPI2_CE2_N" , 0 , }, { "SDA0" , "SDA1" , "SPI0_CE0_N", 0 , 0 , "SPI2_CE1_N", }, { "SCL0" , "SCL1" , "SPI0_MISO" , 0 , 0 , "SPI2_CE0_N", }, { "SD0_CLK" , "FL0" , "SPI0_MOSI" , "SD1_CLK" , "ARM_TRST" , "SPI2_SCLK" , }, { "SD0_CMD" , "GPCLK0" , "SPI0_SCLK" , "SD1_CMD" , "ARM_RTCK" , "SPI2_MOSI" , }, { "SD0_DAT0" , "GPCLK1" , "PCM_CLK" , "SD1_DAT0" , "ARM_TDO" , 0 , }, { "SD0_DAT1" , "GPCLK2" , "PCM_FS" , "SD1_DAT1" , "ARM_TCK" , 0 , }, { "SD0_DAT2" , "PWM0" , "PCM_DIN" , "SD1_DAT2" , "ARM_TDI" , 0 , }, { "SD0_DAT3" , "PWM1" , "PCM_DOUT" , "SD1_DAT3" , "ARM_TMS" , 0 , }, }; static const char *bcm2711_gpio_alt_names[BCM2711_NUM_GPIOS][BCM2711_ALT_COUNT] = { { "SDA0" , "SA5" , "PCLK" , "SPI3_CE0_N" , "TXD2" , "SDA6" , }, { "SCL0" , "SA4" , "DE" , "SPI3_MISO" , "RXD2" , "SCL6" , }, { "SDA1" , "SA3" , "LCD_VSYNC" , "SPI3_MOSI" , "CTS2" , "SDA3" , }, { "SCL1" , "SA2" , "LCD_HSYNC" , "SPI3_SCLK" , "RTS2" , "SCL3" , }, { "GPCLK0" , "SA1" , "DPI_D0" , "SPI4_CE0_N" , "TXD3" , "SDA3" , }, { "GPCLK1" , "SA0" , "DPI_D1" , "SPI4_MISO" , "RXD3" , "SCL3" , }, { "GPCLK2" , "SOE_N_SE" , "DPI_D2" , "SPI4_MOSI" , "CTS3" , "SDA4" , }, { "SPI0_CE1_N", "SWE_N_SRW_N", "DPI_D3" , "SPI4_SCLK" , "RTS3" , "SCL4" , }, { "SPI0_CE0_N", "SD0" , "DPI_D4" , "I2CSL_CE_N" , "TXD4" , "SDA4" , }, { "SPI0_MISO" , "SD1" , "DPI_D5" , "I2CSL_SDI_MISO", "RXD4" , "SCL4" , }, { "SPI0_MOSI" , "SD2" , "DPI_D6" , "I2CSL_SDA_MOSI", "CTS4" , "SDA5" , }, { "SPI0_SCLK" , "SD3" , "DPI_D7" , "I2CSL_SCL_SCLK", "RTS4" , "SCL5" , }, { "PWM0_0" , "SD4" , "DPI_D8" , "SPI5_CE0_N" , "TXD5" , "SDA5" , }, { "PWM0_1" , "SD5" , "DPI_D9" , "SPI5_MISO" , "RXD5" , "SCL5" , }, { "TXD0" , "SD6" , "DPI_D10" , "SPI5_MOSI" , "CTS5" , "TXD1" , }, { "RXD0" , "SD7" , "DPI_D11" , "SPI5_SCLK" , "RTS5" , "RXD1" , }, { 0 , "SD8" , "DPI_D12" , "CTS0" , "SPI1_CE2_N" , "CTS1" , }, { 0 , "SD9" , "DPI_D13" , "RTS0" , "SPI1_CE1_N" , "RTS1" , }, { "PCM_CLK" , "SD10" , "DPI_D14" , "SPI6_CE0_N" , "SPI1_CE0_N" , "PWM0_0" , }, { "PCM_FS" , "SD11" , "DPI_D15" , "SPI6_MISO" , "SPI1_MISO" , "PWM0_1" , }, { "PCM_DIN" , "SD12" , "DPI_D16" , "SPI6_MOSI" , "SPI1_MOSI" , "GPCLK0" , }, { "PCM_DOUT" , "SD13" , "DPI_D17" , "SPI6_SCLK" , "SPI1_SCLK" , "GPCLK1" , }, { "SD0_CLK" , "SD14" , "DPI_D18" , "SD1_CLK" , "ARM_TRST" , "SDA6" , }, { "SD0_CMD" , "SD15" , "DPI_D19" , "SD1_CMD" , "ARM_RTCK" , "SCL6" , }, { "SD0_DAT0" , "SD16" , "DPI_D20" , "SD1_DAT0" , "ARM_TDO" , "SPI3_CE1_N" , }, { "SD0_DAT1" , "SD17" , "DPI_D21" , "SD1_DAT1" , "ARM_TCK" , "SPI4_CE1_N" , }, { "SD0_DAT2" , 0 , "DPI_D22" , "SD1_DAT2" , "ARM_TDI" , "SPI5_CE1_N" , }, { "SD0_DAT3" , 0 , "DPI_D23" , "SD1_DAT3" , "ARM_TMS" , "SPI6_CE1_N" , }, { "SDA0" , "SA5" , "PCM_CLK" , 0 , "MII_A_RX_ERR" , "RGMII_MDIO" , }, { "SCL0" , "SA4" , "PCM_FS" , 0 , "MII_A_TX_ERR" , "RGMII_MDC" , }, { 0 , "SA3" , "PCM_DIN" , "CTS0" , "MII_A_CRS" , "CTS1" , }, { 0 , "SA2" , "PCM_DOUT" , "RTS0" , "MII_A_COL" , "RTS1" , }, { "GPCLK0" , "SA1" , 0 , "TXD0" , "SD_CARD_PRES" , "TXD1" , }, { 0 , "SA0" , 0 , "RXD0" , "SD_CARD_WRPROT" , "RXD1" , }, { "GPCLK0" , "SOE_N_SE" , 0 , "SD1_CLK" , "SD_CARD_LED" , "RGMII_IRQ" , }, { "SPI0_CE1_N", "SWE_N_SRW_N", 0 , "SD1_CMD" , "RGMII_START_STOP", 0 , }, { "SPI0_CE0_N", "SD0" , "TXD0" , "SD1_DAT0" , "RGMII_RX_OK" , "MII_A_RX_ERR", }, { "SPI0_MISO" , "SD1" , "RXD0" , "SD1_DAT1" , "RGMII_MDIO" , "MII_A_TX_ERR", }, { "SPI0_MOSI" , "SD2" , "RTS0" , "SD1_DAT2" , "RGMII_MDC" , "MII_A_CRS" , }, { "SPI0_SCLK" , "SD3" , "CTS0" , "SD1_DAT3" , "RGMII_IRQ" , "MII_A_COL" , }, { "PWM1_0" , "SD4" , 0 , "SD1_DAT4" , "SPI0_MISO" , "TXD1" , }, { "PWM1_1" , "SD5" , 0 , "SD1_DAT5" , "SPI0_MOSI" , "RXD1" , }, { "GPCLK1" , "SD6" , 0 , "SD1_DAT6" , "SPI0_SCLK" , "RTS1" , }, { "GPCLK2" , "SD7" , 0 , "SD1_DAT7" , "SPI0_CE0_N" , "CTS1" , }, { "GPCLK1" , "SDA0" , "SDA1" , 0 , "SPI0_CE1_N" , "SD_CARD_VOLT", }, { "PWM0_1" , "SCL0" , "SCL1" , 0 , "SPI0_CE2_N" , "SD_CARD_PWR0", }, { "SDA0" , "SDA1" , "SPI0_CE0_N", 0 , 0 , "SPI2_CE1_N" , }, { "SCL0" , "SCL1" , "SPI0_MISO" , 0 , 0 , "SPI2_CE0_N" , }, { "SD0_CLK" , 0 , "SPI0_MOSI" , "SD1_CLK" , "ARM_TRST" , "SPI2_SCLK" , }, { "SD0_CMD" , "GPCLK0" , "SPI0_SCLK" , "SD1_CMD" , "ARM_RTCK" , "SPI2_MOSI" , }, { "SD0_DAT0" , "GPCLK1" , "PCM_CLK" , "SD1_DAT0" , "ARM_TDO" , "SPI2_MISO" , }, { "SD0_DAT1" , "GPCLK2" , "PCM_FS" , "SD1_DAT1" , "ARM_TCK" , "SD_CARD_LED" , }, { "SD0_DAT2" , "PWM0_0" , "PCM_DIN" , "SD1_DAT2" , "ARM_TDI" , 0 , }, { "SD0_DAT3" , "PWM0_1" , "PCM_DOUT" , "SD1_DAT3" , "ARM_TMS" , 0 , }, { "IR_IN" , 0 , 0 , 0 , 0 , 0 , }, { 0 , 0 , 0 , 0 , 0 , 0 , }, { 0 , 0 , 0 , 0 , 0 , 0 , }, { 0 , 0 , 0 , 0 , 0 , 0 , }, }; static GPIO_FSEL_T bcm2835_gpio_get_fsel(void *priv, unsigned gpio) { struct bcm2835_inst *inst = priv; volatile uint32_t *base = inst->base; /* GPFSEL0-5 with 10 sels per reg, 3 bits per sel (so bits 0:29 used) */ uint32_t reg = GPFSEL0 + (gpio / 10); uint32_t lsb = (gpio % 10) * 3; if (gpio < inst->num_gpios) { switch ((base[reg] >> lsb) & 7) { case 0: return GPIO_FSEL_INPUT; case 1: return GPIO_FSEL_OUTPUT; case 2: return GPIO_FSEL_FUNC5; case 3: return GPIO_FSEL_FUNC4; case 4: return GPIO_FSEL_FUNC0; case 5: return GPIO_FSEL_FUNC1; case 6: return GPIO_FSEL_FUNC2; case 7: return GPIO_FSEL_FUNC3; } } return GPIO_FSEL_MAX; } static void bcm2835_gpio_set_fsel(void *priv, unsigned gpio, const GPIO_FSEL_T func) { struct bcm2835_inst *inst = priv; volatile uint32_t *base = inst->base; /* GPFSEL0-5 with 10 sels per reg, 3 bits per sel (so bits 0:29 used) */ uint32_t reg = GPFSEL0 + (gpio / 10); uint32_t lsb = (gpio % 10) * 3; int fsel; switch (func) { case GPIO_FSEL_INPUT: fsel = 0; break; case GPIO_FSEL_OUTPUT: fsel = 1; break; case GPIO_FSEL_FUNC0: fsel = 4; break; case GPIO_FSEL_FUNC1: fsel = 5; break; case GPIO_FSEL_FUNC2: fsel = 6; break; case GPIO_FSEL_FUNC3: fsel = 7; break; case GPIO_FSEL_FUNC4: fsel = 3; break; case GPIO_FSEL_FUNC5: fsel = 2; break; default: return; } if (gpio < inst->num_gpios) base[reg] = (base[reg] & ~(0x7 << lsb)) | (fsel << lsb); } static GPIO_DIR_T bcm2835_gpio_get_dir(void *priv, unsigned gpio) { GPIO_FSEL_T fsel = bcm2835_gpio_get_fsel(priv, gpio); if (fsel == GPIO_FSEL_INPUT) return DIR_INPUT; else if (fsel == GPIO_FSEL_OUTPUT) return DIR_OUTPUT; else return DIR_MAX; } static void bcm2835_gpio_set_dir(void *priv, unsigned gpio, GPIO_DIR_T dir) { GPIO_FSEL_T fsel; if (dir == DIR_INPUT) fsel = GPIO_FSEL_INPUT; else if (dir == DIR_OUTPUT) fsel = GPIO_FSEL_OUTPUT; else return; bcm2835_gpio_set_fsel(priv, gpio, fsel); } static int bcm2835_gpio_get_level(void *priv, unsigned gpio) { struct bcm2835_inst *inst = priv; volatile uint32_t *base = inst->base; if (gpio >= inst->num_gpios) return -1; return (base[GPLEV0 + (gpio / 32)] >> (gpio % 32)) & 1; } GPIO_DRIVE_T bcm2835_gpio_get_drive(void *priv, unsigned gpio) { /* This is a write-only mechanism */ UNUSED(priv); UNUSED(gpio); return DRIVE_MAX; } static void bcm2835_gpio_set_drive(void *priv, unsigned gpio, GPIO_DRIVE_T drv) { struct bcm2835_inst *inst = priv; volatile uint32_t *base = inst->base; if (gpio < inst->num_gpios && drv <= DRIVE_HIGH) base[(drv ? GPSET0 : GPCLR0) + (gpio / 32)] = (1 << (gpio % 32)); } static GPIO_PULL_T bcm2835_gpio_get_pull(void *priv, unsigned gpio) { /* This is a write-only mechanism */ UNUSED(priv); UNUSED(gpio); return PULL_MAX; } static void bcm2835_gpio_set_pull(void *priv, unsigned gpio, GPIO_PULL_T pull) { struct bcm2835_inst *inst = priv; volatile uint32_t *base = inst->base; int clkreg = GPPUDCLK0 + (gpio / 32); int clkbit = 1 << (gpio % 32); if (gpio >= inst->num_gpios || pull < PULL_NONE || pull > PULL_UP) return; base[GPPUD] = pull; usleep(10); base[clkreg] = clkbit; usleep(10); base[GPPUD] = 0; usleep(10); base[clkreg] = 0; usleep(10); } static const char *bcm2835_gpio_get_name(void *priv, unsigned gpio) { struct bcm2835_inst *inst = priv; static char name_buf[16]; if (gpio >= inst->num_gpios) return NULL; sprintf(name_buf, "GPIO%d", gpio); return name_buf; } static const char *bcm2835_gpio_get_fsel_name(void *priv, unsigned gpio, GPIO_FSEL_T fsel) { struct bcm2835_inst *inst = priv; const char *name = NULL; switch (fsel) { case GPIO_FSEL_INPUT: name = "input"; break; case GPIO_FSEL_OUTPUT: name = "output"; break; case GPIO_FSEL_FUNC0: case GPIO_FSEL_FUNC1: case GPIO_FSEL_FUNC2: case GPIO_FSEL_FUNC3: case GPIO_FSEL_FUNC4: case GPIO_FSEL_FUNC5: if (gpio < inst->num_gpios) { name = bcm2835_gpio_alt_names[gpio][fsel]; if (!name) name = "-"; } break; default: break; } return name; } static GPIO_PULL_T bcm2711_gpio_get_pull(void *priv, unsigned gpio) { struct bcm2835_inst *inst = priv; volatile uint32_t *base = inst->base; int reg = GPPUPPDN0 + (gpio / 16); int lsb = (gpio % 16) * 2; if (gpio < BCM2711_NUM_GPIOS) { switch ((base[reg] >> lsb) & 3) { case 0: return PULL_NONE; case 1: return PULL_UP; case 2: return PULL_DOWN; } } return PULL_MAX; } static void bcm2711_gpio_set_pull(void *priv, unsigned gpio, GPIO_PULL_T pull) { struct bcm2835_inst *inst = priv; volatile uint32_t *base = inst->base; int reg = GPPUPPDN0 + (gpio / 16); int lsb = (gpio % 16) * 2; int pull_val; if (gpio >= BCM2711_NUM_GPIOS) return; switch (pull) { case PULL_NONE: pull_val = 0; break; case PULL_UP: pull_val = 1; break; case PULL_DOWN: pull_val = 2; break; default: return; } base[reg] = (base[reg] & ~(3 << lsb)) | (pull_val << lsb); } static const char *bcm2711_gpio_get_fsel_name(void *priv, unsigned gpio, GPIO_FSEL_T fsel) { struct bcm2835_inst *inst = priv; const char *name = NULL; switch (fsel) { case GPIO_FSEL_INPUT: name = "input"; break; case GPIO_FSEL_OUTPUT: name = "output"; break; case GPIO_FSEL_FUNC0: case GPIO_FSEL_FUNC1: case GPIO_FSEL_FUNC2: case GPIO_FSEL_FUNC3: case GPIO_FSEL_FUNC4: case GPIO_FSEL_FUNC5: if (gpio < inst->num_gpios) { name = bcm2711_gpio_alt_names[gpio][fsel]; if (!name) name = "-"; } break; default: break; } return name; } static void *bcm2835_gpio_create_instance(const GPIO_CHIP_T *chip, const char *dtnode) { UNUSED(chip); UNUSED(dtnode); return &bcm2835_instance; } static int bcm2835_gpio_count(void *priv) { struct bcm2835_inst *inst = priv; return inst->num_gpios; } static void *bcm2835_gpio_probe_instance(void *priv, volatile uint32_t *base) { struct bcm2835_inst *inst = priv; inst->base = base; return priv; } static const GPIO_CHIP_INTERFACE_T bcm2835_gpio_interface = { .gpio_create_instance = bcm2835_gpio_create_instance, .gpio_count = bcm2835_gpio_count, .gpio_probe_instance = bcm2835_gpio_probe_instance, .gpio_get_fsel = bcm2835_gpio_get_fsel, .gpio_set_fsel = bcm2835_gpio_set_fsel, .gpio_set_drive = bcm2835_gpio_set_drive, .gpio_set_dir = bcm2835_gpio_set_dir, .gpio_get_dir = bcm2835_gpio_get_dir, .gpio_get_level = bcm2835_gpio_get_level, .gpio_get_drive = bcm2835_gpio_get_drive, .gpio_get_pull = bcm2835_gpio_get_pull, .gpio_set_pull = bcm2835_gpio_set_pull, .gpio_get_name = bcm2835_gpio_get_name, .gpio_get_fsel_name = bcm2835_gpio_get_fsel_name, }; DECLARE_GPIO_CHIP(bcm2835, "brcm,bcm2835-gpio", &bcm2835_gpio_interface, 0x30000, 0); static void *bcm2711_gpio_create_instance(const GPIO_CHIP_T *chip, const char *dtnode) { UNUSED(chip); UNUSED(dtnode); return &bcm2711_instance; } static const GPIO_CHIP_INTERFACE_T bcm2711_gpio_interface = { .gpio_create_instance = bcm2711_gpio_create_instance, .gpio_count = bcm2835_gpio_count, .gpio_probe_instance = bcm2835_gpio_probe_instance, .gpio_get_fsel = bcm2835_gpio_get_fsel, .gpio_set_fsel = bcm2835_gpio_set_fsel, .gpio_set_drive = bcm2835_gpio_set_drive, .gpio_set_dir = bcm2835_gpio_set_dir, .gpio_get_dir = bcm2835_gpio_get_dir, .gpio_get_level = bcm2835_gpio_get_level, .gpio_get_drive = bcm2835_gpio_get_drive, .gpio_get_pull = bcm2711_gpio_get_pull, .gpio_set_pull = bcm2711_gpio_set_pull, .gpio_get_name = bcm2835_gpio_get_name, .gpio_get_fsel_name = bcm2711_gpio_get_fsel_name, }; DECLARE_GPIO_CHIP(bcm2711, "brcm,bcm2711-gpio", &bcm2711_gpio_interface, 0x30000, 0); raspi-utils-20250514/pinctrl/gpiochip_rp1.c000066400000000000000000000466751501106437300204570ustar00rootroot00000000000000#include #include #include #include #include "gpiochip.h" #include "util.h" #define RP1_NUM_GPIOS 54 #define RP1_IO_BANK0_OFFSET 0x00000000 #define RP1_IO_BANK1_OFFSET 0x00004000 #define RP1_IO_BANK2_OFFSET 0x00008000 #define RP1_SYS_RIO_BANK0_OFFSET 0x00010000 #define RP1_SYS_RIO_BANK1_OFFSET 0x00014000 #define RP1_SYS_RIO_BANK2_OFFSET 0x00018000 #define RP1_PADS_BANK0_OFFSET 0x00020000 #define RP1_PADS_BANK1_OFFSET 0x00024000 #define RP1_PADS_BANK2_OFFSET 0x00028000 #define RP1_RW_OFFSET 0x0000 #define RP1_XOR_OFFSET 0x1000 #define RP1_SET_OFFSET 0x2000 #define RP1_CLR_OFFSET 0x3000 #define RP1_GPIO_CTRL_FSEL_LSB 0 #define RP1_GPIO_CTRL_FSEL_MASK (0x1f << RP1_GPIO_CTRL_FSEL_LSB) #define RP1_GPIO_CTRL_OUTOVER_LSB 12 #define RP1_GPIO_CTRL_OUTOVER_MASK (0x03 << RP1_GPIO_CTRL_OUTOVER_LSB) #define RP1_GPIO_CTRL_OEOVER_LSB 14 #define RP1_GPIO_CTRL_OEOVER_MASK (0x03 << RP1_GPIO_CTRL_OEOVER_LSB) #define RP1_PADS_OD_SET (1 << 7) #define RP1_PADS_IE_SET (1 << 6) #define RP1_PADS_PUE_SET (1 << 3) #define RP1_PADS_PDE_SET (1 << 2) #define RP1_GPIO_IO_REG_STATUS_OFFSET(offset) (((offset * 2) + 0) * sizeof(uint32_t)) #define RP1_GPIO_IO_REG_CTRL_OFFSET(offset) (((offset * 2) + 1) * sizeof(uint32_t)) #define RP1_GPIO_PADS_REG_OFFSET(offset) (sizeof(uint32_t) + (offset * sizeof(uint32_t))) #define RP1_GPIO_SYS_RIO_REG_OUT_OFFSET 0x0 #define RP1_GPIO_SYS_RIO_REG_OE_OFFSET 0x4 #define RP1_GPIO_SYS_RIO_REG_SYNC_IN_OFFSET 0x8 #define rp1_gpio_write32(base, peri_offset, reg_offset, value) \ base[(peri_offset + reg_offset)/4] = value #define rp1_gpio_read32(base, peri_offset, reg_offset) \ base[(peri_offset + reg_offset)/4] typedef struct { uint32_t io[3]; uint32_t pads[3]; uint32_t sys_rio[3]; } GPIO_STATE_T; typedef enum { RP1_FSEL_ALT0 = 0x0, RP1_FSEL_ALT1 = 0x1, RP1_FSEL_ALT2 = 0x2, RP1_FSEL_ALT3 = 0x3, RP1_FSEL_ALT4 = 0x4, RP1_FSEL_ALT5 = 0x5, RP1_FSEL_ALT6 = 0x6, RP1_FSEL_ALT7 = 0x7, RP1_FSEL_ALT8 = 0x8, RP1_FSEL_COUNT, RP1_FSEL_SYS_RIO = RP1_FSEL_ALT5, RP1_FSEL_NULL = 0x1f } RP1_FSEL_T; static const GPIO_STATE_T gpio_state = { .io = {RP1_IO_BANK0_OFFSET, RP1_IO_BANK1_OFFSET, RP1_IO_BANK2_OFFSET}, .pads = {RP1_PADS_BANK0_OFFSET, RP1_PADS_BANK1_OFFSET, RP1_PADS_BANK2_OFFSET}, .sys_rio = {RP1_SYS_RIO_BANK0_OFFSET, RP1_SYS_RIO_BANK1_OFFSET, RP1_SYS_RIO_BANK2_OFFSET}, }; static const int rp1_bank_base[] = {0, 28, 34}; static const char *rp1_gpio_fsel_names[RP1_NUM_GPIOS][RP1_FSEL_COUNT] = { { "SPI0_SIO3" , "DPI_PCLK" , "TXD1" , "SDA0" , 0 , "SYS_RIO00" , "PROC_RIO00" , "PIO0" , "SPI2_CE0" , }, { "SPI0_SIO2" , "DPI_DE" , "RXD1" , "SCL0" , 0 , "SYS_RIO01" , "PROC_RIO01" , "PIO1" , "SPI2_SIO1", }, { "SPI0_CE3" , "DPI_VSYNC" , "CTS1" , "SDA1" , "IR_RX0" , "SYS_RIO02" , "PROC_RIO02" , "PIO2" , "SPI2_SIO0", }, { "SPI0_CE2" , "DPI_HSYNC" , "RTS1" , "SCL1" , "IR_TX0" , "SYS_RIO03" , "PROC_RIO03" , "PIO3" , "SPI2_SCLK", }, { "GPCLK0" , "DPI_D0" , "TXD2" , "SDA2" , "RI0" , "SYS_RIO04" , "PROC_RIO04" , "PIO4" , "SPI3_CE0" , }, { "GPCLK1" , "DPI_D1" , "RXD2" , "SCL2" , "DTR0" , "SYS_RIO05" , "PROC_RIO05" , "PIO5" , "SPI3_SIO1", }, { "GPCLK2" , "DPI_D2" , "CTS2" , "SDA3" , "DCD0" , "SYS_RIO06" , "PROC_RIO06" , "PIO6" , "SPI3_SIO0", }, { "SPI0_CE1" , "DPI_D3" , "RTS2" , "SCL3" , "DSR0" , "SYS_RIO07" , "PROC_RIO07" , "PIO7" , "SPI3_SCLK", }, { "SPI0_CE0" , "DPI_D4" , "TXD3" , "SDA0" , 0 , "SYS_RIO08" , "PROC_RIO08" , "PIO8" , "SPI4_CE0" , }, { "SPI0_MISO" , "DPI_D5" , "RXD3" , "SCL0" , 0 , "SYS_RIO09" , "PROC_RIO09" , "PIO9" , "SPI4_SIO0", }, { "SPI0_MOSI" , "DPI_D6" , "CTS3" , "SDA1" , 0 , "SYS_RIO010", "PROC_RIO010", "PIO10" , "SPI4_SIO1", }, { "SPI0_SCLK" , "DPI_D7" , "RTS3" , "SCL1" , 0 , "SYS_RIO011", "PROC_RIO011", "PIO11" , "SPI4_SCLK", }, { "PWM0_CHAN0", "DPI_D8" , "TXD4" , "SDA2" , "AAUD_LEFT" , "SYS_RIO012", "PROC_RIO012", "PIO12" , "SPI5_CE0" , }, { "PWM0_CHAN1", "DPI_D9" , "RXD4" , "SCL2" , "AAUD_RIGHT" , "SYS_RIO013", "PROC_RIO013", "PIO13" , "SPI5_SIO1", }, { "PWM0_CHAN2", "DPI_D10" , "CTS4" , "SDA3" , "TXD0" , "SYS_RIO014", "PROC_RIO014", "PIO14" , "SPI5_SIO0", }, { "PWM0_CHAN3", "DPI_D11" , "RTS4" , "SCL3" , "RXD0" , "SYS_RIO015", "PROC_RIO015", "PIO15" , "SPI5_SCLK", }, { "SPI1_CE2" , "DPI_D12" , "DSI0_TE_EXT" , 0 , "CTS0" , "SYS_RIO016", "PROC_RIO016", "PIO16" , }, { "SPI1_CE1" , "DPI_D13" , "DSI1_TE_EXT" , 0 , "RTS0" , "SYS_RIO017", "PROC_RIO017", "PIO17" , }, { "SPI1_CE0" , "DPI_D14" , "I2S0_SCLK" , "PWM0_CHAN2" , "I2S1_SCLK" , "SYS_RIO018", "PROC_RIO018", "PIO18" , "GPCLK1", }, { "SPI1_MISO" , "DPI_D15" , "I2S0_WS" , "PWM0_CHAN3" , "I2S1_WS" , "SYS_RIO019", "PROC_RIO019", "PIO19" , }, { "SPI1_MOSI" , "DPI_D16" , "I2S0_SDI0" , "GPCLK0" , "I2S1_SDI0" , "SYS_RIO020", "PROC_RIO020", "PIO20" , }, { "SPI1_SCLK" , "DPI_D17" , "I2S0_SDO0" , "GPCLK1" , "I2S1_SDO0" , "SYS_RIO021", "PROC_RIO021", "PIO21" , }, { "SD0_CLK" , "DPI_D18" , "I2S0_SDI1" , "SDA3" , "I2S1_SDI1" , "SYS_RIO022", "PROC_RIO022", "PIO22" , }, { "SD0_CMD" , "DPI_D19" , "I2S0_SDO1" , "SCL3" , "I2S1_SDO1" , "SYS_RIO023", "PROC_RIO023", "PIO23" , }, { "SD0_DAT0" , "DPI_D20" , "I2S0_SDI2" , 0 , "I2S1_SDI2" , "SYS_RIO024", "PROC_RIO024", "PIO24" , "SPI2_CE1" , }, { "SD0_DAT1" , "DPI_D21" , "I2S0_SDO2" , "MIC_CLK" , "I2S1_SDO2" , "SYS_RIO025", "PROC_RIO025", "PIO25" , "SPI3_CE1" , }, { "SD0_DAT2" , "DPI_D22" , "I2S0_SDI3" , "MIC_DAT0" , "I2S1_SDI3" , "SYS_RIO026", "PROC_RIO026", "PIO26" , "SPI5_CE1" , }, { "SD0_DAT3" , "DPI_D23" , "I2S0_SDO3" , "MIC_DAT1" , "I2S1_SDO3" , "SYS_RIO027", "PROC_RIO027", "PIO27" , "SPI1_CE1" , }, { "SD1_CLK" , "SDA4" , "I2S2_SCLK" , "SPI6_MISO" , "VBUS_EN0" , "SYS_RIO10" , "PROC_RIO10" , }, { "SD1_CMD" , "SCL4" , "I2S2_WS" , "SPI6_MOSI" , "VBUS_OC0" , "SYS_RIO11" , "PROC_RIO11" , }, { "SD1_DAT0" , "SDA5" , "I2S2_SDI0" , "SPI6_SCLK" , "TXD5" , "SYS_RIO12" , "PROC_RIO12" , }, { "SD1_DAT1" , "SCL5" , "I2S2_SDO0" , "SPI6_CE0" , "RXD5" , "SYS_RIO13" , "PROC_RIO13" , }, { "SD1_DAT2" , "GPCLK3" , "I2S2_SDI1" , "SPI6_CE1" , "CTS5" , "SYS_RIO14" , "PROC_RIO14" , }, { "SD1_DAT3" , "GPCLK4" , "I2S2_SDO1" , "SPI6_CE2" , "RTS5" , "SYS_RIO15" , "PROC_RIO15" , }, { "PWM1_CHAN2", "GPCLK3" , "VBUS_EN0" , "SDA4" , "MIC_CLK" , "SYS_RIO20" , "PROC_RIO20" , }, { "SPI8_CE1" , "PWM1_CHAN0" , "VBUS_OC0" , "SCL4" , "MIC_DAT0" , "SYS_RIO21" , "PROC_RIO21" , }, { "SPI8_CE0" , "TXD5" , "PCIE_CLKREQ_N", "SDA5" , "MIC_DAT1" , "SYS_RIO22" , "PROC_RIO22" , }, { "SPI8_MISO" , "RXD5" , "MIC_CLK" , "SCL5" , "PCIE_CLKREQ_N", "SYS_RIO23" , "PROC_RIO23" , }, { "SPI8_MOSI" , "RTS5" , "MIC_DAT0" , "SDA6" , "AAUD_LEFT" , "SYS_RIO24" , "PROC_RIO24" , "DSI0_TE_EXT", }, { "SPI8_SCLK" , "CTS5" , "MIC_DAT1" , "SCL6" , "AAUD_RIGHT" , "SYS_RIO25" , "PROC_RIO25" , "DSI1_TE_EXT", }, { "PWM1_CHAN1", "TXD5" , "SDA4" , "SPI6_MISO" , "AAUD_LEFT" , "SYS_RIO26" , "PROC_RIO26" , }, { "PWM1_CHAN2", "RXD5" , "SCL4" , "SPI6_MOSI" , "AAUD_RIGHT" , "SYS_RIO27" , "PROC_RIO27" , }, { "GPCLK5" , "RTS5" , "VBUS_EN1" , "SPI6_SCLK" , "I2S2_SCLK" , "SYS_RIO28" , "PROC_RIO28" , }, { "GPCLK4" , "CTS5" , "VBUS_OC1" , "SPI6_CE0" , "I2S2_WS" , "SYS_RIO29" , "PROC_RIO29" , }, { "GPCLK5" , "SDA5" , "PWM1_CHAN0" , "SPI6_CE1" , "I2S2_SDI0" , "SYS_RIO210", "PROC_RIO210", }, { "PWM1_CHAN3", "SCL5" , "SPI7_CE0" , "SPI6_CE2" , "I2S2_SDO0" , "SYS_RIO211", "PROC_RIO211", }, { "GPCLK3" , "SDA4" , "SPI7_MOSI" , "MIC_CLK" , "I2S2_SDI1" , "SYS_RIO212", "PROC_RIO212", "DSI0_TE_EXT", }, { "GPCLK5" , "SCL4" , "SPI7_MISO" , "MIC_DAT0" , "I2S2_SDO1" , "SYS_RIO213", "PROC_RIO213", "DSI1_TE_EXT", }, { "PWM1_CHAN0", "PCIE_CLKREQ_N", "SPI7_SCLK" , "MIC_DAT1" , "TXD5" , "SYS_RIO214", "PROC_RIO214", }, { "SPI8_SCLK" , "SPI7_SCLK" , "SDA5" , "AAUD_LEFT" , "RXD5" , "SYS_RIO215", "PROC_RIO215", }, { "SPI8_MISO" , "SPI7_MOSI" , "SCL5" , "AAUD_RIGHT" , "VBUS_EN2" , "SYS_RIO216", "PROC_RIO216", }, { "SPI8_MOSI" , "SPI7_MISO" , "SDA6" , "AAUD_LEFT" , "VBUS_OC2" , "SYS_RIO217", "PROC_RIO217", }, { "SPI8_CE0" , 0 , "SCL6" , "AAUD_RIGHT" , "VBUS_EN3" , "SYS_RIO218", "PROC_RIO218", }, { "SPI8_CE1" , "SPI7_CE0" , 0 , "PCIE_CLKREQ_N", "VBUS_OC3" , "SYS_RIO219", "PROC_RIO219", }, }; static void rp1_gpio_get_bank(int num, int *bank, int *offset) { *bank = *offset = 0; if (num >= RP1_NUM_GPIOS) { assert(0); return; } if (num < rp1_bank_base[1]) *bank = 0; else if (num < rp1_bank_base[2]) *bank = 1; else *bank = 2; *offset = num - rp1_bank_base[*bank]; } static uint32_t rp1_gpio_ctrl_read(volatile uint32_t *base, int bank, int offset) { return rp1_gpio_read32(base, gpio_state.io[bank], RP1_GPIO_IO_REG_CTRL_OFFSET(offset)); } static void rp1_gpio_ctrl_write(volatile uint32_t *base, int bank, int offset, uint32_t value) { rp1_gpio_write32(base, gpio_state.io[bank], RP1_GPIO_IO_REG_CTRL_OFFSET(offset), value); } static uint32_t rp1_gpio_pads_read(volatile uint32_t *base, int bank, int offset) { return rp1_gpio_read32(base, gpio_state.pads[bank], RP1_GPIO_PADS_REG_OFFSET(offset)); } static void rp1_gpio_pads_write(volatile uint32_t *base, int bank, int offset, uint32_t value) { rp1_gpio_write32(base, gpio_state.pads[bank], RP1_GPIO_PADS_REG_OFFSET(offset), value); } static uint32_t rp1_gpio_sys_rio_out_read(volatile uint32_t *base, int bank, int offset) { UNUSED(offset); return rp1_gpio_read32(base, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OUT_OFFSET); } static uint32_t rp1_gpio_sys_rio_sync_in_read(volatile uint32_t *base, int bank, int offset) { UNUSED(offset); return rp1_gpio_read32(base, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_SYNC_IN_OFFSET); } static void rp1_gpio_sys_rio_out_set(volatile uint32_t *base, int bank, int offset) { rp1_gpio_write32(base, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OUT_OFFSET + RP1_SET_OFFSET, 1U << offset); } static void rp1_gpio_sys_rio_out_clr(volatile uint32_t *base, int bank, int offset) { rp1_gpio_write32(base, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OUT_OFFSET + RP1_CLR_OFFSET, 1U << offset); } static uint32_t rp1_gpio_sys_rio_oe_read(volatile uint32_t *base, int bank) { return rp1_gpio_read32(base, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OE_OFFSET); } static void rp1_gpio_sys_rio_oe_clr(volatile uint32_t *base, int bank, int offset) { rp1_gpio_write32(base, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OE_OFFSET + RP1_CLR_OFFSET, 1U << offset); } static void rp1_gpio_sys_rio_oe_set(volatile uint32_t *base, int bank, int offset) { rp1_gpio_write32(base, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OE_OFFSET + RP1_SET_OFFSET, 1U << offset); } static void rp1_gpio_set_dir(void *priv, uint32_t gpio, GPIO_DIR_T dir) { volatile uint32_t *base = priv; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); if (dir == DIR_INPUT) rp1_gpio_sys_rio_oe_clr(base, bank, offset); else if (dir == DIR_OUTPUT) rp1_gpio_sys_rio_oe_set(base, bank, offset); else assert(0); } static GPIO_DIR_T rp1_gpio_get_dir(void *priv, unsigned gpio) { volatile uint32_t *base = priv; int bank, offset; GPIO_DIR_T dir; uint32_t reg; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_sys_rio_oe_read(base, bank); dir = (reg & (1U << offset)) ? DIR_OUTPUT : DIR_INPUT; return dir; } static GPIO_FSEL_T rp1_gpio_get_fsel(void *priv, unsigned gpio) { volatile uint32_t *base = priv; int bank, offset; uint32_t reg; GPIO_FSEL_T fsel; RP1_FSEL_T rsel; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_ctrl_read(base, bank, offset); rsel = ((reg & RP1_GPIO_CTRL_FSEL_MASK) >> RP1_GPIO_CTRL_FSEL_LSB); if (rsel == RP1_FSEL_SYS_RIO) fsel = GPIO_FSEL_GPIO; else if (rsel == RP1_FSEL_NULL) fsel = GPIO_FSEL_NONE; else if (rsel < RP1_FSEL_COUNT) fsel = (GPIO_FSEL_T)rsel; else fsel = GPIO_FSEL_MAX; return fsel; } static void rp1_gpio_set_fsel(void *priv, unsigned gpio, const GPIO_FSEL_T func) { volatile uint32_t *base = priv; int bank, offset; uint32_t ctrl_reg; uint32_t pad_reg; uint32_t old_pad_reg; RP1_FSEL_T rsel; if (func < (GPIO_FSEL_T)RP1_FSEL_COUNT) rsel = (RP1_FSEL_T)func; else if (func == GPIO_FSEL_INPUT || func == GPIO_FSEL_OUTPUT || func == GPIO_FSEL_GPIO) rsel = RP1_FSEL_SYS_RIO; else if (func == GPIO_FSEL_NONE) rsel = RP1_FSEL_NULL; else return; rp1_gpio_get_bank(gpio, &bank, &offset); if (func == GPIO_FSEL_INPUT) rp1_gpio_set_dir(priv, gpio, DIR_INPUT); else if (func == GPIO_FSEL_OUTPUT) rp1_gpio_set_dir(priv, gpio, DIR_OUTPUT); ctrl_reg = rp1_gpio_ctrl_read(base, bank, offset) & ~RP1_GPIO_CTRL_FSEL_MASK; ctrl_reg |= rsel << RP1_GPIO_CTRL_FSEL_LSB; rp1_gpio_ctrl_write(base, bank, offset, ctrl_reg); pad_reg = rp1_gpio_pads_read(base, bank, offset); old_pad_reg = pad_reg; if (rsel == RP1_FSEL_NULL) { // Disable input pad_reg &= ~RP1_PADS_IE_SET; } else { // Enable input pad_reg |= RP1_PADS_IE_SET; } if (rsel != RP1_FSEL_NULL) { // Enable peripheral func output pad_reg &= ~RP1_PADS_OD_SET; } else { // Disable peripheral func output pad_reg |= RP1_PADS_OD_SET; } if (pad_reg != old_pad_reg) rp1_gpio_pads_write(base, bank, offset, pad_reg); } static int rp1_gpio_get_level(void *priv, unsigned gpio) { volatile uint32_t *base = priv; int bank, offset; uint32_t pad_reg; uint32_t reg; int level; rp1_gpio_get_bank(gpio, &bank, &offset); pad_reg = rp1_gpio_pads_read(base, bank, offset); if (!(pad_reg & RP1_PADS_IE_SET)) return -1; reg = rp1_gpio_sys_rio_sync_in_read(base, bank, offset); level = (reg & (1U << offset)) ? 1 : 0; return level; } static void rp1_gpio_set_drive(void *priv, unsigned gpio, GPIO_DRIVE_T drv) { volatile uint32_t *base = priv; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); if (drv == DRIVE_HIGH) rp1_gpio_sys_rio_out_set(base, bank, offset); else if (drv == DRIVE_LOW) rp1_gpio_sys_rio_out_clr(base, bank, offset); } static void rp1_gpio_set_pull(void *priv, unsigned gpio, GPIO_PULL_T pull) { volatile uint32_t *base = priv; uint32_t reg; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_pads_read(base, bank, offset); reg &= ~(RP1_PADS_PDE_SET | RP1_PADS_PUE_SET); if (pull == PULL_UP) reg |= RP1_PADS_PUE_SET; else if (pull == PULL_DOWN) reg |= RP1_PADS_PDE_SET; rp1_gpio_pads_write(base, bank, offset, reg); } static GPIO_PULL_T rp1_gpio_get_pull(void *priv, unsigned gpio) { volatile uint32_t *base = priv; uint32_t reg; GPIO_PULL_T pull = PULL_NONE; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_pads_read(base, bank, offset); if (reg & RP1_PADS_PUE_SET) pull = PULL_UP; else if (reg & RP1_PADS_PDE_SET) pull = PULL_DOWN; return pull; } static GPIO_DRIVE_T rp1_gpio_get_drive(void *priv, unsigned gpio) { volatile uint32_t *base = priv; uint32_t reg; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_sys_rio_out_read(base, bank, offset); return (reg & (1U << offset)) ? DRIVE_HIGH : DRIVE_LOW; } static const char *rp1_gpio_get_name(void *priv, unsigned gpio) { static char name_buf[16]; UNUSED(priv); if (gpio >= RP1_NUM_GPIOS) return NULL; sprintf(name_buf, "GPIO%d", gpio); return name_buf; } static const char *rp1_gpio_get_fsel_name(void *priv, unsigned gpio, GPIO_FSEL_T fsel) { const char *name = NULL; UNUSED(priv); switch (fsel) { case GPIO_FSEL_GPIO: name = "gpio"; break; case GPIO_FSEL_INPUT: name = "input"; break; case GPIO_FSEL_OUTPUT: name = "output"; break; case GPIO_FSEL_NONE: name = "none"; break; case GPIO_FSEL_FUNC0: case GPIO_FSEL_FUNC1: case GPIO_FSEL_FUNC2: case GPIO_FSEL_FUNC3: case GPIO_FSEL_FUNC4: case GPIO_FSEL_FUNC5: case GPIO_FSEL_FUNC6: case GPIO_FSEL_FUNC7: case GPIO_FSEL_FUNC8: if (gpio < RP1_NUM_GPIOS) { name = rp1_gpio_fsel_names[gpio][fsel - GPIO_FSEL_FUNC0]; if (!name) name = "-"; } break; default: return NULL; } return name; } static void *rp1_gpio_create_instance(const GPIO_CHIP_T *chip, const char *dtnode) { UNUSED(dtnode); return (void *)chip; } static int rp1_gpio_count(void *priv) { UNUSED(priv); return RP1_NUM_GPIOS; } static void *rp1_gpio_probe_instance(void *priv, volatile uint32_t *base) { UNUSED(priv); return (void *)base; } static const GPIO_CHIP_INTERFACE_T rp1_gpio_interface = { .gpio_create_instance = rp1_gpio_create_instance, .gpio_count = rp1_gpio_count, .gpio_probe_instance = rp1_gpio_probe_instance, .gpio_get_fsel = rp1_gpio_get_fsel, .gpio_set_fsel = rp1_gpio_set_fsel, .gpio_set_drive = rp1_gpio_set_drive, .gpio_set_dir = rp1_gpio_set_dir, .gpio_get_dir = rp1_gpio_get_dir, .gpio_get_level = rp1_gpio_get_level, .gpio_get_drive = rp1_gpio_get_drive, .gpio_get_pull = rp1_gpio_get_pull, .gpio_set_pull = rp1_gpio_set_pull, .gpio_get_name = rp1_gpio_get_name, .gpio_get_fsel_name = rp1_gpio_get_fsel_name, }; DECLARE_GPIO_CHIP(rp1, "raspberrypi,rp1-gpio", &rp1_gpio_interface, 0x30000, 0); raspi-utils-20250514/pinctrl/gpiolib.c000066400000000000000000000446461501106437300175140ustar00rootroot00000000000000#define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include "gpiochip.h" #include "util.h" #define MAX_GPIO_CHIPS 8 typedef struct GPIO_CHIP_INSTANCE_ { const GPIO_CHIP_T *chip; const char *name; const char *dtnode; int mem_fd; void *priv; uint64_t phys_addr; unsigned num_gpios; uint32_t base; } GPIO_CHIP_INSTANCE_T; static unsigned num_gpio_chips; static GPIO_CHIP_INSTANCE_T gpio_chips[MAX_GPIO_CHIPS]; static unsigned num_gpios; static unsigned first_hdr_pin = GPIO_INVALID; static unsigned last_hdr_pin = GPIO_INVALID; static const char *gpio_names[MAX_GPIO_PINS]; static unsigned hdr_gpios[NUM_HDR_PINS + 1]; const char *pull_names[] = { "pn", "pd", "pu", "--" }; const char *drive_names[] = { "dl", "dh", "--" }; const char *fsel_names[] = { "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "??", "??", "??", "??", "??", "??", "??", "ip", "op", "gp", "no" }; void (*verbose_callback)(const char *); static GPIO_CHIP_INSTANCE_T *gpio_create_instance(const GPIO_CHIP_T *chip, uint64_t phys_addr, const char *name, const char *dtnode) { GPIO_CHIP_INSTANCE_T *inst = &gpio_chips[num_gpio_chips]; if (num_gpio_chips >= MAX_GPIO_CHIPS) { assert(0); return NULL; } inst->chip = chip; inst->name = name ? name : chip->name; inst->dtnode = dtnode; inst->phys_addr = phys_addr; inst->priv = NULL; inst->base = 0; inst->priv = chip->interface->gpio_create_instance(chip, dtnode); if (!inst->priv) return NULL; num_gpio_chips++; return inst; } static int gpio_get_interface(unsigned gpio, const GPIO_CHIP_INTERFACE_T **iface_ptr, void **priv, unsigned *offset) { unsigned i; *iface_ptr = NULL; for (i = 0; i < num_gpio_chips; i++) { GPIO_CHIP_INSTANCE_T *inst = &gpio_chips[i]; const GPIO_CHIP_T *chip = inst->chip; if (gpio >= inst->base && gpio < (inst->base + inst->num_gpios)) { *iface_ptr = chip->interface; *priv = inst->priv; *offset = gpio - inst->base; return 0; } } return -1; } int gpio_num_is_valid(unsigned gpio) { return gpio < MAX_GPIO_PINS && !!gpio_names[gpio]; } GPIO_DIR_T gpio_get_dir(unsigned gpio) { const GPIO_CHIP_INTERFACE_T *iface = NULL; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) return iface->gpio_get_dir(priv, gpio_offset); return DIR_MAX; } void gpio_set_dir(unsigned gpio, GPIO_DIR_T dir) { const GPIO_CHIP_INTERFACE_T *iface = NULL; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) iface->gpio_set_dir(priv, gpio_offset, dir); } GPIO_FSEL_T gpio_get_fsel(unsigned gpio) { const GPIO_CHIP_INTERFACE_T *iface = NULL; GPIO_FSEL_T fsel = GPIO_FSEL_MAX; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) fsel = iface->gpio_get_fsel(priv, gpio_offset); if (fsel == GPIO_FSEL_GPIO) { if (gpio_get_dir(gpio) == DIR_OUTPUT) fsel = GPIO_FSEL_OUTPUT; else fsel = GPIO_FSEL_INPUT; } return fsel; } void gpio_set_fsel(unsigned gpio, const GPIO_FSEL_T func) { const GPIO_CHIP_INTERFACE_T *iface = NULL; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) iface->gpio_set_fsel(priv, gpio_offset, func); } void gpio_set_drive(unsigned gpio, GPIO_DRIVE_T drv) { const GPIO_CHIP_INTERFACE_T *iface = NULL; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) iface->gpio_set_drive(priv, gpio_offset, drv); } void gpio_set(unsigned gpio) { const GPIO_CHIP_INTERFACE_T *iface = NULL; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) { iface->gpio_set_drive(priv, gpio_offset, 1); iface->gpio_set_dir(priv, gpio_offset, DIR_OUTPUT); } } void gpio_clear(unsigned gpio) { const GPIO_CHIP_INTERFACE_T *iface = NULL; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) { iface->gpio_set_drive(priv, gpio_offset, 0); iface->gpio_set_dir(priv, gpio_offset, DIR_OUTPUT); } } int gpio_get_level(unsigned gpio) { const GPIO_CHIP_INTERFACE_T *iface = NULL; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) return iface->gpio_get_level(priv, gpio_offset); return 0; } GPIO_DRIVE_T gpio_get_drive(unsigned gpio) { const GPIO_CHIP_INTERFACE_T *iface = NULL; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) return iface->gpio_get_drive(priv, gpio_offset); return DRIVE_MAX; } GPIO_PULL_T gpio_get_pull(unsigned gpio) { const GPIO_CHIP_INTERFACE_T *iface = NULL; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) return iface->gpio_get_pull(priv, gpio_offset); return PULL_MAX; } void gpio_set_pull(unsigned gpio, GPIO_PULL_T pull) { const GPIO_CHIP_INTERFACE_T *iface = NULL; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) iface->gpio_set_pull(priv, gpio_offset, pull); } void gpio_get_pin_range(unsigned *first, unsigned *last) { if (first_hdr_pin == GPIO_INVALID) { unsigned i; for (i = 0; i < num_gpio_chips; i++) { if (!strncmp(gpio_chips[i].name, "bcm2", 4) || !strcmp(gpio_chips[i].name, "rp1")) { // Assume it's the standard RPi 40-pin header layout uint32_t base = gpio_chips[i].base; hdr_gpios[3] = base + 2; hdr_gpios[5] = base + 3; hdr_gpios[7] = base + 4; hdr_gpios[8] = base + 14; hdr_gpios[10] = base + 15; hdr_gpios[11] = base + 17; hdr_gpios[12] = base + 18; hdr_gpios[13] = base + 27; hdr_gpios[15] = base + 22; hdr_gpios[16] = base + 23; hdr_gpios[18] = base + 24; hdr_gpios[19] = base + 10; hdr_gpios[21] = base + 9; hdr_gpios[18] = base + 24; hdr_gpios[22] = base + 25; hdr_gpios[23] = base + 11; hdr_gpios[24] = base + 8; hdr_gpios[26] = base + 7; hdr_gpios[27] = base + 0; hdr_gpios[28] = base + 1; hdr_gpios[29] = base + 5; hdr_gpios[31] = base + 6; hdr_gpios[32] = base + 12; hdr_gpios[33] = base + 13; hdr_gpios[35] = base + 19; hdr_gpios[36] = base + 16; hdr_gpios[37] = base + 26; hdr_gpios[38] = base + 20; hdr_gpios[40] = base + 21; first_hdr_pin = 1; last_hdr_pin = 40; break; } } } if (first) *first = first_hdr_pin; if (last) *last = last_hdr_pin; } unsigned gpio_for_pin(int pin) { if (pin >= 1 && pin <= NUM_HDR_PINS) return hdr_gpios[pin]; return GPIO_INVALID; } int gpio_to_pin(unsigned gpio) { int i; for (i = 1; i <= NUM_HDR_PINS; i++) { if (hdr_gpios[i] == gpio) return i; } return -1; } unsigned gpio_get_gpio_by_name(const char *name, int name_len) { unsigned gpio; if (!name_len) name_len = strlen(name); for (gpio = 0; gpio < num_gpios; gpio++) { const char *gpio_name = gpio_names[gpio]; const char *p; if (!gpio_name) continue; for (p = gpio_name; *p; ) { int len = strcspn(p, "/"); if (len == name_len && memcmp(name, p, name_len) == 0) return gpio; p += len; if (*p == '/') p++; } } return GPIO_INVALID; } const char *gpio_get_name(unsigned gpio) { if (gpio < num_gpios) return gpio_names[gpio]; switch (gpio) { case GPIO_INVALID: return "-"; case GPIO_GND: return "gnd"; case GPIO_5V: return "5v"; case GPIO_3V3: return "3v3"; case GPIO_1V8: return "1v8"; case GPIO_OTHER: default: return "???"; } } const char *gpio_get_gpio_fsel_name(unsigned gpio, GPIO_FSEL_T fsel) { const GPIO_CHIP_INTERFACE_T *iface = NULL; unsigned gpio_offset; void *priv; if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0) return iface->gpio_get_fsel_name(priv, gpio_offset, fsel); return NULL; } const char *gpio_get_fsel_name(GPIO_FSEL_T fsel) { if ((unsigned)fsel < ARRAY_SIZE(fsel_names)) return fsel_names[fsel]; return NULL; } const char *gpio_get_pull_name(GPIO_PULL_T pull) { if ((unsigned)pull < ARRAY_SIZE(pull_names)) return pull_names[pull]; return NULL; } const char *gpio_get_drive_name(GPIO_DRIVE_T drive) { if ((unsigned)drive < ARRAY_SIZE(drive_names)) return drive_names[drive]; return NULL; } static const GPIO_CHIP_T *gpio_find_chip(const char *name) { #if LIBRARY_BUILD const GPIO_CHIP_T *const *start = &library_gpiochips[0]; const GPIO_CHIP_T *const *end = &library_gpiochips[0] + library_gpiochips_count; #else const GPIO_CHIP_T *const *start = &__start_gpiochips; const GPIO_CHIP_T *const *end = &__stop_gpiochips; #endif const GPIO_CHIP_T *const *chip_ptr; for (chip_ptr = start; name && chip_ptr < end; chip_ptr++) { const GPIO_CHIP_T *chip = *chip_ptr; if (!strcmp(name, chip->name) || !strcmp(name, chip->compatible)) return chip; } return NULL; } int gpiolib_init(void) { const GPIO_CHIP_T *chip; GPIO_CHIP_INSTANCE_T *inst; char pathbuf[FILENAME_MAX]; char gpiomem_idx[4]; const char *dtpath = "/proc/device-tree"; const char *p; char *alias = NULL, *names, *end, *compatible; uint64_t phys_addr; size_t names_len; unsigned gpio_base; unsigned pin, i; for (pin = 0; pin <= NUM_HDR_PINS; pin++) hdr_gpios[pin] = GPIO_INVALID; // There is currently only one header layout hdr_gpios[1] = GPIO_3V3; hdr_gpios[17] = GPIO_3V3; hdr_gpios[2] = GPIO_5V; hdr_gpios[4] = GPIO_5V; hdr_gpios[6] = GPIO_GND; hdr_gpios[9] = GPIO_GND; hdr_gpios[14] = GPIO_GND; hdr_gpios[20] = GPIO_GND; hdr_gpios[25] = GPIO_GND; hdr_gpios[30] = GPIO_GND; hdr_gpios[34] = GPIO_GND; hdr_gpios[39] = GPIO_GND; if (verbose_callback) (*verbose_callback)("GPIO chips:\n"); dt_set_path(dtpath); for (i = 0; ; i++) { sprintf(pathbuf, "gpio%d", i); sprintf(gpiomem_idx, "%d", i); alias = dt_read_prop("/aliases", pathbuf, NULL); if (!alias && i == 0) { alias = dt_read_prop("/aliases", "gpio", NULL); gpiomem_idx[0] = 0; } if (!alias) break; compatible = dt_read_prop(alias, "compatible", NULL); if (!compatible) { sprintf(pathbuf, "%s/..", alias); compatible = dt_read_prop(pathbuf, "compatible", NULL); } chip = gpio_find_chip(compatible); dt_free(compatible); if (!chip) { // Skip the unknown gpio chip dt_free(alias); continue; } phys_addr = dt_parse_addr(alias); if (phys_addr == INVALID_ADDRESS) { dt_free(alias); return -1; } inst = gpio_create_instance(chip, phys_addr, NULL, alias); if (!inst) { dt_free(alias); return -1; } sprintf(pathbuf, "/dev/gpiomem%s", gpiomem_idx); inst->mem_fd = open(pathbuf, O_RDWR|O_SYNC); } gpio_base = 0; num_gpios = 0; for (i = 0; i < num_gpio_chips; i++) { unsigned gpio; inst = &gpio_chips[i]; inst->base = gpio_base; chip = inst->chip; inst->num_gpios = chip->interface->gpio_count(inst->priv); if (verbose_callback) { char msg_buf[100]; snprintf(msg_buf, sizeof(msg_buf), " %" PRIx64 ": %s (%d gpios)\n", inst->phys_addr, chip->name, inst->num_gpios); (*verbose_callback)(msg_buf); } if (!inst->num_gpios) continue; num_gpios = gpio_base + inst->num_gpios; gpio_base = ROUND_UP(num_gpios, 100); if (num_gpios > MAX_GPIO_PINS) return -1; names = dt_read_prop(inst->dtnode, "gpio-line-names", &names_len); end = names + names_len; for (gpio = 0, p = names; gpio < inst->num_gpios; gpio++) { static const char *names[] = { "ID_SD", "ID_SC" }; static const int pins[] = { 27, 28 }; char name_buf[32]; const char *name = "-", *arch_name; if (p && p < end) { name = p; p += strlen(p) + 1; if (sscanf(name, "PIN%u", &pin) != 1 || pin < 1 || pin > NUM_HDR_PINS) { unsigned i; pin = 0; for (i = 0; i < ARRAY_SIZE(names); i++) { if (strcmp(name, names[i]) == 0) { pin = pins[i]; break; } } } if (pin >= 1) { hdr_gpios[pin] = inst->base + gpio; if ((pin < first_hdr_pin) || (first_hdr_pin == GPIO_INVALID)) first_hdr_pin = pin; if ((pin > last_hdr_pin) || (last_hdr_pin == GPIO_INVALID)) last_hdr_pin = pin; } } arch_name = chip->interface->gpio_get_name(inst->priv, gpio); if (!name[0] || !arch_name) { gpio_names[inst->base + gpio] = NULL; continue; } if (strcmp(name, arch_name) != 0) { if (strcmp(name, "-") == 0) { name = arch_name; } else { if (snprintf(name_buf, sizeof(name_buf), "%s/%s", name, arch_name) >= (int)sizeof(name_buf)) name_buf[sizeof(name_buf) - 1] = '\0'; name = name_buf; } } gpio_names[inst->base + gpio] = strdup(name); } dt_free(names); } // On a board with PINs, show pins 1-40 if (first_hdr_pin == 3) first_hdr_pin = 1; return (int)num_gpios; } int gpiolib_init_by_name(const char *name) { const GPIO_CHIP_T *chip; GPIO_CHIP_INSTANCE_T *inst; unsigned gpio; int pin; for (pin = 0; pin <= NUM_HDR_PINS; pin++) hdr_gpios[pin] = GPIO_INVALID; if (verbose_callback) (*verbose_callback)("GPIO chips:\n"); chip = gpio_find_chip(name); if (!chip) return 0; inst = gpio_create_instance(chip, 0, NULL, NULL); if (!inst) return -1; inst->num_gpios = chip->interface->gpio_count(inst->priv); num_gpios = inst->num_gpios; for (gpio = 0; gpio < inst->num_gpios; gpio++) { const char *name = chip->interface->gpio_get_name(inst->priv, gpio); if (!name) { gpio_names[inst->base + gpio] = NULL; continue; } gpio_names[gpio] = strdup(name); } if (inst->num_gpios && verbose_callback) { char msg_buf[100]; snprintf(msg_buf, sizeof(msg_buf), " %s (%d gpios)\n", chip->name, inst->num_gpios); (*verbose_callback)(msg_buf); } return (int)num_gpios; } int gpiolib_mmap(void) { int pagesize = getpagesize(); int mem_fd = -1; unsigned i; for (i = 0; i < num_gpio_chips; i++) { GPIO_CHIP_INSTANCE_T *inst; const GPIO_CHIP_T *chip; void *gpio_map; void *new_priv; unsigned align; inst = &gpio_chips[i]; chip = inst->chip; align = inst->phys_addr & (pagesize - 1); if (inst->mem_fd >= 0) { gpio_map = mmap( NULL, /* Any address in our space will do */ chip->size + align, /* Map length */ PROT_READ | PROT_WRITE, /* Enable reading & writing */ MAP_SHARED, /* Shared with other processes */ inst->mem_fd, /* File to map */ 0 /* Offset to GPIO peripheral */ ); } else { if (mem_fd < 0) { mem_fd = open("/dev/mem", O_RDWR|O_SYNC); if (mem_fd < 0) return errno; } gpio_map = mmap( NULL, /* Any address in our space will do */ chip->size + align, /* Map length */ PROT_READ | PROT_WRITE, /* Enable reading & writing */ MAP_SHARED, /* Shared with other processes */ mem_fd, /* File to map */ inst->phys_addr - align /* Offset to GPIO peripheral */ ); } if (gpio_map == MAP_FAILED) return errno; new_priv = chip->interface->gpio_probe_instance(inst->priv, (void *)((char *)gpio_map + align)); if (!new_priv) return -1; inst->priv = new_priv; } return 0; } void gpiolib_set_verbose(void (*callback)(const char *)) { verbose_callback = callback; } raspi-utils-20250514/pinctrl/gpiolib.h000066400000000000000000000040751501106437300175110ustar00rootroot00000000000000#ifndef GPIOLIB_H #define GPIOLIB_H #include #define NUM_HDR_PINS 40 #define MAX_GPIO_PINS 300 #define GPIO_INVALID (~0U) #define GPIO_GND (~1U) #define GPIO_5V (~2U) #define GPIO_3V3 (~3U) #define GPIO_1V8 (~4U) #define GPIO_OTHER (~5U) typedef enum { GPIO_FSEL_FUNC0, GPIO_FSEL_FUNC1, GPIO_FSEL_FUNC2, GPIO_FSEL_FUNC3, GPIO_FSEL_FUNC4, GPIO_FSEL_FUNC5, GPIO_FSEL_FUNC6, GPIO_FSEL_FUNC7, GPIO_FSEL_FUNC8, /* ... */ GPIO_FSEL_INPUT = 0x10, GPIO_FSEL_OUTPUT, GPIO_FSEL_GPIO, /* Preserves direction if possible, else input */ GPIO_FSEL_NONE, /* If possible, else input */ GPIO_FSEL_MAX } GPIO_FSEL_T; typedef enum { PULL_NONE, PULL_DOWN, PULL_UP, PULL_MAX } GPIO_PULL_T; typedef enum { DIR_INPUT, DIR_OUTPUT, DIR_MAX, } GPIO_DIR_T; typedef enum { DRIVE_LOW, DRIVE_HIGH, DRIVE_MAX } GPIO_DRIVE_T; int gpiolib_init(void); int gpiolib_init_by_name(const char *name); int gpiolib_mmap(void); void gpiolib_set_verbose(void (*callback)(const char *)); int gpio_num_is_valid(unsigned gpio); GPIO_DIR_T gpio_get_dir(unsigned gpio); void gpio_set_dir(unsigned gpio, GPIO_DIR_T dir); GPIO_FSEL_T gpio_get_fsel(unsigned gpio); void gpio_set_fsel(unsigned gpio, const GPIO_FSEL_T func); void gpio_set_drive(unsigned gpio, GPIO_DRIVE_T drv); void gpio_set(unsigned gpio); void gpio_clear(unsigned gpio); int gpio_get_level(unsigned gpio); /* The actual level observed */ GPIO_DRIVE_T gpio_get_drive(unsigned gpio); /* What it is being driven as */ GPIO_PULL_T gpio_get_pull(unsigned gpio); void gpio_set_pull(unsigned gpio, GPIO_PULL_T pull); void gpio_get_pin_range(unsigned *first, unsigned *last); unsigned gpio_for_pin(int pin); int gpio_to_pin(unsigned gpio); unsigned gpio_get_gpio_by_name(const char *name, int namelen); const char *gpio_get_name(unsigned gpio); const char *gpio_get_gpio_fsel_name(unsigned gpio, GPIO_FSEL_T fsel); const char *gpio_get_fsel_name(GPIO_FSEL_T fsel); const char *gpio_get_pull_name(GPIO_PULL_T pull); const char *gpio_get_drive_name(GPIO_DRIVE_T drive); #endif raspi-utils-20250514/pinctrl/library_gpiochips.c000066400000000000000000000014711501106437300215650ustar00rootroot00000000000000#include "gpiochip.h" #include "util.h" #define GPIO_CHIP(name) name ## _chip #define EXTERN_GPIO_CHIP(name) extern const GPIO_CHIP_T GPIO_CHIP(name) EXTERN_GPIO_CHIP(bcm2835); EXTERN_GPIO_CHIP(bcm2711); EXTERN_GPIO_CHIP(bcm2712); EXTERN_GPIO_CHIP(bcm2712_aon); EXTERN_GPIO_CHIP(bcm2712c0); EXTERN_GPIO_CHIP(bcm2712c0_aon); EXTERN_GPIO_CHIP(bcm2712d0); EXTERN_GPIO_CHIP(bcm2712d0_aon); EXTERN_GPIO_CHIP(brcmstb); EXTERN_GPIO_CHIP(rp1); const GPIO_CHIP_T *const library_gpiochips[] = { &GPIO_CHIP(bcm2835), &GPIO_CHIP(bcm2711), &GPIO_CHIP(bcm2712), &GPIO_CHIP(bcm2712_aon), &GPIO_CHIP(bcm2712c0), &GPIO_CHIP(bcm2712c0_aon), &GPIO_CHIP(bcm2712d0), &GPIO_CHIP(bcm2712d0_aon), &GPIO_CHIP(brcmstb), &GPIO_CHIP(rp1), }; const int library_gpiochips_count = ARRAY_SIZE(library_gpiochips); raspi-utils-20250514/pinctrl/pinctrl-completion.bash000066400000000000000000000052731501106437300223750ustar00rootroot00000000000000_pinctrl () { local cur prev words cword split cmd pins pincomp ALLPINS CHIPS chip pinmode=false prefix arg i func pull; local opts="no op ip a0 a1 a2 a3 a4 a5 a6 a7 a8 dh dl pu pd pn "; _init_completion -s || return; i=1 while [[ $i -lt $cword && ${COMP_WORDS[$i]} =~ ^- ]]; do arg=${COMP_WORDS[$i]} if [[ "$arg" == "-c" ]]; then chip=${COMP_WORDS[$((i + 1))]} i=$((i + 2)) else if [[ "$arg" == "-p" ]]; then pinmode=true fi i=$((i + 1)) fi done if [[ $i -lt $cword && ${COMP_WORDS[$i]} =~ ^(get|set|funcs|poll|help) ]]; then cmd=${COMP_WORDS[$i]} i=$((i + 1)) elif [[ ${COMP_WORDS[$i]} =~ ^[A-Z0-9] ]]; then cmd="set" fi if [[ "$cmd" != "" && $i -lt $cword ]]; then pins=${COMP_WORDS[$i]}; i=$((i + 1)) while [[ $i -lt $cword && ${COMP_WORDS[$i]} =~ ^[-,] ]]; do pins="$pins${COMP_WORDS[$i]}" i=$((i + 1)) done fi if [[ "$pins" != "" ]]; then while [[ $i -lt $cword ]]; do arg=${COMP_WORDS[$i]} if [[ $arg =~ ^(no|ip|op|a[0-8])$ ]]; then func=$arg opts=$(echo "$opts" | sed -E "s/(no|ip|op|a[0-8]) //g") if [[ $func != "op" ]]; then opts=$(echo "$opts" | sed -E "s/(dh|dl) //g") fi elif [[ $arg =~ ^(pu|pd|pn)$ ]]; then pull=$arg opts=$(echo "$opts" | sed -E "s/(pu|pd|pn) //g") fi i=$((i + 1)) done fi if [[ "$cmd" != "" && ( "$pins" == "" || "${cur}" =~ [-,] ) ]]; then prefix=$(echo $cur | sed 's/[^-,]*$//') if $pinmode; then ALLPINS={1..40} else ALLPINS=($(pinctrl get | cut -d'/' -f4 | cut -d' ' -f1) $(pinctrl get | cut -d'/' -f3 | cut -d' ' -f2)) fi pincomp="${ALLPINS[@]/#/$prefix}" COMPREPLY+=($(compgen -W "$pincomp" -- $cur)) elif [[ "$cmd" != "" ]]; then if [[ "$cmd" == "set" ]]; then COMPREPLY+=($(compgen -W "$opts" -- $cur)) fi else if [[ "$prev" == "-c" ]]; then CHIPS=($(pinctrl -v -p 0 | grep 'gpios)' | cut -d' ' -f4 | sort | uniq)) chips="${CHIPS[@]}" COMPREPLY+=($(compgen -W "$chips" -- $cur)) elif [[ "$cur" =~ ^- ]]; then COMPREPLY+=($(compgen -W "-p -h -v -c" -- $cur)) elif [[ "$chip" == "" ]]; then COMPREPLY+=($(compgen -W "get set poll funcs help" -- $cur)) else COMPREPLY+=($(compgen -W "funcs help" -- $cur)) fi fi } complete -F _pinctrl pinctrl raspi-utils-20250514/pinctrl/pinctrl.c000066400000000000000000000450661501106437300175370ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gpiolib.h" #define ARRAY_SIZE(_a) (sizeof(_a)/sizeof(_a[0])) const char *program_name = "pinctrl"; static int pin_mode = 0; static int verbose_mode = 0; static unsigned num_gpios; struct poll_gpio_state { unsigned int num; unsigned int gpio; const char *name; int level; }; int num_poll_gpios; struct poll_gpio_state *poll_gpios; static void print_gpio_alts_info(unsigned gpio) { const char *name; unsigned int num = gpio; int fsel; if (pin_mode) gpio = gpio_for_pin(num); if (!gpio_num_is_valid(gpio)) return; name = gpio_get_name(gpio); if (pin_mode && strchr(name, '/')) name = strchr(name, '/') + 1; printf("%d", num); if (name[0]) printf(", %s", name); for (fsel = GPIO_FSEL_FUNC0; ; fsel++) { name = gpio_get_gpio_fsel_name(gpio, fsel); if (!name) break; printf(", %s", name); } printf("\n"); } static void usage(void) { const char *name = program_name; printf("\n"); printf("WARNING! %s set writes directly to the GPIO control registers\n", name); printf("ignoring whatever else may be using them (such as Linux drivers) -\n"); printf("it is designed as a debug tool, only use it if you know what you\n"); printf("are doing and at your own risk!\n"); printf("\n"); printf("Running %s with the help argument prints this help.\n", name); printf("%s can get and print the state of a GPIO (or all GPIOs)\n", name); printf("and can be used to set the function, pulls and value of a GPIO.\n"); printf("%s must be run as root.\n", name); printf("Use:\n"); printf(" %s [-p] [-v] get [GPIO]\n", name); printf("OR\n"); printf(" %s [-p] [-v] [-e] set [options]\n", name); printf("OR\n"); printf(" %s [-p] [-v] poll \n", name); printf("OR\n"); printf(" %s [-p] [-v] funcs [GPIO]\n", name); printf("OR\n"); printf(" %s [-p] [-v] lev [GPIO]\n", name); printf("OR\n"); printf(" %s -c [funcs] [GPIO]\n", name); printf("\n"); printf("GPIO is a comma-separated list of GPIO names, numbers or ranges (without\n"); printf("spaces), e.g. 4 or 18-21 or BT_ON,9-11\n"); printf("\n"); printf("Note that omitting [GPIO] from \"%s get\" prints all GPIOs.\n", name); printf("If the -p option is given, GPIO numbers are replaced by pin numbers on the\n"); printf("40-way header. If the -v option is given, the output is more verbose. Including\n"); printf("the -e option in a \"set\" causes pinctrl to echo back the new pin states.\n"); printf("%s funcs will dump all the possible GPIO alt functions in CSV format\n", name); printf("or if [GPIO] is specified the alternate funcs just for that specific GPIO.\n"); printf("The -c option allows the alt functions (and only the alt function) for a named\n"); printf("chip to be displayed, even if that chip is not present in the current system.\n"); printf("\n"); printf("Valid [options] for %s set are:\n", name); printf(" ip set GPIO as input\n"); printf(" op set GPIO as output\n"); printf(" a0-a8 set GPIO to alt function in the range 0 to 8 (range varies by model)\n"); printf(" no set GPIO to no function (NONE)\n"); printf(" pu set GPIO in-pad pull up\n"); printf(" pd set GPIO in-pad pull down\n"); printf(" pn set GPIO pull none (no pull)\n"); printf(" dh set GPIO to drive high (1) level (only valid if set to be an output)\n"); printf(" dl set GPIO to drive low (0) level (only valid if set to be an output)\n"); printf("Examples:\n"); printf(" %s get Prints state of all GPIOs one per line\n", name); printf(" %s get 10 Prints state of GPIO10\n", name); printf(" %s get 10,11 Prints state of GPIO10 and GPIO11\n", name); printf(" %s set 10 a2 Set GPIO10 to fsel 2 function (nand_wen_clk)\n", name); printf(" %s -e set 10 pu Enable GPIO10 ~50k in-pad pull up, echoing the result\n", name); printf(" %s set 10 pd Enable GPIO10 ~50k in-pad pull down\n", name); printf(" %s set 10 op Set GPIO10 to be an output\n", name); printf(" %s set 10 dl Set GPIO10 to output low/zero (must already be set as an output)\n", name); printf(" %s set 10 ip pd Set GPIO10 to input with pull down\n", name); printf(" %s set 35 a1 pu Set GPIO35 to fsel 1 (jtag_2_clk) with pull up\n", name); printf(" %s set 20 op pn dh Set GPIO20 to output with no pull and driving high\n", name); printf(" %s lev 4 Prints the level (1 or 0) of GPIO4\n", name); printf(" %s -c bcm2835 9-11 Display the alt functions for GPIOs 9-11 on bcm2835\n", name); } static int do_gpio_get(unsigned int gpio) { unsigned int num = gpio; const char *name; int fsel; int level; if (pin_mode) { gpio = gpio_for_pin(num); switch (gpio) { case GPIO_INVALID: case GPIO_GND: case GPIO_5V: case GPIO_3V3: case GPIO_1V8: case GPIO_OTHER: name = gpio_get_name(gpio); printf("%2d: %s\n", num, name); return 0; } } if (!gpio_num_is_valid(gpio)) return 1; fsel = gpio_get_fsel(gpio); printf("%2d: %2s ", num, gpio_get_fsel_name(fsel)); if (fsel == GPIO_FSEL_OUTPUT) printf("%s", gpio_get_drive_name(gpio_get_drive(gpio))); else printf(" "); name = gpio_get_name(gpio); if (pin_mode && strchr(name, '/')) name = strchr(name, '/') + 1; level = gpio_get_level(gpio); printf(" %s | %s // %s%s%s\n", gpio_get_pull_name(gpio_get_pull(gpio)), (level == 1) ? "hi" : (level == 0) ? "lo" : "--", name ? name : "", name ? " = " : "", gpio_get_gpio_fsel_name(gpio, fsel)); return 0; } static int do_gpio_set(unsigned int gpio, int fsparam, int drive, int pull) { unsigned int num = gpio; if (pin_mode) { gpio = gpio_for_pin(num); if (gpio >= num_gpios) { printf("Pin %d cannot be set\n", num); return 1; } } if (!gpio_num_is_valid(gpio)) return 1; if (fsparam != GPIO_FSEL_MAX) gpio_set_fsel(gpio, fsparam); else fsparam = gpio_get_fsel(gpio); if (drive != DRIVE_MAX) { if (fsparam == GPIO_FSEL_OUTPUT) { gpio_set_drive(gpio, drive); } else { printf("Can't set pin value, not an output\n"); return 1; } } if (pull != PULL_MAX) gpio_set_pull(gpio, pull); return 0; } static int do_gpio_level(unsigned int gpio) { unsigned int num = gpio; int level; if (pin_mode) { gpio = gpio_for_pin(num); switch (gpio) { case GPIO_INVALID: return 1; case GPIO_GND: printf("G"); return 0; case GPIO_5V: printf("5"); return 0; case GPIO_3V3: printf("3"); return 0; case GPIO_1V8: printf("8"); return 0; case GPIO_OTHER: printf("?"); return 0; } } if (!gpio_num_is_valid(gpio)) return 1; level = gpio_get_level(gpio); if (level >= 0) printf("%d", level); else printf("-"); return 0; } static int do_gpio_poll_add(unsigned int gpio) { struct poll_gpio_state *new_gpio; unsigned int num = gpio; if (pin_mode) gpio = gpio_for_pin(num); if (!gpio_num_is_valid(gpio)) return 1; poll_gpios = reallocarray(poll_gpios, num_poll_gpios + 1, sizeof(*poll_gpios)); new_gpio = &poll_gpios[num_poll_gpios]; new_gpio->num = num; new_gpio->gpio = gpio; new_gpio->name = gpio_get_name(gpio); new_gpio->level = -1; /* Unknown */ num_poll_gpios++; return 0; } static void do_gpio_poll(void) { unsigned int idle_count = 0; struct timeval idle_start; int i; while (num_poll_gpios) { int changed = 0; for (i = 0; i < num_poll_gpios; i++) { struct poll_gpio_state *state = &poll_gpios[i]; int level = gpio_get_level(state->gpio); if (level != state->level) { if (idle_count) { struct timeval now; uint64_t interval_us; gettimeofday(&now, NULL); interval_us = (uint64_t)(now.tv_sec - idle_start.tv_sec) * 1000000 + (now.tv_usec - idle_start.tv_usec); printf("+%" PRIu64 "us\n", interval_us); idle_count = 0; } printf("%2d: %s // %s\n", state->num, level ? "hi" : "lo", state->name); state->level = level; changed = 1; fflush(stdout); } } if (!changed) { if (!idle_count) gettimeofday(&idle_start, NULL); idle_count++; } } } static void verbose_callback(const char *msg) { printf("%s", msg); } int main(int argc, char *argv[]) { int ret; /* arg parsing */ const char *named_chip = NULL; int set = 0; int get = 0; int level = 0; int poll = 0; int funcs = 0; int echo = 0; int pull = PULL_MAX; int infer_cmd = 0; int fsparam = GPIO_FSEL_MAX; int drive = DRIVE_MAX; uint32_t gpiomask[(MAX_GPIO_PINS + 31)/32] = { 0 }; unsigned start_pin = GPIO_INVALID, end_pin, pin; int first_pin = 1; int i; argv++; argc--; while (argc && (argv[0][0] == '-')) { const char *arg = *(argv++); argc--; if (strcmp(arg, "-h") == 0) { usage(); return 0; } else if (strcmp(arg, "-p") == 0) { pin_mode = 1; } else if (strcmp(arg, "-e") == 0) { echo = 1; } else if (strcmp(arg, "-v") == 0) { verbose_mode = 1; } else if (strcmp(arg, "-c") == 0) { if (!argc) { printf("* chip name expected - use 'pinctrl -h' for help\n"); return -1; } named_chip = *(argv++); argc--; } else { printf("Unknown option '%s' - try \"%s help\"\n", arg, program_name); exit(1); } } if (verbose_mode) gpiolib_set_verbose(&verbose_callback); if (named_chip) ret = gpiolib_init_by_name(named_chip); else ret = gpiolib_init(); if (ret < 0) { printf("Failed to initialise gpiolib - %d\n", ret); return -1; } num_gpios = ret; if (!num_gpios) { printf("No GPIO chips found\n"); return -1; } if (argc) { const char *cmd = *(argv++); argc--; if (strcmp(cmd, "help") == 0) { usage(); return 0; } get = strcmp(cmd, "get") == 0; set = strcmp(cmd, "set") == 0; level = strcmp(cmd, "level") == 0 || strcmp(cmd, "lev") == 0; poll = strcmp(cmd, "poll") == 0; funcs = strcmp(cmd, "funcs") == 0; if (!set && !get && !level && !poll && !funcs) { /* Back up in case we can decode this as a pin */ argv--; argc++; infer_cmd = 1; } } else if (named_chip) { funcs = 1; } else { get = 1; } if (pin_mode) { gpio_get_pin_range(&start_pin, &end_pin); if (start_pin == GPIO_INVALID) { printf("No PIN numbers declared in DT - pin mode disabled\n"); pin_mode = 0; } } if (start_pin == GPIO_INVALID) { start_pin = 0; end_pin = num_gpios - 1; } if (argc) /* expect pin number/name(s) next */ { char *p = *(argv++); argc--; while (p) { unsigned gpio, gpio2; int len, len2; len = strcspn(p, "-,"); ret = sscanf(p, "%u%n", &gpio, &len2); if (ret == 1 && len == len2 && gpio >= num_gpios) break; else if (ret != 1 || len != len2) { gpio = gpio_get_gpio_by_name(p, len); if (gpio == GPIO_INVALID) break; if (pin_mode) { int pin = gpio_to_pin(gpio); if (pin < 0) { printf("Signal \"%*s\" is not on a header pin\n", len, p); return 1; } gpio = (unsigned)pin; } } p += len; if (*p == '\0' && argc && (argv[0][0] == '-' || argv[0][0] == ',')) { p = *(argv++); argc--; } if (*p == '-') { p++; len = strcspn(p, "-,"); ret = sscanf(p, "%u%n", &gpio2, &len2); if (ret == 1 && len == len2 && gpio2 >= num_gpios) break; else if (ret != 1 || len != len2) { gpio2 = gpio_get_gpio_by_name(p, len); if (gpio2 == GPIO_INVALID) break; if (pin_mode) { int pin = gpio_to_pin(gpio2); if (pin < 0) { printf("Signal \"%*s\" is not on a header pin\n", len, p); return 1; } gpio2 = (unsigned)pin; } } if (gpio2 < gpio) { int tmp = gpio2; gpio2 = gpio; gpio = tmp; } p += len; } else { gpio2 = gpio; } while (gpio <= gpio2) { gpiomask[gpio/32] |= (1 << (gpio % 32)); gpio++; } if (*p == '\0' && argc && argv[0][0] == ',') { p = *(argv++); argc--; } if (*p == '\0') { p = NULL; } else { if (*p != ',') break; p++; } } if (p) { if (infer_cmd && p == argv[-1]) printf("Unknown command \"%s\"\n", p); else printf("Unknown GPIO \"%s\"\n", p); return 1; } } else if (set) { printf("Need GPIO number to set\n"); return 1; } else if (poll) { printf("Need GPIO number to poll\n"); return 1; } if (set && !argc) { printf("Need a function or pull to set\n"); return 1; } if ((get || funcs) && argc) { printf("Too many arguments\n"); return 1; } if (infer_cmd) { if (named_chip) funcs = 1; else if (argc) set = 1; else get = 1; } /* parse remaining args */ while (argc) { const char *arg = *(argv++); argc--; if (strcmp(arg, "dh") == 0) drive = DRIVE_HIGH; else if (strcmp(arg, "dl") == 0) drive = DRIVE_LOW; else if (strcmp(arg, "gp") == 0) fsparam = GPIO_FSEL_GPIO; else if (strcmp(arg, "ip") == 0) fsparam = GPIO_FSEL_INPUT; else if (strcmp(arg, "op") == 0) fsparam = GPIO_FSEL_OUTPUT; else if (strcmp(arg, "no") == 0) fsparam = GPIO_FSEL_NONE; else if (strcmp(arg, "a0") == 0) fsparam = GPIO_FSEL_FUNC0; else if (strcmp(arg, "a1") == 0) fsparam = GPIO_FSEL_FUNC1; else if (strcmp(arg, "a2") == 0) fsparam = GPIO_FSEL_FUNC2; else if (strcmp(arg, "a3") == 0) fsparam = GPIO_FSEL_FUNC3; else if (strcmp(arg, "a4") == 0) fsparam = GPIO_FSEL_FUNC4; else if (strcmp(arg, "a5") == 0) fsparam = GPIO_FSEL_FUNC5; else if (strcmp(arg, "a6") == 0) fsparam = GPIO_FSEL_FUNC6; else if (strcmp(arg, "a7") == 0) fsparam = GPIO_FSEL_FUNC7; else if (strcmp(arg, "a8") == 0) fsparam = GPIO_FSEL_FUNC8; else if (strcmp(arg, "pu") == 0) pull = PULL_UP; else if (strcmp(arg, "pd") == 0) pull = PULL_DOWN; else if (strcmp(arg, "pn") == 0) pull = PULL_NONE; else { printf("Unknown argument \"%s\"\n", arg); return 1; } } for (i = ARRAY_SIZE(gpiomask) - 1; i >= 0; i--) { if (gpiomask[i]) break; } if (i < 0) memset(gpiomask, 0xff, sizeof(gpiomask)); if (!funcs) { ret = gpiolib_mmap(); if (ret) { if (ret == EACCES && geteuid()) printf("Must be root\n"); else printf("Failed to mmap gpiolib - %s\n", strerror(ret)); return -1; } } for (pin = start_pin; pin < end_pin + 1; pin++) { if (!(gpiomask[pin / 32] & (1 << (pin % 32)))) continue; if (get) do_gpio_get(pin); if (set) do_gpio_set(pin, fsparam, drive, pull); if (level) { if (!first_pin) printf(" "); do_gpio_level(pin); first_pin = 0; } if (poll) do_gpio_poll_add(pin); if (funcs) print_gpio_alts_info(pin); } if (level) printf("\n"); if (set && echo) { for (pin = start_pin; pin < end_pin + 1; pin++) { if (!(gpiomask[pin / 32] & (1 << (pin % 32)))) continue; do_gpio_get(pin); } } if (poll) do_gpio_poll(); return 0; } raspi-utils-20250514/pinctrl/util.c000066400000000000000000000127061501106437300170340ustar00rootroot00000000000000#include #include #include #include #include #include #include "util.h" // We're actually going to cheat and cast the pointers, but define // a structure to keep the compiler happy. struct dt_subnode_iter { DIR *dh; }; const char *dtpath; static void *do_read_file(const char *fname, const char *mode, size_t *plen) { FILE *fp = fopen(fname, mode); void *buf; long len; if (fp == NULL) return NULL; fseek(fp, 0, SEEK_END); len = ftell(fp); if (plen) *plen = len; buf = malloc(len); fseek(fp, 0, SEEK_SET); if (buf) { if (fread(buf, 1, len, fp) != (size_t)len) { free(buf); buf = NULL; } } fclose(fp); return (char *)buf; } char *read_text_file(const char *fname, size_t *plen) { return do_read_file(fname, "rt", plen); } void *read_file(const char *fname, size_t *plen) { return do_read_file(fname, "rb", plen); } void dt_set_path(const char *path) { dtpath = path; } char *dt_read_prop(const char *node, const char *prop, size_t *plen) { char filename[FILENAME_MAX]; size_t len; len = snprintf(filename, sizeof(filename), "%s%s/%s", dtpath, node, prop); if (len >= sizeof(filename)) { assert(0); return NULL; } filename[sizeof(filename) - 1] = '\0'; return read_file(filename, plen); } uint32_t *dt_read_cells(const char *node, const char *prop, unsigned *num_cells) { uint8_t *buf; size_t len, i; buf = (uint8_t *)dt_read_prop(node, prop, &len); if (buf) { for (i = 0; i + 3 < len; i += 4) { *(uint32_t *)(buf + i) = (buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + (buf[i + 3] << 0); } *num_cells = i >> 2; } return (uint32_t *)buf; } uint64_t dt_extract_num(const uint32_t *cells, int size) { uint64_t val = 0; int i; /* PCIe uses 3 cells for an address, but we can ignore the first cell. * In this case, the big-endian representation makes it easy because * the unwanted portion is shifted off the top. */ for (i = 0; i < size; i++) { val = (val << 32) | cells[i]; } return val; } uint64_t dt_read_num(const char *node, const char *prop, size_t size) { unsigned num_cells; uint32_t *cells = dt_read_cells(node, prop, &num_cells); uint64_t val = 0; if (cells) { if (size <= num_cells) val = dt_extract_num(cells, size); dt_free(cells); } return val; } uint32_t dt_read_u32(const char *node, const char *prop) { return dt_read_num(node, prop, 1); } static uint64_t dt_translate_addr(const uint32_t *ranges, unsigned ranges_cells, int npa, int nps, int nca, uint64_t addr) { /* Entries in the ranges table take the form: * * where the elements are big-endian numbers of lengths nca, npa and nps * respectively. */ unsigned pos = 0; while (pos + npa + nps + nca <= ranges_cells) { uint64_t ca, pa, ps; ca = dt_extract_num(ranges + pos, nca); pa = dt_extract_num(ranges + pos + nca, npa); ps = dt_extract_num(ranges + pos + nca + npa, nps); if (addr >= ca && addr <= ca + ps) { addr -= ca; addr += pa; break; } pos += npa + nps + nca; } return addr; } uint64_t dt_parse_addr(const char *node) { char buf1[FILENAME_MAX], buf2[FILENAME_MAX]; char *parent, *nextparent; uint32_t *ranges = NULL; unsigned ranges_cells = 0; uint64_t addr = INVALID_ADDRESS; unsigned npa, nps, nca = 0; parent = buf1; nextparent = buf2; while (1) { char *tmp, *p; strcpy(parent, node); p = strrchr(parent, '/'); if (!p) return INVALID_ADDRESS; if (p == parent) p[1] = '\0'; else p[0] = '\0'; npa = dt_read_u32(parent, "#address-cells"); nps = dt_read_u32(parent, "#size-cells"); if (!npa || !nps) { addr = INVALID_ADDRESS; break; } if (addr == INVALID_ADDRESS) { addr = dt_read_num(node, "reg", npa); } else if (ranges) { addr = dt_translate_addr(ranges, ranges_cells, npa, nps, nca, addr); dt_free(ranges); ranges = NULL; } if (parent[1] == '\0') break; ranges = dt_read_cells(parent, "ranges", &ranges_cells); nca = npa; node = parent; /* Swap parent and nextparent */ tmp = parent; parent = nextparent; nextparent = tmp; } dt_free(ranges); return addr; } void dt_free(void *value) { free(value); } DT_SUBNODE_HANDLE dt_open_subnodes(const char *node) { char dirpath[FILENAME_MAX]; size_t len; len = snprintf(dirpath, sizeof(dirpath), "%s%s", dtpath, node); if (len >= sizeof(dirpath)) { assert(0); return NULL; } return (DT_SUBNODE_HANDLE)opendir(dirpath); } const char *dt_next_subnode(DT_SUBNODE_HANDLE handle) { DIR *dirh = (DIR *)handle; struct dirent *dent; dent = readdir(dirh); return dent ? dent->d_name : NULL; } void dt_close_subnodes(DT_SUBNODE_HANDLE handle) { DIR *dirh = (DIR *)handle; closedir(dirh); } raspi-utils-20250514/pinctrl/util.h000066400000000000000000000017771501106437300170470ustar00rootroot00000000000000#ifndef _UTIL_H #define _UTIL_H #include #include #define INVALID_ADDRESS ((uint64_t)~0) #define ROUND_UP(n, d) ((((n) + (d) - 1) / (d)) * (d)) #define UNUSED(x) (void)(x) #define ARRAY_SIZE(_a) (sizeof(_a)/sizeof(_a[0])) typedef struct dt_subnode_iter *DT_SUBNODE_HANDLE; char *read_text_file(const char *fname, size_t *plen); void *read_file(const char *fname, size_t *plen); void dt_set_path(const char *path); char *dt_read_prop(const char *node, const char *prop, size_t *len); uint32_t *dt_read_cells(const char *node, const char *prop, unsigned *num_cells); uint64_t dt_extract_num(const uint32_t *cells, int size); uint64_t dt_read_num(const char *node, const char *prop, size_t size); uint32_t dt_read_u32(const char *node, const char *prop); uint64_t dt_parse_addr(const char *node); void dt_free(void *value); DT_SUBNODE_HANDLE dt_open_subnodes(const char *node); const char *dt_next_subnode(DT_SUBNODE_HANDLE handle); void dt_close_subnodes(DT_SUBNODE_HANDLE handle); #endif raspi-utils-20250514/piolib/000077500000000000000000000000001501106437300155105ustar00rootroot00000000000000raspi-utils-20250514/piolib/CMakeLists.txt000066400000000000000000000017031501106437300202510ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) add_subdirectory(examples) #set project name project(piolib) add_compile_definitions(LIBRARY_BUILD=1) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror") if (CMAKE_COMPILER_IS_GNUCC) add_definitions (-ffunction-sections) endif () add_library (pio piolib.c library_piochips.c pio_rp1.c) target_include_directories(pio PUBLIC include) set_target_properties(pio PROPERTIES SOVERSION 0) set(INCLUDE_FILES "piolib.h" "pio_platform.h" "hardware/clocks.h" "hardware/gpio.h" "hardware/pio.h" "hardware/pio_instructions.h" "hardware/timer.h" "pico/stdlib.h" ) install(TARGETS pio ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT piolib) foreach ( file ${INCLUDE_FILES} ) get_filename_component( dir ${file} DIRECTORY ) install( FILES "include/${file}" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/piolib/${dir}" COMPONENT piolib) endforeach() raspi-utils-20250514/piolib/README.md000066400000000000000000000050611501106437300167710ustar00rootroot00000000000000# piolib PIOlib/libPIO is a user-space API to the rp1-pio driver, which gives access to the PIO hardware of RP1. It takes the form of a clone of the PICO SDK PIO API, where most of the methods are implemented as RPC calls to RP1. This allows existing PIO code to be run without (much) alteration, but runs into problems when it relies on the PIO state machine and the support code being closely coupled. **Build Instructions** Install the prerequisites with `sudo apt install build-essential cmake` - you need at least version 3.10 of cmake. Run the following commands, either here or in the top-level directory to build and install everything: 1. `cmake .` (or create a build subdirectory and run `cmake ..`) N.B. Use *cmake -DBUILD_SHARED_LIBS=1 .* to build piolib as a shared (as opposed to static) library. 2. `make` If `ls -l /dev/pio0` reports that the file is not found, you may need to update your Pi 5 firmware to one with PIO support and make sure that you are running a suitably recent kernel. If `ls -l /dev/pio0` reports that the file is owned by `root` and group `root`, you should add the following to `/etc/udev/rules.d/99-com.rules`: ``` SUBSYSTEM=="*-pio", GROUP="gpio", MODE="0660" ``` Make sure to reboot or run `sudo udevadm control --reload-rules && sudo udevadm trigger` in order to apply the new group ownership. **Examples** * piotest: This is the normal WS2812 example LED PIO code, but using DMA to send the data. The optional parameter is the GPIO number to drive; the default is 2. * piopwm: The PWM example, unmodified except for dynamic SM allocation and a command line parameter to choose the GPIO. The optional parameter is the GPIO number to drive; the default is 4. * piows2812: The ws2812 example, unmodified except for dynamic SM allocation and a command line parameter to choose the GPIO. The optional parameter is the GPIO number to drive; the default is 2. * rp1sm: Show the state of the hardware for a particular SM. The parameter is the number of the state machine to inspect. * dpi_csync: Nick's DPI composite sync generator. More of an example than something usable - it may conflict with the DPI driver in interlaced modes. Many parameters; defaults to SDTV line rate. * quadenc: A decoder for quadrature-encoded signals, as generated by rotary encoders such as old mechanical mice. The optional parameter is the base GPIO number for an adjacent pair of input signals; the default is 10 (the other half of the pair being 11). **Known issues** * Blocking operations block the whole RP1 firmware interface until they complete. raspi-utils-20250514/piolib/examples/000077500000000000000000000000001501106437300173265ustar00rootroot00000000000000raspi-utils-20250514/piolib/examples/CMakeLists.txt000066400000000000000000000034141501106437300220700ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) #set project name project(examples) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror") if (CMAKE_COMPILER_IS_GNUCC) add_definitions (-ffunction-sections) endif () add_executable(apitest apitest.c) target_include_directories(apitest PRIVATE ../include) target_link_libraries(apitest pio) install(TARGETS apitest RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) add_executable(piotest piotest.c) target_include_directories(piotest PRIVATE ../include) target_link_libraries(piotest pio) install(TARGETS piotest RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) add_executable(piopwm pwm.c) target_include_directories(piopwm PRIVATE ../include) target_link_libraries(piopwm pio) install(TARGETS piopwm RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) add_executable(pioseq genseq.c) target_include_directories(pioseq PRIVATE ../include) target_link_libraries(pioseq pio) install(TARGETS pioseq RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) add_executable(piows2812 ws2812.c) target_include_directories(piows2812 PRIVATE ../include) target_link_libraries(piows2812 pio) install(TARGETS piows2812 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) add_executable(dpi_csync dpi_csync.c) target_include_directories(dpi_csync PRIVATE ../include) target_link_libraries(dpi_csync pio) install(TARGETS dpi_csync RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) add_executable(rp1sm rp1sm.c) target_include_directories(rp1sm PRIVATE ../include) target_link_libraries(rp1sm pio) install(TARGETS rp1sm RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) add_executable(quadenc quadrature_encoder.c) target_include_directories(quadenc PRIVATE ../include) target_link_libraries(quadenc pio) install(TARGETS quadenc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) raspi-utils-20250514/piolib/examples/apitest.c000066400000000000000000000170531501106437300211510ustar00rootroot00000000000000/** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "piolib.h" #include "pico/stdlib.h" #include "hardware/pio.h" #include "echo.pio.h" #include "genseq.pio.h" #define DATA_WORDS 16 int main(int argc, const char **argv) { uint32_t databuf[DATA_WORDS]; bool use_dma = true; int ret = 0; int i, j; uint16_t dummy_program_instructions[1]; struct pio_program dummy_program = { .instructions = dummy_program_instructions, .length = 1, .origin = -1, }; pio_sm_config c; PIO pio = pio0; uint sm; uint offset; uint gpio = 4; if (argc == 2) gpio = (uint)strtoul(argv[1], NULL, 0); pio_enable_fatal_errors(pio, false); for (sm = 0; sm < pio_get_sm_count(pio); sm++) { if (pio_sm_is_claimed(pio, sm)) pio_panic("SM claimed (1)"); pio_sm_claim(pio, sm); if (pio_get_error(pio)) pio_panic("SM claim failed"); if (!pio_sm_is_claimed(pio, sm)) pio_panic("SM not claimed (1)"); pio_sm_claim(pio, sm); if (!pio_get_error(pio)) pio_panic("SM not claimed (2)"); pio_clear_error(pio); } for (sm = 0; sm < pio_get_sm_count(pio); sm++) { pio_sm_unclaim(pio, sm); if (pio_sm_is_claimed(pio, sm)) pio_panic("SM still claimed"); } pio_claim_sm_mask(pio, (1 << (pio_get_sm_count(pio) - 1)) - 1); if (pio_get_error(pio)) pio_panic("masked claim failed"); if ((uint)pio_claim_unused_sm(pio, false) != pio_get_sm_count(pio) - 1) pio_panic("wrong SM (expected the last)"); for (sm = 0; sm < pio_get_sm_count(pio); sm++) { pio_sm_unclaim(pio, sm); if (pio_sm_is_claimed(pio, sm)) pio_panic("SM still claimed"); } sm = pio_claim_unused_sm(pio, true); for (offset = 0; offset < pio_get_instruction_count(pio); offset++) { dummy_program_instructions[0] = offset + 1; if (!pio_can_add_program(pio, &dummy_program)) pio_panic("can't add program"); if (pio_add_program(pio, &dummy_program) == PIO_ORIGIN_ANY) pio_panic("failed to add program (1)"); } dummy_program_instructions[0] = offset + 1; if (pio_can_add_program(pio, &dummy_program)) pio_panic("can add program"); if (pio_add_program(pio, &dummy_program) != PIO_ORIGIN_INVALID) pio_panic("added program again"); pio_clear_error(pio); for (offset = 0; offset < pio_get_instruction_count(pio); offset++) { pio_remove_program(pio, &dummy_program, offset); if (pio_get_error(pio)) pio_panic("remove program failed"); pio_remove_program(pio, &dummy_program, offset); if (!pio_get_error(pio)) pio_panic("removed program again"); pio_clear_error(pio); } for (offset = 0; offset < pio_get_instruction_count(pio); offset++) { dummy_program_instructions[0] = offset + 1; if (!pio_can_add_program_at_offset(pio, &dummy_program, offset)) pio_panic("can't add program at offset"); pio_add_program_at_offset(pio, &dummy_program, offset); } for (offset = 0; offset < pio_get_instruction_count(pio); offset++) { pio_remove_program(pio, &dummy_program, offset); } offset = pio_add_program(pio, &echo_program); c = pio_get_default_sm_config(); sm_config_set_wrap(&c, offset + echo_wrap_target, offset + echo_wrap); pio_sm_init(pio, sm, offset, &c); pio_sm_put(pio, sm, 0); if (pio_sm_is_tx_fifo_empty(pio, sm)) pio_panic("TX FIFO is empty (1)"); pio_sm_clear_fifos(pio, sm); if (!pio_sm_is_tx_fifo_empty(pio, sm)) pio_panic("TX FIFO is not empty (1)"); if (pio_sm_get_tx_fifo_level(pio, sm) != 0) pio_panic("TX FIFO level is not zero"); for (i = 1; i <= (int)pio_get_fifo_depth(pio); i++) { if (pio_sm_is_tx_fifo_full(pio, sm)) pio_panic("TX FIFO is full (1)"); pio_sm_put(pio, sm, i); if (pio_sm_is_tx_fifo_empty(pio, sm)) pio_panic("TX FIFO is empty (2)"); if ((int)pio_sm_get_tx_fifo_level(pio, sm) != i) pio_panic("Wrong TX FIFO level"); } if (!pio_sm_is_tx_fifo_full(pio, sm)) pio_panic("TX FIFO is not full (1)"); pio_sm_drain_tx_fifo(pio, sm); if (!pio_sm_is_tx_fifo_empty(pio, sm)) pio_panic("TX FIFO is not empty (2)"); for (i = 1; i <= (int)pio_get_fifo_depth(pio); i++) pio_sm_put(pio, sm, i); if (!pio_sm_is_tx_fifo_full(pio, sm)) pio_panic("TX FIFO is not full (2)"); if (!pio_sm_is_rx_fifo_empty(pio, sm)) pio_panic("RX FIFO not empty"); if ((int)pio_sm_get_rx_fifo_level(pio, sm) != 0) pio_panic("RX FIFO level not 0"); pio_sm_set_enabled(pio, sm, true); if (!pio_sm_is_tx_fifo_empty(pio, sm)) pio_panic("TX FIFO is not empty (3)"); if (!pio_sm_is_rx_fifo_full(pio, sm)) pio_panic("RX FIFO is not full"); if (pio_sm_get_rx_fifo_level(pio, sm) != pio_get_fifo_depth(pio)) pio_panic("RX FIFO level not the maximum"); for (i = pio_get_fifo_depth(pio) - 1; i >= 0; i--) { if (pio_sm_is_rx_fifo_empty(pio, sm)) pio_panic("RX FIFO is empty"); if (pio_sm_get(pio, sm) != (uint)(pio_get_fifo_depth(pio) - i)) pio_panic("wrong RX data"); if ((int)pio_sm_get_rx_fifo_level(pio, sm) != i) pio_panic("wrong RX FIFO level"); } if (!pio_sm_is_rx_fifo_empty(pio, sm)) pio_panic("RX FIFO is not empty"); offset = pio_add_program(pio, &genseq_program); if (!pio_sm_config_xfer(pio, sm, PIO_DIR_FROM_SM, 4096, 0)) pio_panic("DMA configuration didn't fail"); if (!pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, 4, NULL) || !pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, 0, databuf)) pio_panic("DMA transfer didn't fail"); if (pio_sm_config_xfer(pio, sm, PIO_DIR_FROM_SM, 4096, 2)) pio_panic("DMA configuration failed"); pio_gpio_init(pio, gpio); pio_sm_set_consecutive_pindirs(pio, sm, gpio, 1, true); c = genseq_program_get_default_config(offset); sm_config_set_sideset_pins(&c, gpio); pio_sm_init(pio, sm, offset, &c); pio_sm_set_enabled(pio, sm, true); for (i = 0; i < DATA_WORDS; i++) { printf("Iter %d:\n", i); pio_sm_put_blocking(pio, sm, i); if (use_dma) { ret = pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, (i + 1) * sizeof(databuf[0]), databuf); if (ret) break; for (j = i; j >= 0; j--) { int v = databuf[i - j]; if (v != j) printf(" %d: %d\n", j, v); } } else { for (j = i; j >= 0; j--) { int v = pio_sm_get_blocking(pio, sm); if (v != j) printf(" %d: %d\n", j, v); } } sleep_ms(10); } if (!ret) { const uint32_t words = 0x10000; uint32_t *bigbuf = malloc(words * sizeof(bigbuf[0])); pio_sm_put_blocking(pio, sm, words - 1); ret = pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, words * sizeof(bigbuf[0]), bigbuf); if (!ret) { for (i = words - 1; i >= 0; i--) { int v = bigbuf[words - 1 - i]; if (v != i) printf(" %x: %x\n", i, v); } } free(bigbuf); } if (ret) printf("* error %d\n", ret); return ret; } raspi-utils-20250514/piolib/examples/dpi_csync.c000066400000000000000000000425021501106437300214500ustar00rootroot00000000000000/** * Copyright (c) 2024 Raspberry Pi Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ // Generate COMPOSITE SYNC for DPI on Raspberry Pi 5. // // There are two cases, for progressive and interlaced video. // The progressive case works on any kernel that supports PIOLIB. // Interlaced DPI requires a recent kernel, and this program will // only work when GPIO 1 has NOT been assigned to DPI, or when // DPI has not yet been configured in an interlaced video mode // (otherwise, the DPI driver will have claimed PIO already!) // // For correct CSYNC output, horizontal sync width and line period // (in microseconds) and H and V sync polarities must all be given. // CSYNC can be mapped to any GPIO except 2,3. The default is GPIO 1. #include #include #include #include "pico/stdlib.h" #include "hardware/pio.h" #include "hardware/clocks.h" static void usage(const char * progname) { fprintf(stderr, "Usage: %s [options]\n", progname); fprintf(stderr, "\t-h Assume HSync is active low (default)\n"); fprintf(stderr, "\t+h Assume HSync is active high\n"); fprintf(stderr, "\t-v Assume VSync is active low (default)\n"); fprintf(stderr, "\t+v Assume VSync is active high\n"); fprintf(stderr, "\t-c CSync output will be active low (default)\n"); fprintf(stderr, "\t+c CSync output will be active high\n"); fprintf(stderr, "\t-o Select output GPIO pin number. Default 1\n"); fprintf(stderr, "\t-s Set HSync width in microseconds. Default 4.7\n"); fprintf(stderr, "\t-t Set line period in microseconds. Default 64.0\n"); fprintf(stderr, "\t-e Set EQ pulse width when interlaced. Default 0.0\n"); fprintf(stderr, "\t-w Set VSW in half-lines when interlaced. Default 6\n"); fprintf(stderr, "\t-i | --interlace Generate serrated CSync for an interlaced mode\n"); fprintf(stderr, "\t-p | --pal Equivalent to -i -s 4.7 -t 64.0 -e 2.35 -w 5\n"); fprintf(stderr, "\t-n | --ntsc Equivalent to -i -s 4.7 -t 63.56 -e 2.3 -w 6\n"); } unsigned int opt_hpos = 0; unsigned int opt_vpos = 0; unsigned int opt_cpos = 0; unsigned int opt_gpio = 1; unsigned int opt_ilace = 0; unsigned int opt_vsw = 6; double opt_hsw = 4.7; double opt_period = 64.0; double opt_eqp = 0.0; static void setpal() { opt_ilace = 1; opt_hsw = 4.7; opt_period = 64.0; opt_vsw = 5; opt_eqp = 2.35; } static void setntsc() { opt_ilace = 1; opt_hsw = 4.7; opt_period = 63.56; opt_vsw = 6; opt_eqp = 2.3; } static int getopts(int argc, const char **argv) { for (; argc > 1; argc--, argv++) { if (argv[1][0] != '+' && argv[1][0] != '-') return -1; if (argv[1][1] == '-') { if (!strcmp(argv[1], "--interlace")) opt_ilace = 1; else if (!strcasecmp(argv[1], "--pal")) setpal(); else if (!strcasecmp(argv[1], "--ntsc")) setntsc(); else return -1; continue; } switch (argv[1][1]) { case 'h': opt_hpos = (argv[1][0]=='+'); break; case 'v': opt_vpos = (argv[1][0]=='+'); break; case 'c': opt_cpos = (argv[1][0]=='+'); break; case 'o': if (--argc < 2) return -1; argv++; opt_gpio = atoi(argv[1]); break; case 's': if (--argc < 2) return -1; argv++; opt_hsw = atof(argv[1]); break; case 't': if (--argc < 2) return -1; argv++; opt_period = atof(argv[1]); break; case 'e': if (--argc < 2) return -1; argv++; opt_eqp = atof(argv[1]); break; case 'w': if (--argc < 2) return -1; argv++; opt_vsw = atoi(argv[1]); break; case 'i': opt_ilace = 1; break; case 'p': setpal(); break; case 'n': setntsc(); break; default: return -1; } } return 0; } /* * COMPOSITE SYNC FOR PROGRESSIVE * * Copy HSYNC pulses to CSYNC (adding 1 cycle); then when VSYNC * is asserted, extend each pulse by an additional Y + 1 cycles. * * The following time constant should be written to the FIFO: * (htotal - 2 * hsync_width) * sys_clock / dpi_clock - 2. * * The default configuration is +HSync, +VSync, -CSync; other * polarities can be made by modifying the PIO program code. */ static int setup_pio_for_csync_prog(PIO pio) { double tc; unsigned int i, offset; unsigned short instructions[] = { /* This is mutable */ 0x90a0, // 0: pull block side 1 0x7040, // 1: out y, 32 side 1 // .wrap_target 0xb322, // 2: mov x, y side 1 [3] 0x3083, // 3: wait 1 gpio, 3 side 1 0xa422, // 4: mov x, y side 0 [4] 0x2003, // 5: wait 0 gpio, 3 side 0 0x00c7, // 6: jmp pin, 7 side 0 ; modify to flip VSync polarity // .wrap ; modify to flip VSync polarity 0x0047, // 7: jmp x--, 7 side 0 0x1002, // 8: jmp 2 side 1 }; struct pio_program prog = { .instructions = instructions, .length = sizeof(instructions) / sizeof(instructions[0]), .origin = -1 }; pio_sm_config cfg = pio_get_default_sm_config(); int sm = pio_claim_unused_sm(pio, true); if (sm < 0) return -1; /* Adapt program code for sync polarity; configure program */ pio_sm_set_enabled(pio, sm, false); if (!opt_vpos) instructions[6] = 0x00c2; /* jmp pin, 2 side 0 */ if (!opt_hpos) { instructions[3] ^= 0x80; instructions[5] ^= 0x80; } if (opt_cpos) { for (i = 0; i < prog.length; i++) instructions[i] ^= 0x1000; } offset = pio_add_program(pio, &prog); if (offset == PIO_ORIGIN_ANY) return -1; /* Configure pins and SM */ sm_config_set_wrap(&cfg, offset + 2, offset + (opt_vpos ? 6 : 7)); sm_config_set_sideset(&cfg, 1, false, false); sm_config_set_sideset_pins(&cfg, opt_gpio); pio_gpio_init(pio, opt_gpio); sm_config_set_jmp_pin(&cfg, 2); /* VSync on GPIO 2 */ pio_sm_init(pio, sm, offset, &cfg); pio_sm_set_consecutive_pindirs(pio, sm, opt_gpio, 1, true); /* Place time constant into the FIFO; start the SM */ tc = 1.0e-6 * ((opt_period - 2.0 * opt_hsw) * (double) clock_get_hz(clk_sys)); pio_sm_put(pio, sm, (unsigned int)(tc - 2.0)); pio_sm_set_enabled(pio, sm, true); return 0; } static int start_timers(PIO pio, int hedge, const unsigned int tc[3]) { static const unsigned short instructions[2][4] = { { 0xa022, 0x2083, 0x0042, 0xc010 }, /* posedge */ { 0xa022, 0x2003, 0x0042, 0xc010 }, /* negedge */ }; const struct pio_program prog = { .instructions = instructions[hedge], .length = 4, .origin = -1 }; unsigned int offset, i; pio_claim_sm_mask(pio, 0xE); /* Claim three SMs, starting from #1 */ offset = pio_add_program(pio, &prog); if (offset == PIO_ORIGIN_ANY) return -1; for (i = 0; i < 3; i++) { pio_sm_config cfg = pio_get_default_sm_config(); pio_sm_set_enabled(pio, i + 1, false); sm_config_set_wrap(&cfg, offset, offset + 3); pio_sm_init(pio, i + 1, offset, &cfg); pio_sm_put(pio, i + 1, tc[i] - 4); pio_sm_exec(pio, i + 1, pio_encode_pull(false, false)); pio_sm_exec(pio, i + 1, pio_encode_out(pio_y, 32)); pio_sm_set_enabled(pio, i + 1, true); } return 0; } /* * COMPOSITE SYNC FOR INTERLACED * * DPI VSYNC (GPIO2) must be a modified signal which is always active-low. * It should go low for 1 or 2 scanlines, 2 or 2.5 lines before Vsync-start. * Desired VSync width minus 1 (in half-lines) should be written to the FIFO. * * Three PIO SMs will be configured as timers, to fire at the end of a left * broad pulse, the middle of a scanline, and the end of a right broad pulse. * * HSYNC->CSYNC latency is about 5 cycles, with a jitter of up to 1 cycle. * * Default program is compiled for +HSync, -CSync. The program may be * modified for other polarities. GPIO2 polarity is always active low. */ static int setup_pio_for_csync_ilace(PIO pio) { static const int wrap_target = 2; static const int wrap = 23; unsigned short instructions[] = { /* This is mutable */ 0x90a0, // 0: pull block side 1 0x7040, // 1: out y, 32 side 1 // .wrap_target 0x3083, // 2: wait 1 gpio, 3 side 1 0xa422, // 3: mov x, y side 0 [4] 0x2003, // 4: wait 0 gpio, 3 side 0 0x12c2, // 5: jmp pin, 2 side 1 [2] 0x3083, // 6: wait 1 gpio, 3 side 1 0xc442, // 7: irq clear 2 side 0 [4] 0x2003, // 8: wait 0 gpio, 3 side 0 0x30c2, // 9: wait 1 irq, 2 side 1 0x10d4, // 10: jmp pin, 20 side 1 0x3083, // 11: wait 1 gpio, 3 side 1 0xa442, // 12: nop side 0 [4] 0x2003, // 13: wait 0 gpio, 3 side 0 0xd042, // 14: irq clear 2 side 1 0xd043, // 15: irq clear 3 side 1 0x30c2, // 16: wait 1 irq, 2 side 1 0x20c3, // 17: wait 1 irq, 3 side 0 0x1054, // 18: jmp x--, 20 side 1 0x1002, // 19: jmp 2 side 1 0xd041, // 20: irq clear 1 side 1 0x3083, // 21: wait 1 gpio, 3 side 1 0x20c1, // 22: wait 1 irq, 1 side 0 0x104e, // 23: jmp x--, 14 side 1 // .wrap }; struct pio_program prog = { .instructions = instructions, .length = sizeof(instructions)/sizeof(instructions[0]), .origin = -1 }; pio_sm_config cfg = pio_get_default_sm_config(); unsigned int i, offset; unsigned int tc[3]; unsigned int sm = 0; pio_claim_sm_mask(pio, 1); /* Compute mid-line and broad-sync time constants and start the 3 "timer" SMs */ tc[1] = 5.0e-7 * opt_period * (double)clock_get_hz(clk_sys); tc[0] = tc[1] - 1.0e-6 * opt_hsw * (double)clock_get_hz(clk_sys); tc[2] = tc[1] + tc[0]; if (start_timers(pio, opt_hpos ? 0 : 1, tc) < 0) { pio_sm_unclaim(pio, sm); return -1; } /* Adapt program code according to CSync polarity; configure program */ pio_sm_set_enabled(pio, sm, false); for (i = 0; i < prog.length; i++) { if (opt_cpos) instructions[i] ^= 0x1000; if (!opt_hpos && (instructions[i] & 0xe07f) == 0x2003) instructions[i] ^= 0x0080; } offset = pio_add_program(pio, &prog); if (offset == PIO_ORIGIN_ANY) return -1; /* Configure pins and SM; set VSync width; start the SM */ sm_config_set_wrap(&cfg, offset + wrap_target, offset + wrap); sm_config_set_sideset(&cfg, 1, false, false); sm_config_set_sideset_pins(&cfg, opt_gpio); pio_gpio_init(pio, opt_gpio); sm_config_set_jmp_pin(&cfg, 2); /* DPI VSync "helper" signal is GPIO 2 */ pio_sm_init(pio, sm, offset, &cfg); pio_sm_set_consecutive_pindirs(pio, sm, opt_gpio, 1, true); pio_sm_put(pio, sm, opt_vsw - 1); pio_sm_set_enabled(pio, sm, true); return 0; } /* * COMPOSITE SYNC (TV-STYLE) for 625/25i [-w 5] and 525/30i [-w 6] only. * * DPI VSYNC (GPIO2) must be a modified signal which is always active-low. * It should go low for 1 or 2 scanlines, VSyncWidth/2.0 or (VSyncWidth+1)/2.0 * lines before Vsync-start, i.e. just after the last full active TV line * (noting that RP1 DPI does not generate half-lines). * * This will push the image up by 1 line compared to customary DRM timings in * "PAL" mode, or 2 lines in "NTSC" mode (which is arguably too low anyway), * but avoids a collision between an active line and an equalizing pulse. * * Another wrinkle is that when the first equalizing pulse aligns with HSync, * it becomes a normal-width sync pulse. This was a deliberate simplification. * It is unlikely that any TV will notice or care. */ static int setup_pio_for_csync_tv(PIO pio) { static const int wrap_target = 6; static const int wrap = 27; unsigned short instructions[] = { /* This is mutable */ 0x3703, // 0: wait 0 gpio, 3 side 1 [7] ; while (HSync) delay; 0x3083, // 1: wait 1 gpio, 3 side 1 ; do { @HSync 0xa7e6, // 2: mov osr, isr side 0 [7] ; CSYNC: rewind sequence 0x2003, // 3: wait 0 gpio, 3 side 0 ; CSYNC: HSync->CSync 0xb7e6, // 4: mov osr, isr side 1 [7] ; delay 0x10c1, // 5: jmp pin, 1 side 1 ; } while (VSync) // .wrap_target ; while (true) { 0xd042, // 6: irq clear 2 side 1 ; flush stale IRQ 0xd043, // 7: irq clear 3 side 1 ; flush stale IRQ 0xb022, // 8: mov x, y side 1 ; X = EQWidth - 3 0x30c2, // 9: wait 1 irq, 2 side 1 ; @midline 0x004a, // 10: jmp x--, 10 side 0 ; CSYNC: while (x--) ; 0x6021, // 11: out x, 1 side 0 ; CSYNC: next pulse broad? 0x002e, // 12: jmp !x, 14 side 0 ; CSYNC: if (broad) 0x20c3, // 13: wait 1 irq, 3 side 0 ; CSYNC: @BroadRight 0x7021, // 14: out x, 1 side 1 ; sequence not finished? 0x1020, // 15: jmp !x, 0 side 1 ; if (finished) break 0xd041, // 16: irq clear 1 side 1 ; flush stale IRQ 0xb022, // 17: mov x, y side 1 ; X = EQWidth - 3 0x3083, // 18: wait 1 gpio, 3 side 1 ; @HSync 0x0053, // 19: jmp x--, 19 side 0 ; CSYNC: while (x--) ; 0x6021, // 20: out x, 1 side 0 ; CSYNC: next pulse broad? 0x0037, // 21: jmp !x, 23 side 0 ; CSYNC: if (broad) 0x20c1, // 22: wait 1 irq, 1 side 0 ; CSYNC: @BroadLeft 0x7021, // 23: out x, 1 side 1 ; sequence not finished? 0x1020, // 24: jmp !x, 0 side 1 ; if (finished) break 0x10c6, // 25: jmp pin, 6 side 1 ; if (VSync) continue 0xb0e6, // 26: mov osr, isr side 1 ; rewind sequence 0x7022, // 27: out x, 2 side 1 ; skip 2 bits // .wrap ; } }; struct pio_program prog = { .instructions = instructions, .length = sizeof(instructions)/sizeof(instructions[0]), .origin = -1 }; pio_sm_config cfg = pio_get_default_sm_config(); unsigned int i, offset; unsigned int tc[3]; unsigned int sm = 0; pio_claim_sm_mask(pio, 1); /* Compute mid-line and broad-sync time constants and start the 3 "timer" SMs */ tc[1] = 5.0e-7 * opt_period * (double)clock_get_hz(clk_sys); tc[0] = tc[1] - 1.0e-6 * opt_hsw * (double)clock_get_hz(clk_sys); tc[2] = tc[1] + tc[0]; if (start_timers(pio, opt_hpos ? 0 : 1, tc) < 0) { pio_sm_unclaim(pio, sm); return -1; } /* Adapt program code according to CSync polarity; configure program */ pio_sm_set_enabled(pio, sm, false); for (i = 0; i < prog.length; i++) { if (opt_cpos) instructions[i] ^= 0x1000; if (!opt_hpos && (instructions[i] & 0xe07f) == 0x2003) instructions[i] ^= 0x0080; } offset = pio_add_program(pio, &prog); if (offset == PIO_ORIGIN_ANY) return -1; /* Configure pins and SM */ sm_config_set_wrap(&cfg, offset + wrap_target, offset + wrap); sm_config_set_sideset(&cfg, 1, false, false); sm_config_set_sideset_pins(&cfg, opt_gpio); pio_gpio_init(pio, opt_gpio); sm_config_set_jmp_pin(&cfg, 2); /* DPI VSync "helper" signal is GPIO 2 */ pio_sm_init(pio, sm, offset, &cfg); pio_sm_set_consecutive_pindirs(pio, sm, opt_gpio, 1, true); /* Load parameters (Vsync pattern; EQ pulse width) into ISR and Y */ tc[0] = (unsigned)(1.0e-6 * opt_eqp * (double) clock_get_hz(clk_sys)); pio_sm_put(pio, sm, (opt_vsw <= 5) ? 0x02ABFFAA : 0xAABFFEAA); pio_sm_put(pio, sm, tc[0] - 3); pio_sm_exec(pio, sm, pio_encode_pull(false, false)); pio_sm_exec(pio, sm, pio_encode_out(pio_y, 32)); pio_sm_exec(pio, sm, pio_encode_in(pio_y, 32)); pio_sm_exec(pio, sm, pio_encode_pull(false, false)); pio_sm_exec(pio, sm, pio_encode_out(pio_y, 32)); pio_sm_set_enabled(pio, sm, true); /* Start the SM */ pio_sm_set_enabled(pio, sm, true); return 0; } int main(int argc, const char **argv) { int r = 0; if (getopts(argc, argv)) { const char * progname = (argc > 0 && argv[0]) ? argv[0] : "dpi_csync"; usage(progname); return 1; } if (!opt_ilace) r = setup_pio_for_csync_prog(pio0); else if (opt_eqp <= 0 || opt_vsw < 5 || opt_vsw > 6) r = setup_pio_for_csync_ilace(pio0); else r = setup_pio_for_csync_tv(pio0); if (r) { fprintf(stderr, "PIO setup failed\n"); return 1; } while (true) sleep_ms(1000); return 0; } raspi-utils-20250514/piolib/examples/echo.pio.h000066400000000000000000000017611501106437300212100ustar00rootroot00000000000000// -------------------------------------------------- // // This file is autogenerated by pioasm; do not edit! // // -------------------------------------------------- // #pragma once #if !PICO_NO_HARDWARE #include "hardware/pio.h" #endif // ---- // // echo // // ---- // #define echo_wrap_target 0 #define echo_wrap 2 #define echo_pio_version 0 static const uint16_t echo_program_instructions[] = { // .wrap_target 0x80a0, // 0: pull block 0xa0c7, // 1: mov isr, osr 0x8020, // 2: push block // .wrap }; #if !PICO_NO_HARDWARE static const struct pio_program echo_program = { .instructions = echo_program_instructions, .length = 3, .origin = -1, }; static inline pio_sm_config echo_program_get_default_config(uint offset) { pio_sm_config c = pio_get_default_sm_config(); sm_config_set_wrap(&c, offset + echo_wrap_target, offset + echo_wrap); return c; } #endif raspi-utils-20250514/piolib/examples/genseq.c000066400000000000000000000034051501106437300207560ustar00rootroot00000000000000/** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "pico/stdlib.h" #include "hardware/pio.h" #include "genseq.pio.h" #define DATA_WORDS 1024 int main(int argc, const char **argv) { uint32_t databuf[DATA_WORDS]; bool use_dma = true; int ret = 0; int i, j; stdio_init_all(); PIO pio = pio0; int sm = pio_claim_unused_sm(pio, true); uint offset = pio_add_program(pio, &genseq_program); uint gpio = 4; if (argc == 2) gpio = (uint)strtoul(argv[1], NULL, 0); printf("Loaded program at %d, using sm %d, gpio %d\n", offset, sm, gpio); pio_sm_config_xfer(pio, sm, PIO_DIR_FROM_SM, 256, 1); pio_gpio_init(pio, gpio); pio_sm_set_consecutive_pindirs(pio, sm, gpio, 1, true); pio_sm_config c = genseq_program_get_default_config(offset); sm_config_set_sideset_pins(&c, gpio); pio_sm_init(pio, sm, offset, &c); pio_sm_set_enabled(pio, sm, true); for (i = 0; i < DATA_WORDS; i++) { printf("Iter %d:\n", i); pio_sm_put_blocking(pio, sm, i); if (use_dma) { ret = pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, (i + 1) * sizeof(databuf[0]), databuf); if (ret) break; for (j = i; j >= 0; j--) { int v = databuf[i - j]; if (v != j) printf(" %d: %d\n", j, v); } } else { for (j = i; j >= 0; j--) { int v = pio_sm_get_blocking(pio, sm); if (v != j) printf(" %d: %d\n", j, v); } } sleep_ms(10); } if (ret) printf("* error %d\n", ret); return ret; } raspi-utils-20250514/piolib/examples/genseq.pio.h000066400000000000000000000022111501106437300215430ustar00rootroot00000000000000// -------------------------------------------------- // // This file is autogenerated by pioasm; do not edit! // // -------------------------------------------------- // #pragma once #if !PICO_NO_HARDWARE #include "hardware/pio.h" #endif // ------ // // genseq // // ------ // #define genseq_wrap_target 0 #define genseq_wrap 4 static const uint16_t genseq_program_instructions[] = { // .wrap_target 0x80a0, // 0: pull block side 0 0xa027, // 1: mov x, osr side 0 0xa0c1, // 2: mov isr, x side 0 0x9020, // 3: push block side 1 0x0042, // 4: jmp x--, 2 side 0 // .wrap }; #if !PICO_NO_HARDWARE static const struct pio_program genseq_program = { .instructions = genseq_program_instructions, .length = 5, .origin = -1, }; static inline pio_sm_config genseq_program_get_default_config(uint offset) { pio_sm_config c = pio_get_default_sm_config(); sm_config_set_wrap(&c, offset + genseq_wrap_target, offset + genseq_wrap); sm_config_set_sideset(&c, 1, false, false); return c; } #endif raspi-utils-20250514/piolib/examples/piotest.c000066400000000000000000000027051501106437300211650ustar00rootroot00000000000000#include #include #include #include "piolib.h" #include "ws2812.pio.h" int main(int argc, const char **argv) { PIO pio; int sm; uint offset; const uint pixels = 60; uint8_t databuf[pixels * 4]; uint gpio = 2; int pass; uint i; if (argc == 2) gpio = (uint)strtoul(argv[1], NULL, 0); pio = pio0; sm = pio_claim_unused_sm(pio, true); pio_sm_config_xfer(pio, sm, PIO_DIR_TO_SM, 256, 1); offset = pio_add_program(pio, &ws2812_program); printf("Loaded program at %d, using sm %d, gpio %d\n", offset, sm, gpio); pio_sm_clear_fifos(pio, sm); pio_sm_set_clkdiv(pio, sm, 1.0); ws2812_program_init(pio, sm, offset, gpio, 800000.0, false); pass = 0; while (1) { for (i = 0; i < pixels; i++) { if (i == (pass % pixels)) { databuf[4*i + 0] = 0; databuf[4*i + 1] = 255; databuf[4*i + 2] = 255; databuf[4*i + 3] = 255; } else { int led = i % 3; databuf[4*i + 0] = 0; databuf[4*i + 1] = (led == 0) ? 255 : 0; databuf[4*i + 2] = (led == 1) ? 255 : 0; databuf[4*i + 3] = (led == 2) ? 255 : 0; } } pio_sm_xfer_data(pio, sm, PIO_DIR_TO_SM, sizeof(databuf), databuf); sleep_ms(10); pass++; } return 0; } raspi-utils-20250514/piolib/examples/pwm.c000066400000000000000000000025251501106437300203010ustar00rootroot00000000000000/** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "pico/stdlib.h" #include "hardware/pio.h" #include "pwm.pio.h" // Write `period` to the input shift register void pio_pwm_set_period(PIO pio, uint sm, uint32_t period) { pio_sm_set_enabled(pio, sm, false); pio_sm_put_blocking(pio, sm, period); pio_sm_exec(pio, sm, pio_encode_pull(false, false)); pio_sm_exec(pio, sm, pio_encode_out(pio_isr, 32)); pio_sm_set_enabled(pio, sm, true); } // Write `level` to TX FIFO. State machine will copy this into X. void pio_pwm_set_level(PIO pio, uint sm, uint32_t level) { pio_sm_put_blocking(pio, sm, level); } int main(int argc, const char **argv) { stdio_init_all(); PIO pio = pio0; int sm = pio_claim_unused_sm(pio, true); uint offset = pio_add_program(pio, &pwm_program); uint gpio = 4; if (argc == 2) gpio = (uint)strtoul(argv[1], NULL, 0); printf("Loaded program at %d, using sm %d, gpio %d\n", offset, sm, gpio); pwm_program_init(pio, sm, offset, gpio); pio_pwm_set_period(pio, sm, (1u << 16) - 1); int level = 0; while (true) { //printf("Level = %d\n", level); pio_pwm_set_level(pio, sm, level * level); level = (level + 1) % 256; sleep_ms(50); } } raspi-utils-20250514/piolib/examples/pwm.pio.h000066400000000000000000000030061501106437300210670ustar00rootroot00000000000000// -------------------------------------------------- // // This file is autogenerated by pioasm; do not edit! // // -------------------------------------------------- // #pragma once #if !PICO_NO_HARDWARE #include "hardware/pio.h" #endif // --- // // pwm // // --- // #define pwm_wrap_target 0 #define pwm_wrap 6 static const uint16_t pwm_program_instructions[] = { // .wrap_target 0x9080, // 0: pull noblock side 0 0xa027, // 1: mov x, osr 0xa046, // 2: mov y, isr 0x00a5, // 3: jmp x != y, 5 0x1806, // 4: jmp 6 side 1 0xa042, // 5: nop 0x0083, // 6: jmp y--, 3 // .wrap }; #if !PICO_NO_HARDWARE static const struct pio_program pwm_program = { .instructions = pwm_program_instructions, .length = 7, .origin = -1, }; static inline pio_sm_config pwm_program_get_default_config(uint offset) { pio_sm_config c = pio_get_default_sm_config(); sm_config_set_wrap(&c, offset + pwm_wrap_target, offset + pwm_wrap); sm_config_set_sideset(&c, 2, true, false); return c; } static inline void pwm_program_init(PIO pio, uint sm, uint offset, uint pin) { pio_gpio_init(pio, pin); pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); pio_sm_config c = pwm_program_get_default_config(offset); sm_config_set_sideset_pins(&c, pin); pio_sm_init(pio, sm, offset, &c); } #endif raspi-utils-20250514/piolib/examples/quadrature_encoder.c000066400000000000000000000043611501106437300233520ustar00rootroot00000000000000/** * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include "pico/stdlib.h" #include "hardware/pio.h" #include "hardware/timer.h" #include "quadrature_encoder.pio.h" // // ---- quadrature encoder interface example // // the PIO program reads phase A/B of a quadrature encoder and increments or // decrements an internal counter to keep the current absolute step count // updated. At any point, the main code can query the current count by using // the quadrature_encoder_*_count functions. The counter is kept in a full // 32 bit register that just wraps around. Two's complement arithmetic means // that it can be interpreted as a 32-bit signed or unsigned value, and it will // work anyway. // // As an example, a two wheel robot being controlled at 100Hz, can use two // state machines to read the two encoders and in the main control loop it can // simply ask for the current encoder counts to get the absolute step count. It // can also subtract the values from the last sample to check how many steps // each wheel as done since the last sample period. // // One advantage of this approach is that it requires zero CPU time to keep the // encoder count updated and because of that it supports very high step rates. // int main(int argc, const char **argv) { int new_value, delta, old_value = 0; // Base pin to connect the A phase of the encoder. // The B phase must be connected to the next pin uint pin_ab = 10; stdio_init_all(); PIO pio = pio0; uint sm = pio_claim_unused_sm(pio, true); if (argc == 2) pin_ab = (uint)strtoul(argv[1], NULL, 0); // we don't really need to keep the offset, as this program must be loaded // at offset 0 pio_add_program(pio, &quadrature_encoder_program); quadrature_encoder_program_init(pio, sm, pin_ab, 0); while (1) { // note: thanks to two's complement arithmetic delta will always // be correct even when new_value wraps around MAXINT / MININT new_value = quadrature_encoder_get_count(pio, sm); delta = new_value - old_value; old_value = new_value; if (delta) printf("position %8d, delta %6d\n", new_value, delta); sleep_ms(100); } } raspi-utils-20250514/piolib/examples/quadrature_encoder.pio.h000066400000000000000000000076551501106437300241560ustar00rootroot00000000000000// -------------------------------------------------- // // This file is autogenerated by pioasm; do not edit! // // -------------------------------------------------- // #pragma once #if !PICO_NO_HARDWARE #include "hardware/pio.h" #endif // ------------------ // // quadrature_encoder // // ------------------ // #define quadrature_encoder_wrap_target 15 #define quadrature_encoder_wrap 23 #define quadrature_encoder_pio_version 0 static const uint16_t quadrature_encoder_program_instructions[] = { 0x000f, // 0: jmp 15 0x000e, // 1: jmp 14 0x0015, // 2: jmp 21 0x000f, // 3: jmp 15 0x0015, // 4: jmp 21 0x000f, // 5: jmp 15 0x000f, // 6: jmp 15 0x000e, // 7: jmp 14 0x000e, // 8: jmp 14 0x000f, // 9: jmp 15 0x000f, // 10: jmp 15 0x0015, // 11: jmp 21 0x000f, // 12: jmp 15 0x0015, // 13: jmp 21 0x008f, // 14: jmp y--, 15 // .wrap_target 0xa0c2, // 15: mov isr, y 0x8000, // 16: push noblock 0x60c2, // 17: out isr, 2 0x4002, // 18: in pins, 2 0xa0e6, // 19: mov osr, isr 0xa0a6, // 20: mov pc, isr 0xa04a, // 21: mov y, !y 0x0097, // 22: jmp y--, 23 0xa04a, // 23: mov y, !y // .wrap }; #if !PICO_NO_HARDWARE static const struct pio_program quadrature_encoder_program = { .instructions = quadrature_encoder_program_instructions, .length = 24, .origin = 0, .pio_version = 0, #if PICO_PIO_VERSION > 0 .used_gpio_ranges = 0x0 #endif }; static inline pio_sm_config quadrature_encoder_program_get_default_config(uint offset) { pio_sm_config c = pio_get_default_sm_config(); sm_config_set_wrap(&c, offset + quadrature_encoder_wrap_target, offset + quadrature_encoder_wrap); return c; } #include "hardware/clocks.h" #include "hardware/gpio.h" // max_step_rate is used to lower the clock of the state machine to save power // if the application doesn't require a very high sampling rate. Passing zero // will set the clock to the maximum static inline void quadrature_encoder_program_init(PIO pio, uint sm, uint pin, int max_step_rate) { pio_sm_set_consecutive_pindirs(pio, sm, pin, 2, false); pio_gpio_init(pio, pin); pio_gpio_init(pio, pin + 1); gpio_pull_up(pin); gpio_pull_up(pin + 1); pio_sm_config c = quadrature_encoder_program_get_default_config(0); sm_config_set_in_pins(&c, pin); // for WAIT, IN sm_config_set_jmp_pin(&c, pin); // for JMP // shift to left, autopull disabled sm_config_set_in_shift(&c, false, false, 32); // don't join FIFO's sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_NONE); // passing "0" as the sample frequency, if (max_step_rate == 0) { sm_config_set_clkdiv(&c, 1.0); } else { // one state machine loop takes at most 10 cycles float div = (float)clock_get_hz(clk_sys) / (10 * max_step_rate); sm_config_set_clkdiv(&c, div); } pio_sm_init(pio, sm, 0, &c); pio_sm_set_enabled(pio, sm, true); } static inline int32_t quadrature_encoder_get_count(PIO pio, uint sm) { uint ret = 0; int n; // if the FIFO has N entries, we fetch them to drain the FIFO, // plus one entry which will be guaranteed to not be stale n = pio_sm_get_rx_fifo_level(pio, sm) + 1; while (n > 0) { ret = pio_sm_get_blocking(pio, sm); n--; } return ret; } #endif raspi-utils-20250514/piolib/examples/rp1sm.c000066400000000000000000000050071501106437300205360ustar00rootroot00000000000000#include #include #include #include #include #include "pio_platform.h" #include "rp1_pio_if.h" #define min(a,b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a < _b ? _a : _b; }) #if 0 static void dump_words(const uint32_t *buf, int words, uint32_t addr) { int i = 0; while (i < words) { int block = min(words - i, 4); int j; printf("%08x:", addr + i * 4); for (j = 0; j < block; j++) printf(" %08x", buf[i++]); printf("\n"); } } #endif static void usage(void) { printf("Usage: rp1sm \n"); exit(1); } int main(int argc, const char **argv) { const char *devname = "/dev/pio0"; struct rp1_access_hw_args args; uint32_t databuf[64]; uint sm; int ret; int fd; if (argc < 2) usage(); fd = open(devname, O_RDWR, O_CLOEXEC); if (fd < 0) return -errno; sm = (uint)strtoul(argv[1], NULL, 16); args.addr = 0xf0000000; args.len = 0x20; args.data = databuf; ret = ioctl(fd, PIO_IOC_READ_HW, &args); if (ret < 0) return ret; uint ctrl = databuf[0]; uint fstat = databuf[1]; uint flevel = databuf[3]; uint flevel2 = databuf[4]; args.addr = 0xf00000cc + sm * 0x20; args.len = 0x20; args.data = databuf; ret = ioctl(fd, PIO_IOC_READ_HW, &args); if (ret < 0) return ret; uint clkdiv = databuf[0]; uint execctrl = databuf[1]; uint shiftctrl = databuf[2]; uint pc = databuf[3]; uint instr = databuf[4]; uint pinctrl = databuf[5]; uint dmactrl_tx = databuf[6]; uint dmactrl_rx = databuf[7]; printf("enabled: %d\n", (ctrl >> sm) & 1); printf("clkdiv: %08x\n", clkdiv); printf("execctrl: %08x\n", execctrl); printf("shiftctrl: %08x\n", shiftctrl); printf("pc: %08x\n", pc); printf("instr: %08x\n", instr); printf("pinctrl: %08x\n", pinctrl); printf("dmactrl_tx: %08x\n", dmactrl_tx); printf("dmactrl_rx: %08x\n", dmactrl_rx); printf("TX fifo: level %x, flags %c%c\n", ((flevel >> (sm * 8)) & 0xf) + (((flevel2 >> (sm * 8)) & 1) << 4), (fstat & (0x10000 << sm)) ? 'F' : 'f', (fstat & (0x1000000 << sm)) ? 'E' : 'e'); printf("RX fifo: level %x, flags %c%c\n", ((flevel >> (sm * 8 + 4)) & 0xf) + (((flevel2 >> (sm * 8 + 4)) & 1) << 4), (fstat & (0x1 << sm)) ? 'F' : 'f', (fstat & (0x100 << sm)) ? 'E' : 'e'); close(fd); return 0; } raspi-utils-20250514/piolib/examples/ws2812.c000066400000000000000000000051071501106437300204430ustar00rootroot00000000000000/** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "pico/stdlib.h" #include "hardware/pio.h" #include "hardware/clocks.h" #include "ws2812.pio.h" #define IS_RGBW true #define NUM_PIXELS 150 PIO pio; int sm; static inline void put_pixel(uint32_t pixel_grb) { pio_sm_put_blocking(pio, sm, pixel_grb << 8u); } static inline uint32_t urgb_u32(uint8_t r, uint8_t g, uint8_t b) { return ((uint32_t) (r) << 8) | ((uint32_t) (g) << 16) | (uint32_t) (b); } void pattern_snakes(uint len, uint t) { for (uint i = 0; i < len; ++i) { uint x = (i + (t >> 1)) % 64; if (x < 10) put_pixel(urgb_u32(0xff, 0, 0)); else if (x >= 15 && x < 25) put_pixel(urgb_u32(0, 0xff, 0)); else if (x >= 30 && x < 40) put_pixel(urgb_u32(0, 0, 0xff)); else put_pixel(0); } } void pattern_random(uint len, uint t) { if (t % 8) return; for (uint i = 0; i < len; ++i) put_pixel(rand()); } void pattern_sparkle(uint len, uint t) { if (t % 8) return; for (uint i = 0; i < len; ++i) put_pixel(rand() % 16 ? 0 : 0xffffffff); } void pattern_greys(uint len, uint t) { uint max = 100; // let's not draw too much current! t %= max; for (uint i = 0; i < len; ++i) { put_pixel(t * 0x10101); if (++t >= max) t = 0; } } typedef void (*pattern)(uint len, uint t); const struct { pattern pat; const char *name; } pattern_table[] = { {pattern_snakes, "Snakes!"}, {pattern_random, "Random data"}, {pattern_sparkle, "Sparkles"}, {pattern_greys, "Greys"}, }; int main(int argc, const char **argv) { uint offset; uint gpio; stdio_init_all(); pio = pio0; sm = pio_claim_unused_sm(pio, true); offset = pio_add_program(pio, &ws2812_program); gpio = 2; if (argc == 2) gpio = (uint)strtoul(argv[1], NULL, 0); printf("WS2812 Smoke Test, using pin %d\n", gpio); printf("Loaded program at %d, using sm %d\n", offset, sm); ws2812_program_init(pio, sm, offset, gpio, 800000, IS_RGBW); int t = 0; while (1) { int pat = rand() % count_of(pattern_table); int dir = (rand() >> 30) & 1 ? 1 : -1; puts(pattern_table[pat].name); puts(dir == 1 ? "(forward)" : "(backward)"); for (int i = 0; i < 1000; ++i) { pattern_table[pat].pat(NUM_PIXELS, t); sleep_ms(10); t += dir; } } } raspi-utils-20250514/piolib/examples/ws2812.pio.h000066400000000000000000000072061501106437300212400ustar00rootroot00000000000000// -------------------------------------------------- // // This file is autogenerated by pioasm; do not edit! // // -------------------------------------------------- // #pragma once #if !PICO_NO_HARDWARE #include "hardware/pio.h" #endif // ------ // // ws2812 // // ------ // #define ws2812_wrap_target 0 #define ws2812_wrap 3 #define ws2812_T1 3 #define ws2812_T2 4 #define ws2812_T3 3 static const uint16_t ws2812_program_instructions[] = { // .wrap_target 0x6221, // 0: out x, 1 side 0 [2] 0x1223, // 1: jmp !x, 3 side 1 [2] 0x1300, // 2: jmp 0 side 1 [3] 0xa342, // 3: nop side 0 [3] // .wrap }; #if !PICO_NO_HARDWARE static const struct pio_program ws2812_program = { .instructions = ws2812_program_instructions, .length = 4, .origin = -1, }; static inline pio_sm_config ws2812_program_get_default_config(uint offset) { pio_sm_config c = pio_get_default_sm_config(); sm_config_set_wrap(&c, offset + ws2812_wrap_target, offset + ws2812_wrap); sm_config_set_sideset(&c, 1, false, false); return c; } #include "hardware/clocks.h" static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) { pio_gpio_init(pio, pin); pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); pio_sm_config c = ws2812_program_get_default_config(offset); sm_config_set_sideset_pins(&c, pin); sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit); sm_config_set_clkdiv(&c, div); pio_sm_init(pio, sm, offset, &c); pio_sm_set_enabled(pio, sm, true); } #endif // --------------- // // ws2812_parallel // // --------------- // #define ws2812_parallel_wrap_target 0 #define ws2812_parallel_wrap 3 #define ws2812_parallel_T1 3 #define ws2812_parallel_T2 4 #define ws2812_parallel_T3 3 static const uint16_t ws2812_parallel_program_instructions[] = { // .wrap_target 0x6020, // 0: out x, 32 0xa10b, // 1: mov pins, !null [1] 0xa401, // 2: mov pins, x [4] 0xa103, // 3: mov pins, null [1] // .wrap }; static const struct pio_program ws2812_parallel_program = { .instructions = ws2812_parallel_program_instructions, .length = 4, .origin = -1, }; #if !PICO_NO_HARDWARE static inline pio_sm_config ws2812_parallel_program_get_default_config(uint offset) { pio_sm_config c = pio_get_default_sm_config(); sm_config_set_wrap(&c, offset + ws2812_parallel_wrap_target, offset + ws2812_parallel_wrap); return c; } #include "hardware/clocks.h" static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) { for(uint i=pin_base; i= NUM_BANK0_GPIOS); } #endif raspi-utils-20250514/piolib/include/hardware/pio.h000066400000000000000000000002751501106437300216740ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2024 Raspberry Pi Ltd. * All rights reserved. */ #ifndef _HARDWARE_PIO_H #define _HARDWARE_PIO_H #include "piolib.h" #endif raspi-utils-20250514/piolib/include/hardware/pio_instructions.h000066400000000000000000000462451501106437300245270ustar00rootroot00000000000000/* * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _HARDWARE_PIO_INSTRUCTIONS_H #define _HARDWARE_PIO_INSTRUCTIONS_H //#include "pico.h" /** \brief PIO instruction encoding * \defgroup pio_instructions pio_instructions * \ingroup hardware_pio * * Functions for generating PIO instruction encodings programmatically. In debug builds *`PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` can be set to 1 to enable validation of encoding function * parameters. * * For fuller descriptions of the instructions in question see the "RP2040 Datasheet" */ // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS, Enable/disable assertions in the PIO instructions, type=bool, default=0, group=pio_instructions #ifndef PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS #define PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS 0 #endif #ifdef __cplusplus extern "C" { #endif enum pio_instr_bits { pio_instr_bits_jmp = 0x0000, pio_instr_bits_wait = 0x2000, pio_instr_bits_in = 0x4000, pio_instr_bits_out = 0x6000, pio_instr_bits_push = 0x8000, pio_instr_bits_pull = 0x8080, pio_instr_bits_mov = 0xa000, pio_instr_bits_irq = 0xc000, pio_instr_bits_set = 0xe000, }; #ifndef NDEBUG #define _PIO_INVALID_IN_SRC 0x08u #define _PIO_INVALID_OUT_DEST 0x10u #define _PIO_INVALID_SET_DEST 0x20u #define _PIO_INVALID_MOV_SRC 0x40u #define _PIO_INVALID_MOV_DEST 0x80u #else #define _PIO_INVALID_IN_SRC 0u #define _PIO_INVALID_OUT_DEST 0u #define _PIO_INVALID_SET_DEST 0u #define _PIO_INVALID_MOV_SRC 0u #define _PIO_INVALID_MOV_DEST 0u #endif /*! \brief Enumeration of values to pass for source/destination args for instruction encoding functions * \ingroup pio_instructions * * \note Not all values are suitable for all functions. Validity is only checked in debug mode when * `PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` is 1 */ enum pio_src_dest { pio_pins = 0u, pio_x = 1u, pio_y = 2u, pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, pio_isr = 6u | _PIO_INVALID_SET_DEST, pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST, pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, }; static inline uint _pio_major_instr_bits(uint instr) { return instr & 0xe000u; } static inline uint _pio_encode_instr_and_args(enum pio_instr_bits instr_bits, uint arg1, uint arg2) { valid_params_if(PIO_INSTRUCTIONS, arg1 <= 0x7); #if PARAM_ASSERTIONS_ENABLED(PIO_INSTRUCTIONS) uint32_t major = _pio_major_instr_bits(instr_bits); if (major == pio_instr_bits_in || major == pio_instr_bits_out) { assert(arg2 && arg2 <= 32); } else { assert(arg2 <= 31); } #endif return instr_bits | (arg1 << 5u) | (arg2 & 0x1fu); } static inline uint _pio_encode_instr_and_src_dest(enum pio_instr_bits instr_bits, enum pio_src_dest dest, uint value) { return _pio_encode_instr_and_args(instr_bits, dest & 7u, value); } /*! \brief Encode just the delay slot bits of an instruction * \ingroup pio_instructions * * \note This function does not return a valid instruction encoding; instead it returns an encoding of the delay * slot suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when * combining the results of this function with the results of \ref pio_encode_sideset and \ref pio_encode_sideset_opt * as they share the same bits within the instruction encoding. * * \param cycles the number of cycles 0-31 (or less if side set is being used) * \return the delay slot bits to be ORed with an instruction encoding */ static inline uint pio_encode_delay(uint cycles) { // note that the maximum cycles will be smaller if sideset_bit_count > 0 valid_params_if(PIO_INSTRUCTIONS, cycles <= 0x1f); return cycles << 8u; } /*! \brief Encode just the side set bits of an instruction (in non optional side set mode) * \ingroup pio_instructions * * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits * within the instruction encoding. * * \param sideset_bit_count number of side set bits as would be specified via `.sideset` in pioasm * \param value the value to sideset on the pins * \return the side set bits to be ORed with an instruction encoding */ static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) { valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 5); valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); return value << (13u - sideset_bit_count); } /*! \brief Encode just the side set bits of an instruction (in optional -`opt` side set mode) * \ingroup pio_instructions * * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits * within the instruction encoding. * * \param sideset_bit_count number of side set bits as would be specified via `.sideset opt` in pioasm * \param value the value to sideset on the pins * \return the side set bits to be ORed with an instruction encoding */ static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) { valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 4); valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); return 0x1000u | value << (12u - sideset_bit_count); } /*! \brief Encode an unconditional JMP instruction * \ingroup pio_instructions * * This is the equivalent of `JMP ` * * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_jmp(uint addr) { return _pio_encode_instr_and_args(pio_instr_bits_jmp, 0, addr); } /*! \brief Encode a conditional JMP if scratch X zero instruction * \ingroup pio_instructions * * This is the equivalent of `JMP !X ` * * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_jmp_not_x(uint addr) { return _pio_encode_instr_and_args(pio_instr_bits_jmp, 1, addr); } /*! \brief Encode a conditional JMP if scratch X non-zero (and post-decrement X) instruction * \ingroup pio_instructions * * This is the equivalent of `JMP X-- ` * * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_jmp_x_dec(uint addr) { return _pio_encode_instr_and_args(pio_instr_bits_jmp, 2, addr); } /*! \brief Encode a conditional JMP if scratch Y zero instruction * \ingroup pio_instructions * * This is the equivalent of `JMP !Y ` * * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_jmp_not_y(uint addr) { return _pio_encode_instr_and_args(pio_instr_bits_jmp, 3, addr); } /*! \brief Encode a conditional JMP if scratch Y non-zero (and post-decrement Y) instruction * \ingroup pio_instructions * * This is the equivalent of `JMP Y-- ` * * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_jmp_y_dec(uint addr) { return _pio_encode_instr_and_args(pio_instr_bits_jmp, 4, addr); } /*! \brief Encode a conditional JMP if scratch X not equal scratch Y instruction * \ingroup pio_instructions * * This is the equivalent of `JMP X!=Y ` * * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_jmp_x_ne_y(uint addr) { return _pio_encode_instr_and_args(pio_instr_bits_jmp, 5, addr); } /*! \brief Encode a conditional JMP if input pin high instruction * \ingroup pio_instructions * * This is the equivalent of `JMP PIN ` * * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_jmp_pin(uint addr) { return _pio_encode_instr_and_args(pio_instr_bits_jmp, 6, addr); } /*! \brief Encode a conditional JMP if output shift register not empty instruction * \ingroup pio_instructions * * This is the equivalent of `JMP !OSRE ` * * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_jmp_not_osre(uint addr) { return _pio_encode_instr_and_args(pio_instr_bits_jmp, 7, addr); } static inline uint _pio_encode_irq(bool relative, uint irq) { valid_params_if(PIO_INSTRUCTIONS, irq <= 7); return (relative ? 0x10u : 0x0u) | irq; } /*! \brief Encode a WAIT for GPIO pin instruction * \ingroup pio_instructions * * This is the equivalent of `WAIT GPIO ` * * \param polarity true for `WAIT 1`, false for `WAIT 0` * \param gpio The real GPIO number 0-31 * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) { return _pio_encode_instr_and_args(pio_instr_bits_wait, 0u | (polarity ? 4u : 0u), gpio); } /*! \brief Encode a WAIT for pin instruction * \ingroup pio_instructions * * This is the equivalent of `WAIT PIN ` * * \param polarity true for `WAIT 1`, false for `WAIT 0` * \param pin The pin number 0-31 relative to the executing SM's input pin mapping * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_wait_pin(bool polarity, uint pin) { return _pio_encode_instr_and_args(pio_instr_bits_wait, 1u | (polarity ? 4u : 0u), pin); } /*! \brief Encode a WAIT for IRQ instruction * \ingroup pio_instructions * * This is the equivalent of `WAIT IRQ ` * * \param polarity true for `WAIT 1`, false for `WAIT 0` * \param relative true for a `WAIT IRQ REL`, false for regular `WAIT IRQ ` * \param irq the irq number 0-7 * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) { valid_params_if(PIO_INSTRUCTIONS, irq <= 7); return _pio_encode_instr_and_args(pio_instr_bits_wait, 2u | (polarity ? 4u : 0u), _pio_encode_irq(relative, irq)); } /*! \brief Encode an IN instruction * \ingroup pio_instructions * * This is the equivalent of `IN , ` * * \param src The source to take data from * \param count The number of bits 1-32 * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_in(enum pio_src_dest src, uint count) { valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_IN_SRC)); return _pio_encode_instr_and_src_dest(pio_instr_bits_in, src, count); } /*! \brief Encode an OUT instruction * \ingroup pio_instructions * * This is the equivalent of `OUT , ` * * \param dest The destination to write data to * \param count The number of bits 1-32 * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_out(enum pio_src_dest dest, uint count) { valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_OUT_DEST)); return _pio_encode_instr_and_src_dest(pio_instr_bits_out, dest, count); } /*! \brief Encode a PUSH instruction * \ingroup pio_instructions * * This is the equivalent of `PUSH , ` * * \param if_full true for `PUSH IF_FULL ...`, false for `PUSH ...` * \param block true for `PUSH ... BLOCK`, false for `PUSH ...` * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_push(bool if_full, bool block) { return _pio_encode_instr_and_args(pio_instr_bits_push, (if_full ? 2u : 0u) | (block ? 1u : 0u), 0); } /*! \brief Encode a PULL instruction * \ingroup pio_instructions * * This is the equivalent of `PULL , ` * * \param if_empty true for `PULL IF_EMPTY ...`, false for `PULL ...` * \param block true for `PULL ... BLOCK`, false for `PULL ...` * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_pull(bool if_empty, bool block) { return _pio_encode_instr_and_args(pio_instr_bits_pull, (if_empty ? 2u : 0u) | (block ? 1u : 0u), 0); } /*! \brief Encode a MOV instruction * \ingroup pio_instructions * * This is the equivalent of `MOV , ` * * \param dest The destination to write data to * \param src The source to take data from * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src) { valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, src & 7u); } /*! \brief Encode a MOV instruction with bit invert * \ingroup pio_instructions * * This is the equivalent of `MOV , ~` * * \param dest The destination to write inverted data to * \param src The source to take data from * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src) { valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (1u << 3u) | (src & 7u)); } /*! \brief Encode a MOV instruction with bit reverse * \ingroup pio_instructions * * This is the equivalent of `MOV , ::` * * \param dest The destination to write bit reversed data to * \param src The source to take data from * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src) { valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (2u << 3u) | (src & 7u)); } /*! \brief Encode a IRQ SET instruction * \ingroup pio_instructions * * This is the equivalent of `IRQ SET ` * * \param relative true for a `IRQ SET REL`, false for regular `IRQ SET ` * \param irq the irq number 0-7 * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_irq_set(bool relative, uint irq) { return _pio_encode_instr_and_args(pio_instr_bits_irq, 0, _pio_encode_irq(relative, irq)); } /*! \brief Encode a IRQ WAIT instruction * \ingroup pio_instructions * * This is the equivalent of `IRQ WAIT ` * * \param relative true for a `IRQ WAIT REL`, false for regular `IRQ WAIT ` * \param irq the irq number 0-7 * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_irq_wait(bool relative, uint irq) { return _pio_encode_instr_and_args(pio_instr_bits_irq, 1, _pio_encode_irq(relative, irq)); } /*! \brief Encode a IRQ CLEAR instruction * \ingroup pio_instructions * * This is the equivalent of `IRQ CLEAR ` * * \param relative true for a `IRQ CLEAR REL`, false for regular `IRQ CLEAR ` * \param irq the irq number 0-7 * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_irq_clear(bool relative, uint irq) { return _pio_encode_instr_and_args(pio_instr_bits_irq, 2, _pio_encode_irq(relative, irq)); } /*! \brief Encode a SET instruction * \ingroup pio_instructions * * This is the equivalent of `SET , ` * * \param dest The destination to apply the value to * \param value The value 0-31 * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_set(enum pio_src_dest dest, uint value) { valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_SET_DEST)); return _pio_encode_instr_and_src_dest(pio_instr_bits_set, dest, value); } /*! \brief Encode a NOP instruction * \ingroup pio_instructions * * This is the equivalent of `NOP` which is itself encoded as `MOV y, y` * * \return The instruction encoding with 0 delay and no side set value * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt */ static inline uint pio_encode_nop(void) { return pio_encode_mov(pio_y, pio_y); } #ifdef __cplusplus } #endif #endif raspi-utils-20250514/piolib/include/hardware/regs/000077500000000000000000000000001501106437300216705ustar00rootroot00000000000000raspi-utils-20250514/piolib/include/hardware/regs/proc_pio.h000066400000000000000000005227401501106437300236650ustar00rootroot00000000000000/** * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ // ============================================================================= // Register block : PROC_PIO // Version : 1 // Bus type : ahbl // Description : Programmable IO block // ============================================================================= #ifndef HARDWARE_REGS_PROC_PIO_DEFINED #define HARDWARE_REGS_PROC_PIO_DEFINED // ============================================================================= // Register : PROC_PIO_CTRL // Description : PIO control register #define PROC_PIO_CTRL_OFFSET _u(0x00000000) #define PROC_PIO_CTRL_BITS _u(0x00000fff) #define PROC_PIO_CTRL_RESET _u(0x00000000) #define PROC_PIO_CTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_CTRL_CLKDIV_RESTART // Description : Force clock dividers to restart their count and clear // fractional // accumulators. Restart multiple dividers to synchronise them. #define PROC_PIO_CTRL_CLKDIV_RESTART_RESET _u(0x0) #define PROC_PIO_CTRL_CLKDIV_RESTART_BITS _u(0x00000f00) #define PROC_PIO_CTRL_CLKDIV_RESTART_MSB _u(11) #define PROC_PIO_CTRL_CLKDIV_RESTART_LSB _u(8) #define PROC_PIO_CTRL_CLKDIV_RESTART_ACCESS "SC" // ----------------------------------------------------------------------------- // Field : PROC_PIO_CTRL_SM_RESTART // Description : Clear internal SM state which is otherwise difficult to access // (e.g. shift counters). Self-clearing. #define PROC_PIO_CTRL_SM_RESTART_RESET _u(0x0) #define PROC_PIO_CTRL_SM_RESTART_BITS _u(0x000000f0) #define PROC_PIO_CTRL_SM_RESTART_MSB _u(7) #define PROC_PIO_CTRL_SM_RESTART_LSB _u(4) #define PROC_PIO_CTRL_SM_RESTART_ACCESS "SC" // ----------------------------------------------------------------------------- // Field : PROC_PIO_CTRL_SM_ENABLE // Description : Enable state machine #define PROC_PIO_CTRL_SM_ENABLE_RESET _u(0x0) #define PROC_PIO_CTRL_SM_ENABLE_BITS _u(0x0000000f) #define PROC_PIO_CTRL_SM_ENABLE_MSB _u(3) #define PROC_PIO_CTRL_SM_ENABLE_LSB _u(0) #define PROC_PIO_CTRL_SM_ENABLE_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_FSTAT // Description : FIFO status register #define PROC_PIO_FSTAT_OFFSET _u(0x00000004) #define PROC_PIO_FSTAT_BITS _u(0x0f0f0f0f) #define PROC_PIO_FSTAT_RESET _u(0x0f000f00) #define PROC_PIO_FSTAT_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_FSTAT_TXEMPTY // Description : State machine TX FIFO is empty #define PROC_PIO_FSTAT_TXEMPTY_RESET _u(0xf) #define PROC_PIO_FSTAT_TXEMPTY_BITS _u(0x0f000000) #define PROC_PIO_FSTAT_TXEMPTY_MSB _u(27) #define PROC_PIO_FSTAT_TXEMPTY_LSB _u(24) #define PROC_PIO_FSTAT_TXEMPTY_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FSTAT_TXFULL // Description : State machine TX FIFO is full #define PROC_PIO_FSTAT_TXFULL_RESET _u(0x0) #define PROC_PIO_FSTAT_TXFULL_BITS _u(0x000f0000) #define PROC_PIO_FSTAT_TXFULL_MSB _u(19) #define PROC_PIO_FSTAT_TXFULL_LSB _u(16) #define PROC_PIO_FSTAT_TXFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FSTAT_RXEMPTY // Description : State machine RX FIFO is empty #define PROC_PIO_FSTAT_RXEMPTY_RESET _u(0xf) #define PROC_PIO_FSTAT_RXEMPTY_BITS _u(0x00000f00) #define PROC_PIO_FSTAT_RXEMPTY_MSB _u(11) #define PROC_PIO_FSTAT_RXEMPTY_LSB _u(8) #define PROC_PIO_FSTAT_RXEMPTY_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FSTAT_RXFULL // Description : State machine RX FIFO is full #define PROC_PIO_FSTAT_RXFULL_RESET _u(0x0) #define PROC_PIO_FSTAT_RXFULL_BITS _u(0x0000000f) #define PROC_PIO_FSTAT_RXFULL_MSB _u(3) #define PROC_PIO_FSTAT_RXFULL_LSB _u(0) #define PROC_PIO_FSTAT_RXFULL_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_FDEBUG // Description : FIFO debug register #define PROC_PIO_FDEBUG_OFFSET _u(0x00000008) #define PROC_PIO_FDEBUG_BITS _u(0x0f0f0f0f) #define PROC_PIO_FDEBUG_RESET _u(0x00000000) #define PROC_PIO_FDEBUG_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_FDEBUG_TXSTALL // Description : State machine has stalled on empty TX FIFO. Write 1 to clear. #define PROC_PIO_FDEBUG_TXSTALL_RESET _u(0x0) #define PROC_PIO_FDEBUG_TXSTALL_BITS _u(0x0f000000) #define PROC_PIO_FDEBUG_TXSTALL_MSB _u(27) #define PROC_PIO_FDEBUG_TXSTALL_LSB _u(24) #define PROC_PIO_FDEBUG_TXSTALL_ACCESS "WC" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FDEBUG_TXOVER // Description : TX FIFO overflow has occurred. Write 1 to clear. #define PROC_PIO_FDEBUG_TXOVER_RESET _u(0x0) #define PROC_PIO_FDEBUG_TXOVER_BITS _u(0x000f0000) #define PROC_PIO_FDEBUG_TXOVER_MSB _u(19) #define PROC_PIO_FDEBUG_TXOVER_LSB _u(16) #define PROC_PIO_FDEBUG_TXOVER_ACCESS "WC" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FDEBUG_RXUNDER // Description : RX FIFO underflow has occurred. Write 1 to clear. #define PROC_PIO_FDEBUG_RXUNDER_RESET _u(0x0) #define PROC_PIO_FDEBUG_RXUNDER_BITS _u(0x00000f00) #define PROC_PIO_FDEBUG_RXUNDER_MSB _u(11) #define PROC_PIO_FDEBUG_RXUNDER_LSB _u(8) #define PROC_PIO_FDEBUG_RXUNDER_ACCESS "WC" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FDEBUG_RXSTALL // Description : State machine has stalled on full RX FIFO. Write 1 to clear. #define PROC_PIO_FDEBUG_RXSTALL_RESET _u(0x0) #define PROC_PIO_FDEBUG_RXSTALL_BITS _u(0x0000000f) #define PROC_PIO_FDEBUG_RXSTALL_MSB _u(3) #define PROC_PIO_FDEBUG_RXSTALL_LSB _u(0) #define PROC_PIO_FDEBUG_RXSTALL_ACCESS "WC" // ============================================================================= // Register : PROC_PIO_FLEVEL // Description : FIFO levels // These count up to 15 only. When counting higher the extra bit // is set in flevel2 register and this value saturates #define PROC_PIO_FLEVEL_OFFSET _u(0x0000000c) #define PROC_PIO_FLEVEL_BITS _u(0xffffffff) #define PROC_PIO_FLEVEL_RESET _u(0x00000000) #define PROC_PIO_FLEVEL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL_RX3 // Description : None #define PROC_PIO_FLEVEL_RX3_RESET _u(0x0) #define PROC_PIO_FLEVEL_RX3_BITS _u(0xf0000000) #define PROC_PIO_FLEVEL_RX3_MSB _u(31) #define PROC_PIO_FLEVEL_RX3_LSB _u(28) #define PROC_PIO_FLEVEL_RX3_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL_TX3 // Description : None #define PROC_PIO_FLEVEL_TX3_RESET _u(0x0) #define PROC_PIO_FLEVEL_TX3_BITS _u(0x0f000000) #define PROC_PIO_FLEVEL_TX3_MSB _u(27) #define PROC_PIO_FLEVEL_TX3_LSB _u(24) #define PROC_PIO_FLEVEL_TX3_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL_RX2 // Description : None #define PROC_PIO_FLEVEL_RX2_RESET _u(0x0) #define PROC_PIO_FLEVEL_RX2_BITS _u(0x00f00000) #define PROC_PIO_FLEVEL_RX2_MSB _u(23) #define PROC_PIO_FLEVEL_RX2_LSB _u(20) #define PROC_PIO_FLEVEL_RX2_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL_TX2 // Description : None #define PROC_PIO_FLEVEL_TX2_RESET _u(0x0) #define PROC_PIO_FLEVEL_TX2_BITS _u(0x000f0000) #define PROC_PIO_FLEVEL_TX2_MSB _u(19) #define PROC_PIO_FLEVEL_TX2_LSB _u(16) #define PROC_PIO_FLEVEL_TX2_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL_RX1 // Description : None #define PROC_PIO_FLEVEL_RX1_RESET _u(0x0) #define PROC_PIO_FLEVEL_RX1_BITS _u(0x0000f000) #define PROC_PIO_FLEVEL_RX1_MSB _u(15) #define PROC_PIO_FLEVEL_RX1_LSB _u(12) #define PROC_PIO_FLEVEL_RX1_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL_TX1 // Description : None #define PROC_PIO_FLEVEL_TX1_RESET _u(0x0) #define PROC_PIO_FLEVEL_TX1_BITS _u(0x00000f00) #define PROC_PIO_FLEVEL_TX1_MSB _u(11) #define PROC_PIO_FLEVEL_TX1_LSB _u(8) #define PROC_PIO_FLEVEL_TX1_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL_RX0 // Description : None #define PROC_PIO_FLEVEL_RX0_RESET _u(0x0) #define PROC_PIO_FLEVEL_RX0_BITS _u(0x000000f0) #define PROC_PIO_FLEVEL_RX0_MSB _u(7) #define PROC_PIO_FLEVEL_RX0_LSB _u(4) #define PROC_PIO_FLEVEL_RX0_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL_TX0 // Description : None #define PROC_PIO_FLEVEL_TX0_RESET _u(0x0) #define PROC_PIO_FLEVEL_TX0_BITS _u(0x0000000f) #define PROC_PIO_FLEVEL_TX0_MSB _u(3) #define PROC_PIO_FLEVEL_TX0_LSB _u(0) #define PROC_PIO_FLEVEL_TX0_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_FLEVEL2 // Description : FIFO level extra bits // These are only used in double fifo mode, and the fifo has more // than 15 elements #define PROC_PIO_FLEVEL2_OFFSET _u(0x00000010) #define PROC_PIO_FLEVEL2_BITS _u(0x11111111) #define PROC_PIO_FLEVEL2_RESET _u(0x00000000) #define PROC_PIO_FLEVEL2_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL2_RX3 // Description : None #define PROC_PIO_FLEVEL2_RX3_RESET _u(0x0) #define PROC_PIO_FLEVEL2_RX3_BITS _u(0x10000000) #define PROC_PIO_FLEVEL2_RX3_MSB _u(28) #define PROC_PIO_FLEVEL2_RX3_LSB _u(28) #define PROC_PIO_FLEVEL2_RX3_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL2_TX3 // Description : None #define PROC_PIO_FLEVEL2_TX3_RESET _u(0x0) #define PROC_PIO_FLEVEL2_TX3_BITS _u(0x01000000) #define PROC_PIO_FLEVEL2_TX3_MSB _u(24) #define PROC_PIO_FLEVEL2_TX3_LSB _u(24) #define PROC_PIO_FLEVEL2_TX3_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL2_RX2 // Description : None #define PROC_PIO_FLEVEL2_RX2_RESET _u(0x0) #define PROC_PIO_FLEVEL2_RX2_BITS _u(0x00100000) #define PROC_PIO_FLEVEL2_RX2_MSB _u(20) #define PROC_PIO_FLEVEL2_RX2_LSB _u(20) #define PROC_PIO_FLEVEL2_RX2_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL2_TX2 // Description : None #define PROC_PIO_FLEVEL2_TX2_RESET _u(0x0) #define PROC_PIO_FLEVEL2_TX2_BITS _u(0x00010000) #define PROC_PIO_FLEVEL2_TX2_MSB _u(16) #define PROC_PIO_FLEVEL2_TX2_LSB _u(16) #define PROC_PIO_FLEVEL2_TX2_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL2_RX1 // Description : None #define PROC_PIO_FLEVEL2_RX1_RESET _u(0x0) #define PROC_PIO_FLEVEL2_RX1_BITS _u(0x00001000) #define PROC_PIO_FLEVEL2_RX1_MSB _u(12) #define PROC_PIO_FLEVEL2_RX1_LSB _u(12) #define PROC_PIO_FLEVEL2_RX1_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL2_TX1 // Description : None #define PROC_PIO_FLEVEL2_TX1_RESET _u(0x0) #define PROC_PIO_FLEVEL2_TX1_BITS _u(0x00000100) #define PROC_PIO_FLEVEL2_TX1_MSB _u(8) #define PROC_PIO_FLEVEL2_TX1_LSB _u(8) #define PROC_PIO_FLEVEL2_TX1_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL2_RX0 // Description : None #define PROC_PIO_FLEVEL2_RX0_RESET _u(0x0) #define PROC_PIO_FLEVEL2_RX0_BITS _u(0x00000010) #define PROC_PIO_FLEVEL2_RX0_MSB _u(4) #define PROC_PIO_FLEVEL2_RX0_LSB _u(4) #define PROC_PIO_FLEVEL2_RX0_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_FLEVEL2_TX0 // Description : None #define PROC_PIO_FLEVEL2_TX0_RESET _u(0x0) #define PROC_PIO_FLEVEL2_TX0_BITS _u(0x00000001) #define PROC_PIO_FLEVEL2_TX0_MSB _u(0) #define PROC_PIO_FLEVEL2_TX0_LSB _u(0) #define PROC_PIO_FLEVEL2_TX0_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_TXF0 // Description : Direct write access to the TX FIFO for this state machine. Each // write pushes one word to the FIFO. #define PROC_PIO_TXF0_OFFSET _u(0x00000014) #define PROC_PIO_TXF0_BITS _u(0xffffffff) #define PROC_PIO_TXF0_RESET _u(0x00000000) #define PROC_PIO_TXF0_WIDTH _u(32) #define PROC_PIO_TXF0_MSB _u(31) #define PROC_PIO_TXF0_LSB _u(0) #define PROC_PIO_TXF0_ACCESS "WF" // ============================================================================= // Register : PROC_PIO_TXF1 // Description : Direct write access to the TX FIFO for this state machine. Each // write pushes one word to the FIFO. #define PROC_PIO_TXF1_OFFSET _u(0x00000018) #define PROC_PIO_TXF1_BITS _u(0xffffffff) #define PROC_PIO_TXF1_RESET _u(0x00000000) #define PROC_PIO_TXF1_WIDTH _u(32) #define PROC_PIO_TXF1_MSB _u(31) #define PROC_PIO_TXF1_LSB _u(0) #define PROC_PIO_TXF1_ACCESS "WF" // ============================================================================= // Register : PROC_PIO_TXF2 // Description : Direct write access to the TX FIFO for this state machine. Each // write pushes one word to the FIFO. #define PROC_PIO_TXF2_OFFSET _u(0x0000001c) #define PROC_PIO_TXF2_BITS _u(0xffffffff) #define PROC_PIO_TXF2_RESET _u(0x00000000) #define PROC_PIO_TXF2_WIDTH _u(32) #define PROC_PIO_TXF2_MSB _u(31) #define PROC_PIO_TXF2_LSB _u(0) #define PROC_PIO_TXF2_ACCESS "WF" // ============================================================================= // Register : PROC_PIO_TXF3 // Description : Direct write access to the TX FIFO for this state machine. Each // write pushes one word to the FIFO. #define PROC_PIO_TXF3_OFFSET _u(0x00000020) #define PROC_PIO_TXF3_BITS _u(0xffffffff) #define PROC_PIO_TXF3_RESET _u(0x00000000) #define PROC_PIO_TXF3_WIDTH _u(32) #define PROC_PIO_TXF3_MSB _u(31) #define PROC_PIO_TXF3_LSB _u(0) #define PROC_PIO_TXF3_ACCESS "WF" // ============================================================================= // Register : PROC_PIO_RXF0 // Description : Direct read access to the RX FIFO for this state machine. Each // read pops one word from the FIFO. #define PROC_PIO_RXF0_OFFSET _u(0x00000024) #define PROC_PIO_RXF0_BITS _u(0xffffffff) #define PROC_PIO_RXF0_RESET "-" #define PROC_PIO_RXF0_WIDTH _u(32) #define PROC_PIO_RXF0_MSB _u(31) #define PROC_PIO_RXF0_LSB _u(0) #define PROC_PIO_RXF0_ACCESS "RF" // ============================================================================= // Register : PROC_PIO_RXF1 // Description : Direct read access to the RX FIFO for this state machine. Each // read pops one word from the FIFO. #define PROC_PIO_RXF1_OFFSET _u(0x00000028) #define PROC_PIO_RXF1_BITS _u(0xffffffff) #define PROC_PIO_RXF1_RESET "-" #define PROC_PIO_RXF1_WIDTH _u(32) #define PROC_PIO_RXF1_MSB _u(31) #define PROC_PIO_RXF1_LSB _u(0) #define PROC_PIO_RXF1_ACCESS "RF" // ============================================================================= // Register : PROC_PIO_RXF2 // Description : Direct read access to the RX FIFO for this state machine. Each // read pops one word from the FIFO. #define PROC_PIO_RXF2_OFFSET _u(0x0000002c) #define PROC_PIO_RXF2_BITS _u(0xffffffff) #define PROC_PIO_RXF2_RESET "-" #define PROC_PIO_RXF2_WIDTH _u(32) #define PROC_PIO_RXF2_MSB _u(31) #define PROC_PIO_RXF2_LSB _u(0) #define PROC_PIO_RXF2_ACCESS "RF" // ============================================================================= // Register : PROC_PIO_RXF3 // Description : Direct read access to the RX FIFO for this state machine. Each // read pops one word from the FIFO. #define PROC_PIO_RXF3_OFFSET _u(0x00000030) #define PROC_PIO_RXF3_BITS _u(0xffffffff) #define PROC_PIO_RXF3_RESET "-" #define PROC_PIO_RXF3_WIDTH _u(32) #define PROC_PIO_RXF3_MSB _u(31) #define PROC_PIO_RXF3_LSB _u(0) #define PROC_PIO_RXF3_ACCESS "RF" // ============================================================================= // Register : PROC_PIO_IRQ // Description : Interrupt request register. Write 1 to clear #define PROC_PIO_IRQ_OFFSET _u(0x00000034) #define PROC_PIO_IRQ_BITS _u(0x000000ff) #define PROC_PIO_IRQ_RESET _u(0x00000000) #define PROC_PIO_IRQ_WIDTH _u(32) #define PROC_PIO_IRQ_MSB _u(7) #define PROC_PIO_IRQ_LSB _u(0) #define PROC_PIO_IRQ_ACCESS "WC" // ============================================================================= // Register : PROC_PIO_IRQ_FORCE // Description : Writing a 1 to each of these bits will forcibly assert the // corresponding IRQ. // Note this is different to the INTF register: writing here // affects PIO internal // state. INTF just asserts the processor-facing IRQ signal for // testing ISRs, // and is not visible to the state machines. #define PROC_PIO_IRQ_FORCE_OFFSET _u(0x00000038) #define PROC_PIO_IRQ_FORCE_BITS _u(0x000000ff) #define PROC_PIO_IRQ_FORCE_RESET _u(0x00000000) #define PROC_PIO_IRQ_FORCE_WIDTH _u(32) #define PROC_PIO_IRQ_FORCE_MSB _u(7) #define PROC_PIO_IRQ_FORCE_LSB _u(0) #define PROC_PIO_IRQ_FORCE_ACCESS "WF" // ============================================================================= // Register : PROC_PIO_INPUT_SYNC_BYPASS // Description : There is a 2-flipflop synchronizer on each GPIO input, which // protects // PIO logic from metastabilities. This increases input delay, and // for fast // synchronous IO (e.g. SPI) these synchronizers may need to be // bypassed. // Each bit in this register corresponds to one GPIO. // 0 -> input is synchronized (default) // 1 -> synchronizer is bypassed // If in doubt, leave this register as all zeroes. #define PROC_PIO_INPUT_SYNC_BYPASS_OFFSET _u(0x0000003c) #define PROC_PIO_INPUT_SYNC_BYPASS_BITS _u(0xffffffff) #define PROC_PIO_INPUT_SYNC_BYPASS_RESET _u(0x00000000) #define PROC_PIO_INPUT_SYNC_BYPASS_WIDTH _u(32) #define PROC_PIO_INPUT_SYNC_BYPASS_MSB _u(31) #define PROC_PIO_INPUT_SYNC_BYPASS_LSB _u(0) #define PROC_PIO_INPUT_SYNC_BYPASS_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_DBG_PADOUT // Description : Read to sample the pad output values PIO is currently driving // to the GPIOs. #define PROC_PIO_DBG_PADOUT_OFFSET _u(0x00000040) #define PROC_PIO_DBG_PADOUT_BITS _u(0xffffffff) #define PROC_PIO_DBG_PADOUT_RESET _u(0x00000000) #define PROC_PIO_DBG_PADOUT_WIDTH _u(32) #define PROC_PIO_DBG_PADOUT_MSB _u(31) #define PROC_PIO_DBG_PADOUT_LSB _u(0) #define PROC_PIO_DBG_PADOUT_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_DBG_PADOE // Description : Read to sample the pad output enables (direction) PIO is // currently driving to the GPIOs. #define PROC_PIO_DBG_PADOE_OFFSET _u(0x00000044) #define PROC_PIO_DBG_PADOE_BITS _u(0xffffffff) #define PROC_PIO_DBG_PADOE_RESET _u(0x00000000) #define PROC_PIO_DBG_PADOE_WIDTH _u(32) #define PROC_PIO_DBG_PADOE_MSB _u(31) #define PROC_PIO_DBG_PADOE_LSB _u(0) #define PROC_PIO_DBG_PADOE_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_DBG_CFGINFO // Description : The PIO hardware has some free parameters that may vary between // chip products. // These should be provided in the chip datasheet, but are also // exposed here. #define PROC_PIO_DBG_CFGINFO_OFFSET _u(0x00000048) #define PROC_PIO_DBG_CFGINFO_BITS _u(0x003f0f3f) #define PROC_PIO_DBG_CFGINFO_RESET _u(0x00000000) #define PROC_PIO_DBG_CFGINFO_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_DBG_CFGINFO_IMEM_SIZE // Description : The size of the instruction memory, measured in units of one // instruction #define PROC_PIO_DBG_CFGINFO_IMEM_SIZE_RESET "-" #define PROC_PIO_DBG_CFGINFO_IMEM_SIZE_BITS _u(0x003f0000) #define PROC_PIO_DBG_CFGINFO_IMEM_SIZE_MSB _u(21) #define PROC_PIO_DBG_CFGINFO_IMEM_SIZE_LSB _u(16) #define PROC_PIO_DBG_CFGINFO_IMEM_SIZE_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_DBG_CFGINFO_SM_COUNT // Description : The number of state machines this PIO instance is equipped // with. #define PROC_PIO_DBG_CFGINFO_SM_COUNT_RESET "-" #define PROC_PIO_DBG_CFGINFO_SM_COUNT_BITS _u(0x00000f00) #define PROC_PIO_DBG_CFGINFO_SM_COUNT_MSB _u(11) #define PROC_PIO_DBG_CFGINFO_SM_COUNT_LSB _u(8) #define PROC_PIO_DBG_CFGINFO_SM_COUNT_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_DBG_CFGINFO_FIFO_DEPTH // Description : The depth of the state machine TX/RX FIFOs, measured in words. // Joining fifos via SHIFTCTRL_FJOIN gives one FIFO with double // this depth. #define PROC_PIO_DBG_CFGINFO_FIFO_DEPTH_RESET "-" #define PROC_PIO_DBG_CFGINFO_FIFO_DEPTH_BITS _u(0x0000003f) #define PROC_PIO_DBG_CFGINFO_FIFO_DEPTH_MSB _u(5) #define PROC_PIO_DBG_CFGINFO_FIFO_DEPTH_LSB _u(0) #define PROC_PIO_DBG_CFGINFO_FIFO_DEPTH_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM0 // Description : Write-only access to instruction memory location 0 #define PROC_PIO_INSTR_MEM0_OFFSET _u(0x0000004c) #define PROC_PIO_INSTR_MEM0_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM0_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM0_WIDTH _u(32) #define PROC_PIO_INSTR_MEM0_MSB _u(15) #define PROC_PIO_INSTR_MEM0_LSB _u(0) #define PROC_PIO_INSTR_MEM0_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM1 // Description : Write-only access to instruction memory location 1 #define PROC_PIO_INSTR_MEM1_OFFSET _u(0x00000050) #define PROC_PIO_INSTR_MEM1_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM1_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM1_WIDTH _u(32) #define PROC_PIO_INSTR_MEM1_MSB _u(15) #define PROC_PIO_INSTR_MEM1_LSB _u(0) #define PROC_PIO_INSTR_MEM1_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM2 // Description : Write-only access to instruction memory location 2 #define PROC_PIO_INSTR_MEM2_OFFSET _u(0x00000054) #define PROC_PIO_INSTR_MEM2_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM2_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM2_WIDTH _u(32) #define PROC_PIO_INSTR_MEM2_MSB _u(15) #define PROC_PIO_INSTR_MEM2_LSB _u(0) #define PROC_PIO_INSTR_MEM2_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM3 // Description : Write-only access to instruction memory location 3 #define PROC_PIO_INSTR_MEM3_OFFSET _u(0x00000058) #define PROC_PIO_INSTR_MEM3_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM3_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM3_WIDTH _u(32) #define PROC_PIO_INSTR_MEM3_MSB _u(15) #define PROC_PIO_INSTR_MEM3_LSB _u(0) #define PROC_PIO_INSTR_MEM3_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM4 // Description : Write-only access to instruction memory location 4 #define PROC_PIO_INSTR_MEM4_OFFSET _u(0x0000005c) #define PROC_PIO_INSTR_MEM4_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM4_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM4_WIDTH _u(32) #define PROC_PIO_INSTR_MEM4_MSB _u(15) #define PROC_PIO_INSTR_MEM4_LSB _u(0) #define PROC_PIO_INSTR_MEM4_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM5 // Description : Write-only access to instruction memory location 5 #define PROC_PIO_INSTR_MEM5_OFFSET _u(0x00000060) #define PROC_PIO_INSTR_MEM5_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM5_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM5_WIDTH _u(32) #define PROC_PIO_INSTR_MEM5_MSB _u(15) #define PROC_PIO_INSTR_MEM5_LSB _u(0) #define PROC_PIO_INSTR_MEM5_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM6 // Description : Write-only access to instruction memory location 6 #define PROC_PIO_INSTR_MEM6_OFFSET _u(0x00000064) #define PROC_PIO_INSTR_MEM6_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM6_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM6_WIDTH _u(32) #define PROC_PIO_INSTR_MEM6_MSB _u(15) #define PROC_PIO_INSTR_MEM6_LSB _u(0) #define PROC_PIO_INSTR_MEM6_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM7 // Description : Write-only access to instruction memory location 7 #define PROC_PIO_INSTR_MEM7_OFFSET _u(0x00000068) #define PROC_PIO_INSTR_MEM7_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM7_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM7_WIDTH _u(32) #define PROC_PIO_INSTR_MEM7_MSB _u(15) #define PROC_PIO_INSTR_MEM7_LSB _u(0) #define PROC_PIO_INSTR_MEM7_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM8 // Description : Write-only access to instruction memory location 8 #define PROC_PIO_INSTR_MEM8_OFFSET _u(0x0000006c) #define PROC_PIO_INSTR_MEM8_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM8_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM8_WIDTH _u(32) #define PROC_PIO_INSTR_MEM8_MSB _u(15) #define PROC_PIO_INSTR_MEM8_LSB _u(0) #define PROC_PIO_INSTR_MEM8_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM9 // Description : Write-only access to instruction memory location 9 #define PROC_PIO_INSTR_MEM9_OFFSET _u(0x00000070) #define PROC_PIO_INSTR_MEM9_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM9_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM9_WIDTH _u(32) #define PROC_PIO_INSTR_MEM9_MSB _u(15) #define PROC_PIO_INSTR_MEM9_LSB _u(0) #define PROC_PIO_INSTR_MEM9_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM10 // Description : Write-only access to instruction memory location 10 #define PROC_PIO_INSTR_MEM10_OFFSET _u(0x00000074) #define PROC_PIO_INSTR_MEM10_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM10_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM10_WIDTH _u(32) #define PROC_PIO_INSTR_MEM10_MSB _u(15) #define PROC_PIO_INSTR_MEM10_LSB _u(0) #define PROC_PIO_INSTR_MEM10_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM11 // Description : Write-only access to instruction memory location 11 #define PROC_PIO_INSTR_MEM11_OFFSET _u(0x00000078) #define PROC_PIO_INSTR_MEM11_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM11_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM11_WIDTH _u(32) #define PROC_PIO_INSTR_MEM11_MSB _u(15) #define PROC_PIO_INSTR_MEM11_LSB _u(0) #define PROC_PIO_INSTR_MEM11_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM12 // Description : Write-only access to instruction memory location 12 #define PROC_PIO_INSTR_MEM12_OFFSET _u(0x0000007c) #define PROC_PIO_INSTR_MEM12_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM12_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM12_WIDTH _u(32) #define PROC_PIO_INSTR_MEM12_MSB _u(15) #define PROC_PIO_INSTR_MEM12_LSB _u(0) #define PROC_PIO_INSTR_MEM12_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM13 // Description : Write-only access to instruction memory location 13 #define PROC_PIO_INSTR_MEM13_OFFSET _u(0x00000080) #define PROC_PIO_INSTR_MEM13_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM13_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM13_WIDTH _u(32) #define PROC_PIO_INSTR_MEM13_MSB _u(15) #define PROC_PIO_INSTR_MEM13_LSB _u(0) #define PROC_PIO_INSTR_MEM13_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM14 // Description : Write-only access to instruction memory location 14 #define PROC_PIO_INSTR_MEM14_OFFSET _u(0x00000084) #define PROC_PIO_INSTR_MEM14_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM14_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM14_WIDTH _u(32) #define PROC_PIO_INSTR_MEM14_MSB _u(15) #define PROC_PIO_INSTR_MEM14_LSB _u(0) #define PROC_PIO_INSTR_MEM14_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM15 // Description : Write-only access to instruction memory location 15 #define PROC_PIO_INSTR_MEM15_OFFSET _u(0x00000088) #define PROC_PIO_INSTR_MEM15_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM15_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM15_WIDTH _u(32) #define PROC_PIO_INSTR_MEM15_MSB _u(15) #define PROC_PIO_INSTR_MEM15_LSB _u(0) #define PROC_PIO_INSTR_MEM15_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM16 // Description : Write-only access to instruction memory location 16 #define PROC_PIO_INSTR_MEM16_OFFSET _u(0x0000008c) #define PROC_PIO_INSTR_MEM16_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM16_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM16_WIDTH _u(32) #define PROC_PIO_INSTR_MEM16_MSB _u(15) #define PROC_PIO_INSTR_MEM16_LSB _u(0) #define PROC_PIO_INSTR_MEM16_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM17 // Description : Write-only access to instruction memory location 17 #define PROC_PIO_INSTR_MEM17_OFFSET _u(0x00000090) #define PROC_PIO_INSTR_MEM17_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM17_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM17_WIDTH _u(32) #define PROC_PIO_INSTR_MEM17_MSB _u(15) #define PROC_PIO_INSTR_MEM17_LSB _u(0) #define PROC_PIO_INSTR_MEM17_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM18 // Description : Write-only access to instruction memory location 18 #define PROC_PIO_INSTR_MEM18_OFFSET _u(0x00000094) #define PROC_PIO_INSTR_MEM18_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM18_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM18_WIDTH _u(32) #define PROC_PIO_INSTR_MEM18_MSB _u(15) #define PROC_PIO_INSTR_MEM18_LSB _u(0) #define PROC_PIO_INSTR_MEM18_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM19 // Description : Write-only access to instruction memory location 19 #define PROC_PIO_INSTR_MEM19_OFFSET _u(0x00000098) #define PROC_PIO_INSTR_MEM19_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM19_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM19_WIDTH _u(32) #define PROC_PIO_INSTR_MEM19_MSB _u(15) #define PROC_PIO_INSTR_MEM19_LSB _u(0) #define PROC_PIO_INSTR_MEM19_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM20 // Description : Write-only access to instruction memory location 20 #define PROC_PIO_INSTR_MEM20_OFFSET _u(0x0000009c) #define PROC_PIO_INSTR_MEM20_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM20_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM20_WIDTH _u(32) #define PROC_PIO_INSTR_MEM20_MSB _u(15) #define PROC_PIO_INSTR_MEM20_LSB _u(0) #define PROC_PIO_INSTR_MEM20_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM21 // Description : Write-only access to instruction memory location 21 #define PROC_PIO_INSTR_MEM21_OFFSET _u(0x000000a0) #define PROC_PIO_INSTR_MEM21_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM21_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM21_WIDTH _u(32) #define PROC_PIO_INSTR_MEM21_MSB _u(15) #define PROC_PIO_INSTR_MEM21_LSB _u(0) #define PROC_PIO_INSTR_MEM21_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM22 // Description : Write-only access to instruction memory location 22 #define PROC_PIO_INSTR_MEM22_OFFSET _u(0x000000a4) #define PROC_PIO_INSTR_MEM22_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM22_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM22_WIDTH _u(32) #define PROC_PIO_INSTR_MEM22_MSB _u(15) #define PROC_PIO_INSTR_MEM22_LSB _u(0) #define PROC_PIO_INSTR_MEM22_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM23 // Description : Write-only access to instruction memory location 23 #define PROC_PIO_INSTR_MEM23_OFFSET _u(0x000000a8) #define PROC_PIO_INSTR_MEM23_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM23_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM23_WIDTH _u(32) #define PROC_PIO_INSTR_MEM23_MSB _u(15) #define PROC_PIO_INSTR_MEM23_LSB _u(0) #define PROC_PIO_INSTR_MEM23_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM24 // Description : Write-only access to instruction memory location 24 #define PROC_PIO_INSTR_MEM24_OFFSET _u(0x000000ac) #define PROC_PIO_INSTR_MEM24_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM24_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM24_WIDTH _u(32) #define PROC_PIO_INSTR_MEM24_MSB _u(15) #define PROC_PIO_INSTR_MEM24_LSB _u(0) #define PROC_PIO_INSTR_MEM24_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM25 // Description : Write-only access to instruction memory location 25 #define PROC_PIO_INSTR_MEM25_OFFSET _u(0x000000b0) #define PROC_PIO_INSTR_MEM25_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM25_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM25_WIDTH _u(32) #define PROC_PIO_INSTR_MEM25_MSB _u(15) #define PROC_PIO_INSTR_MEM25_LSB _u(0) #define PROC_PIO_INSTR_MEM25_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM26 // Description : Write-only access to instruction memory location 26 #define PROC_PIO_INSTR_MEM26_OFFSET _u(0x000000b4) #define PROC_PIO_INSTR_MEM26_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM26_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM26_WIDTH _u(32) #define PROC_PIO_INSTR_MEM26_MSB _u(15) #define PROC_PIO_INSTR_MEM26_LSB _u(0) #define PROC_PIO_INSTR_MEM26_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM27 // Description : Write-only access to instruction memory location 27 #define PROC_PIO_INSTR_MEM27_OFFSET _u(0x000000b8) #define PROC_PIO_INSTR_MEM27_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM27_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM27_WIDTH _u(32) #define PROC_PIO_INSTR_MEM27_MSB _u(15) #define PROC_PIO_INSTR_MEM27_LSB _u(0) #define PROC_PIO_INSTR_MEM27_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM28 // Description : Write-only access to instruction memory location 28 #define PROC_PIO_INSTR_MEM28_OFFSET _u(0x000000bc) #define PROC_PIO_INSTR_MEM28_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM28_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM28_WIDTH _u(32) #define PROC_PIO_INSTR_MEM28_MSB _u(15) #define PROC_PIO_INSTR_MEM28_LSB _u(0) #define PROC_PIO_INSTR_MEM28_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM29 // Description : Write-only access to instruction memory location 29 #define PROC_PIO_INSTR_MEM29_OFFSET _u(0x000000c0) #define PROC_PIO_INSTR_MEM29_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM29_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM29_WIDTH _u(32) #define PROC_PIO_INSTR_MEM29_MSB _u(15) #define PROC_PIO_INSTR_MEM29_LSB _u(0) #define PROC_PIO_INSTR_MEM29_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM30 // Description : Write-only access to instruction memory location 30 #define PROC_PIO_INSTR_MEM30_OFFSET _u(0x000000c4) #define PROC_PIO_INSTR_MEM30_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM30_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM30_WIDTH _u(32) #define PROC_PIO_INSTR_MEM30_MSB _u(15) #define PROC_PIO_INSTR_MEM30_LSB _u(0) #define PROC_PIO_INSTR_MEM30_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_INSTR_MEM31 // Description : Write-only access to instruction memory location 31 #define PROC_PIO_INSTR_MEM31_OFFSET _u(0x000000c8) #define PROC_PIO_INSTR_MEM31_BITS _u(0x0000ffff) #define PROC_PIO_INSTR_MEM31_RESET _u(0x00000000) #define PROC_PIO_INSTR_MEM31_WIDTH _u(32) #define PROC_PIO_INSTR_MEM31_MSB _u(15) #define PROC_PIO_INSTR_MEM31_LSB _u(0) #define PROC_PIO_INSTR_MEM31_ACCESS "WO" // ============================================================================= // Register : PROC_PIO_SM0_CLKDIV // Description : Clock divider register for state machine 0 // Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) #define PROC_PIO_SM0_CLKDIV_OFFSET _u(0x000000cc) #define PROC_PIO_SM0_CLKDIV_BITS _u(0xffffff00) #define PROC_PIO_SM0_CLKDIV_RESET _u(0x00010000) #define PROC_PIO_SM0_CLKDIV_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_CLKDIV_INT // Description : Effective frequency is sysclk/int. // Value of 0 is interpreted as max possible value #define PROC_PIO_SM0_CLKDIV_INT_RESET _u(0x0001) #define PROC_PIO_SM0_CLKDIV_INT_BITS _u(0xffff0000) #define PROC_PIO_SM0_CLKDIV_INT_MSB _u(31) #define PROC_PIO_SM0_CLKDIV_INT_LSB _u(16) #define PROC_PIO_SM0_CLKDIV_INT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_CLKDIV_FRAC // Description : Fractional part of clock divider #define PROC_PIO_SM0_CLKDIV_FRAC_RESET _u(0x00) #define PROC_PIO_SM0_CLKDIV_FRAC_BITS _u(0x0000ff00) #define PROC_PIO_SM0_CLKDIV_FRAC_MSB _u(15) #define PROC_PIO_SM0_CLKDIV_FRAC_LSB _u(8) #define PROC_PIO_SM0_CLKDIV_FRAC_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM0_EXECCTRL // Description : Execution/behavioural settings for state machine 0 #define PROC_PIO_SM0_EXECCTRL_OFFSET _u(0x000000d0) #define PROC_PIO_SM0_EXECCTRL_BITS _u(0xffffffbf) #define PROC_PIO_SM0_EXECCTRL_RESET _u(0x0001f000) #define PROC_PIO_SM0_EXECCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_EXECCTRL_EXEC_STALLED // Description : An instruction written to SMx_INSTR is stalled, and latched by // the // state machine. Will clear once the instruction completes. #define PROC_PIO_SM0_EXECCTRL_EXEC_STALLED_RESET _u(0x0) #define PROC_PIO_SM0_EXECCTRL_EXEC_STALLED_BITS _u(0x80000000) #define PROC_PIO_SM0_EXECCTRL_EXEC_STALLED_MSB _u(31) #define PROC_PIO_SM0_EXECCTRL_EXEC_STALLED_LSB _u(31) #define PROC_PIO_SM0_EXECCTRL_EXEC_STALLED_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_EXECCTRL_SIDE_EN // Description : If 1, the delay MSB is used as side-set enable, rather than a // side-set data bit. This allows instructions to perform side-set // optionally, // rather than on every instruction. #define PROC_PIO_SM0_EXECCTRL_SIDE_EN_RESET _u(0x0) #define PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS _u(0x40000000) #define PROC_PIO_SM0_EXECCTRL_SIDE_EN_MSB _u(30) #define PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB _u(30) #define PROC_PIO_SM0_EXECCTRL_SIDE_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR // Description : Side-set data is asserted to pin OEs instead of pin values #define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_RESET _u(0x0) #define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS _u(0x20000000) #define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_MSB _u(29) #define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB _u(29) #define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_EXECCTRL_JMP_PIN // Description : The GPIO number to use as condition for JMP PIN. Unaffected by // input mapping. #define PROC_PIO_SM0_EXECCTRL_JMP_PIN_RESET _u(0x00) #define PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS _u(0x1f000000) #define PROC_PIO_SM0_EXECCTRL_JMP_PIN_MSB _u(28) #define PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB _u(24) #define PROC_PIO_SM0_EXECCTRL_JMP_PIN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL // Description : Which data bit to use for inline OUT enable #define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_RESET _u(0x00) #define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS _u(0x00f80000) #define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_MSB _u(23) #define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB _u(19) #define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN // Description : If 1, use a bit of OUT data as an auxiliary write enable // When used in conjunction with OUT_STICKY, writes with an enable // of 0 will // deassert the latest pin write. This can create useful // masking/override behaviour // due to the priority ordering of state machine pin writes (SM0 < // SM1 < ...) #define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_RESET _u(0x0) #define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS _u(0x00040000) #define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_MSB _u(18) #define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB _u(18) #define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_EXECCTRL_OUT_STICKY // Description : Continuously assert the most recent OUT/SET to the pins #define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_RESET _u(0x0) #define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS _u(0x00020000) #define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_MSB _u(17) #define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB _u(17) #define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_EXECCTRL_WRAP_TOP // Description : After reaching this address, execution is wrapped to // wrap_bottom. // If the instruction is a jump, and the jump condition is true, // the jump takes priority. #define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_RESET _u(0x1f) #define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS _u(0x0001f000) #define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_MSB _u(16) #define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB _u(12) #define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM // Description : After reaching wrap_top, execution is wrapped to this address. #define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_RESET _u(0x00) #define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS _u(0x00000f80) #define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_MSB _u(11) #define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB _u(7) #define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_EXECCTRL_STATUS_SEL // Description : Comparison used for the MOV x, STATUS instruction. // 0x0 -> All-ones if TX FIFO level < N, otherwise all-zeroes // 0x1 -> All-ones if RX FIFO level < N, otherwise all-zeroes #define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_RESET _u(0x0) #define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS _u(0x00000020) #define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_MSB _u(5) #define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB _u(5) #define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_ACCESS "RW" #define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_VALUE_TXLEVEL _u(0x0) #define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_VALUE_RXLEVEL _u(0x1) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_EXECCTRL_STATUS_N // Description : Comparison level for the MOV x, STATUS instruction #define PROC_PIO_SM0_EXECCTRL_STATUS_N_RESET _u(0x00) #define PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS _u(0x0000001f) #define PROC_PIO_SM0_EXECCTRL_STATUS_N_MSB _u(4) #define PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB _u(0) #define PROC_PIO_SM0_EXECCTRL_STATUS_N_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM0_SHIFTCTRL // Description : Control behaviour of the input/output shift registers for state // machine 0 #define PROC_PIO_SM0_SHIFTCTRL_OFFSET _u(0x000000d4) #define PROC_PIO_SM0_SHIFTCTRL_BITS _u(0xffff0000) #define PROC_PIO_SM0_SHIFTCTRL_RESET _u(0x000c0000) #define PROC_PIO_SM0_SHIFTCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX // Description : When 1, RX FIFO steals the TX FIFO's storage, and becomes twice // as deep. // TX FIFO is disabled as a result (always reads as both full and // empty). // FIFOs are flushed when this bit is changed. #define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_RESET _u(0x0) #define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS _u(0x80000000) #define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_MSB _u(31) #define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_LSB _u(31) #define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX // Description : When 1, TX FIFO steals the RX FIFO's storage, and becomes twice // as deep. // RX FIFO is disabled as a result (always reads as both full and // empty). // FIFOs are flushed when this bit is changed. #define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_RESET _u(0x0) #define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS _u(0x40000000) #define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_MSB _u(30) #define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB _u(30) #define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH // Description : Number of bits shifted out of TXSR before autopull or // conditional pull. // Write 0 for value of 32. #define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_RESET _u(0x00) #define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS _u(0x3e000000) #define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_MSB _u(29) #define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB _u(25) #define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH // Description : Number of bits shifted into RXSR before autopush or conditional // push. // Write 0 for value of 32. #define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_RESET _u(0x00) #define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS _u(0x01f00000) #define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_MSB _u(24) #define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB _u(20) #define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR // Description : 1 = shift out of output shift register to right. 0 = to left. #define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_RESET _u(0x1) #define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS _u(0x00080000) #define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_MSB _u(19) #define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB _u(19) #define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR // Description : 1 = shift input shift register to right (data enters from // left). 0 = to left. #define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_RESET _u(0x1) #define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS _u(0x00040000) #define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_MSB _u(18) #define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB _u(18) #define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_SHIFTCTRL_AUTOPULL // Description : Pull automatically when the output shift register is emptied #define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_RESET _u(0x0) #define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS _u(0x00020000) #define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_MSB _u(17) #define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB _u(17) #define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH // Description : Push automatically when the input shift register is filled #define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_RESET _u(0x0) #define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS _u(0x00010000) #define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_MSB _u(16) #define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB _u(16) #define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM0_ADDR // Description : Current instruction address of state machine 0 #define PROC_PIO_SM0_ADDR_OFFSET _u(0x000000d8) #define PROC_PIO_SM0_ADDR_BITS _u(0x0000001f) #define PROC_PIO_SM0_ADDR_RESET _u(0x00000000) #define PROC_PIO_SM0_ADDR_WIDTH _u(32) #define PROC_PIO_SM0_ADDR_MSB _u(4) #define PROC_PIO_SM0_ADDR_LSB _u(0) #define PROC_PIO_SM0_ADDR_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_SM0_INSTR // Description : Instruction currently being executed by state machine 0 // Write to execute an instruction immediately (including jumps) // and then resume execution. #define PROC_PIO_SM0_INSTR_OFFSET _u(0x000000dc) #define PROC_PIO_SM0_INSTR_BITS _u(0x0000ffff) #define PROC_PIO_SM0_INSTR_RESET "-" #define PROC_PIO_SM0_INSTR_WIDTH _u(32) #define PROC_PIO_SM0_INSTR_MSB _u(15) #define PROC_PIO_SM0_INSTR_LSB _u(0) #define PROC_PIO_SM0_INSTR_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM0_PINCTRL // Description : State machine pin control #define PROC_PIO_SM0_PINCTRL_OFFSET _u(0x000000e0) #define PROC_PIO_SM0_PINCTRL_BITS _u(0xffffffff) #define PROC_PIO_SM0_PINCTRL_RESET _u(0x14000000) #define PROC_PIO_SM0_PINCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_PINCTRL_SIDESET_COUNT // Description : The number of delay bits co-opted for side-set. Inclusive of // the enable bit, if present. #define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_RESET _u(0x0) #define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS _u(0xe0000000) #define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_MSB _u(31) #define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB _u(29) #define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_PINCTRL_SET_COUNT // Description : The number of pins asserted by a SET. Max of 5 #define PROC_PIO_SM0_PINCTRL_SET_COUNT_RESET _u(0x5) #define PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS _u(0x1c000000) #define PROC_PIO_SM0_PINCTRL_SET_COUNT_MSB _u(28) #define PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB _u(26) #define PROC_PIO_SM0_PINCTRL_SET_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_PINCTRL_OUT_COUNT // Description : The number of pins asserted by an OUT. Value of 0 -> 32 pins #define PROC_PIO_SM0_PINCTRL_OUT_COUNT_RESET _u(0x00) #define PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS _u(0x03f00000) #define PROC_PIO_SM0_PINCTRL_OUT_COUNT_MSB _u(25) #define PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB _u(20) #define PROC_PIO_SM0_PINCTRL_OUT_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_PINCTRL_IN_BASE // Description : The virtual pin corresponding to IN bit 0 #define PROC_PIO_SM0_PINCTRL_IN_BASE_RESET _u(0x00) #define PROC_PIO_SM0_PINCTRL_IN_BASE_BITS _u(0x000f8000) #define PROC_PIO_SM0_PINCTRL_IN_BASE_MSB _u(19) #define PROC_PIO_SM0_PINCTRL_IN_BASE_LSB _u(15) #define PROC_PIO_SM0_PINCTRL_IN_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_PINCTRL_SIDESET_BASE // Description : The virtual pin corresponding to delay field bit 0 #define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_RESET _u(0x00) #define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS _u(0x00007c00) #define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_MSB _u(14) #define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB _u(10) #define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_PINCTRL_SET_BASE // Description : The virtual pin corresponding to SET bit 0 #define PROC_PIO_SM0_PINCTRL_SET_BASE_RESET _u(0x00) #define PROC_PIO_SM0_PINCTRL_SET_BASE_BITS _u(0x000003e0) #define PROC_PIO_SM0_PINCTRL_SET_BASE_MSB _u(9) #define PROC_PIO_SM0_PINCTRL_SET_BASE_LSB _u(5) #define PROC_PIO_SM0_PINCTRL_SET_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_PINCTRL_OUT_BASE // Description : The virtual pin corresponding to OUT bit 0 #define PROC_PIO_SM0_PINCTRL_OUT_BASE_RESET _u(0x00) #define PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS _u(0x0000001f) #define PROC_PIO_SM0_PINCTRL_OUT_BASE_MSB _u(4) #define PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB _u(0) #define PROC_PIO_SM0_PINCTRL_OUT_BASE_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM0_DMACTRL_TX // Description : State machine DMA control #define PROC_PIO_SM0_DMACTRL_TX_OFFSET _u(0x000000e4) #define PROC_PIO_SM0_DMACTRL_TX_BITS _u(0xc0000f9f) #define PROC_PIO_SM0_DMACTRL_TX_RESET _u(0x00000104) #define PROC_PIO_SM0_DMACTRL_TX_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_DMACTRL_TX_DREQ_EN // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM0_DMACTRL_TX_DREQ_EN_RESET _u(0x0) #define PROC_PIO_SM0_DMACTRL_TX_DREQ_EN_BITS _u(0x80000000) #define PROC_PIO_SM0_DMACTRL_TX_DREQ_EN_MSB _u(31) #define PROC_PIO_SM0_DMACTRL_TX_DREQ_EN_LSB _u(31) #define PROC_PIO_SM0_DMACTRL_TX_DREQ_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_DMACTRL_TX_ACTIVE // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM0_DMACTRL_TX_ACTIVE_RESET "-" #define PROC_PIO_SM0_DMACTRL_TX_ACTIVE_BITS _u(0x40000000) #define PROC_PIO_SM0_DMACTRL_TX_ACTIVE_MSB _u(30) #define PROC_PIO_SM0_DMACTRL_TX_ACTIVE_LSB _u(30) #define PROC_PIO_SM0_DMACTRL_TX_ACTIVE_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME // Description : Delay in number of bus cycles before successive DREQs are // generated. // Used to account for system bus latency in write data arriving // at the FIFO. #define PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME_RESET _u(0x02) #define PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME_BITS _u(0x00000f80) #define PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME_MSB _u(11) #define PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME_LSB _u(7) #define PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD // Description : Threshold control. If there are no more than THRESHOLD items in // the TX FIFO, DMA dreq and/or the interrupt line is asserted. #define PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD_RESET _u(0x04) #define PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD_BITS _u(0x0000001f) #define PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD_MSB _u(4) #define PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD_LSB _u(0) #define PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM0_DMACTRL_RX // Description : State machine DMA control #define PROC_PIO_SM0_DMACTRL_RX_OFFSET _u(0x000000e8) #define PROC_PIO_SM0_DMACTRL_RX_BITS _u(0xc0000f9f) #define PROC_PIO_SM0_DMACTRL_RX_RESET _u(0x00000104) #define PROC_PIO_SM0_DMACTRL_RX_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_DMACTRL_RX_DREQ_EN // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM0_DMACTRL_RX_DREQ_EN_RESET _u(0x0) #define PROC_PIO_SM0_DMACTRL_RX_DREQ_EN_BITS _u(0x80000000) #define PROC_PIO_SM0_DMACTRL_RX_DREQ_EN_MSB _u(31) #define PROC_PIO_SM0_DMACTRL_RX_DREQ_EN_LSB _u(31) #define PROC_PIO_SM0_DMACTRL_RX_DREQ_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_DMACTRL_RX_ACTIVE // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM0_DMACTRL_RX_ACTIVE_RESET "-" #define PROC_PIO_SM0_DMACTRL_RX_ACTIVE_BITS _u(0x40000000) #define PROC_PIO_SM0_DMACTRL_RX_ACTIVE_MSB _u(30) #define PROC_PIO_SM0_DMACTRL_RX_ACTIVE_LSB _u(30) #define PROC_PIO_SM0_DMACTRL_RX_ACTIVE_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME // Description : Delay in number of bus cycles before successive DREQs are // generated. // Used to account for system bus latency in write data arriving // at the FIFO. #define PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME_RESET _u(0x02) #define PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME_BITS _u(0x00000f80) #define PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME_MSB _u(11) #define PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME_LSB _u(7) #define PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD // Description : Threshold control. If there are at least THRESHOLD items in the // RX FIFO, DMA dreq and/or the interrupt line is asserted. #define PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD_RESET _u(0x04) #define PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD_BITS _u(0x0000001f) #define PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD_MSB _u(4) #define PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD_LSB _u(0) #define PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM1_CLKDIV // Description : Clock divider register for state machine 1 // Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) #define PROC_PIO_SM1_CLKDIV_OFFSET _u(0x000000ec) #define PROC_PIO_SM1_CLKDIV_BITS _u(0xffffff00) #define PROC_PIO_SM1_CLKDIV_RESET _u(0x00010000) #define PROC_PIO_SM1_CLKDIV_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_CLKDIV_INT // Description : Effective frequency is sysclk/int. // Value of 0 is interpreted as max possible value #define PROC_PIO_SM1_CLKDIV_INT_RESET _u(0x0001) #define PROC_PIO_SM1_CLKDIV_INT_BITS _u(0xffff0000) #define PROC_PIO_SM1_CLKDIV_INT_MSB _u(31) #define PROC_PIO_SM1_CLKDIV_INT_LSB _u(16) #define PROC_PIO_SM1_CLKDIV_INT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_CLKDIV_FRAC // Description : Fractional part of clock divider #define PROC_PIO_SM1_CLKDIV_FRAC_RESET _u(0x00) #define PROC_PIO_SM1_CLKDIV_FRAC_BITS _u(0x0000ff00) #define PROC_PIO_SM1_CLKDIV_FRAC_MSB _u(15) #define PROC_PIO_SM1_CLKDIV_FRAC_LSB _u(8) #define PROC_PIO_SM1_CLKDIV_FRAC_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM1_EXECCTRL // Description : Execution/behavioural settings for state machine 1 #define PROC_PIO_SM1_EXECCTRL_OFFSET _u(0x000000f0) #define PROC_PIO_SM1_EXECCTRL_BITS _u(0xffffffbf) #define PROC_PIO_SM1_EXECCTRL_RESET _u(0x0001f000) #define PROC_PIO_SM1_EXECCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_EXECCTRL_EXEC_STALLED // Description : An instruction written to SMx_INSTR is stalled, and latched by // the // state machine. Will clear once the instruction completes. #define PROC_PIO_SM1_EXECCTRL_EXEC_STALLED_RESET _u(0x0) #define PROC_PIO_SM1_EXECCTRL_EXEC_STALLED_BITS _u(0x80000000) #define PROC_PIO_SM1_EXECCTRL_EXEC_STALLED_MSB _u(31) #define PROC_PIO_SM1_EXECCTRL_EXEC_STALLED_LSB _u(31) #define PROC_PIO_SM1_EXECCTRL_EXEC_STALLED_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_EXECCTRL_SIDE_EN // Description : If 1, the delay MSB is used as side-set enable, rather than a // side-set data bit. This allows instructions to perform side-set // optionally, // rather than on every instruction. #define PROC_PIO_SM1_EXECCTRL_SIDE_EN_RESET _u(0x0) #define PROC_PIO_SM1_EXECCTRL_SIDE_EN_BITS _u(0x40000000) #define PROC_PIO_SM1_EXECCTRL_SIDE_EN_MSB _u(30) #define PROC_PIO_SM1_EXECCTRL_SIDE_EN_LSB _u(30) #define PROC_PIO_SM1_EXECCTRL_SIDE_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR // Description : Side-set data is asserted to pin OEs instead of pin values #define PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR_RESET _u(0x0) #define PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR_BITS _u(0x20000000) #define PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR_MSB _u(29) #define PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR_LSB _u(29) #define PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_EXECCTRL_JMP_PIN // Description : The GPIO number to use as condition for JMP PIN. Unaffected by // input mapping. #define PROC_PIO_SM1_EXECCTRL_JMP_PIN_RESET _u(0x00) #define PROC_PIO_SM1_EXECCTRL_JMP_PIN_BITS _u(0x1f000000) #define PROC_PIO_SM1_EXECCTRL_JMP_PIN_MSB _u(28) #define PROC_PIO_SM1_EXECCTRL_JMP_PIN_LSB _u(24) #define PROC_PIO_SM1_EXECCTRL_JMP_PIN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL // Description : Which data bit to use for inline OUT enable #define PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL_RESET _u(0x00) #define PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL_BITS _u(0x00f80000) #define PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL_MSB _u(23) #define PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL_LSB _u(19) #define PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN // Description : If 1, use a bit of OUT data as an auxiliary write enable // When used in conjunction with OUT_STICKY, writes with an enable // of 0 will // deassert the latest pin write. This can create useful // masking/override behaviour // due to the priority ordering of state machine pin writes (SM0 < // SM1 < ...) #define PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN_RESET _u(0x0) #define PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN_BITS _u(0x00040000) #define PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN_MSB _u(18) #define PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN_LSB _u(18) #define PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_EXECCTRL_OUT_STICKY // Description : Continuously assert the most recent OUT/SET to the pins #define PROC_PIO_SM1_EXECCTRL_OUT_STICKY_RESET _u(0x0) #define PROC_PIO_SM1_EXECCTRL_OUT_STICKY_BITS _u(0x00020000) #define PROC_PIO_SM1_EXECCTRL_OUT_STICKY_MSB _u(17) #define PROC_PIO_SM1_EXECCTRL_OUT_STICKY_LSB _u(17) #define PROC_PIO_SM1_EXECCTRL_OUT_STICKY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_EXECCTRL_WRAP_TOP // Description : After reaching this address, execution is wrapped to // wrap_bottom. // If the instruction is a jump, and the jump condition is true, // the jump takes priority. #define PROC_PIO_SM1_EXECCTRL_WRAP_TOP_RESET _u(0x1f) #define PROC_PIO_SM1_EXECCTRL_WRAP_TOP_BITS _u(0x0001f000) #define PROC_PIO_SM1_EXECCTRL_WRAP_TOP_MSB _u(16) #define PROC_PIO_SM1_EXECCTRL_WRAP_TOP_LSB _u(12) #define PROC_PIO_SM1_EXECCTRL_WRAP_TOP_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM // Description : After reaching wrap_top, execution is wrapped to this address. #define PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM_RESET _u(0x00) #define PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM_BITS _u(0x00000f80) #define PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM_MSB _u(11) #define PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM_LSB _u(7) #define PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_EXECCTRL_STATUS_SEL // Description : Comparison used for the MOV x, STATUS instruction. // 0x0 -> All-ones if TX FIFO level < N, otherwise all-zeroes // 0x1 -> All-ones if RX FIFO level < N, otherwise all-zeroes #define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_RESET _u(0x0) #define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_BITS _u(0x00000020) #define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_MSB _u(5) #define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_LSB _u(5) #define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_ACCESS "RW" #define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_VALUE_TXLEVEL _u(0x0) #define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_VALUE_RXLEVEL _u(0x1) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_EXECCTRL_STATUS_N // Description : Comparison level for the MOV x, STATUS instruction #define PROC_PIO_SM1_EXECCTRL_STATUS_N_RESET _u(0x00) #define PROC_PIO_SM1_EXECCTRL_STATUS_N_BITS _u(0x0000001f) #define PROC_PIO_SM1_EXECCTRL_STATUS_N_MSB _u(4) #define PROC_PIO_SM1_EXECCTRL_STATUS_N_LSB _u(0) #define PROC_PIO_SM1_EXECCTRL_STATUS_N_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM1_SHIFTCTRL // Description : Control behaviour of the input/output shift registers for state // machine 1 #define PROC_PIO_SM1_SHIFTCTRL_OFFSET _u(0x000000f4) #define PROC_PIO_SM1_SHIFTCTRL_BITS _u(0xffff0000) #define PROC_PIO_SM1_SHIFTCTRL_RESET _u(0x000c0000) #define PROC_PIO_SM1_SHIFTCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX // Description : When 1, RX FIFO steals the TX FIFO's storage, and becomes twice // as deep. // TX FIFO is disabled as a result (always reads as both full and // empty). // FIFOs are flushed when this bit is changed. #define PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX_RESET _u(0x0) #define PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX_BITS _u(0x80000000) #define PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX_MSB _u(31) #define PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX_LSB _u(31) #define PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX // Description : When 1, TX FIFO steals the RX FIFO's storage, and becomes twice // as deep. // RX FIFO is disabled as a result (always reads as both full and // empty). // FIFOs are flushed when this bit is changed. #define PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX_RESET _u(0x0) #define PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX_BITS _u(0x40000000) #define PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX_MSB _u(30) #define PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX_LSB _u(30) #define PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH // Description : Number of bits shifted out of TXSR before autopull or // conditional pull. // Write 0 for value of 32. #define PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH_RESET _u(0x00) #define PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH_BITS _u(0x3e000000) #define PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH_MSB _u(29) #define PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH_LSB _u(25) #define PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH // Description : Number of bits shifted into RXSR before autopush or conditional // push. // Write 0 for value of 32. #define PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH_RESET _u(0x00) #define PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH_BITS _u(0x01f00000) #define PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH_MSB _u(24) #define PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH_LSB _u(20) #define PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR // Description : 1 = shift out of output shift register to right. 0 = to left. #define PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR_RESET _u(0x1) #define PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR_BITS _u(0x00080000) #define PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR_MSB _u(19) #define PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR_LSB _u(19) #define PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR // Description : 1 = shift input shift register to right (data enters from // left). 0 = to left. #define PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR_RESET _u(0x1) #define PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR_BITS _u(0x00040000) #define PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR_MSB _u(18) #define PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR_LSB _u(18) #define PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_SHIFTCTRL_AUTOPULL // Description : Pull automatically when the output shift register is emptied #define PROC_PIO_SM1_SHIFTCTRL_AUTOPULL_RESET _u(0x0) #define PROC_PIO_SM1_SHIFTCTRL_AUTOPULL_BITS _u(0x00020000) #define PROC_PIO_SM1_SHIFTCTRL_AUTOPULL_MSB _u(17) #define PROC_PIO_SM1_SHIFTCTRL_AUTOPULL_LSB _u(17) #define PROC_PIO_SM1_SHIFTCTRL_AUTOPULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH // Description : Push automatically when the input shift register is filled #define PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH_RESET _u(0x0) #define PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH_BITS _u(0x00010000) #define PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH_MSB _u(16) #define PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH_LSB _u(16) #define PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM1_ADDR // Description : Current instruction address of state machine 1 #define PROC_PIO_SM1_ADDR_OFFSET _u(0x000000f8) #define PROC_PIO_SM1_ADDR_BITS _u(0x0000001f) #define PROC_PIO_SM1_ADDR_RESET _u(0x00000000) #define PROC_PIO_SM1_ADDR_WIDTH _u(32) #define PROC_PIO_SM1_ADDR_MSB _u(4) #define PROC_PIO_SM1_ADDR_LSB _u(0) #define PROC_PIO_SM1_ADDR_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_SM1_INSTR // Description : Instruction currently being executed by state machine 1 // Write to execute an instruction immediately (including jumps) // and then resume execution. #define PROC_PIO_SM1_INSTR_OFFSET _u(0x000000fc) #define PROC_PIO_SM1_INSTR_BITS _u(0x0000ffff) #define PROC_PIO_SM1_INSTR_RESET "-" #define PROC_PIO_SM1_INSTR_WIDTH _u(32) #define PROC_PIO_SM1_INSTR_MSB _u(15) #define PROC_PIO_SM1_INSTR_LSB _u(0) #define PROC_PIO_SM1_INSTR_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM1_PINCTRL // Description : State machine pin control #define PROC_PIO_SM1_PINCTRL_OFFSET _u(0x00000100) #define PROC_PIO_SM1_PINCTRL_BITS _u(0xffffffff) #define PROC_PIO_SM1_PINCTRL_RESET _u(0x14000000) #define PROC_PIO_SM1_PINCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_PINCTRL_SIDESET_COUNT // Description : The number of delay bits co-opted for side-set. Inclusive of // the enable bit, if present. #define PROC_PIO_SM1_PINCTRL_SIDESET_COUNT_RESET _u(0x0) #define PROC_PIO_SM1_PINCTRL_SIDESET_COUNT_BITS _u(0xe0000000) #define PROC_PIO_SM1_PINCTRL_SIDESET_COUNT_MSB _u(31) #define PROC_PIO_SM1_PINCTRL_SIDESET_COUNT_LSB _u(29) #define PROC_PIO_SM1_PINCTRL_SIDESET_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_PINCTRL_SET_COUNT // Description : The number of pins asserted by a SET. Max of 5 #define PROC_PIO_SM1_PINCTRL_SET_COUNT_RESET _u(0x5) #define PROC_PIO_SM1_PINCTRL_SET_COUNT_BITS _u(0x1c000000) #define PROC_PIO_SM1_PINCTRL_SET_COUNT_MSB _u(28) #define PROC_PIO_SM1_PINCTRL_SET_COUNT_LSB _u(26) #define PROC_PIO_SM1_PINCTRL_SET_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_PINCTRL_OUT_COUNT // Description : The number of pins asserted by an OUT. Value of 0 -> 32 pins #define PROC_PIO_SM1_PINCTRL_OUT_COUNT_RESET _u(0x00) #define PROC_PIO_SM1_PINCTRL_OUT_COUNT_BITS _u(0x03f00000) #define PROC_PIO_SM1_PINCTRL_OUT_COUNT_MSB _u(25) #define PROC_PIO_SM1_PINCTRL_OUT_COUNT_LSB _u(20) #define PROC_PIO_SM1_PINCTRL_OUT_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_PINCTRL_IN_BASE // Description : The virtual pin corresponding to IN bit 0 #define PROC_PIO_SM1_PINCTRL_IN_BASE_RESET _u(0x00) #define PROC_PIO_SM1_PINCTRL_IN_BASE_BITS _u(0x000f8000) #define PROC_PIO_SM1_PINCTRL_IN_BASE_MSB _u(19) #define PROC_PIO_SM1_PINCTRL_IN_BASE_LSB _u(15) #define PROC_PIO_SM1_PINCTRL_IN_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_PINCTRL_SIDESET_BASE // Description : The virtual pin corresponding to delay field bit 0 #define PROC_PIO_SM1_PINCTRL_SIDESET_BASE_RESET _u(0x00) #define PROC_PIO_SM1_PINCTRL_SIDESET_BASE_BITS _u(0x00007c00) #define PROC_PIO_SM1_PINCTRL_SIDESET_BASE_MSB _u(14) #define PROC_PIO_SM1_PINCTRL_SIDESET_BASE_LSB _u(10) #define PROC_PIO_SM1_PINCTRL_SIDESET_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_PINCTRL_SET_BASE // Description : The virtual pin corresponding to SET bit 0 #define PROC_PIO_SM1_PINCTRL_SET_BASE_RESET _u(0x00) #define PROC_PIO_SM1_PINCTRL_SET_BASE_BITS _u(0x000003e0) #define PROC_PIO_SM1_PINCTRL_SET_BASE_MSB _u(9) #define PROC_PIO_SM1_PINCTRL_SET_BASE_LSB _u(5) #define PROC_PIO_SM1_PINCTRL_SET_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_PINCTRL_OUT_BASE // Description : The virtual pin corresponding to OUT bit 0 #define PROC_PIO_SM1_PINCTRL_OUT_BASE_RESET _u(0x00) #define PROC_PIO_SM1_PINCTRL_OUT_BASE_BITS _u(0x0000001f) #define PROC_PIO_SM1_PINCTRL_OUT_BASE_MSB _u(4) #define PROC_PIO_SM1_PINCTRL_OUT_BASE_LSB _u(0) #define PROC_PIO_SM1_PINCTRL_OUT_BASE_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM1_DMACTRL_TX // Description : State machine DMA control #define PROC_PIO_SM1_DMACTRL_TX_OFFSET _u(0x00000104) #define PROC_PIO_SM1_DMACTRL_TX_BITS _u(0xc0000f9f) #define PROC_PIO_SM1_DMACTRL_TX_RESET _u(0x00000104) #define PROC_PIO_SM1_DMACTRL_TX_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_DMACTRL_TX_DREQ_EN // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM1_DMACTRL_TX_DREQ_EN_RESET _u(0x0) #define PROC_PIO_SM1_DMACTRL_TX_DREQ_EN_BITS _u(0x80000000) #define PROC_PIO_SM1_DMACTRL_TX_DREQ_EN_MSB _u(31) #define PROC_PIO_SM1_DMACTRL_TX_DREQ_EN_LSB _u(31) #define PROC_PIO_SM1_DMACTRL_TX_DREQ_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_DMACTRL_TX_ACTIVE // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM1_DMACTRL_TX_ACTIVE_RESET "-" #define PROC_PIO_SM1_DMACTRL_TX_ACTIVE_BITS _u(0x40000000) #define PROC_PIO_SM1_DMACTRL_TX_ACTIVE_MSB _u(30) #define PROC_PIO_SM1_DMACTRL_TX_ACTIVE_LSB _u(30) #define PROC_PIO_SM1_DMACTRL_TX_ACTIVE_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME // Description : Delay in number of bus cycles before successive DREQs are // generated. // Used to account for system bus latency in write data arriving // at the FIFO. #define PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME_RESET _u(0x02) #define PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME_BITS _u(0x00000f80) #define PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME_MSB _u(11) #define PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME_LSB _u(7) #define PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD // Description : Threshold control. If there are no more than THRESHOLD items in // the TX FIFO, DMA dreq and/or the interrupt line is asserted. #define PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD_RESET _u(0x04) #define PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD_BITS _u(0x0000001f) #define PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD_MSB _u(4) #define PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD_LSB _u(0) #define PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM1_DMACTRL_RX // Description : State machine DMA control #define PROC_PIO_SM1_DMACTRL_RX_OFFSET _u(0x00000108) #define PROC_PIO_SM1_DMACTRL_RX_BITS _u(0xc0000f9f) #define PROC_PIO_SM1_DMACTRL_RX_RESET _u(0x00000104) #define PROC_PIO_SM1_DMACTRL_RX_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_DMACTRL_RX_DREQ_EN // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM1_DMACTRL_RX_DREQ_EN_RESET _u(0x0) #define PROC_PIO_SM1_DMACTRL_RX_DREQ_EN_BITS _u(0x80000000) #define PROC_PIO_SM1_DMACTRL_RX_DREQ_EN_MSB _u(31) #define PROC_PIO_SM1_DMACTRL_RX_DREQ_EN_LSB _u(31) #define PROC_PIO_SM1_DMACTRL_RX_DREQ_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_DMACTRL_RX_ACTIVE // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM1_DMACTRL_RX_ACTIVE_RESET "-" #define PROC_PIO_SM1_DMACTRL_RX_ACTIVE_BITS _u(0x40000000) #define PROC_PIO_SM1_DMACTRL_RX_ACTIVE_MSB _u(30) #define PROC_PIO_SM1_DMACTRL_RX_ACTIVE_LSB _u(30) #define PROC_PIO_SM1_DMACTRL_RX_ACTIVE_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME // Description : Delay in number of bus cycles before successive DREQs are // generated. // Used to account for system bus latency in write data arriving // at the FIFO. #define PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME_RESET _u(0x02) #define PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME_BITS _u(0x00000f80) #define PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME_MSB _u(11) #define PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME_LSB _u(7) #define PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD // Description : Threshold control. If there are at least THRESHOLD items in the // RX FIFO, DMA dreq and/or the interrupt line is asserted. #define PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD_RESET _u(0x04) #define PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD_BITS _u(0x0000001f) #define PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD_MSB _u(4) #define PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD_LSB _u(0) #define PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM2_CLKDIV // Description : Clock divider register for state machine 2 // Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) #define PROC_PIO_SM2_CLKDIV_OFFSET _u(0x0000010c) #define PROC_PIO_SM2_CLKDIV_BITS _u(0xffffff00) #define PROC_PIO_SM2_CLKDIV_RESET _u(0x00010000) #define PROC_PIO_SM2_CLKDIV_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_CLKDIV_INT // Description : Effective frequency is sysclk/int. // Value of 0 is interpreted as max possible value #define PROC_PIO_SM2_CLKDIV_INT_RESET _u(0x0001) #define PROC_PIO_SM2_CLKDIV_INT_BITS _u(0xffff0000) #define PROC_PIO_SM2_CLKDIV_INT_MSB _u(31) #define PROC_PIO_SM2_CLKDIV_INT_LSB _u(16) #define PROC_PIO_SM2_CLKDIV_INT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_CLKDIV_FRAC // Description : Fractional part of clock divider #define PROC_PIO_SM2_CLKDIV_FRAC_RESET _u(0x00) #define PROC_PIO_SM2_CLKDIV_FRAC_BITS _u(0x0000ff00) #define PROC_PIO_SM2_CLKDIV_FRAC_MSB _u(15) #define PROC_PIO_SM2_CLKDIV_FRAC_LSB _u(8) #define PROC_PIO_SM2_CLKDIV_FRAC_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM2_EXECCTRL // Description : Execution/behavioural settings for state machine 2 #define PROC_PIO_SM2_EXECCTRL_OFFSET _u(0x00000110) #define PROC_PIO_SM2_EXECCTRL_BITS _u(0xffffffbf) #define PROC_PIO_SM2_EXECCTRL_RESET _u(0x0001f000) #define PROC_PIO_SM2_EXECCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_EXECCTRL_EXEC_STALLED // Description : An instruction written to SMx_INSTR is stalled, and latched by // the // state machine. Will clear once the instruction completes. #define PROC_PIO_SM2_EXECCTRL_EXEC_STALLED_RESET _u(0x0) #define PROC_PIO_SM2_EXECCTRL_EXEC_STALLED_BITS _u(0x80000000) #define PROC_PIO_SM2_EXECCTRL_EXEC_STALLED_MSB _u(31) #define PROC_PIO_SM2_EXECCTRL_EXEC_STALLED_LSB _u(31) #define PROC_PIO_SM2_EXECCTRL_EXEC_STALLED_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_EXECCTRL_SIDE_EN // Description : If 1, the delay MSB is used as side-set enable, rather than a // side-set data bit. This allows instructions to perform side-set // optionally, // rather than on every instruction. #define PROC_PIO_SM2_EXECCTRL_SIDE_EN_RESET _u(0x0) #define PROC_PIO_SM2_EXECCTRL_SIDE_EN_BITS _u(0x40000000) #define PROC_PIO_SM2_EXECCTRL_SIDE_EN_MSB _u(30) #define PROC_PIO_SM2_EXECCTRL_SIDE_EN_LSB _u(30) #define PROC_PIO_SM2_EXECCTRL_SIDE_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR // Description : Side-set data is asserted to pin OEs instead of pin values #define PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR_RESET _u(0x0) #define PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR_BITS _u(0x20000000) #define PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR_MSB _u(29) #define PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR_LSB _u(29) #define PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_EXECCTRL_JMP_PIN // Description : The GPIO number to use as condition for JMP PIN. Unaffected by // input mapping. #define PROC_PIO_SM2_EXECCTRL_JMP_PIN_RESET _u(0x00) #define PROC_PIO_SM2_EXECCTRL_JMP_PIN_BITS _u(0x1f000000) #define PROC_PIO_SM2_EXECCTRL_JMP_PIN_MSB _u(28) #define PROC_PIO_SM2_EXECCTRL_JMP_PIN_LSB _u(24) #define PROC_PIO_SM2_EXECCTRL_JMP_PIN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL // Description : Which data bit to use for inline OUT enable #define PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL_RESET _u(0x00) #define PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL_BITS _u(0x00f80000) #define PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL_MSB _u(23) #define PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL_LSB _u(19) #define PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN // Description : If 1, use a bit of OUT data as an auxiliary write enable // When used in conjunction with OUT_STICKY, writes with an enable // of 0 will // deassert the latest pin write. This can create useful // masking/override behaviour // due to the priority ordering of state machine pin writes (SM0 < // SM1 < ...) #define PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN_RESET _u(0x0) #define PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN_BITS _u(0x00040000) #define PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN_MSB _u(18) #define PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN_LSB _u(18) #define PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_EXECCTRL_OUT_STICKY // Description : Continuously assert the most recent OUT/SET to the pins #define PROC_PIO_SM2_EXECCTRL_OUT_STICKY_RESET _u(0x0) #define PROC_PIO_SM2_EXECCTRL_OUT_STICKY_BITS _u(0x00020000) #define PROC_PIO_SM2_EXECCTRL_OUT_STICKY_MSB _u(17) #define PROC_PIO_SM2_EXECCTRL_OUT_STICKY_LSB _u(17) #define PROC_PIO_SM2_EXECCTRL_OUT_STICKY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_EXECCTRL_WRAP_TOP // Description : After reaching this address, execution is wrapped to // wrap_bottom. // If the instruction is a jump, and the jump condition is true, // the jump takes priority. #define PROC_PIO_SM2_EXECCTRL_WRAP_TOP_RESET _u(0x1f) #define PROC_PIO_SM2_EXECCTRL_WRAP_TOP_BITS _u(0x0001f000) #define PROC_PIO_SM2_EXECCTRL_WRAP_TOP_MSB _u(16) #define PROC_PIO_SM2_EXECCTRL_WRAP_TOP_LSB _u(12) #define PROC_PIO_SM2_EXECCTRL_WRAP_TOP_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM // Description : After reaching wrap_top, execution is wrapped to this address. #define PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM_RESET _u(0x00) #define PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM_BITS _u(0x00000f80) #define PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM_MSB _u(11) #define PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM_LSB _u(7) #define PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_EXECCTRL_STATUS_SEL // Description : Comparison used for the MOV x, STATUS instruction. // 0x0 -> All-ones if TX FIFO level < N, otherwise all-zeroes // 0x1 -> All-ones if RX FIFO level < N, otherwise all-zeroes #define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_RESET _u(0x0) #define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_BITS _u(0x00000020) #define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_MSB _u(5) #define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_LSB _u(5) #define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_ACCESS "RW" #define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_VALUE_TXLEVEL _u(0x0) #define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_VALUE_RXLEVEL _u(0x1) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_EXECCTRL_STATUS_N // Description : Comparison level for the MOV x, STATUS instruction #define PROC_PIO_SM2_EXECCTRL_STATUS_N_RESET _u(0x00) #define PROC_PIO_SM2_EXECCTRL_STATUS_N_BITS _u(0x0000001f) #define PROC_PIO_SM2_EXECCTRL_STATUS_N_MSB _u(4) #define PROC_PIO_SM2_EXECCTRL_STATUS_N_LSB _u(0) #define PROC_PIO_SM2_EXECCTRL_STATUS_N_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM2_SHIFTCTRL // Description : Control behaviour of the input/output shift registers for state // machine 2 #define PROC_PIO_SM2_SHIFTCTRL_OFFSET _u(0x00000114) #define PROC_PIO_SM2_SHIFTCTRL_BITS _u(0xffff0000) #define PROC_PIO_SM2_SHIFTCTRL_RESET _u(0x000c0000) #define PROC_PIO_SM2_SHIFTCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX // Description : When 1, RX FIFO steals the TX FIFO's storage, and becomes twice // as deep. // TX FIFO is disabled as a result (always reads as both full and // empty). // FIFOs are flushed when this bit is changed. #define PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX_RESET _u(0x0) #define PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX_BITS _u(0x80000000) #define PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX_MSB _u(31) #define PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX_LSB _u(31) #define PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX // Description : When 1, TX FIFO steals the RX FIFO's storage, and becomes twice // as deep. // RX FIFO is disabled as a result (always reads as both full and // empty). // FIFOs are flushed when this bit is changed. #define PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX_RESET _u(0x0) #define PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX_BITS _u(0x40000000) #define PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX_MSB _u(30) #define PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX_LSB _u(30) #define PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH // Description : Number of bits shifted out of TXSR before autopull or // conditional pull. // Write 0 for value of 32. #define PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH_RESET _u(0x00) #define PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH_BITS _u(0x3e000000) #define PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH_MSB _u(29) #define PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH_LSB _u(25) #define PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH // Description : Number of bits shifted into RXSR before autopush or conditional // push. // Write 0 for value of 32. #define PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH_RESET _u(0x00) #define PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH_BITS _u(0x01f00000) #define PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH_MSB _u(24) #define PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH_LSB _u(20) #define PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR // Description : 1 = shift out of output shift register to right. 0 = to left. #define PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR_RESET _u(0x1) #define PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR_BITS _u(0x00080000) #define PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR_MSB _u(19) #define PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR_LSB _u(19) #define PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR // Description : 1 = shift input shift register to right (data enters from // left). 0 = to left. #define PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR_RESET _u(0x1) #define PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR_BITS _u(0x00040000) #define PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR_MSB _u(18) #define PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR_LSB _u(18) #define PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_SHIFTCTRL_AUTOPULL // Description : Pull automatically when the output shift register is emptied #define PROC_PIO_SM2_SHIFTCTRL_AUTOPULL_RESET _u(0x0) #define PROC_PIO_SM2_SHIFTCTRL_AUTOPULL_BITS _u(0x00020000) #define PROC_PIO_SM2_SHIFTCTRL_AUTOPULL_MSB _u(17) #define PROC_PIO_SM2_SHIFTCTRL_AUTOPULL_LSB _u(17) #define PROC_PIO_SM2_SHIFTCTRL_AUTOPULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH // Description : Push automatically when the input shift register is filled #define PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH_RESET _u(0x0) #define PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH_BITS _u(0x00010000) #define PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH_MSB _u(16) #define PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH_LSB _u(16) #define PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM2_ADDR // Description : Current instruction address of state machine 2 #define PROC_PIO_SM2_ADDR_OFFSET _u(0x00000118) #define PROC_PIO_SM2_ADDR_BITS _u(0x0000001f) #define PROC_PIO_SM2_ADDR_RESET _u(0x00000000) #define PROC_PIO_SM2_ADDR_WIDTH _u(32) #define PROC_PIO_SM2_ADDR_MSB _u(4) #define PROC_PIO_SM2_ADDR_LSB _u(0) #define PROC_PIO_SM2_ADDR_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_SM2_INSTR // Description : Instruction currently being executed by state machine 2 // Write to execute an instruction immediately (including jumps) // and then resume execution. #define PROC_PIO_SM2_INSTR_OFFSET _u(0x0000011c) #define PROC_PIO_SM2_INSTR_BITS _u(0x0000ffff) #define PROC_PIO_SM2_INSTR_RESET "-" #define PROC_PIO_SM2_INSTR_WIDTH _u(32) #define PROC_PIO_SM2_INSTR_MSB _u(15) #define PROC_PIO_SM2_INSTR_LSB _u(0) #define PROC_PIO_SM2_INSTR_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM2_PINCTRL // Description : State machine pin control #define PROC_PIO_SM2_PINCTRL_OFFSET _u(0x00000120) #define PROC_PIO_SM2_PINCTRL_BITS _u(0xffffffff) #define PROC_PIO_SM2_PINCTRL_RESET _u(0x14000000) #define PROC_PIO_SM2_PINCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_PINCTRL_SIDESET_COUNT // Description : The number of delay bits co-opted for side-set. Inclusive of // the enable bit, if present. #define PROC_PIO_SM2_PINCTRL_SIDESET_COUNT_RESET _u(0x0) #define PROC_PIO_SM2_PINCTRL_SIDESET_COUNT_BITS _u(0xe0000000) #define PROC_PIO_SM2_PINCTRL_SIDESET_COUNT_MSB _u(31) #define PROC_PIO_SM2_PINCTRL_SIDESET_COUNT_LSB _u(29) #define PROC_PIO_SM2_PINCTRL_SIDESET_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_PINCTRL_SET_COUNT // Description : The number of pins asserted by a SET. Max of 5 #define PROC_PIO_SM2_PINCTRL_SET_COUNT_RESET _u(0x5) #define PROC_PIO_SM2_PINCTRL_SET_COUNT_BITS _u(0x1c000000) #define PROC_PIO_SM2_PINCTRL_SET_COUNT_MSB _u(28) #define PROC_PIO_SM2_PINCTRL_SET_COUNT_LSB _u(26) #define PROC_PIO_SM2_PINCTRL_SET_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_PINCTRL_OUT_COUNT // Description : The number of pins asserted by an OUT. Value of 0 -> 32 pins #define PROC_PIO_SM2_PINCTRL_OUT_COUNT_RESET _u(0x00) #define PROC_PIO_SM2_PINCTRL_OUT_COUNT_BITS _u(0x03f00000) #define PROC_PIO_SM2_PINCTRL_OUT_COUNT_MSB _u(25) #define PROC_PIO_SM2_PINCTRL_OUT_COUNT_LSB _u(20) #define PROC_PIO_SM2_PINCTRL_OUT_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_PINCTRL_IN_BASE // Description : The virtual pin corresponding to IN bit 0 #define PROC_PIO_SM2_PINCTRL_IN_BASE_RESET _u(0x00) #define PROC_PIO_SM2_PINCTRL_IN_BASE_BITS _u(0x000f8000) #define PROC_PIO_SM2_PINCTRL_IN_BASE_MSB _u(19) #define PROC_PIO_SM2_PINCTRL_IN_BASE_LSB _u(15) #define PROC_PIO_SM2_PINCTRL_IN_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_PINCTRL_SIDESET_BASE // Description : The virtual pin corresponding to delay field bit 0 #define PROC_PIO_SM2_PINCTRL_SIDESET_BASE_RESET _u(0x00) #define PROC_PIO_SM2_PINCTRL_SIDESET_BASE_BITS _u(0x00007c00) #define PROC_PIO_SM2_PINCTRL_SIDESET_BASE_MSB _u(14) #define PROC_PIO_SM2_PINCTRL_SIDESET_BASE_LSB _u(10) #define PROC_PIO_SM2_PINCTRL_SIDESET_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_PINCTRL_SET_BASE // Description : The virtual pin corresponding to SET bit 0 #define PROC_PIO_SM2_PINCTRL_SET_BASE_RESET _u(0x00) #define PROC_PIO_SM2_PINCTRL_SET_BASE_BITS _u(0x000003e0) #define PROC_PIO_SM2_PINCTRL_SET_BASE_MSB _u(9) #define PROC_PIO_SM2_PINCTRL_SET_BASE_LSB _u(5) #define PROC_PIO_SM2_PINCTRL_SET_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_PINCTRL_OUT_BASE // Description : The virtual pin corresponding to OUT bit 0 #define PROC_PIO_SM2_PINCTRL_OUT_BASE_RESET _u(0x00) #define PROC_PIO_SM2_PINCTRL_OUT_BASE_BITS _u(0x0000001f) #define PROC_PIO_SM2_PINCTRL_OUT_BASE_MSB _u(4) #define PROC_PIO_SM2_PINCTRL_OUT_BASE_LSB _u(0) #define PROC_PIO_SM2_PINCTRL_OUT_BASE_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM2_DMACTRL_TX // Description : State machine DMA control #define PROC_PIO_SM2_DMACTRL_TX_OFFSET _u(0x00000124) #define PROC_PIO_SM2_DMACTRL_TX_BITS _u(0xc0000f9f) #define PROC_PIO_SM2_DMACTRL_TX_RESET _u(0x00000104) #define PROC_PIO_SM2_DMACTRL_TX_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_DMACTRL_TX_DREQ_EN // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM2_DMACTRL_TX_DREQ_EN_RESET _u(0x0) #define PROC_PIO_SM2_DMACTRL_TX_DREQ_EN_BITS _u(0x80000000) #define PROC_PIO_SM2_DMACTRL_TX_DREQ_EN_MSB _u(31) #define PROC_PIO_SM2_DMACTRL_TX_DREQ_EN_LSB _u(31) #define PROC_PIO_SM2_DMACTRL_TX_DREQ_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_DMACTRL_TX_ACTIVE // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM2_DMACTRL_TX_ACTIVE_RESET "-" #define PROC_PIO_SM2_DMACTRL_TX_ACTIVE_BITS _u(0x40000000) #define PROC_PIO_SM2_DMACTRL_TX_ACTIVE_MSB _u(30) #define PROC_PIO_SM2_DMACTRL_TX_ACTIVE_LSB _u(30) #define PROC_PIO_SM2_DMACTRL_TX_ACTIVE_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME // Description : Delay in number of bus cycles before successive DREQs are // generated. // Used to account for system bus latency in write data arriving // at the FIFO. #define PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME_RESET _u(0x02) #define PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME_BITS _u(0x00000f80) #define PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME_MSB _u(11) #define PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME_LSB _u(7) #define PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD // Description : Threshold control. If there are no more than THRESHOLD items in // the TX FIFO, DMA dreq and/or the interrupt line is asserted. #define PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD_RESET _u(0x04) #define PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD_BITS _u(0x0000001f) #define PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD_MSB _u(4) #define PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD_LSB _u(0) #define PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM2_DMACTRL_RX // Description : State machine DMA control #define PROC_PIO_SM2_DMACTRL_RX_OFFSET _u(0x00000128) #define PROC_PIO_SM2_DMACTRL_RX_BITS _u(0xc0000f9f) #define PROC_PIO_SM2_DMACTRL_RX_RESET _u(0x00000104) #define PROC_PIO_SM2_DMACTRL_RX_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_DMACTRL_RX_DREQ_EN // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM2_DMACTRL_RX_DREQ_EN_RESET _u(0x0) #define PROC_PIO_SM2_DMACTRL_RX_DREQ_EN_BITS _u(0x80000000) #define PROC_PIO_SM2_DMACTRL_RX_DREQ_EN_MSB _u(31) #define PROC_PIO_SM2_DMACTRL_RX_DREQ_EN_LSB _u(31) #define PROC_PIO_SM2_DMACTRL_RX_DREQ_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_DMACTRL_RX_ACTIVE // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM2_DMACTRL_RX_ACTIVE_RESET "-" #define PROC_PIO_SM2_DMACTRL_RX_ACTIVE_BITS _u(0x40000000) #define PROC_PIO_SM2_DMACTRL_RX_ACTIVE_MSB _u(30) #define PROC_PIO_SM2_DMACTRL_RX_ACTIVE_LSB _u(30) #define PROC_PIO_SM2_DMACTRL_RX_ACTIVE_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME // Description : Delay in number of bus cycles before successive DREQs are // generated. // Used to account for system bus latency in write data arriving // at the FIFO. #define PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME_RESET _u(0x02) #define PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME_BITS _u(0x00000f80) #define PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME_MSB _u(11) #define PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME_LSB _u(7) #define PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD // Description : Threshold control. If there are at least THRESHOLD items in the // RX FIFO, DMA dreq and/or the interrupt line is asserted. #define PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD_RESET _u(0x04) #define PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD_BITS _u(0x0000001f) #define PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD_MSB _u(4) #define PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD_LSB _u(0) #define PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM3_CLKDIV // Description : Clock divider register for state machine 3 // Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) #define PROC_PIO_SM3_CLKDIV_OFFSET _u(0x0000012c) #define PROC_PIO_SM3_CLKDIV_BITS _u(0xffffff00) #define PROC_PIO_SM3_CLKDIV_RESET _u(0x00010000) #define PROC_PIO_SM3_CLKDIV_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_CLKDIV_INT // Description : Effective frequency is sysclk/int. // Value of 0 is interpreted as max possible value #define PROC_PIO_SM3_CLKDIV_INT_RESET _u(0x0001) #define PROC_PIO_SM3_CLKDIV_INT_BITS _u(0xffff0000) #define PROC_PIO_SM3_CLKDIV_INT_MSB _u(31) #define PROC_PIO_SM3_CLKDIV_INT_LSB _u(16) #define PROC_PIO_SM3_CLKDIV_INT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_CLKDIV_FRAC // Description : Fractional part of clock divider #define PROC_PIO_SM3_CLKDIV_FRAC_RESET _u(0x00) #define PROC_PIO_SM3_CLKDIV_FRAC_BITS _u(0x0000ff00) #define PROC_PIO_SM3_CLKDIV_FRAC_MSB _u(15) #define PROC_PIO_SM3_CLKDIV_FRAC_LSB _u(8) #define PROC_PIO_SM3_CLKDIV_FRAC_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM3_EXECCTRL // Description : Execution/behavioural settings for state machine 3 #define PROC_PIO_SM3_EXECCTRL_OFFSET _u(0x00000130) #define PROC_PIO_SM3_EXECCTRL_BITS _u(0xffffffbf) #define PROC_PIO_SM3_EXECCTRL_RESET _u(0x0001f000) #define PROC_PIO_SM3_EXECCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_EXECCTRL_EXEC_STALLED // Description : An instruction written to SMx_INSTR is stalled, and latched by // the // state machine. Will clear once the instruction completes. #define PROC_PIO_SM3_EXECCTRL_EXEC_STALLED_RESET _u(0x0) #define PROC_PIO_SM3_EXECCTRL_EXEC_STALLED_BITS _u(0x80000000) #define PROC_PIO_SM3_EXECCTRL_EXEC_STALLED_MSB _u(31) #define PROC_PIO_SM3_EXECCTRL_EXEC_STALLED_LSB _u(31) #define PROC_PIO_SM3_EXECCTRL_EXEC_STALLED_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_EXECCTRL_SIDE_EN // Description : If 1, the delay MSB is used as side-set enable, rather than a // side-set data bit. This allows instructions to perform side-set // optionally, // rather than on every instruction. #define PROC_PIO_SM3_EXECCTRL_SIDE_EN_RESET _u(0x0) #define PROC_PIO_SM3_EXECCTRL_SIDE_EN_BITS _u(0x40000000) #define PROC_PIO_SM3_EXECCTRL_SIDE_EN_MSB _u(30) #define PROC_PIO_SM3_EXECCTRL_SIDE_EN_LSB _u(30) #define PROC_PIO_SM3_EXECCTRL_SIDE_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR // Description : Side-set data is asserted to pin OEs instead of pin values #define PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR_RESET _u(0x0) #define PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR_BITS _u(0x20000000) #define PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR_MSB _u(29) #define PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR_LSB _u(29) #define PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_EXECCTRL_JMP_PIN // Description : The GPIO number to use as condition for JMP PIN. Unaffected by // input mapping. #define PROC_PIO_SM3_EXECCTRL_JMP_PIN_RESET _u(0x00) #define PROC_PIO_SM3_EXECCTRL_JMP_PIN_BITS _u(0x1f000000) #define PROC_PIO_SM3_EXECCTRL_JMP_PIN_MSB _u(28) #define PROC_PIO_SM3_EXECCTRL_JMP_PIN_LSB _u(24) #define PROC_PIO_SM3_EXECCTRL_JMP_PIN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL // Description : Which data bit to use for inline OUT enable #define PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL_RESET _u(0x00) #define PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL_BITS _u(0x00f80000) #define PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL_MSB _u(23) #define PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL_LSB _u(19) #define PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN // Description : If 1, use a bit of OUT data as an auxiliary write enable // When used in conjunction with OUT_STICKY, writes with an enable // of 0 will // deassert the latest pin write. This can create useful // masking/override behaviour // due to the priority ordering of state machine pin writes (SM0 < // SM1 < ...) #define PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN_RESET _u(0x0) #define PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN_BITS _u(0x00040000) #define PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN_MSB _u(18) #define PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN_LSB _u(18) #define PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_EXECCTRL_OUT_STICKY // Description : Continuously assert the most recent OUT/SET to the pins #define PROC_PIO_SM3_EXECCTRL_OUT_STICKY_RESET _u(0x0) #define PROC_PIO_SM3_EXECCTRL_OUT_STICKY_BITS _u(0x00020000) #define PROC_PIO_SM3_EXECCTRL_OUT_STICKY_MSB _u(17) #define PROC_PIO_SM3_EXECCTRL_OUT_STICKY_LSB _u(17) #define PROC_PIO_SM3_EXECCTRL_OUT_STICKY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_EXECCTRL_WRAP_TOP // Description : After reaching this address, execution is wrapped to // wrap_bottom. // If the instruction is a jump, and the jump condition is true, // the jump takes priority. #define PROC_PIO_SM3_EXECCTRL_WRAP_TOP_RESET _u(0x1f) #define PROC_PIO_SM3_EXECCTRL_WRAP_TOP_BITS _u(0x0001f000) #define PROC_PIO_SM3_EXECCTRL_WRAP_TOP_MSB _u(16) #define PROC_PIO_SM3_EXECCTRL_WRAP_TOP_LSB _u(12) #define PROC_PIO_SM3_EXECCTRL_WRAP_TOP_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM // Description : After reaching wrap_top, execution is wrapped to this address. #define PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM_RESET _u(0x00) #define PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM_BITS _u(0x00000f80) #define PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM_MSB _u(11) #define PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM_LSB _u(7) #define PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_EXECCTRL_STATUS_SEL // Description : Comparison used for the MOV x, STATUS instruction. // 0x0 -> All-ones if TX FIFO level < N, otherwise all-zeroes // 0x1 -> All-ones if RX FIFO level < N, otherwise all-zeroes #define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_RESET _u(0x0) #define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_BITS _u(0x00000020) #define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_MSB _u(5) #define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_LSB _u(5) #define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_ACCESS "RW" #define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_VALUE_TXLEVEL _u(0x0) #define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_VALUE_RXLEVEL _u(0x1) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_EXECCTRL_STATUS_N // Description : Comparison level for the MOV x, STATUS instruction #define PROC_PIO_SM3_EXECCTRL_STATUS_N_RESET _u(0x00) #define PROC_PIO_SM3_EXECCTRL_STATUS_N_BITS _u(0x0000001f) #define PROC_PIO_SM3_EXECCTRL_STATUS_N_MSB _u(4) #define PROC_PIO_SM3_EXECCTRL_STATUS_N_LSB _u(0) #define PROC_PIO_SM3_EXECCTRL_STATUS_N_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM3_SHIFTCTRL // Description : Control behaviour of the input/output shift registers for state // machine 3 #define PROC_PIO_SM3_SHIFTCTRL_OFFSET _u(0x00000134) #define PROC_PIO_SM3_SHIFTCTRL_BITS _u(0xffff0000) #define PROC_PIO_SM3_SHIFTCTRL_RESET _u(0x000c0000) #define PROC_PIO_SM3_SHIFTCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX // Description : When 1, RX FIFO steals the TX FIFO's storage, and becomes twice // as deep. // TX FIFO is disabled as a result (always reads as both full and // empty). // FIFOs are flushed when this bit is changed. #define PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX_RESET _u(0x0) #define PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX_BITS _u(0x80000000) #define PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX_MSB _u(31) #define PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX_LSB _u(31) #define PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX // Description : When 1, TX FIFO steals the RX FIFO's storage, and becomes twice // as deep. // RX FIFO is disabled as a result (always reads as both full and // empty). // FIFOs are flushed when this bit is changed. #define PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX_RESET _u(0x0) #define PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX_BITS _u(0x40000000) #define PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX_MSB _u(30) #define PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX_LSB _u(30) #define PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH // Description : Number of bits shifted out of TXSR before autopull or // conditional pull. // Write 0 for value of 32. #define PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH_RESET _u(0x00) #define PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH_BITS _u(0x3e000000) #define PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH_MSB _u(29) #define PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH_LSB _u(25) #define PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH // Description : Number of bits shifted into RXSR before autopush or conditional // push. // Write 0 for value of 32. #define PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH_RESET _u(0x00) #define PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH_BITS _u(0x01f00000) #define PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH_MSB _u(24) #define PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH_LSB _u(20) #define PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR // Description : 1 = shift out of output shift register to right. 0 = to left. #define PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR_RESET _u(0x1) #define PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR_BITS _u(0x00080000) #define PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR_MSB _u(19) #define PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR_LSB _u(19) #define PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR // Description : 1 = shift input shift register to right (data enters from // left). 0 = to left. #define PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR_RESET _u(0x1) #define PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR_BITS _u(0x00040000) #define PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR_MSB _u(18) #define PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR_LSB _u(18) #define PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_SHIFTCTRL_AUTOPULL // Description : Pull automatically when the output shift register is emptied #define PROC_PIO_SM3_SHIFTCTRL_AUTOPULL_RESET _u(0x0) #define PROC_PIO_SM3_SHIFTCTRL_AUTOPULL_BITS _u(0x00020000) #define PROC_PIO_SM3_SHIFTCTRL_AUTOPULL_MSB _u(17) #define PROC_PIO_SM3_SHIFTCTRL_AUTOPULL_LSB _u(17) #define PROC_PIO_SM3_SHIFTCTRL_AUTOPULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH // Description : Push automatically when the input shift register is filled #define PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH_RESET _u(0x0) #define PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH_BITS _u(0x00010000) #define PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH_MSB _u(16) #define PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH_LSB _u(16) #define PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM3_ADDR // Description : Current instruction address of state machine 3 #define PROC_PIO_SM3_ADDR_OFFSET _u(0x00000138) #define PROC_PIO_SM3_ADDR_BITS _u(0x0000001f) #define PROC_PIO_SM3_ADDR_RESET _u(0x00000000) #define PROC_PIO_SM3_ADDR_WIDTH _u(32) #define PROC_PIO_SM3_ADDR_MSB _u(4) #define PROC_PIO_SM3_ADDR_LSB _u(0) #define PROC_PIO_SM3_ADDR_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_SM3_INSTR // Description : Instruction currently being executed by state machine 3 // Write to execute an instruction immediately (including jumps) // and then resume execution. #define PROC_PIO_SM3_INSTR_OFFSET _u(0x0000013c) #define PROC_PIO_SM3_INSTR_BITS _u(0x0000ffff) #define PROC_PIO_SM3_INSTR_RESET "-" #define PROC_PIO_SM3_INSTR_WIDTH _u(32) #define PROC_PIO_SM3_INSTR_MSB _u(15) #define PROC_PIO_SM3_INSTR_LSB _u(0) #define PROC_PIO_SM3_INSTR_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM3_PINCTRL // Description : State machine pin control #define PROC_PIO_SM3_PINCTRL_OFFSET _u(0x00000140) #define PROC_PIO_SM3_PINCTRL_BITS _u(0xffffffff) #define PROC_PIO_SM3_PINCTRL_RESET _u(0x14000000) #define PROC_PIO_SM3_PINCTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_PINCTRL_SIDESET_COUNT // Description : The number of delay bits co-opted for side-set. Inclusive of // the enable bit, if present. #define PROC_PIO_SM3_PINCTRL_SIDESET_COUNT_RESET _u(0x0) #define PROC_PIO_SM3_PINCTRL_SIDESET_COUNT_BITS _u(0xe0000000) #define PROC_PIO_SM3_PINCTRL_SIDESET_COUNT_MSB _u(31) #define PROC_PIO_SM3_PINCTRL_SIDESET_COUNT_LSB _u(29) #define PROC_PIO_SM3_PINCTRL_SIDESET_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_PINCTRL_SET_COUNT // Description : The number of pins asserted by a SET. Max of 5 #define PROC_PIO_SM3_PINCTRL_SET_COUNT_RESET _u(0x5) #define PROC_PIO_SM3_PINCTRL_SET_COUNT_BITS _u(0x1c000000) #define PROC_PIO_SM3_PINCTRL_SET_COUNT_MSB _u(28) #define PROC_PIO_SM3_PINCTRL_SET_COUNT_LSB _u(26) #define PROC_PIO_SM3_PINCTRL_SET_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_PINCTRL_OUT_COUNT // Description : The number of pins asserted by an OUT. Value of 0 -> 32 pins #define PROC_PIO_SM3_PINCTRL_OUT_COUNT_RESET _u(0x00) #define PROC_PIO_SM3_PINCTRL_OUT_COUNT_BITS _u(0x03f00000) #define PROC_PIO_SM3_PINCTRL_OUT_COUNT_MSB _u(25) #define PROC_PIO_SM3_PINCTRL_OUT_COUNT_LSB _u(20) #define PROC_PIO_SM3_PINCTRL_OUT_COUNT_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_PINCTRL_IN_BASE // Description : The virtual pin corresponding to IN bit 0 #define PROC_PIO_SM3_PINCTRL_IN_BASE_RESET _u(0x00) #define PROC_PIO_SM3_PINCTRL_IN_BASE_BITS _u(0x000f8000) #define PROC_PIO_SM3_PINCTRL_IN_BASE_MSB _u(19) #define PROC_PIO_SM3_PINCTRL_IN_BASE_LSB _u(15) #define PROC_PIO_SM3_PINCTRL_IN_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_PINCTRL_SIDESET_BASE // Description : The virtual pin corresponding to delay field bit 0 #define PROC_PIO_SM3_PINCTRL_SIDESET_BASE_RESET _u(0x00) #define PROC_PIO_SM3_PINCTRL_SIDESET_BASE_BITS _u(0x00007c00) #define PROC_PIO_SM3_PINCTRL_SIDESET_BASE_MSB _u(14) #define PROC_PIO_SM3_PINCTRL_SIDESET_BASE_LSB _u(10) #define PROC_PIO_SM3_PINCTRL_SIDESET_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_PINCTRL_SET_BASE // Description : The virtual pin corresponding to SET bit 0 #define PROC_PIO_SM3_PINCTRL_SET_BASE_RESET _u(0x00) #define PROC_PIO_SM3_PINCTRL_SET_BASE_BITS _u(0x000003e0) #define PROC_PIO_SM3_PINCTRL_SET_BASE_MSB _u(9) #define PROC_PIO_SM3_PINCTRL_SET_BASE_LSB _u(5) #define PROC_PIO_SM3_PINCTRL_SET_BASE_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_PINCTRL_OUT_BASE // Description : The virtual pin corresponding to OUT bit 0 #define PROC_PIO_SM3_PINCTRL_OUT_BASE_RESET _u(0x00) #define PROC_PIO_SM3_PINCTRL_OUT_BASE_BITS _u(0x0000001f) #define PROC_PIO_SM3_PINCTRL_OUT_BASE_MSB _u(4) #define PROC_PIO_SM3_PINCTRL_OUT_BASE_LSB _u(0) #define PROC_PIO_SM3_PINCTRL_OUT_BASE_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM3_DMACTRL_TX // Description : State machine DMA control #define PROC_PIO_SM3_DMACTRL_TX_OFFSET _u(0x00000144) #define PROC_PIO_SM3_DMACTRL_TX_BITS _u(0xc0000f9f) #define PROC_PIO_SM3_DMACTRL_TX_RESET _u(0x00000104) #define PROC_PIO_SM3_DMACTRL_TX_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_DMACTRL_TX_DREQ_EN // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM3_DMACTRL_TX_DREQ_EN_RESET _u(0x0) #define PROC_PIO_SM3_DMACTRL_TX_DREQ_EN_BITS _u(0x80000000) #define PROC_PIO_SM3_DMACTRL_TX_DREQ_EN_MSB _u(31) #define PROC_PIO_SM3_DMACTRL_TX_DREQ_EN_LSB _u(31) #define PROC_PIO_SM3_DMACTRL_TX_DREQ_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_DMACTRL_TX_ACTIVE // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM3_DMACTRL_TX_ACTIVE_RESET "-" #define PROC_PIO_SM3_DMACTRL_TX_ACTIVE_BITS _u(0x40000000) #define PROC_PIO_SM3_DMACTRL_TX_ACTIVE_MSB _u(30) #define PROC_PIO_SM3_DMACTRL_TX_ACTIVE_LSB _u(30) #define PROC_PIO_SM3_DMACTRL_TX_ACTIVE_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME // Description : Delay in number of bus cycles before successive DREQs are // generated. // Used to account for system bus latency in write data arriving // at the FIFO. #define PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME_RESET _u(0x02) #define PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME_BITS _u(0x00000f80) #define PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME_MSB _u(11) #define PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME_LSB _u(7) #define PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD // Description : Threshold control. If there are no more than THRESHOLD items in // the TX FIFO, DMA dreq and/or the interrupt line is asserted. #define PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD_RESET _u(0x04) #define PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD_BITS _u(0x0000001f) #define PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD_MSB _u(4) #define PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD_LSB _u(0) #define PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_SM3_DMACTRL_RX // Description : State machine DMA control #define PROC_PIO_SM3_DMACTRL_RX_OFFSET _u(0x00000148) #define PROC_PIO_SM3_DMACTRL_RX_BITS _u(0xc0000f9f) #define PROC_PIO_SM3_DMACTRL_RX_RESET _u(0x00000104) #define PROC_PIO_SM3_DMACTRL_RX_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_DMACTRL_RX_DREQ_EN // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM3_DMACTRL_RX_DREQ_EN_RESET _u(0x0) #define PROC_PIO_SM3_DMACTRL_RX_DREQ_EN_BITS _u(0x80000000) #define PROC_PIO_SM3_DMACTRL_RX_DREQ_EN_MSB _u(31) #define PROC_PIO_SM3_DMACTRL_RX_DREQ_EN_LSB _u(31) #define PROC_PIO_SM3_DMACTRL_RX_DREQ_EN_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_DMACTRL_RX_ACTIVE // Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces // are available // 0 - Don't assert DREQ #define PROC_PIO_SM3_DMACTRL_RX_ACTIVE_RESET "-" #define PROC_PIO_SM3_DMACTRL_RX_ACTIVE_BITS _u(0x40000000) #define PROC_PIO_SM3_DMACTRL_RX_ACTIVE_MSB _u(30) #define PROC_PIO_SM3_DMACTRL_RX_ACTIVE_LSB _u(30) #define PROC_PIO_SM3_DMACTRL_RX_ACTIVE_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME // Description : Delay in number of bus cycles before successive DREQs are // generated. // Used to account for system bus latency in write data arriving // at the FIFO. #define PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME_RESET _u(0x02) #define PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME_BITS _u(0x00000f80) #define PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME_MSB _u(11) #define PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME_LSB _u(7) #define PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD // Description : Threshold control. If there are at least THRESHOLD items in the // RX FIFO, DMA dreq and/or the interrupt line is asserted. #define PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD_RESET _u(0x04) #define PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD_BITS _u(0x0000001f) #define PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD_MSB _u(4) #define PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD_LSB _u(0) #define PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_INTR // Description : Raw Interrupts #define PROC_PIO_INTR_OFFSET _u(0x0000014c) #define PROC_PIO_INTR_BITS _u(0x00000fff) #define PROC_PIO_INTR_RESET _u(0x00000000) #define PROC_PIO_INTR_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM3 // Description : None #define PROC_PIO_INTR_SM3_RESET _u(0x0) #define PROC_PIO_INTR_SM3_BITS _u(0x00000800) #define PROC_PIO_INTR_SM3_MSB _u(11) #define PROC_PIO_INTR_SM3_LSB _u(11) #define PROC_PIO_INTR_SM3_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM2 // Description : None #define PROC_PIO_INTR_SM2_RESET _u(0x0) #define PROC_PIO_INTR_SM2_BITS _u(0x00000400) #define PROC_PIO_INTR_SM2_MSB _u(10) #define PROC_PIO_INTR_SM2_LSB _u(10) #define PROC_PIO_INTR_SM2_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM1 // Description : None #define PROC_PIO_INTR_SM1_RESET _u(0x0) #define PROC_PIO_INTR_SM1_BITS _u(0x00000200) #define PROC_PIO_INTR_SM1_MSB _u(9) #define PROC_PIO_INTR_SM1_LSB _u(9) #define PROC_PIO_INTR_SM1_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM0 // Description : None #define PROC_PIO_INTR_SM0_RESET _u(0x0) #define PROC_PIO_INTR_SM0_BITS _u(0x00000100) #define PROC_PIO_INTR_SM0_MSB _u(8) #define PROC_PIO_INTR_SM0_LSB _u(8) #define PROC_PIO_INTR_SM0_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM3_TXNFULL // Description : None #define PROC_PIO_INTR_SM3_TXNFULL_RESET _u(0x0) #define PROC_PIO_INTR_SM3_TXNFULL_BITS _u(0x00000080) #define PROC_PIO_INTR_SM3_TXNFULL_MSB _u(7) #define PROC_PIO_INTR_SM3_TXNFULL_LSB _u(7) #define PROC_PIO_INTR_SM3_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM2_TXNFULL // Description : None #define PROC_PIO_INTR_SM2_TXNFULL_RESET _u(0x0) #define PROC_PIO_INTR_SM2_TXNFULL_BITS _u(0x00000040) #define PROC_PIO_INTR_SM2_TXNFULL_MSB _u(6) #define PROC_PIO_INTR_SM2_TXNFULL_LSB _u(6) #define PROC_PIO_INTR_SM2_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM1_TXNFULL // Description : None #define PROC_PIO_INTR_SM1_TXNFULL_RESET _u(0x0) #define PROC_PIO_INTR_SM1_TXNFULL_BITS _u(0x00000020) #define PROC_PIO_INTR_SM1_TXNFULL_MSB _u(5) #define PROC_PIO_INTR_SM1_TXNFULL_LSB _u(5) #define PROC_PIO_INTR_SM1_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM0_TXNFULL // Description : None #define PROC_PIO_INTR_SM0_TXNFULL_RESET _u(0x0) #define PROC_PIO_INTR_SM0_TXNFULL_BITS _u(0x00000010) #define PROC_PIO_INTR_SM0_TXNFULL_MSB _u(4) #define PROC_PIO_INTR_SM0_TXNFULL_LSB _u(4) #define PROC_PIO_INTR_SM0_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM3_RXNEMPTY // Description : None #define PROC_PIO_INTR_SM3_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_INTR_SM3_RXNEMPTY_BITS _u(0x00000008) #define PROC_PIO_INTR_SM3_RXNEMPTY_MSB _u(3) #define PROC_PIO_INTR_SM3_RXNEMPTY_LSB _u(3) #define PROC_PIO_INTR_SM3_RXNEMPTY_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM2_RXNEMPTY // Description : None #define PROC_PIO_INTR_SM2_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_INTR_SM2_RXNEMPTY_BITS _u(0x00000004) #define PROC_PIO_INTR_SM2_RXNEMPTY_MSB _u(2) #define PROC_PIO_INTR_SM2_RXNEMPTY_LSB _u(2) #define PROC_PIO_INTR_SM2_RXNEMPTY_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM1_RXNEMPTY // Description : None #define PROC_PIO_INTR_SM1_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_INTR_SM1_RXNEMPTY_BITS _u(0x00000002) #define PROC_PIO_INTR_SM1_RXNEMPTY_MSB _u(1) #define PROC_PIO_INTR_SM1_RXNEMPTY_LSB _u(1) #define PROC_PIO_INTR_SM1_RXNEMPTY_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_INTR_SM0_RXNEMPTY // Description : None #define PROC_PIO_INTR_SM0_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_INTR_SM0_RXNEMPTY_BITS _u(0x00000001) #define PROC_PIO_INTR_SM0_RXNEMPTY_MSB _u(0) #define PROC_PIO_INTR_SM0_RXNEMPTY_LSB _u(0) #define PROC_PIO_INTR_SM0_RXNEMPTY_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_IRQ0_INTE // Description : Interrupt Enable for irq0 #define PROC_PIO_IRQ0_INTE_OFFSET _u(0x00000150) #define PROC_PIO_IRQ0_INTE_BITS _u(0x00000fff) #define PROC_PIO_IRQ0_INTE_RESET _u(0x00000000) #define PROC_PIO_IRQ0_INTE_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM3 // Description : None #define PROC_PIO_IRQ0_INTE_SM3_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM3_BITS _u(0x00000800) #define PROC_PIO_IRQ0_INTE_SM3_MSB _u(11) #define PROC_PIO_IRQ0_INTE_SM3_LSB _u(11) #define PROC_PIO_IRQ0_INTE_SM3_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM2 // Description : None #define PROC_PIO_IRQ0_INTE_SM2_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM2_BITS _u(0x00000400) #define PROC_PIO_IRQ0_INTE_SM2_MSB _u(10) #define PROC_PIO_IRQ0_INTE_SM2_LSB _u(10) #define PROC_PIO_IRQ0_INTE_SM2_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM1 // Description : None #define PROC_PIO_IRQ0_INTE_SM1_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM1_BITS _u(0x00000200) #define PROC_PIO_IRQ0_INTE_SM1_MSB _u(9) #define PROC_PIO_IRQ0_INTE_SM1_LSB _u(9) #define PROC_PIO_IRQ0_INTE_SM1_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM0 // Description : None #define PROC_PIO_IRQ0_INTE_SM0_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM0_BITS _u(0x00000100) #define PROC_PIO_IRQ0_INTE_SM0_MSB _u(8) #define PROC_PIO_IRQ0_INTE_SM0_LSB _u(8) #define PROC_PIO_IRQ0_INTE_SM0_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM3_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTE_SM3_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM3_TXNFULL_BITS _u(0x00000080) #define PROC_PIO_IRQ0_INTE_SM3_TXNFULL_MSB _u(7) #define PROC_PIO_IRQ0_INTE_SM3_TXNFULL_LSB _u(7) #define PROC_PIO_IRQ0_INTE_SM3_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM2_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTE_SM2_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM2_TXNFULL_BITS _u(0x00000040) #define PROC_PIO_IRQ0_INTE_SM2_TXNFULL_MSB _u(6) #define PROC_PIO_IRQ0_INTE_SM2_TXNFULL_LSB _u(6) #define PROC_PIO_IRQ0_INTE_SM2_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM1_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTE_SM1_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM1_TXNFULL_BITS _u(0x00000020) #define PROC_PIO_IRQ0_INTE_SM1_TXNFULL_MSB _u(5) #define PROC_PIO_IRQ0_INTE_SM1_TXNFULL_LSB _u(5) #define PROC_PIO_IRQ0_INTE_SM1_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM0_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTE_SM0_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM0_TXNFULL_BITS _u(0x00000010) #define PROC_PIO_IRQ0_INTE_SM0_TXNFULL_MSB _u(4) #define PROC_PIO_IRQ0_INTE_SM0_TXNFULL_LSB _u(4) #define PROC_PIO_IRQ0_INTE_SM0_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY_BITS _u(0x00000008) #define PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY_MSB _u(3) #define PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY_LSB _u(3) #define PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY_BITS _u(0x00000004) #define PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY_MSB _u(2) #define PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY_LSB _u(2) #define PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY_BITS _u(0x00000002) #define PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY_MSB _u(1) #define PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY_LSB _u(1) #define PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY_BITS _u(0x00000001) #define PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY_MSB _u(0) #define PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY_LSB _u(0) #define PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_IRQ0_INTF // Description : Interrupt Force for irq0 #define PROC_PIO_IRQ0_INTF_OFFSET _u(0x00000154) #define PROC_PIO_IRQ0_INTF_BITS _u(0x00000fff) #define PROC_PIO_IRQ0_INTF_RESET _u(0x00000000) #define PROC_PIO_IRQ0_INTF_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM3 // Description : None #define PROC_PIO_IRQ0_INTF_SM3_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM3_BITS _u(0x00000800) #define PROC_PIO_IRQ0_INTF_SM3_MSB _u(11) #define PROC_PIO_IRQ0_INTF_SM3_LSB _u(11) #define PROC_PIO_IRQ0_INTF_SM3_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM2 // Description : None #define PROC_PIO_IRQ0_INTF_SM2_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM2_BITS _u(0x00000400) #define PROC_PIO_IRQ0_INTF_SM2_MSB _u(10) #define PROC_PIO_IRQ0_INTF_SM2_LSB _u(10) #define PROC_PIO_IRQ0_INTF_SM2_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM1 // Description : None #define PROC_PIO_IRQ0_INTF_SM1_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM1_BITS _u(0x00000200) #define PROC_PIO_IRQ0_INTF_SM1_MSB _u(9) #define PROC_PIO_IRQ0_INTF_SM1_LSB _u(9) #define PROC_PIO_IRQ0_INTF_SM1_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM0 // Description : None #define PROC_PIO_IRQ0_INTF_SM0_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM0_BITS _u(0x00000100) #define PROC_PIO_IRQ0_INTF_SM0_MSB _u(8) #define PROC_PIO_IRQ0_INTF_SM0_LSB _u(8) #define PROC_PIO_IRQ0_INTF_SM0_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM3_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTF_SM3_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM3_TXNFULL_BITS _u(0x00000080) #define PROC_PIO_IRQ0_INTF_SM3_TXNFULL_MSB _u(7) #define PROC_PIO_IRQ0_INTF_SM3_TXNFULL_LSB _u(7) #define PROC_PIO_IRQ0_INTF_SM3_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM2_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTF_SM2_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM2_TXNFULL_BITS _u(0x00000040) #define PROC_PIO_IRQ0_INTF_SM2_TXNFULL_MSB _u(6) #define PROC_PIO_IRQ0_INTF_SM2_TXNFULL_LSB _u(6) #define PROC_PIO_IRQ0_INTF_SM2_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM1_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTF_SM1_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM1_TXNFULL_BITS _u(0x00000020) #define PROC_PIO_IRQ0_INTF_SM1_TXNFULL_MSB _u(5) #define PROC_PIO_IRQ0_INTF_SM1_TXNFULL_LSB _u(5) #define PROC_PIO_IRQ0_INTF_SM1_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM0_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTF_SM0_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM0_TXNFULL_BITS _u(0x00000010) #define PROC_PIO_IRQ0_INTF_SM0_TXNFULL_MSB _u(4) #define PROC_PIO_IRQ0_INTF_SM0_TXNFULL_LSB _u(4) #define PROC_PIO_IRQ0_INTF_SM0_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY_BITS _u(0x00000008) #define PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY_MSB _u(3) #define PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY_LSB _u(3) #define PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY_BITS _u(0x00000004) #define PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY_MSB _u(2) #define PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY_LSB _u(2) #define PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY_BITS _u(0x00000002) #define PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY_MSB _u(1) #define PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY_LSB _u(1) #define PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS _u(0x00000001) #define PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY_MSB _u(0) #define PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY_LSB _u(0) #define PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_IRQ0_INTS // Description : Interrupt status after masking & forcing for irq0 #define PROC_PIO_IRQ0_INTS_OFFSET _u(0x00000158) #define PROC_PIO_IRQ0_INTS_BITS _u(0x00000fff) #define PROC_PIO_IRQ0_INTS_RESET _u(0x00000000) #define PROC_PIO_IRQ0_INTS_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM3 // Description : None #define PROC_PIO_IRQ0_INTS_SM3_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM3_BITS _u(0x00000800) #define PROC_PIO_IRQ0_INTS_SM3_MSB _u(11) #define PROC_PIO_IRQ0_INTS_SM3_LSB _u(11) #define PROC_PIO_IRQ0_INTS_SM3_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM2 // Description : None #define PROC_PIO_IRQ0_INTS_SM2_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM2_BITS _u(0x00000400) #define PROC_PIO_IRQ0_INTS_SM2_MSB _u(10) #define PROC_PIO_IRQ0_INTS_SM2_LSB _u(10) #define PROC_PIO_IRQ0_INTS_SM2_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM1 // Description : None #define PROC_PIO_IRQ0_INTS_SM1_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM1_BITS _u(0x00000200) #define PROC_PIO_IRQ0_INTS_SM1_MSB _u(9) #define PROC_PIO_IRQ0_INTS_SM1_LSB _u(9) #define PROC_PIO_IRQ0_INTS_SM1_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM0 // Description : None #define PROC_PIO_IRQ0_INTS_SM0_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM0_BITS _u(0x00000100) #define PROC_PIO_IRQ0_INTS_SM0_MSB _u(8) #define PROC_PIO_IRQ0_INTS_SM0_LSB _u(8) #define PROC_PIO_IRQ0_INTS_SM0_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM3_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTS_SM3_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM3_TXNFULL_BITS _u(0x00000080) #define PROC_PIO_IRQ0_INTS_SM3_TXNFULL_MSB _u(7) #define PROC_PIO_IRQ0_INTS_SM3_TXNFULL_LSB _u(7) #define PROC_PIO_IRQ0_INTS_SM3_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM2_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTS_SM2_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM2_TXNFULL_BITS _u(0x00000040) #define PROC_PIO_IRQ0_INTS_SM2_TXNFULL_MSB _u(6) #define PROC_PIO_IRQ0_INTS_SM2_TXNFULL_LSB _u(6) #define PROC_PIO_IRQ0_INTS_SM2_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM1_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTS_SM1_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM1_TXNFULL_BITS _u(0x00000020) #define PROC_PIO_IRQ0_INTS_SM1_TXNFULL_MSB _u(5) #define PROC_PIO_IRQ0_INTS_SM1_TXNFULL_LSB _u(5) #define PROC_PIO_IRQ0_INTS_SM1_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM0_TXNFULL // Description : None #define PROC_PIO_IRQ0_INTS_SM0_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM0_TXNFULL_BITS _u(0x00000010) #define PROC_PIO_IRQ0_INTS_SM0_TXNFULL_MSB _u(4) #define PROC_PIO_IRQ0_INTS_SM0_TXNFULL_LSB _u(4) #define PROC_PIO_IRQ0_INTS_SM0_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY_BITS _u(0x00000008) #define PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY_MSB _u(3) #define PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY_LSB _u(3) #define PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY_BITS _u(0x00000004) #define PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY_MSB _u(2) #define PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY_LSB _u(2) #define PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY_BITS _u(0x00000002) #define PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY_MSB _u(1) #define PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY_LSB _u(1) #define PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY // Description : None #define PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY_BITS _u(0x00000001) #define PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY_MSB _u(0) #define PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY_LSB _u(0) #define PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_IRQ1_INTE // Description : Interrupt Enable for irq1 #define PROC_PIO_IRQ1_INTE_OFFSET _u(0x0000015c) #define PROC_PIO_IRQ1_INTE_BITS _u(0x00000fff) #define PROC_PIO_IRQ1_INTE_RESET _u(0x00000000) #define PROC_PIO_IRQ1_INTE_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM3 // Description : None #define PROC_PIO_IRQ1_INTE_SM3_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM3_BITS _u(0x00000800) #define PROC_PIO_IRQ1_INTE_SM3_MSB _u(11) #define PROC_PIO_IRQ1_INTE_SM3_LSB _u(11) #define PROC_PIO_IRQ1_INTE_SM3_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM2 // Description : None #define PROC_PIO_IRQ1_INTE_SM2_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM2_BITS _u(0x00000400) #define PROC_PIO_IRQ1_INTE_SM2_MSB _u(10) #define PROC_PIO_IRQ1_INTE_SM2_LSB _u(10) #define PROC_PIO_IRQ1_INTE_SM2_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM1 // Description : None #define PROC_PIO_IRQ1_INTE_SM1_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM1_BITS _u(0x00000200) #define PROC_PIO_IRQ1_INTE_SM1_MSB _u(9) #define PROC_PIO_IRQ1_INTE_SM1_LSB _u(9) #define PROC_PIO_IRQ1_INTE_SM1_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM0 // Description : None #define PROC_PIO_IRQ1_INTE_SM0_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM0_BITS _u(0x00000100) #define PROC_PIO_IRQ1_INTE_SM0_MSB _u(8) #define PROC_PIO_IRQ1_INTE_SM0_LSB _u(8) #define PROC_PIO_IRQ1_INTE_SM0_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM3_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTE_SM3_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM3_TXNFULL_BITS _u(0x00000080) #define PROC_PIO_IRQ1_INTE_SM3_TXNFULL_MSB _u(7) #define PROC_PIO_IRQ1_INTE_SM3_TXNFULL_LSB _u(7) #define PROC_PIO_IRQ1_INTE_SM3_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM2_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTE_SM2_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM2_TXNFULL_BITS _u(0x00000040) #define PROC_PIO_IRQ1_INTE_SM2_TXNFULL_MSB _u(6) #define PROC_PIO_IRQ1_INTE_SM2_TXNFULL_LSB _u(6) #define PROC_PIO_IRQ1_INTE_SM2_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM1_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTE_SM1_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM1_TXNFULL_BITS _u(0x00000020) #define PROC_PIO_IRQ1_INTE_SM1_TXNFULL_MSB _u(5) #define PROC_PIO_IRQ1_INTE_SM1_TXNFULL_LSB _u(5) #define PROC_PIO_IRQ1_INTE_SM1_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM0_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTE_SM0_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM0_TXNFULL_BITS _u(0x00000010) #define PROC_PIO_IRQ1_INTE_SM0_TXNFULL_MSB _u(4) #define PROC_PIO_IRQ1_INTE_SM0_TXNFULL_LSB _u(4) #define PROC_PIO_IRQ1_INTE_SM0_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY_BITS _u(0x00000008) #define PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY_MSB _u(3) #define PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY_LSB _u(3) #define PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY_BITS _u(0x00000004) #define PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY_MSB _u(2) #define PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY_LSB _u(2) #define PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY_BITS _u(0x00000002) #define PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY_MSB _u(1) #define PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY_LSB _u(1) #define PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY_BITS _u(0x00000001) #define PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY_MSB _u(0) #define PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY_LSB _u(0) #define PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_IRQ1_INTF // Description : Interrupt Force for irq1 #define PROC_PIO_IRQ1_INTF_OFFSET _u(0x00000160) #define PROC_PIO_IRQ1_INTF_BITS _u(0x00000fff) #define PROC_PIO_IRQ1_INTF_RESET _u(0x00000000) #define PROC_PIO_IRQ1_INTF_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM3 // Description : None #define PROC_PIO_IRQ1_INTF_SM3_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM3_BITS _u(0x00000800) #define PROC_PIO_IRQ1_INTF_SM3_MSB _u(11) #define PROC_PIO_IRQ1_INTF_SM3_LSB _u(11) #define PROC_PIO_IRQ1_INTF_SM3_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM2 // Description : None #define PROC_PIO_IRQ1_INTF_SM2_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM2_BITS _u(0x00000400) #define PROC_PIO_IRQ1_INTF_SM2_MSB _u(10) #define PROC_PIO_IRQ1_INTF_SM2_LSB _u(10) #define PROC_PIO_IRQ1_INTF_SM2_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM1 // Description : None #define PROC_PIO_IRQ1_INTF_SM1_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM1_BITS _u(0x00000200) #define PROC_PIO_IRQ1_INTF_SM1_MSB _u(9) #define PROC_PIO_IRQ1_INTF_SM1_LSB _u(9) #define PROC_PIO_IRQ1_INTF_SM1_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM0 // Description : None #define PROC_PIO_IRQ1_INTF_SM0_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM0_BITS _u(0x00000100) #define PROC_PIO_IRQ1_INTF_SM0_MSB _u(8) #define PROC_PIO_IRQ1_INTF_SM0_LSB _u(8) #define PROC_PIO_IRQ1_INTF_SM0_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM3_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTF_SM3_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM3_TXNFULL_BITS _u(0x00000080) #define PROC_PIO_IRQ1_INTF_SM3_TXNFULL_MSB _u(7) #define PROC_PIO_IRQ1_INTF_SM3_TXNFULL_LSB _u(7) #define PROC_PIO_IRQ1_INTF_SM3_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM2_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTF_SM2_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM2_TXNFULL_BITS _u(0x00000040) #define PROC_PIO_IRQ1_INTF_SM2_TXNFULL_MSB _u(6) #define PROC_PIO_IRQ1_INTF_SM2_TXNFULL_LSB _u(6) #define PROC_PIO_IRQ1_INTF_SM2_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM1_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTF_SM1_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM1_TXNFULL_BITS _u(0x00000020) #define PROC_PIO_IRQ1_INTF_SM1_TXNFULL_MSB _u(5) #define PROC_PIO_IRQ1_INTF_SM1_TXNFULL_LSB _u(5) #define PROC_PIO_IRQ1_INTF_SM1_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM0_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTF_SM0_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM0_TXNFULL_BITS _u(0x00000010) #define PROC_PIO_IRQ1_INTF_SM0_TXNFULL_MSB _u(4) #define PROC_PIO_IRQ1_INTF_SM0_TXNFULL_LSB _u(4) #define PROC_PIO_IRQ1_INTF_SM0_TXNFULL_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY_BITS _u(0x00000008) #define PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY_MSB _u(3) #define PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY_LSB _u(3) #define PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY_BITS _u(0x00000004) #define PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY_MSB _u(2) #define PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY_LSB _u(2) #define PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY_BITS _u(0x00000002) #define PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY_MSB _u(1) #define PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY_LSB _u(1) #define PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY_ACCESS "RW" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY_BITS _u(0x00000001) #define PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY_MSB _u(0) #define PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY_LSB _u(0) #define PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_IRQ1_INTS // Description : Interrupt status after masking & forcing for irq1 #define PROC_PIO_IRQ1_INTS_OFFSET _u(0x00000164) #define PROC_PIO_IRQ1_INTS_BITS _u(0x00000fff) #define PROC_PIO_IRQ1_INTS_RESET _u(0x00000000) #define PROC_PIO_IRQ1_INTS_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM3 // Description : None #define PROC_PIO_IRQ1_INTS_SM3_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM3_BITS _u(0x00000800) #define PROC_PIO_IRQ1_INTS_SM3_MSB _u(11) #define PROC_PIO_IRQ1_INTS_SM3_LSB _u(11) #define PROC_PIO_IRQ1_INTS_SM3_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM2 // Description : None #define PROC_PIO_IRQ1_INTS_SM2_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM2_BITS _u(0x00000400) #define PROC_PIO_IRQ1_INTS_SM2_MSB _u(10) #define PROC_PIO_IRQ1_INTS_SM2_LSB _u(10) #define PROC_PIO_IRQ1_INTS_SM2_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM1 // Description : None #define PROC_PIO_IRQ1_INTS_SM1_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM1_BITS _u(0x00000200) #define PROC_PIO_IRQ1_INTS_SM1_MSB _u(9) #define PROC_PIO_IRQ1_INTS_SM1_LSB _u(9) #define PROC_PIO_IRQ1_INTS_SM1_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM0 // Description : None #define PROC_PIO_IRQ1_INTS_SM0_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM0_BITS _u(0x00000100) #define PROC_PIO_IRQ1_INTS_SM0_MSB _u(8) #define PROC_PIO_IRQ1_INTS_SM0_LSB _u(8) #define PROC_PIO_IRQ1_INTS_SM0_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM3_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTS_SM3_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM3_TXNFULL_BITS _u(0x00000080) #define PROC_PIO_IRQ1_INTS_SM3_TXNFULL_MSB _u(7) #define PROC_PIO_IRQ1_INTS_SM3_TXNFULL_LSB _u(7) #define PROC_PIO_IRQ1_INTS_SM3_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM2_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTS_SM2_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM2_TXNFULL_BITS _u(0x00000040) #define PROC_PIO_IRQ1_INTS_SM2_TXNFULL_MSB _u(6) #define PROC_PIO_IRQ1_INTS_SM2_TXNFULL_LSB _u(6) #define PROC_PIO_IRQ1_INTS_SM2_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM1_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTS_SM1_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM1_TXNFULL_BITS _u(0x00000020) #define PROC_PIO_IRQ1_INTS_SM1_TXNFULL_MSB _u(5) #define PROC_PIO_IRQ1_INTS_SM1_TXNFULL_LSB _u(5) #define PROC_PIO_IRQ1_INTS_SM1_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM0_TXNFULL // Description : None #define PROC_PIO_IRQ1_INTS_SM0_TXNFULL_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM0_TXNFULL_BITS _u(0x00000010) #define PROC_PIO_IRQ1_INTS_SM0_TXNFULL_MSB _u(4) #define PROC_PIO_IRQ1_INTS_SM0_TXNFULL_LSB _u(4) #define PROC_PIO_IRQ1_INTS_SM0_TXNFULL_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY_BITS _u(0x00000008) #define PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY_MSB _u(3) #define PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY_LSB _u(3) #define PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY_BITS _u(0x00000004) #define PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY_MSB _u(2) #define PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY_LSB _u(2) #define PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY_BITS _u(0x00000002) #define PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY_MSB _u(1) #define PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY_LSB _u(1) #define PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY // Description : None #define PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY_RESET _u(0x0) #define PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY_BITS _u(0x00000001) #define PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY_MSB _u(0) #define PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY_LSB _u(0) #define PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_BLOCK_ID // Description : Block Identifier // Hexadecimal representation of "pio2" #define PROC_PIO_BLOCK_ID_OFFSET _u(0x00000168) #define PROC_PIO_BLOCK_ID_BITS _u(0xffffffff) #define PROC_PIO_BLOCK_ID_RESET _u(0x70696f32) #define PROC_PIO_BLOCK_ID_WIDTH _u(32) #define PROC_PIO_BLOCK_ID_MSB _u(31) #define PROC_PIO_BLOCK_ID_LSB _u(0) #define PROC_PIO_BLOCK_ID_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_INSTANCE_ID // Description : Block Instance Identifier #define PROC_PIO_INSTANCE_ID_OFFSET _u(0x0000016c) #define PROC_PIO_INSTANCE_ID_BITS _u(0x0000000f) #define PROC_PIO_INSTANCE_ID_RESET _u(0x00000000) #define PROC_PIO_INSTANCE_ID_WIDTH _u(32) #define PROC_PIO_INSTANCE_ID_MSB _u(3) #define PROC_PIO_INSTANCE_ID_LSB _u(0) #define PROC_PIO_INSTANCE_ID_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_RSTSEQ_AUTO // Description : None #define PROC_PIO_RSTSEQ_AUTO_OFFSET _u(0x00000170) #define PROC_PIO_RSTSEQ_AUTO_BITS _u(0x00000001) #define PROC_PIO_RSTSEQ_AUTO_RESET _u(0x00000001) #define PROC_PIO_RSTSEQ_AUTO_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_RSTSEQ_AUTO_BUSADAPTER // Description : 1 = reset is controlled by the sequencer // 0 = reset is controlled by rstseq_ctrl #define PROC_PIO_RSTSEQ_AUTO_BUSADAPTER_RESET _u(0x1) #define PROC_PIO_RSTSEQ_AUTO_BUSADAPTER_BITS _u(0x00000001) #define PROC_PIO_RSTSEQ_AUTO_BUSADAPTER_MSB _u(0) #define PROC_PIO_RSTSEQ_AUTO_BUSADAPTER_LSB _u(0) #define PROC_PIO_RSTSEQ_AUTO_BUSADAPTER_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_RSTSEQ_PARALLEL // Description : None #define PROC_PIO_RSTSEQ_PARALLEL_OFFSET _u(0x00000174) #define PROC_PIO_RSTSEQ_PARALLEL_BITS _u(0x00000001) #define PROC_PIO_RSTSEQ_PARALLEL_RESET _u(0x00000000) #define PROC_PIO_RSTSEQ_PARALLEL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER // Description : Is this reset parallel (i.e. not part of the sequence) #define PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER_RESET _u(0x0) #define PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER_BITS _u(0x00000001) #define PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER_MSB _u(0) #define PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER_LSB _u(0) #define PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER_ACCESS "RO" // ============================================================================= // Register : PROC_PIO_RSTSEQ_CTRL // Description : None #define PROC_PIO_RSTSEQ_CTRL_OFFSET _u(0x00000178) #define PROC_PIO_RSTSEQ_CTRL_BITS _u(0x00000001) #define PROC_PIO_RSTSEQ_CTRL_RESET _u(0x00000000) #define PROC_PIO_RSTSEQ_CTRL_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_RSTSEQ_CTRL_BUSADAPTER // Description : 1 = keep the reset asserted // 0 = keep the reset deasserted // This is ignored if rstseq_auto=1 #define PROC_PIO_RSTSEQ_CTRL_BUSADAPTER_RESET _u(0x0) #define PROC_PIO_RSTSEQ_CTRL_BUSADAPTER_BITS _u(0x00000001) #define PROC_PIO_RSTSEQ_CTRL_BUSADAPTER_MSB _u(0) #define PROC_PIO_RSTSEQ_CTRL_BUSADAPTER_LSB _u(0) #define PROC_PIO_RSTSEQ_CTRL_BUSADAPTER_ACCESS "RW" // ============================================================================= // Register : PROC_PIO_RSTSEQ_TRIG // Description : None #define PROC_PIO_RSTSEQ_TRIG_OFFSET _u(0x0000017c) #define PROC_PIO_RSTSEQ_TRIG_BITS _u(0x00000001) #define PROC_PIO_RSTSEQ_TRIG_RESET _u(0x00000000) #define PROC_PIO_RSTSEQ_TRIG_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_RSTSEQ_TRIG_BUSADAPTER // Description : Pulses the reset output #define PROC_PIO_RSTSEQ_TRIG_BUSADAPTER_RESET _u(0x0) #define PROC_PIO_RSTSEQ_TRIG_BUSADAPTER_BITS _u(0x00000001) #define PROC_PIO_RSTSEQ_TRIG_BUSADAPTER_MSB _u(0) #define PROC_PIO_RSTSEQ_TRIG_BUSADAPTER_LSB _u(0) #define PROC_PIO_RSTSEQ_TRIG_BUSADAPTER_ACCESS "SC" // ============================================================================= // Register : PROC_PIO_RSTSEQ_DONE // Description : None #define PROC_PIO_RSTSEQ_DONE_OFFSET _u(0x00000180) #define PROC_PIO_RSTSEQ_DONE_BITS _u(0x00000001) #define PROC_PIO_RSTSEQ_DONE_RESET _u(0x00000000) #define PROC_PIO_RSTSEQ_DONE_WIDTH _u(32) // ----------------------------------------------------------------------------- // Field : PROC_PIO_RSTSEQ_DONE_BUSADAPTER // Description : Indicates the current state of the reset #define PROC_PIO_RSTSEQ_DONE_BUSADAPTER_RESET _u(0x0) #define PROC_PIO_RSTSEQ_DONE_BUSADAPTER_BITS _u(0x00000001) #define PROC_PIO_RSTSEQ_DONE_BUSADAPTER_MSB _u(0) #define PROC_PIO_RSTSEQ_DONE_BUSADAPTER_LSB _u(0) #define PROC_PIO_RSTSEQ_DONE_BUSADAPTER_ACCESS "RO" // ============================================================================= #endif // HARDWARE_REGS_PROC_PIO_DEFINED raspi-utils-20250514/piolib/include/hardware/timer.h000066400000000000000000000003011501106437300222130ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2024 Raspberry Pi Ltd. * All rights reserved. */ #ifndef _HARDWARE_TIMER_H #define _HARDWARE_TIMER_H #include "piolib.h" #endif raspi-utils-20250514/piolib/include/pico/000077500000000000000000000000001501106437300200655ustar00rootroot00000000000000raspi-utils-20250514/piolib/include/pico/stdlib.h000066400000000000000000000003571501106437300215240ustar00rootroot00000000000000/* * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _PICO_STDLIB_H_ #define _PICO_STDLIB_H_ #include #include #include #include "piolib.h" #endif raspi-utils-20250514/piolib/include/pio_platform.h000066400000000000000000000020021501106437300217710ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2024 Raspberry Pi Ltd. * All rights reserved. */ #ifndef _PIO_PLATFORM_H #define _PIO_PLATFORM_H #include #include #include #ifndef __unused #define __unused __attribute__((unused)) #endif #define PICO_DEFAULT_LED_PIN 4 #ifndef PARAM_ASSERTIONS_ENABLE_ALL #define PARAM_ASSERTIONS_ENABLE_ALL 0 #endif #ifndef PARAM_ASSERTIONS_DISABLE_ALL #define PARAM_ASSERTIONS_DISABLE_ALL 0 #endif #define PARAM_ASSERTIONS_ENABLED(x) ((PARAM_ASSERTIONS_ENABLED_ ## x || PARAM_ASSERTIONS_ENABLE_ALL) && !PARAM_ASSERTIONS_DISABLE_ALL) #define invalid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(!(test));}) #define valid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(test);}) #define STATIC_ASSERT(cond) static_assert(cond, #cond) #define _u(x) ((uint)(x)) #define bool_to_bit(x) ((uint)!!(x)) #ifndef count_of #define count_of(a) (sizeof(a)/sizeof((a)[0])) #endif typedef unsigned int uint; #endif raspi-utils-20250514/piolib/include/piolib.h000066400000000000000000000643531501106437300205750ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2023-24 Raspberry Pi Ltd. * All rights reserved. */ #ifndef _PIOLIB_H #define _PIOLIB_H #ifdef __cplusplus extern "C" { #endif #include "pio_platform.h" #include "hardware/clocks.h" #include "hardware/gpio.h" #ifndef PARAM_ASSERTIONS_ENABLED_PIO #define PARAM_ASSERTIONS_ENABLED_PIO 0 #endif #define PIO_ERR(x)((PIO)(uintptr_t)(x)) #define PIO_IS_ERR(x)(((uintptr_t)(x) >= (uintptr_t)-200)) #define PIO_ERR_VAL(x)((int)(uintptr_t)(x)) #define PIO_ORIGIN_ANY ((uint)(~0)) #define PIO_ORIGIN_INVALID PIO_ORIGIN_ANY #define pio0 pio_open_helper(0) enum pio_fifo_join { PIO_FIFO_JOIN_NONE = 0, PIO_FIFO_JOIN_TX = 1, PIO_FIFO_JOIN_RX = 2, }; enum pio_mov_status_type { STATUS_TX_LESSTHAN = 0, STATUS_RX_LESSTHAN = 1 }; enum pio_xfer_dir { PIO_DIR_TO_SM, PIO_DIR_FROM_SM, PIO_DIR_COUNT }; #ifndef PIOLIB_INTERNALS enum pio_instr_bits { pio_instr_bits_jmp = 0x0000, pio_instr_bits_wait = 0x2000, pio_instr_bits_in = 0x4000, pio_instr_bits_out = 0x6000, pio_instr_bits_push = 0x8000, pio_instr_bits_pull = 0x8080, pio_instr_bits_mov = 0xa000, pio_instr_bits_irq = 0xc000, pio_instr_bits_set = 0xe000, }; #ifndef NDEBUG #define _PIO_INVALID_IN_SRC 0x08u #define _PIO_INVALID_OUT_DEST 0x10u #define _PIO_INVALID_SET_DEST 0x20u #define _PIO_INVALID_MOV_SRC 0x40u #define _PIO_INVALID_MOV_DEST 0x80u #else #define _PIO_INVALID_IN_SRC 0u #define _PIO_INVALID_OUT_DEST 0u #define _PIO_INVALID_SET_DEST 0u #define _PIO_INVALID_MOV_SRC 0u #define _PIO_INVALID_MOV_DEST 0u #endif enum pio_src_dest { pio_pins = 0u, pio_x = 1u, pio_y = 2u, pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, pio_isr = 6u | _PIO_INVALID_SET_DEST, pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST, pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, }; #endif typedef struct pio_program { const uint16_t *instructions; uint8_t length; int8_t origin; // required instruction memory origin or -1 uint8_t pio_version; } pio_program_t; typedef struct { uint32_t content[4]; } pio_sm_config; typedef struct pio_instance *PIO; typedef const struct pio_chip PIO_CHIP_T; struct pio_chip { const char *name; const char *compatible; uint16_t instr_count; uint16_t sm_count; uint16_t fifo_depth; void *hw_state; PIO (*create_instance)(PIO_CHIP_T *chip, uint index); int (*open_instance)(PIO pio); void (*close_instance)(PIO pio); int (*pio_sm_config_xfer)(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count); int (*pio_sm_xfer_data)(PIO pio, uint sm, uint dir, uint data_bytes, void *data); bool (*pio_can_add_program_at_offset)(PIO pio, const pio_program_t *program, uint offset); uint (*pio_add_program_at_offset)(PIO pio, const pio_program_t *program, uint offset); bool (*pio_remove_program)(PIO pio, const pio_program_t *program, uint loaded_offset); bool (*pio_clear_instruction_memory)(PIO pio); uint (*pio_encode_delay)(PIO pio, uint cycles); uint (*pio_encode_sideset)(PIO pio, uint sideset_bit_count, uint value); uint (*pio_encode_sideset_opt)(PIO pio, uint sideset_bit_count, uint value); uint (*pio_encode_jmp)(PIO pio, uint addr); uint (*pio_encode_jmp_not_x)(PIO pio, uint addr); uint (*pio_encode_jmp_x_dec)(PIO pio, uint addr); uint (*pio_encode_jmp_not_y)(PIO pio, uint addr); uint (*pio_encode_jmp_y_dec)(PIO pio, uint addr); uint (*pio_encode_jmp_x_ne_y)(PIO pio, uint addr); uint (*pio_encode_jmp_pin)(PIO pio, uint addr); uint (*pio_encode_jmp_not_osre)(PIO pio, uint addr); uint (*pio_encode_wait_gpio)(PIO pio, bool polarity, uint gpio); uint (*pio_encode_wait_pin)(PIO pio, bool polarity, uint pin); uint (*pio_encode_wait_irq)(PIO pio, bool polarity, bool relative, uint irq); uint (*pio_encode_in)(PIO pio, enum pio_src_dest src, uint count); uint (*pio_encode_out)(PIO pio, enum pio_src_dest dest, uint count); uint (*pio_encode_push)(PIO pio, bool if_full, bool block); uint (*pio_encode_pull)(PIO pio, bool if_empty, bool block); uint (*pio_encode_mov)(PIO pio, enum pio_src_dest dest, enum pio_src_dest src); uint (*pio_encode_mov_not)(PIO pio, enum pio_src_dest dest, enum pio_src_dest src); uint (*pio_encode_mov_reverse)(PIO pio, enum pio_src_dest dest, enum pio_src_dest src); uint (*pio_encode_irq_set)(PIO pio, bool relative, uint irq); uint (*pio_encode_irq_wait)(PIO pio, bool relative, uint irq); uint (*pio_encode_irq_clear)(PIO pio, bool relative, uint irq); uint (*pio_encode_set)(PIO pio, enum pio_src_dest dest, uint value); uint (*pio_encode_nop)(PIO pio); bool (*pio_sm_claim)(PIO pio, uint sm); bool (*pio_sm_claim_mask)(PIO pio, uint mask); int (*pio_sm_claim_unused)(PIO pio, bool required); bool (*pio_sm_unclaim)(PIO pio, uint sm); bool (*pio_sm_is_claimed)(PIO pio, uint sm); void (*pio_sm_init)(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config); void (*pio_sm_set_config)(PIO pio, uint sm, const pio_sm_config *config); void (*pio_sm_exec)(PIO pio, uint sm, uint instr, bool blocking); void (*pio_sm_clear_fifos)(PIO pio, uint sm); void (*pio_sm_set_clkdiv_int_frac)(PIO pio, uint sm, uint16_t div_int, uint8_t div_frac); void (*pio_sm_set_clkdiv)(PIO pio, uint sm, float div); void (*pio_sm_set_pins)(PIO pio, uint sm, uint32_t pin_values); void (*pio_sm_set_pins_with_mask)(PIO pio, uint sm, uint32_t pin_values, uint32_t pin_mask); void (*pio_sm_set_pindirs_with_mask)(PIO pio, uint sm, uint32_t pin_dirs, uint32_t pin_mask); void (*pio_sm_set_consecutive_pindirs)(PIO pio, uint sm, uint pin_base, uint pin_count, bool is_out); void (*pio_sm_set_enabled)(PIO pio, uint sm, bool enabled); void (*pio_sm_set_enabled_mask)(PIO pio, uint32_t mask, bool enabled); void (*pio_sm_restart)(PIO pio, uint sm); void (*pio_sm_restart_mask)(PIO pio, uint32_t mask); void (*pio_sm_clkdiv_restart)(PIO pio, uint sm); void (*pio_sm_clkdiv_restart_mask)(PIO pio, uint32_t mask); void (*pio_sm_enable_sync)(PIO pio, uint32_t mask); void (*pio_sm_put)(PIO pio, uint sm, uint32_t data, bool blocking); uint32_t (*pio_sm_get)(PIO pio, uint sm, bool blocking); void (*pio_sm_set_dmactrl)(PIO pio, uint sm, bool is_tx, uint32_t ctrl); bool (*pio_sm_is_rx_fifo_empty)(PIO pio, uint sm); bool (*pio_sm_is_rx_fifo_full)(PIO pio, uint sm); uint (*pio_sm_get_rx_fifo_level)(PIO pio, uint sm); bool (*pio_sm_is_tx_fifo_empty)(PIO pio, uint sm); bool (*pio_sm_is_tx_fifo_full)(PIO pio, uint sm); uint (*pio_sm_get_tx_fifo_level)(PIO pio, uint sm); void (*pio_sm_drain_tx_fifo)(PIO pio, uint sm); pio_sm_config (*pio_get_default_sm_config)(PIO pio); void (*smc_set_out_pins)(PIO pio, pio_sm_config *c, uint out_base, uint out_count); void (*smc_set_set_pins)(PIO pio, pio_sm_config *c, uint set_base, uint set_count); void (*smc_set_in_pins)(PIO pio, pio_sm_config *c, uint in_base); void (*smc_set_sideset_pins)(PIO pio, pio_sm_config *c, uint sideset_base); void (*smc_set_sideset)(PIO pio, pio_sm_config *c, uint bit_count, bool optional, bool pindirs); void (*smc_set_clkdiv_int_frac)(PIO pio, pio_sm_config *c, uint16_t div_int, uint8_t div_frac); void (*smc_set_clkdiv)(PIO pio, pio_sm_config *c, float div); void (*smc_set_wrap)(PIO pio, pio_sm_config *c, uint wrap_target, uint wrap); void (*smc_set_jmp_pin)(PIO pio, pio_sm_config *c, uint pin); void (*smc_set_in_shift)(PIO pio, pio_sm_config *c, bool shift_right, bool autopush, uint push_threshold); void (*smc_set_out_shift)(PIO pio, pio_sm_config *c, bool shift_right, bool autopull, uint pull_threshold); void (*smc_set_fifo_join)(PIO pio, pio_sm_config *c, enum pio_fifo_join join); void (*smc_set_out_special)(PIO pio, pio_sm_config *c, bool sticky, bool has_enable_pin, uint enable_pin_index); void (*smc_set_mov_status)(PIO pio, pio_sm_config *c, enum pio_mov_status_type status_sel, uint status_n); uint32_t (*clock_get_hz)(PIO pio, enum clock_index clk_index); void (*pio_gpio_init)(PIO, uint pin); void (*gpio_init)(PIO pio, uint gpio); void (*gpio_set_function)(PIO pio, uint gpio, enum gpio_function fn); void (*gpio_set_pulls)(PIO pio, uint gpio, bool up, bool down); void (*gpio_set_outover)(PIO pio, uint gpio, uint value); void (*gpio_set_inover)(PIO pio, uint gpio, uint value); void (*gpio_set_oeover)(PIO pio, uint gpio, uint value); void (*gpio_set_input_enabled)(PIO pio, uint gpio, bool enabled); void (*gpio_set_drive_strength)(PIO pio, uint gpio, enum gpio_drive_strength drive); }; struct pio_instance { const PIO_CHIP_T *chip; int in_use; bool errors_are_fatal; bool error; }; int pio_init(void); PIO pio_open(uint idx); PIO pio_open_by_name(const char *name); PIO pio_open_helper(uint idx); void pio_close(PIO pio); void pio_panic(const char *msg); int pio_get_index(PIO pio); void pio_select(PIO pio); PIO pio_get_current(void); static inline void pio_error(PIO pio, const char *msg) { pio->error = true; if (pio->errors_are_fatal) pio_panic(msg); } static inline bool pio_get_error(PIO pio) { return pio->error; } static inline void pio_clear_error(PIO pio) { pio->error = false; } static inline void pio_enable_fatal_errors(PIO pio, bool enable) { pio->errors_are_fatal = enable; } static inline uint pio_get_sm_count(PIO pio) { return pio->chip->sm_count; } static inline uint pio_get_instruction_count(PIO pio) { return pio->chip->instr_count; } static inline uint pio_get_fifo_depth(PIO pio) { return pio->chip->fifo_depth; } static inline void check_pio_param(__unused PIO pio) { valid_params_if(PIO, pio_get_index(pio) >= 0); } static inline int pio_sm_config_xfer(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count) { check_pio_param(pio); return pio->chip->pio_sm_config_xfer(pio, sm, dir, buf_size, buf_count); } static inline int pio_sm_xfer_data(PIO pio, uint sm, uint dir, uint data_bytes, void *data) { check_pio_param(pio); return pio->chip->pio_sm_xfer_data(pio, sm, dir, data_bytes, data); } static inline bool pio_can_add_program(PIO pio, const pio_program_t *program) { check_pio_param(pio); return pio->chip->pio_can_add_program_at_offset(pio, program, PIO_ORIGIN_ANY); } static inline bool pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) { check_pio_param(pio); return pio->chip->pio_can_add_program_at_offset(pio, program, offset); } static inline uint pio_add_program(PIO pio, const pio_program_t *program) { uint offset; check_pio_param(pio); offset = pio->chip->pio_add_program_at_offset(pio, program, PIO_ORIGIN_ANY); if (offset == PIO_ORIGIN_INVALID) pio_error(pio, "No program space"); return offset; } static inline void pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) { check_pio_param(pio); if (pio->chip->pio_add_program_at_offset(pio, program, offset) == PIO_ORIGIN_INVALID) pio_error(pio, "No program space"); } static inline void pio_remove_program(PIO pio, const pio_program_t *program, uint loaded_offset) { check_pio_param(pio); if (!pio->chip->pio_remove_program(pio, program, loaded_offset)) pio_error(pio, "Failed to remove program"); } static inline void pio_clear_instruction_memory(PIO pio) { check_pio_param(pio); if (!pio->chip->pio_clear_instruction_memory(pio)) pio_error(pio, "Failed to clear instruction memory"); } static inline uint pio_encode_delay(uint cycles) { PIO pio = pio_get_current(); return pio->chip->pio_encode_delay(pio, cycles); } static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) { PIO pio = pio_get_current(); return pio->chip->pio_encode_sideset(pio, sideset_bit_count, value); } static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) { PIO pio = pio_get_current(); return pio->chip->pio_encode_sideset_opt(pio, sideset_bit_count, value); } static inline uint pio_encode_jmp(uint addr) { PIO pio = pio_get_current(); return pio->chip->pio_encode_jmp(pio, addr); } static inline uint pio_encode_jmp_not_x(uint addr) { PIO pio = pio_get_current(); return pio->chip->pio_encode_jmp_not_x(pio, addr); } static inline uint pio_encode_jmp_x_dec(uint addr) { PIO pio = pio_get_current(); return pio->chip->pio_encode_jmp_x_dec(pio, addr); } static inline uint pio_encode_jmp_not_y(uint addr) { PIO pio = pio_get_current(); return pio->chip->pio_encode_jmp_not_y(pio, addr); } static inline uint pio_encode_jmp_y_dec(uint addr) { PIO pio = pio_get_current(); return pio->chip->pio_encode_jmp_y_dec(pio, addr); } static inline uint pio_encode_jmp_x_ne_y(uint addr) { PIO pio = pio_get_current(); return pio->chip->pio_encode_jmp_x_ne_y(pio, addr); } static inline uint pio_encode_jmp_pin(uint addr) { PIO pio = pio_get_current(); return pio->chip->pio_encode_jmp_pin(pio, addr); } static inline uint pio_encode_jmp_not_osre(uint addr) { PIO pio = pio_get_current(); return pio->chip->pio_encode_jmp_not_osre(pio, addr); } static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) { PIO pio = pio_get_current(); return pio->chip->pio_encode_wait_gpio(pio, polarity, gpio); } static inline uint pio_encode_wait_pin(bool polarity, uint pin) { PIO pio = pio_get_current(); return pio->chip->pio_encode_wait_pin(pio, polarity, pin); } static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) { PIO pio = pio_get_current(); return pio->chip->pio_encode_wait_irq(pio, polarity, relative, irq); } static inline uint pio_encode_in(enum pio_src_dest src, uint count) { PIO pio = pio_get_current(); return pio->chip->pio_encode_in(pio, src, count); } static inline uint pio_encode_out(enum pio_src_dest dest, uint count) { PIO pio = pio_get_current(); return pio->chip->pio_encode_out(pio, dest, count); } static inline uint pio_encode_push(bool if_full, bool block) { PIO pio = pio_get_current(); return pio->chip->pio_encode_push(pio, if_full, block); } static inline uint pio_encode_pull(bool if_empty, bool block) { PIO pio = pio_get_current(); return pio->chip->pio_encode_pull(pio, if_empty, block); } static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src) { PIO pio = pio_get_current(); return pio->chip->pio_encode_mov(pio, dest, src); } static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src) { PIO pio = pio_get_current(); return pio->chip->pio_encode_mov_not(pio, dest, src); } static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src) { PIO pio = pio_get_current(); return pio->chip->pio_encode_mov_reverse(pio, dest, src); } static inline uint pio_encode_irq_set(bool relative, uint irq) { PIO pio = pio_get_current(); return pio->chip->pio_encode_irq_set(pio, relative, irq); } static inline uint pio_encode_irq_wait(bool relative, uint irq) { PIO pio = pio_get_current(); return pio->chip->pio_encode_irq_wait(pio, relative, irq); } static inline uint pio_encode_irq_clear(bool relative, uint irq) { PIO pio = pio_get_current(); return pio->chip->pio_encode_irq_clear(pio, relative, irq); } static inline uint pio_encode_set(enum pio_src_dest dest, uint value) { PIO pio = pio_get_current(); return pio->chip->pio_encode_set(pio, dest, value); } static inline uint pio_encode_nop(void) { PIO pio = pio_get_current(); return pio->chip->pio_encode_nop(pio); } static inline void pio_sm_claim(PIO pio, uint sm) { check_pio_param(pio); if (!pio->chip->pio_sm_claim(pio, sm)) pio_error(pio, "Failed to claim SM"); } static inline void pio_claim_sm_mask(PIO pio, uint mask) { check_pio_param(pio); if (!pio->chip->pio_sm_claim_mask(pio, mask)) pio_error(pio, "Failed to claim masked SMs"); } static inline void pio_sm_unclaim(PIO pio, uint sm) { check_pio_param(pio); pio->chip->pio_sm_unclaim(pio, sm); } static inline int pio_claim_unused_sm(PIO pio, bool required) { check_pio_param(pio); return pio->chip->pio_sm_claim_unused(pio, required); } static inline bool pio_sm_is_claimed(PIO pio, uint sm) { check_pio_param(pio); return pio->chip->pio_sm_is_claimed(pio, sm); } static inline void pio_sm_init(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config) { check_pio_param(pio); pio->chip->pio_sm_init(pio, sm, initial_pc, config); } static inline void pio_sm_set_config(PIO pio, uint sm, const pio_sm_config *config) { check_pio_param(pio); pio->chip->pio_sm_set_config(pio, sm, config); } static inline void pio_sm_exec(PIO pio, uint sm, uint instr) { check_pio_param(pio); pio->chip->pio_sm_exec(pio, sm, instr, false); } static inline void pio_sm_exec_wait_blocking(PIO pio, uint sm, uint instr) { check_pio_param(pio); pio->chip->pio_sm_exec(pio, sm, instr, true); } static inline void pio_sm_clear_fifos(PIO pio, uint sm) { check_pio_param(pio); pio->chip->pio_sm_clear_fifos(pio, sm); } static inline void pio_sm_set_clkdiv_int_frac(PIO pio, uint sm, uint16_t div_int, uint8_t div_frac) { check_pio_param(pio); pio->chip->pio_sm_set_clkdiv_int_frac(pio, sm, div_int, div_frac); } static inline void pio_sm_set_clkdiv(PIO pio, uint sm, float div) { check_pio_param(pio); pio->chip->pio_sm_set_clkdiv(pio, sm, div); } static inline void pio_sm_set_pins(PIO pio, uint sm, uint32_t pin_values) { check_pio_param(pio); pio->chip->pio_sm_set_pins(pio, sm, pin_values); } static inline void pio_sm_set_pins_with_mask(PIO pio, uint sm, uint32_t pin_values, uint32_t pin_mask) { check_pio_param(pio); pio->chip->pio_sm_set_pins_with_mask(pio, sm, pin_values, pin_mask); } static inline void pio_sm_set_pindirs_with_mask(PIO pio, uint sm, uint32_t pin_dirs, uint32_t pin_mask) { check_pio_param(pio); pio->chip->pio_sm_set_pindirs_with_mask(pio, sm, pin_dirs, pin_mask); } static inline void pio_sm_set_consecutive_pindirs(PIO pio, uint sm, uint pin_base, uint pin_count, bool is_out) { check_pio_param(pio); pio->chip->pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, is_out); } static inline void pio_sm_set_enabled(PIO pio, uint sm, bool enabled) { check_pio_param(pio); pio->chip->pio_sm_set_enabled(pio, sm, enabled); } static inline void pio_set_sm_mask_enabled(PIO pio, uint32_t mask, bool enabled) { check_pio_param(pio); pio->chip->pio_sm_set_enabled_mask(pio, mask, enabled); } static inline void pio_sm_restart(PIO pio, uint sm) { check_pio_param(pio); pio->chip->pio_sm_restart(pio, sm); } static inline void pio_restart_sm_mask(PIO pio, uint32_t mask) { check_pio_param(pio); pio->chip->pio_sm_restart_mask(pio, mask); } static inline void pio_sm_clkdiv_restart(PIO pio, uint sm) { check_pio_param(pio); pio->chip->pio_sm_clkdiv_restart(pio, sm); } static inline void pio_clkdiv_restart_sm_mask(PIO pio, uint32_t mask) { check_pio_param(pio); pio->chip->pio_sm_clkdiv_restart_mask(pio, mask); } static inline void pio_enable_sm_in_sync_mask(PIO pio, uint32_t mask) { check_pio_param(pio); pio->chip->pio_sm_enable_sync(pio, mask); }; static inline void pio_sm_set_dmactrl(PIO pio, uint sm, bool is_tx, uint32_t ctrl) { check_pio_param(pio); pio->chip->pio_sm_set_dmactrl(pio, sm, is_tx, ctrl); }; static inline bool pio_sm_is_rx_fifo_empty(PIO pio, uint sm) { check_pio_param(pio); return pio->chip->pio_sm_is_rx_fifo_empty(pio, sm); } static inline bool pio_sm_is_rx_fifo_full(PIO pio, uint sm) { check_pio_param(pio); return pio->chip->pio_sm_is_rx_fifo_full(pio, sm); } static inline uint pio_sm_get_rx_fifo_level(PIO pio, uint sm) { check_pio_param(pio); return pio->chip->pio_sm_get_rx_fifo_level(pio, sm); } static inline bool pio_sm_is_tx_fifo_empty(PIO pio, uint sm) { check_pio_param(pio); return pio->chip->pio_sm_is_tx_fifo_empty(pio, sm); } static inline bool pio_sm_is_tx_fifo_full(PIO pio, uint sm) { check_pio_param(pio); return pio->chip->pio_sm_is_tx_fifo_full(pio, sm); } static inline uint pio_sm_get_tx_fifo_level(PIO pio, uint sm) { check_pio_param(pio); return pio->chip->pio_sm_get_tx_fifo_level(pio, sm); } static inline void pio_sm_drain_tx_fifo(PIO pio, uint sm) { check_pio_param(pio); return pio->chip->pio_sm_drain_tx_fifo(pio, sm); } static inline void pio_sm_put(PIO pio, uint sm, uint32_t data) { check_pio_param(pio); pio->chip->pio_sm_put(pio, sm, data, false); } static inline void pio_sm_put_blocking(PIO pio, uint sm, uint32_t data) { check_pio_param(pio); pio->chip->pio_sm_put(pio, sm, data, true); } static inline uint32_t pio_sm_get(PIO pio, uint sm) { check_pio_param(pio); return pio->chip->pio_sm_get(pio, sm, false); } static inline uint32_t pio_sm_get_blocking(PIO pio, uint sm) { check_pio_param(pio); return pio->chip->pio_sm_get(pio, sm, true); } static inline pio_sm_config pio_get_default_sm_config_for_pio(PIO pio) { check_pio_param(pio); return pio->chip->pio_get_default_sm_config(pio); } static inline pio_sm_config pio_get_default_sm_config(void) { PIO pio = pio_get_current(); return pio->chip->pio_get_default_sm_config(pio); } static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base, uint out_count) { PIO pio = pio_get_current(); pio->chip->smc_set_out_pins(pio, c, out_base, out_count); } static inline void sm_config_set_set_pins(pio_sm_config *c, uint set_base, uint set_count) { PIO pio = pio_get_current(); pio->chip->smc_set_set_pins(pio, c, set_base, set_count); } static inline void sm_config_set_in_pins(pio_sm_config *c, uint in_base) { PIO pio = pio_get_current(); pio->chip->smc_set_in_pins(pio, c, in_base); } static inline void sm_config_set_sideset_pins(pio_sm_config *c, uint sideset_base) { PIO pio = pio_get_current(); pio->chip->smc_set_sideset_pins(pio, c, sideset_base); } static inline void sm_config_set_sideset(pio_sm_config *c, uint bit_count, bool optional, bool pindirs) { PIO pio = pio_get_current(); pio->chip->smc_set_sideset(pio, c, bit_count, optional, pindirs); } static inline void sm_config_set_clkdiv_int_frac(pio_sm_config *c, uint16_t div_int, uint8_t div_frac) { PIO pio = pio_get_current(); pio->chip->smc_set_clkdiv_int_frac(pio, c, div_int, div_frac); } static inline void sm_config_set_clkdiv(pio_sm_config *c, float div) { PIO pio = pio_get_current(); pio->chip->smc_set_clkdiv(pio, c, div); } static inline void sm_config_set_wrap(pio_sm_config *c, uint wrap_target, uint wrap) { PIO pio = pio_get_current(); pio->chip->smc_set_wrap(pio, c, wrap_target, wrap); } static inline void sm_config_set_jmp_pin(pio_sm_config *c, uint pin) { PIO pio = pio_get_current(); pio->chip->smc_set_jmp_pin(pio, c, pin); } static inline void sm_config_set_in_shift(pio_sm_config *c, bool shift_right, bool autopush, uint push_threshold) { PIO pio = pio_get_current(); pio->chip->smc_set_in_shift(pio, c, shift_right, autopush, push_threshold); } static inline void sm_config_set_out_shift(pio_sm_config *c, bool shift_right, bool autopull, uint pull_threshold) { PIO pio = pio_get_current(); pio->chip->smc_set_out_shift(pio, c, shift_right, autopull, pull_threshold); } static inline void sm_config_set_fifo_join(pio_sm_config *c, enum pio_fifo_join join) { PIO pio = pio_get_current(); pio->chip->smc_set_fifo_join(pio, c, join); } static inline void sm_config_set_out_special(pio_sm_config *c, bool sticky, bool has_enable_pin, uint enable_pin_index) { PIO pio = pio_get_current(); pio->chip->smc_set_out_special(pio, c, sticky, has_enable_pin, enable_pin_index); } static inline void sm_config_set_mov_status(pio_sm_config *c, enum pio_mov_status_type status_sel, uint status_n) { PIO pio = pio_get_current(); pio->chip->smc_set_mov_status(pio, c, status_sel, status_n); } static inline void pio_gpio_init(PIO pio, uint pin) { check_pio_param(pio); pio->chip->pio_gpio_init(pio, pin); } static inline uint32_t clock_get_hz(enum clock_index clk_index) { PIO pio = pio_get_current(); return pio->chip->clock_get_hz(pio, clk_index); } static inline void gpio_init(uint gpio) { PIO pio = pio_get_current(); pio->chip->gpio_init(pio, gpio); } static inline void gpio_set_function(uint gpio, enum gpio_function fn) { PIO pio = pio_get_current(); pio->chip->gpio_set_function(pio, gpio, fn); } static inline void gpio_set_pulls(uint gpio, bool up, bool down) { PIO pio = pio_get_current(); pio->chip->gpio_set_pulls(pio, gpio, up, down); } static inline void gpio_set_outover(uint gpio, uint value) { PIO pio = pio_get_current(); pio->chip->gpio_set_outover(pio, gpio, value); } static inline void gpio_set_inover(uint gpio, uint value) { PIO pio = pio_get_current(); pio->chip->gpio_set_inover(pio, gpio, value); } static inline void gpio_set_oeover(uint gpio, uint value) { PIO pio = pio_get_current(); pio->chip->gpio_set_oeover(pio, gpio, value); } static inline void gpio_set_input_enabled(uint gpio, bool enabled) { PIO pio = pio_get_current(); pio->chip->gpio_set_input_enabled(pio, gpio, enabled); } static inline void gpio_set_drive_strength(uint gpio, enum gpio_drive_strength drive) { PIO pio = pio_get_current(); pio->chip->gpio_set_drive_strength(pio, gpio, drive); } static inline void gpio_pull_up(uint gpio) { gpio_set_pulls(gpio, true, false); } static inline void gpio_pull_down(uint gpio) { gpio_set_pulls(gpio, false, true); } static inline void gpio_disable_pulls(uint gpio) { gpio_set_pulls(gpio, false, false); } static inline void stdio_init_all(void) { } void sleep_us(uint64_t us); static inline void sleep_ms(uint32_t ms) { sleep_us((uint64_t)(ms * (uint64_t)1000)); } #ifdef __cplusplus } #endif #endif raspi-utils-20250514/piolib/include/piolib_priv.h000066400000000000000000000013621501106437300216240ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2023-24 Raspberry Pi Ltd. * All rights reserved. */ #ifndef _PIOLIB_PRIV_H #define _PIOLIB_PRIV_H #include "pio_platform.h" #define PIO_CHIP(name) name ## _pio_chip #if LIBRARY_BUILD #define DECLARE_PIO_CHIP(chip) \ const PIO_CHIP_T chip ## _pio_chip = extern const PIO_CHIP_T *const library_piochips[]; extern const int library_piochips_count; #else #define DECLARE_PIO_CHIP(chip) \ const PIO_CHIP_T PIO_CHIP(chip); \ const PIO_CHIP_T *__ptr_ ## chip __attribute__ ((section ("piochips"))) __attribute__ ((used)) = &PIO_CHIP(chip); \ const PIO_CHIP_T PIO_CHIP(chip) = extern const PIO_CHIP_T *__start_piochips; extern const PIO_CHIP_T *__stop_piochips; #endif #endif raspi-utils-20250514/piolib/include/rp1_pio_if.h000066400000000000000000000144721501106437300213430ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2023-24 Raspberry Pi Ltd. * All rights reserved. */ #ifndef _RP1_PIO_IF_H #define _RP1_PIO_IF_H #include #define RP1_PIO_INSTRUCTION_COUNT 32 #define RP1_PIO_SM_COUNT 4 #define RP1_PIO_GPIO_COUNT 28 #define RP1_GPIO_FUNC_PIO 7 #define RP1_PIO_ORIGIN_ANY ((uint16_t)(~0)) #define RP1_PIO_DIR_TO_SM 0 #define RP1_PIO_DIR_FROM_SM 1 #define RP1_PIO_DIR_COUNT 2 typedef struct { uint32_t clkdiv; uint32_t execctrl; uint32_t shiftctrl; uint32_t pinctrl; } rp1_pio_sm_config; struct rp1_pio_add_program_args { uint16_t num_instrs; uint16_t origin; uint16_t instrs[RP1_PIO_INSTRUCTION_COUNT]; }; struct rp1_pio_remove_program_args { uint16_t num_instrs; uint16_t origin; }; struct rp1_pio_sm_claim_args { uint16_t mask; }; struct rp1_pio_sm_init_args { uint16_t sm; uint16_t initial_pc; rp1_pio_sm_config config; }; struct rp1_pio_sm_set_config_args { uint16_t sm; uint16_t rsvd; rp1_pio_sm_config config; }; struct rp1_pio_sm_exec_args { uint16_t sm; uint16_t instr; uint8_t blocking; uint8_t rsvd; }; struct rp1_pio_sm_clear_fifos_args { uint16_t sm; }; struct rp1_pio_sm_set_clkdiv_args { uint16_t sm; uint16_t div_int; uint8_t div_frac; uint8_t rsvd; }; struct rp1_pio_sm_set_pins_args { uint16_t sm; uint16_t rsvd; uint32_t values; uint32_t mask; }; struct rp1_pio_sm_set_pindirs_args { uint16_t sm; uint16_t rsvd; uint32_t dirs; uint32_t mask; }; struct rp1_pio_sm_set_enabled_args { uint16_t mask; uint8_t enable; uint8_t rsvd; }; struct rp1_pio_sm_restart_args { uint16_t mask; }; struct rp1_pio_sm_clkdiv_restart_args { uint16_t mask; }; struct rp1_pio_sm_enable_sync_args { uint16_t mask; }; struct rp1_pio_sm_put_args { uint16_t sm; uint8_t blocking; uint8_t rsvd; uint32_t data; }; struct rp1_pio_sm_get_args { uint16_t sm; uint8_t blocking; uint8_t rsvd; uint32_t data; /* OUT */ }; struct rp1_pio_sm_set_dmactrl_args { uint16_t sm; uint8_t is_tx; uint8_t rsvd; uint32_t ctrl; }; struct rp1_pio_sm_fifo_state_args { uint16_t sm; uint8_t tx; uint8_t rsvd; uint16_t level; /* OUT */ uint8_t empty; /* OUT */ uint8_t full; /* OUT */ }; struct rp1_gpio_init_args { uint16_t gpio; }; struct rp1_gpio_set_function_args { uint16_t gpio; uint16_t fn; }; struct rp1_gpio_set_pulls_args { uint16_t gpio; uint8_t up; uint8_t down; }; struct rp1_gpio_set_args { uint16_t gpio; uint16_t value; }; struct rp1_pio_sm_config_xfer_args { uint16_t sm; uint16_t dir; uint16_t buf_size; uint16_t buf_count; }; struct rp1_pio_sm_config_xfer32_args { uint16_t sm; uint16_t dir; uint32_t buf_size; uint32_t buf_count; }; struct rp1_pio_sm_xfer_data_args { uint16_t sm; uint16_t dir; uint16_t data_bytes; uint16_t rsvd; void *data; }; struct rp1_pio_sm_xfer_data32_args { uint16_t sm; uint16_t dir; uint32_t data_bytes; void *data; }; struct rp1_access_hw_args { uint32_t addr; uint32_t len; void *data; }; #define PIO_IOC_MAGIC 102 #define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) #define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args) #define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct rp1_pio_sm_xfer_data32_args) #define PIO_IOC_SM_CONFIG_XFER32 _IOW(PIO_IOC_MAGIC, 3, struct rp1_pio_sm_config_xfer32_args) #define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args) #define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args) #define PIO_IOC_CAN_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 10, struct rp1_pio_add_program_args) #define PIO_IOC_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 11, struct rp1_pio_add_program_args) #define PIO_IOC_REMOVE_PROGRAM _IOW(PIO_IOC_MAGIC, 12, struct rp1_pio_remove_program_args) #define PIO_IOC_CLEAR_INSTR_MEM _IO(PIO_IOC_MAGIC, 13) #define PIO_IOC_SM_CLAIM _IOW(PIO_IOC_MAGIC, 20, struct rp1_pio_sm_claim_args) #define PIO_IOC_SM_UNCLAIM _IOW(PIO_IOC_MAGIC, 21, struct rp1_pio_sm_claim_args) #define PIO_IOC_SM_IS_CLAIMED _IOW(PIO_IOC_MAGIC, 22, struct rp1_pio_sm_claim_args) #define PIO_IOC_SM_INIT _IOW(PIO_IOC_MAGIC, 30, struct rp1_pio_sm_init_args) #define PIO_IOC_SM_SET_CONFIG _IOW(PIO_IOC_MAGIC, 31, struct rp1_pio_sm_set_config_args) #define PIO_IOC_SM_EXEC _IOW(PIO_IOC_MAGIC, 32, struct rp1_pio_sm_exec_args) #define PIO_IOC_SM_CLEAR_FIFOS _IOW(PIO_IOC_MAGIC, 33, struct rp1_pio_sm_clear_fifos_args) #define PIO_IOC_SM_SET_CLKDIV _IOW(PIO_IOC_MAGIC, 34, struct rp1_pio_sm_set_clkdiv_args) #define PIO_IOC_SM_SET_PINS _IOW(PIO_IOC_MAGIC, 35, struct rp1_pio_sm_set_pins_args) #define PIO_IOC_SM_SET_PINDIRS _IOW(PIO_IOC_MAGIC, 36, struct rp1_pio_sm_set_pindirs_args) #define PIO_IOC_SM_SET_ENABLED _IOW(PIO_IOC_MAGIC, 37, struct rp1_pio_sm_set_enabled_args) #define PIO_IOC_SM_RESTART _IOW(PIO_IOC_MAGIC, 38, struct rp1_pio_sm_restart_args) #define PIO_IOC_SM_CLKDIV_RESTART _IOW(PIO_IOC_MAGIC, 39, struct rp1_pio_sm_restart_args) #define PIO_IOC_SM_ENABLE_SYNC _IOW(PIO_IOC_MAGIC, 40, struct rp1_pio_sm_enable_sync_args) #define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args) #define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args) #define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args) #define PIO_IOC_SM_FIFO_STATE _IOW(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args) #define PIO_IOC_SM_DRAIN_TX _IOW(PIO_IOC_MAGIC, 45, struct rp1_pio_sm_clear_fifos_args) #define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args) #define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args) #define PIO_IOC_GPIO_SET_PULLS _IOW(PIO_IOC_MAGIC, 52, struct rp1_gpio_set_pulls_args) #define PIO_IOC_GPIO_SET_OUTOVER _IOW(PIO_IOC_MAGIC, 53, struct rp1_gpio_set_args) #define PIO_IOC_GPIO_SET_INOVER _IOW(PIO_IOC_MAGIC, 54, struct rp1_gpio_set_args) #define PIO_IOC_GPIO_SET_OEOVER _IOW(PIO_IOC_MAGIC, 55, struct rp1_gpio_set_args) #define PIO_IOC_GPIO_SET_INPUT_ENABLED _IOW(PIO_IOC_MAGIC, 56, struct rp1_gpio_set_args) #define PIO_IOC_GPIO_SET_DRIVE_STRENGTH _IOW(PIO_IOC_MAGIC, 57, struct rp1_gpio_set_args) #endif raspi-utils-20250514/piolib/library_piochips.c000066400000000000000000000006641501106437300212240ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2025 Raspberry Pi Ltd. * All rights reserved. */ #include "piolib.h" #include "piolib_priv.h" #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) #define EXTERN_PIO_CHIP(name) extern const PIO_CHIP_T PIO_CHIP(name) EXTERN_PIO_CHIP(rp1); const PIO_CHIP_T *const library_piochips[] = { &PIO_CHIP(rp1), }; const int library_piochips_count = ARRAY_SIZE(library_piochips); raspi-utils-20250514/piolib/pio_rp1.c000066400000000000000000001000541501106437300172250ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2024-25 Raspberry Pi Ltd. * All rights reserved. */ #include #include #include #include #include #include #define PIOLIB_INTERNALS #include "pio_platform.h" #define pio_encode_delay _pio_encode_delay #define pio_encode_sideset _pio_encode_sideset #define pio_encode_sideset_opt _pio_encode_sideset_opt #define pio_encode_jmp _pio_encode_jmp #define pio_encode_jmp_not_x _pio_encode_jmp_not_x #define pio_encode_jmp_x_dec _pio_encode_jmp_x_dec #define pio_encode_jmp_not_y _pio_encode_jmp_not_y #define pio_encode_jmp_y_dec _pio_encode_jmp_y_dec #define pio_encode_jmp_x_ne_y _pio_encode_jmp_x_ne_y #define pio_encode_jmp_pin _pio_encode_jmp_pin #define pio_encode_jmp_not_osre _pio_encode_jmp_not_osre #define pio_encode_wait_gpio _pio_encode_wait_gpio #define pio_encode_wait_pin _pio_encode_wait_pin #define pio_encode_wait_irq _pio_encode_wait_irq #define pio_encode_in _pio_encode_in #define pio_encode_out _pio_encode_out #define pio_encode_push _pio_encode_push #define pio_encode_pull _pio_encode_pull #define pio_encode_mov _pio_encode_mov #define pio_encode_mov_not _pio_encode_mov_not #define pio_encode_mov_reverse _pio_encode_mov_reverse #define pio_encode_irq_set _pio_encode_irq_set #define pio_encode_irq_wait _pio_encode_irq_wait #define pio_encode_irq_clear _pio_encode_irq_clear #define pio_encode_set _pio_encode_set #define pio_encode_nop _pio_encode_nop #include "hardware/pio_instructions.h" #undef pio_encode_delay #undef pio_encode_sideset #undef pio_encode_sideset_opt #undef pio_encode_jmp #undef pio_encode_jmp_not_x #undef pio_encode_jmp_x_dec #undef pio_encode_jmp_not_y #undef pio_encode_jmp_y_dec #undef pio_encode_jmp_x_ne_y #undef pio_encode_jmp_pin #undef pio_encode_jmp_not_osre #undef pio_encode_wait_gpio #undef pio_encode_wait_pin #undef pio_encode_wait_irq #undef pio_encode_in #undef pio_encode_out #undef pio_encode_push #undef pio_encode_pull #undef pio_encode_mov #undef pio_encode_mov_not #undef pio_encode_mov_reverse #undef pio_encode_irq_set #undef pio_encode_irq_wait #undef pio_encode_irq_clear #undef pio_encode_set #undef pio_encode_nop #include "piolib.h" #include "piolib_priv.h" #include "hardware/gpio.h" #include "hardware/pio.h" #include "hardware/regs/proc_pio.h" #include "rp1_pio_if.h" typedef struct rp1_pio_handle { struct pio_instance base; const char *devname; int fd; } *RP1_PIO; #define smc_to_rp1(_config, _c) rp1_pio_sm_config *_c = (rp1_pio_sm_config*)_config #define GPIOS_MASK ((1 << RP1_PIO_GPIO_COUNT) - 1) STATIC_ASSERT(sizeof(rp1_pio_sm_config) <= sizeof(pio_sm_config)); static inline void check_sm_param(__unused uint sm) { valid_params_if(PIO, sm < RP1_PIO_SM_COUNT); } static inline void check_sm_mask(__unused uint mask) { valid_params_if(PIO, mask < (1u << RP1_PIO_SM_COUNT)); } static pio_sm_config rp1_pio_get_default_sm_config(PIO) { pio_sm_config c = { { 0 } }; sm_config_set_clkdiv_int_frac(&c, 1, 0); sm_config_set_wrap(&c, 0, 31); sm_config_set_in_shift(&c, true, false, 32); sm_config_set_out_shift(&c, true, false, 32); return c; } static uint rp1_pio_encode_delay(PIO, uint cycles) { return _pio_encode_delay(cycles); } static uint rp1_pio_encode_sideset(PIO, uint sideset_bit_count, uint value) { return _pio_encode_sideset(sideset_bit_count, value); } static uint rp1_pio_encode_sideset_opt(PIO, uint sideset_bit_count, uint value) { return _pio_encode_sideset_opt(sideset_bit_count, value); } static uint rp1_pio_encode_jmp(PIO, uint addr) { return _pio_encode_jmp(addr); } static uint rp1_pio_encode_jmp_not_x(PIO, uint addr) { return _pio_encode_jmp_not_x(addr); } static uint rp1_pio_encode_jmp_x_dec(PIO, uint addr) { return _pio_encode_jmp_x_dec(addr); } static uint rp1_pio_encode_jmp_not_y(PIO, uint addr) { return _pio_encode_jmp_not_y(addr); } static uint rp1_pio_encode_jmp_y_dec(PIO, uint addr) { return _pio_encode_jmp_y_dec(addr); } static uint rp1_pio_encode_jmp_x_ne_y(PIO, uint addr) { return _pio_encode_jmp_x_ne_y(addr); } static uint rp1_pio_encode_jmp_pin(PIO, uint addr) { return _pio_encode_jmp_pin(addr); } static uint rp1_pio_encode_jmp_not_osre(PIO, uint addr) { return _pio_encode_jmp_not_osre(addr); } static uint rp1_pio_encode_wait_gpio(PIO, bool polarity, uint gpio) { return _pio_encode_wait_gpio(polarity, gpio); } static uint rp1_pio_encode_wait_pin(PIO, bool polarity, uint pin) { return _pio_encode_wait_pin(polarity, pin); } static uint rp1_pio_encode_wait_irq(PIO, bool polarity, bool relative, uint irq) { return _pio_encode_wait_irq(polarity, relative, irq); } static uint rp1_pio_encode_in(PIO, enum pio_src_dest src, uint count) { return _pio_encode_in(src, count); } static uint rp1_pio_encode_out(PIO, enum pio_src_dest dest, uint count) { return _pio_encode_out(dest, count); } static uint rp1_pio_encode_push(PIO, bool if_full, bool block) { return _pio_encode_push(if_full, block); } static uint rp1_pio_encode_pull(PIO, bool if_empty, bool block) { return _pio_encode_pull(if_empty, block); } static uint rp1_pio_encode_mov(PIO, enum pio_src_dest dest, enum pio_src_dest src) { return _pio_encode_mov(dest, src); } static uint rp1_pio_encode_mov_not(PIO, enum pio_src_dest dest, enum pio_src_dest src) { return _pio_encode_mov_not(dest, src); } static uint rp1_pio_encode_mov_reverse(PIO, enum pio_src_dest dest, enum pio_src_dest src) { return _pio_encode_mov_reverse(dest, src); } static uint rp1_pio_encode_irq_set(PIO, bool relative, uint irq) { return _pio_encode_irq_set(relative, irq); } static uint rp1_pio_encode_irq_wait(PIO, bool relative, uint irq) { return _pio_encode_irq_wait(relative, irq); } static uint rp1_pio_encode_irq_clear(PIO, bool relative, uint irq) { return _pio_encode_irq_clear(relative, irq); } static uint rp1_pio_encode_set(PIO, enum pio_src_dest dest, uint value) { return _pio_encode_set(dest, value); } static uint rp1_pio_encode_nop(PIO) { return _pio_encode_nop(); } static int rp1_ioctl(PIO pio, int request, void *args) { RP1_PIO rp = (RP1_PIO)pio; int err = ioctl(rp->fd, request, args); switch (err) { case -EREMOTEIO: case -ETIMEDOUT: pio_panic("Error communicating with RP1"); break; default: break; } return err; } static int rp1_pio_sm_config_xfer(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count) { struct rp1_pio_sm_config_xfer_args args = { .sm = sm, .dir = dir, .buf_size = buf_size, .buf_count = buf_count }; struct rp1_pio_sm_config_xfer32_args args32 = { .sm = sm, .dir = dir, .buf_size = buf_size, .buf_count = buf_count }; int err; check_sm_param(sm); if (buf_size > 0xffff || buf_count > 0xffff) err = rp1_ioctl(pio, PIO_IOC_SM_CONFIG_XFER32, &args32); else err = rp1_ioctl(pio, PIO_IOC_SM_CONFIG_XFER, &args); return err; } static int rp1_pio_sm_xfer_data(PIO pio, uint sm, uint dir, uint data_bytes, void *data) { struct rp1_pio_sm_xfer_data_args args = { .sm = sm, .dir = dir, .data_bytes = data_bytes, .rsvd = 0, .data = data }; struct rp1_pio_sm_xfer_data32_args args32 = { .sm = sm, .dir = dir, .data_bytes = data_bytes, .data = data }; int err; check_sm_param(sm); if (data_bytes > 0xffff) err = rp1_ioctl(pio, PIO_IOC_SM_XFER_DATA32, &args32); else err = rp1_ioctl(pio, PIO_IOC_SM_XFER_DATA, &args); return err; } static bool rp1_pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) { struct rp1_pio_add_program_args args = { .num_instrs = program->length, .origin = program->origin }; int err; valid_params_if(PIO, offset < RP1_PIO_INSTRUCTION_COUNT || offset == PIO_ORIGIN_ANY); valid_params_if(PIO, program->length <= RP1_PIO_INSTRUCTION_COUNT); valid_params_if(PIO, offset + program->length <= RP1_PIO_INSTRUCTION_COUNT || offset == PIO_ORIGIN_ANY); if (program->origin >= 0 && (uint)program->origin != offset) return false; if (offset != PIO_ORIGIN_ANY) args.origin = offset; memcpy(args.instrs, program->instructions, program->length * sizeof(uint16_t)); err = rp1_ioctl(pio, PIO_IOC_CAN_ADD_PROGRAM, &args); return (err > 0); } static uint rp1_pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) { struct rp1_pio_add_program_args args = { .num_instrs = program->length, .origin = program->origin }; valid_params_if(PIO, offset < RP1_PIO_INSTRUCTION_COUNT || offset == PIO_ORIGIN_ANY); valid_params_if(PIO, program->length <= RP1_PIO_INSTRUCTION_COUNT); valid_params_if(PIO, offset + program->length <= RP1_PIO_INSTRUCTION_COUNT || offset == PIO_ORIGIN_ANY); if (offset != PIO_ORIGIN_ANY) args.origin = offset; memcpy(args.instrs, program->instructions, program->length * sizeof(uint16_t)); return rp1_ioctl(pio, PIO_IOC_ADD_PROGRAM, &args); } static bool rp1_pio_remove_program(PIO pio, const pio_program_t *program, uint offset) { struct rp1_pio_remove_program_args args = { .num_instrs = program->length, .origin = offset }; valid_params_if(PIO, offset < RP1_PIO_INSTRUCTION_COUNT); valid_params_if(PIO, offset + program->length <= RP1_PIO_INSTRUCTION_COUNT); return !rp1_ioctl(pio, PIO_IOC_REMOVE_PROGRAM, &args); } static bool rp1_pio_clear_instruction_memory(PIO pio) { return !rp1_ioctl(pio, PIO_IOC_CLEAR_INSTR_MEM, NULL); } static bool rp1_pio_sm_claim(PIO pio, uint sm) { struct rp1_pio_sm_claim_args args = { .mask = (1 << sm) }; check_sm_param(sm); return (rp1_ioctl(pio, PIO_IOC_SM_CLAIM, &args) >= 0); } static bool rp1_pio_sm_claim_mask(PIO pio, uint mask) { struct rp1_pio_sm_claim_args args = { .mask = mask }; valid_params_if(PIO, !!mask); check_sm_mask(mask); return (rp1_ioctl(pio, PIO_IOC_SM_CLAIM, &args) >= 0); } static bool rp1_pio_sm_unclaim(PIO pio, uint sm) { struct rp1_pio_sm_claim_args args = { .mask = (1 << sm) }; check_sm_param(sm); return !rp1_ioctl(pio, PIO_IOC_SM_UNCLAIM, &args); } static int rp1_pio_sm_claim_unused(PIO pio, bool required) { struct rp1_pio_sm_claim_args args = { .mask = 0 }; int sm = rp1_ioctl(pio, PIO_IOC_SM_CLAIM, &args); if (sm < 0 && required) pio_panic("No PIO state machines are available"); return sm; } static bool rp1_pio_sm_is_claimed(PIO pio, uint sm) { struct rp1_pio_sm_claim_args args = { .mask = (1 << sm) }; check_sm_param(sm); int err = rp1_ioctl(pio, PIO_IOC_SM_IS_CLAIMED, &args); return (err > 0); } static void rp1_pio_sm_init(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config) { smc_to_rp1(config, c); struct rp1_pio_sm_init_args args = { .sm = sm, .initial_pc = initial_pc, .config = *c }; valid_params_if(PIO, initial_pc < RP1_PIO_INSTRUCTION_COUNT); (void)rp1_ioctl(pio, PIO_IOC_SM_INIT, &args); } static void rp1_pio_sm_set_config(PIO pio, uint sm, const pio_sm_config *config) { smc_to_rp1(config, c); struct rp1_pio_sm_init_args args = { .sm = sm, .config = *c }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_SET_CONFIG, &args); } static void rp1_pio_sm_exec(PIO pio, uint sm, uint instr, bool blocking) { struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = blocking }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_EXEC, &args); } static void rp1_pio_sm_clear_fifos(PIO pio, uint sm) { struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_CLEAR_FIFOS, &args); } static void rp1_pio_calculate_clkdiv_from_float(float div, uint16_t *div_int, uint8_t *div_frac) { valid_params_if(PIO, div >= 1 && div <= 65536); *div_int = (uint16_t)div; if (*div_int == 0) { *div_frac = 0; } else { *div_frac = (uint8_t)((div - (float)*div_int) * (1u << 8u)); } } static void rp1_pio_sm_set_clkdiv_int_frac(PIO pio, uint sm, uint16_t div_int, uint8_t div_frac) { struct rp1_pio_sm_set_clkdiv_args args = { .sm = sm, .div_int = div_int, .div_frac = div_frac }; check_sm_param(sm); invalid_params_if(PIO, div_int == 0 && div_frac != 0); (void)rp1_ioctl(pio, PIO_IOC_SM_SET_CLKDIV, &args); } static void rp1_pio_sm_set_clkdiv(PIO pio, uint sm, float div) { uint16_t div_int; uint8_t div_frac; check_sm_param(sm); rp1_pio_calculate_clkdiv_from_float(div, &div_int, &div_frac); rp1_pio_sm_set_clkdiv_int_frac(pio, sm, div_int, div_frac); } static void rp1_pio_sm_set_pins(PIO pio, uint sm, uint32_t pin_values) { struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, .mask = GPIOS_MASK }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_SET_PINS, &args); } static void rp1_pio_sm_set_pins_with_mask(PIO pio, uint sm, uint32_t pin_values, uint32_t pin_mask) { struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, .mask = pin_mask }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_SET_PINS, &args); } static void rp1_pio_sm_set_pindirs_with_mask(PIO pio, uint sm, uint32_t pin_dirs, uint32_t pin_mask) { struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = pin_dirs, .mask = pin_mask }; check_sm_param(sm); valid_params_if(PIO, (pin_dirs & GPIOS_MASK) == pin_dirs); valid_params_if(PIO, (pin_mask & pin_mask) == pin_mask); (void)rp1_ioctl(pio, PIO_IOC_SM_SET_PINDIRS, &args); } static void rp1_pio_sm_set_consecutive_pindirs(PIO pio, uint sm, uint pin_base, uint pin_count, bool is_out) { uint32_t mask = ((1 << pin_count) - 1) << pin_base; struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = is_out ? mask : 0, .mask = mask }; check_sm_param(sm); valid_params_if(PIO, pin_base < RP1_PIO_GPIO_COUNT && pin_count < RP1_PIO_GPIO_COUNT && (pin_base + pin_count) < RP1_PIO_GPIO_COUNT); (void)rp1_ioctl(pio, PIO_IOC_SM_SET_PINDIRS, &args); } static void rp1_pio_sm_set_enabled(PIO pio, uint sm, bool enabled) { struct rp1_pio_sm_set_enabled_args args = { .mask = (1 << sm), .enable = enabled }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_SET_ENABLED, &args); } static void rp1_pio_sm_set_enabled_mask(PIO pio, uint32_t mask, bool enabled) { struct rp1_pio_sm_set_enabled_args args = { .mask = (uint16_t)mask, .enable = enabled }; check_sm_mask(mask); (void)rp1_ioctl(pio, PIO_IOC_SM_SET_ENABLED, &args); } static void rp1_pio_sm_restart(PIO pio, uint sm) { struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_RESTART, &args); } static void rp1_pio_sm_restart_mask(PIO pio, uint32_t mask) { struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; check_sm_mask(mask); (void)rp1_ioctl(pio, PIO_IOC_SM_RESTART, &args); } static void rp1_pio_sm_clkdiv_restart(PIO pio, uint sm) { struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_CLKDIV_RESTART, &args); } static void rp1_pio_sm_clkdiv_restart_mask(PIO pio, uint32_t mask) { struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; check_sm_mask(mask); (void)rp1_ioctl(pio, PIO_IOC_SM_CLKDIV_RESTART, &args); } static void rp1_pio_sm_enable_sync(PIO pio, uint32_t mask) { struct rp1_pio_sm_enable_sync_args args = { .mask = (uint16_t)mask }; check_sm_mask(mask); (void)rp1_ioctl(pio, PIO_IOC_SM_ENABLE_SYNC, &args); } static void rp1_pio_sm_put(PIO pio, uint sm, uint32_t data, bool blocking) { struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = blocking, .data = data }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_PUT, &args); } static uint32_t rp1_pio_sm_get(PIO pio, uint sm, bool blocking) { struct rp1_pio_sm_get_args args = { .sm = (uint16_t)sm, .blocking = blocking }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_GET, &args); return args.data; } static void rp1_pio_sm_set_dmactrl(PIO pio, uint sm, bool is_tx, uint32_t ctrl) { struct rp1_pio_sm_set_dmactrl_args args = { .sm = sm, .is_tx = is_tx, .ctrl = ctrl }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_SET_DMACTRL, &args); } static bool rp1_pio_sm_is_rx_fifo_empty(PIO pio, uint sm) { struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); return args.empty; } static bool rp1_pio_sm_is_rx_fifo_full(PIO pio, uint sm) { struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); return args.full; } static uint rp1_pio_sm_get_rx_fifo_level(PIO pio, uint sm) { struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); return args.level; } static bool rp1_pio_sm_is_tx_fifo_empty(PIO pio, uint sm) { struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); return args.empty; } static bool rp1_pio_sm_is_tx_fifo_full(PIO pio, uint sm) { struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); return args.full; } static uint rp1_pio_sm_get_tx_fifo_level(PIO pio, uint sm) { struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); return args.level; } static void rp1_pio_sm_drain_tx_fifo(PIO pio, uint sm) { struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; check_sm_param(sm); (void)rp1_ioctl(pio, PIO_IOC_SM_DRAIN_TX, &args); } static void rp1_smc_set_out_pins(PIO, pio_sm_config *config, uint out_base, uint out_count) { smc_to_rp1(config, c); valid_params_if(PIO, out_base < RP1_PIO_GPIO_COUNT); valid_params_if(PIO, out_count <= RP1_PIO_GPIO_COUNT); c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS | PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS)) | (out_base << PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB) | (out_count << PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB); } static void rp1_smc_set_set_pins(PIO, pio_sm_config *config, uint set_base, uint set_count) { smc_to_rp1(config, c); valid_params_if(PIO, set_base < RP1_PIO_GPIO_COUNT); valid_params_if(PIO, set_count <= 5); c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_SET_BASE_BITS | PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS)) | (set_base << PROC_PIO_SM0_PINCTRL_SET_BASE_LSB) | (set_count << PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB); } static void rp1_smc_set_in_pins(PIO, pio_sm_config *config, uint in_base) { smc_to_rp1(config, c); valid_params_if(PIO, in_base < RP1_PIO_GPIO_COUNT); c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_IN_BASE_BITS) | (in_base << PROC_PIO_SM0_PINCTRL_IN_BASE_LSB); } static void rp1_smc_set_sideset_pins(PIO, pio_sm_config *config, uint sideset_base) { smc_to_rp1(config, c); valid_params_if(PIO, sideset_base < RP1_PIO_GPIO_COUNT); c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS) | (sideset_base << PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB); } static void rp1_smc_set_sideset(PIO, pio_sm_config *config, uint bit_count, bool optional, bool pindirs) { smc_to_rp1(config, c); valid_params_if(PIO, bit_count <= 5); valid_params_if(PIO, !optional || bit_count >= 1); c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS) | (bit_count << PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB); c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS | PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS)) | (bool_to_bit(optional) << PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB) | (bool_to_bit(pindirs) << PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB); } static void rp1_smc_set_clkdiv_int_frac(PIO, pio_sm_config *config, uint16_t div_int, uint8_t div_frac) { smc_to_rp1(config, c); invalid_params_if(PIO, div_int == 0 && div_frac != 0); c->clkdiv = (((uint)div_frac) << PROC_PIO_SM0_CLKDIV_FRAC_LSB) | (((uint)div_int) << PROC_PIO_SM0_CLKDIV_INT_LSB); } static void rp1_smc_set_clkdiv(PIO, pio_sm_config *config, float div) { uint16_t div_int; uint8_t div_frac; rp1_pio_calculate_clkdiv_from_float(div, &div_int, &div_frac); sm_config_set_clkdiv_int_frac(config, div_int, div_frac); } static void rp1_smc_set_wrap(PIO, pio_sm_config *config, uint wrap_target, uint wrap) { smc_to_rp1(config, c); valid_params_if(PIO, wrap < RP1_PIO_INSTRUCTION_COUNT); valid_params_if(PIO, wrap_target < RP1_PIO_INSTRUCTION_COUNT); c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS | PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS)) | (wrap_target << PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) | (wrap << PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB); } static void rp1_smc_set_jmp_pin(PIO, pio_sm_config *config, uint pin) { smc_to_rp1(config, c); valid_params_if(PIO, pin < RP1_PIO_GPIO_COUNT); c->execctrl = (c->execctrl & ~PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS) | (pin << PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB); } static void rp1_smc_set_in_shift(PIO, pio_sm_config *config, bool shift_right, bool autopush, uint push_threshold) { smc_to_rp1(config, c); valid_params_if(PIO, push_threshold <= 32); c->shiftctrl = (c->shiftctrl & ~(PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS | PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS | PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS)) | (bool_to_bit(shift_right) << PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB) | (bool_to_bit(autopush) << PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB) | ((push_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB); } static void rp1_smc_set_out_shift(PIO, pio_sm_config *config, bool shift_right, bool autopull, uint pull_threshold) { smc_to_rp1(config, c); valid_params_if(PIO, pull_threshold <= 32); c->shiftctrl = (c->shiftctrl & ~(PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS | PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS | PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS)) | (bool_to_bit(shift_right) << PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB) | (bool_to_bit(autopull) << PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB) | ((pull_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB); } static void rp1_smc_set_fifo_join(PIO, pio_sm_config *config, enum pio_fifo_join join) { smc_to_rp1(config, c); valid_params_if(PIO, join == PIO_FIFO_JOIN_NONE || join == PIO_FIFO_JOIN_TX || join == PIO_FIFO_JOIN_RX); c->shiftctrl = (c->shiftctrl & (uint)~(PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS | PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS)) | (((uint)join) << PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB); } static void rp1_smc_set_out_special(PIO, pio_sm_config *config, bool sticky, bool has_enable_pin, uint enable_pin_index) { smc_to_rp1(config, c); c->execctrl = (c->execctrl & (uint)~(PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS | PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS | PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS)) | (bool_to_bit(sticky) << PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB) | (bool_to_bit(has_enable_pin) << PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB) | ((enable_pin_index << PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB) & PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS); } static void rp1_smc_set_mov_status(PIO, pio_sm_config *config, enum pio_mov_status_type status_sel, uint status_n) { smc_to_rp1(config, c); valid_params_if(PIO, status_sel == STATUS_TX_LESSTHAN || status_sel == STATUS_RX_LESSTHAN); c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS | PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS)) | ((((uint)status_sel) << PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB) & PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS) | ((status_n << PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB) & PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS); } static uint32_t rp1_clock_get_hz(PIO, enum clock_index clk_index) { const uint32_t MHZ = 1000000; switch (clk_index) { case clk_sys: return 200 * MHZ; default: break; } return PIO_ORIGIN_ANY; } static void rp1_gpio_init(PIO pio, uint gpio) { struct rp1_gpio_init_args args = { .gpio = gpio }; valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); (void)rp1_ioctl(pio, PIO_IOC_GPIO_INIT, &args); } static void rp1_gpio_set_function(PIO pio, uint gpio, enum gpio_function fn) { struct rp1_gpio_set_function_args args = { .gpio = gpio, .fn = fn }; valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_FUNCTION, &args); } static void rp1_gpio_set_pulls(PIO pio, uint gpio, bool up, bool down) { struct rp1_gpio_set_pulls_args args = { .gpio = gpio, .up = up, .down = down }; valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_PULLS, &args); } static void rp1_gpio_set_outover(PIO pio, uint gpio, uint value) { struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_OUTOVER, &args); } static void rp1_gpio_set_inover(PIO pio, uint gpio, uint value) { struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_INOVER, &args); } static void rp1_gpio_set_oeover(PIO pio, uint gpio, uint value) { struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_OEOVER, &args); } static void rp1_gpio_set_input_enabled(PIO pio, uint gpio, bool enabled) { struct rp1_gpio_set_args args = { .gpio = gpio, .value = enabled }; valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_INPUT_ENABLED, &args); } static void rp1_gpio_set_drive_strength(PIO pio, uint gpio, enum gpio_drive_strength drive) { struct rp1_gpio_set_args args = { .gpio = gpio, .value = drive }; valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_DRIVE_STRENGTH, &args); } static void rp1_pio_gpio_init(PIO pio, uint pin) { valid_params_if(PIO, pin < RP1_PIO_GPIO_COUNT); rp1_gpio_set_function(pio, pin, RP1_GPIO_FUNC_PIO); } static PIO rp1_create_instance(PIO_CHIP_T *chip, uint index) { char pathbuf[20]; RP1_PIO pio = NULL; sprintf(pathbuf, "/dev/pio%u", index); if (access(pathbuf, F_OK) != 0) return NULL; pio = calloc(1, sizeof(*pio)); if (!pio) return PIO_ERR(-ENOMEM); pio->base.chip = chip; pio->fd = -1; pio->devname = strdup(pathbuf); rp1_pio_clear_instruction_memory(&pio->base); return &pio->base; } static int rp1_open_instance(PIO pio) { RP1_PIO rp = (RP1_PIO)pio; int fd; fd = open(rp->devname, O_RDWR, O_CLOEXEC); if (fd < 0) return -errno; rp->fd = fd; return 0; } static void rp1_close_instance(PIO pio) { RP1_PIO rp = (RP1_PIO)pio; close(rp->fd); } DECLARE_PIO_CHIP(rp1) { .name = "rp1", .compatible = "raspberrypi,rp1-pio", .instr_count = RP1_PIO_INSTRUCTION_COUNT, .sm_count = RP1_PIO_SM_COUNT, .fifo_depth = 8, .create_instance = rp1_create_instance, .open_instance = rp1_open_instance, .close_instance = rp1_close_instance, .pio_sm_config_xfer = rp1_pio_sm_config_xfer, .pio_sm_xfer_data = rp1_pio_sm_xfer_data, .pio_can_add_program_at_offset = rp1_pio_can_add_program_at_offset, .pio_add_program_at_offset = rp1_pio_add_program_at_offset, .pio_remove_program = rp1_pio_remove_program, .pio_clear_instruction_memory = rp1_pio_clear_instruction_memory, .pio_encode_delay = rp1_pio_encode_delay, .pio_encode_sideset = rp1_pio_encode_sideset, .pio_encode_sideset_opt = rp1_pio_encode_sideset_opt, .pio_encode_jmp = rp1_pio_encode_jmp, .pio_encode_jmp_not_x = rp1_pio_encode_jmp_not_x, .pio_encode_jmp_x_dec = rp1_pio_encode_jmp_x_dec, .pio_encode_jmp_not_y = rp1_pio_encode_jmp_not_y, .pio_encode_jmp_y_dec = rp1_pio_encode_jmp_y_dec, .pio_encode_jmp_x_ne_y = rp1_pio_encode_jmp_x_ne_y, .pio_encode_jmp_pin = rp1_pio_encode_jmp_pin, .pio_encode_jmp_not_osre = rp1_pio_encode_jmp_not_osre, .pio_encode_wait_gpio = rp1_pio_encode_wait_gpio, .pio_encode_wait_pin = rp1_pio_encode_wait_pin, .pio_encode_wait_irq = rp1_pio_encode_wait_irq, .pio_encode_in = rp1_pio_encode_in, .pio_encode_out = rp1_pio_encode_out, .pio_encode_push = rp1_pio_encode_push, .pio_encode_pull = rp1_pio_encode_pull, .pio_encode_mov = rp1_pio_encode_mov, .pio_encode_mov_not = rp1_pio_encode_mov_not, .pio_encode_mov_reverse = rp1_pio_encode_mov_reverse, .pio_encode_irq_set = rp1_pio_encode_irq_set, .pio_encode_irq_wait = rp1_pio_encode_irq_wait, .pio_encode_irq_clear = rp1_pio_encode_irq_clear, .pio_encode_set = rp1_pio_encode_set, .pio_encode_nop = rp1_pio_encode_nop, .pio_sm_claim = rp1_pio_sm_claim, .pio_sm_claim_mask = rp1_pio_sm_claim_mask, .pio_sm_claim_unused = rp1_pio_sm_claim_unused, .pio_sm_unclaim = rp1_pio_sm_unclaim, .pio_sm_is_claimed = rp1_pio_sm_is_claimed, .pio_sm_init = rp1_pio_sm_init, .pio_sm_set_config = rp1_pio_sm_set_config, .pio_sm_exec = rp1_pio_sm_exec, .pio_sm_clear_fifos = rp1_pio_sm_clear_fifos, .pio_sm_set_clkdiv_int_frac = &rp1_pio_sm_set_clkdiv_int_frac, .pio_sm_set_clkdiv = rp1_pio_sm_set_clkdiv, .pio_sm_set_pins = rp1_pio_sm_set_pins, .pio_sm_set_pins_with_mask = rp1_pio_sm_set_pins_with_mask, .pio_sm_set_pindirs_with_mask = rp1_pio_sm_set_pindirs_with_mask, .pio_sm_set_consecutive_pindirs = rp1_pio_sm_set_consecutive_pindirs, .pio_sm_set_enabled = rp1_pio_sm_set_enabled, .pio_sm_set_enabled_mask = rp1_pio_sm_set_enabled_mask, .pio_sm_restart = rp1_pio_sm_restart, .pio_sm_restart_mask = rp1_pio_sm_restart_mask, .pio_sm_clkdiv_restart = rp1_pio_sm_clkdiv_restart, .pio_sm_clkdiv_restart_mask = rp1_pio_sm_clkdiv_restart_mask, .pio_sm_enable_sync = rp1_pio_sm_enable_sync, .pio_sm_put = rp1_pio_sm_put, .pio_sm_get = rp1_pio_sm_get, .pio_sm_set_dmactrl = rp1_pio_sm_set_dmactrl, .pio_sm_is_rx_fifo_empty = rp1_pio_sm_is_rx_fifo_empty, .pio_sm_is_rx_fifo_full = rp1_pio_sm_is_rx_fifo_full, .pio_sm_get_rx_fifo_level = rp1_pio_sm_get_rx_fifo_level, .pio_sm_is_tx_fifo_empty = rp1_pio_sm_is_tx_fifo_empty, .pio_sm_is_tx_fifo_full = rp1_pio_sm_is_tx_fifo_full, .pio_sm_get_tx_fifo_level = rp1_pio_sm_get_tx_fifo_level, .pio_sm_drain_tx_fifo = rp1_pio_sm_drain_tx_fifo, .pio_get_default_sm_config = rp1_pio_get_default_sm_config, .smc_set_out_pins = rp1_smc_set_out_pins, .smc_set_set_pins = rp1_smc_set_set_pins, .smc_set_in_pins = rp1_smc_set_in_pins, .smc_set_sideset_pins = rp1_smc_set_sideset_pins, .smc_set_sideset = rp1_smc_set_sideset, .smc_set_clkdiv_int_frac = rp1_smc_set_clkdiv_int_frac, .smc_set_clkdiv = rp1_smc_set_clkdiv, .smc_set_wrap = rp1_smc_set_wrap, .smc_set_jmp_pin = rp1_smc_set_jmp_pin, .smc_set_in_shift = rp1_smc_set_in_shift, .smc_set_out_shift = rp1_smc_set_out_shift, .smc_set_fifo_join = rp1_smc_set_fifo_join, .smc_set_out_special = rp1_smc_set_out_special, .smc_set_mov_status = rp1_smc_set_mov_status, .clock_get_hz = rp1_clock_get_hz, .pio_gpio_init = rp1_pio_gpio_init, .gpio_init = rp1_gpio_init, .gpio_set_function = rp1_gpio_set_function, .gpio_set_pulls = rp1_gpio_set_pulls, .gpio_set_outover = rp1_gpio_set_outover, .gpio_set_inover = rp1_gpio_set_inover, .gpio_set_oeover = rp1_gpio_set_oeover, .gpio_set_input_enabled = rp1_gpio_set_input_enabled, .gpio_set_drive_strength = rp1_gpio_set_drive_strength, }; raspi-utils-20250514/piolib/piolib.c000066400000000000000000000066411501106437300171410ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2023-25 Raspberry Pi Ltd. * All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include "piolib.h" #include "piolib_priv.h" #define PIO_MAX_INSTANCES 4 static __thread PIO __pio; static PIO pio_instances[PIO_MAX_INSTANCES]; static uint num_instances; static pthread_mutex_t pio_handle_lock; void pio_select(PIO pio) { __pio = pio; } PIO pio_get_current(void) { PIO pio = __pio; check_pio_param(pio); return pio; } int pio_get_index(PIO pio) { int i; for (i = 0; i < PIO_MAX_INSTANCES; i++) { if (pio == pio_instances[i]) return i; } return -1; } int pio_init(void) { #if LIBRARY_BUILD const PIO_CHIP_T *const *start = &library_piochips[0]; const PIO_CHIP_T *const *end = &library_piochips[0] + library_piochips_count; #else const PIO_CHIP_T *const *start = &__start_piochips; const PIO_CHIP_T *const *end = &__stop_piochips; #endif static bool initialised; const PIO_CHIP_T * const *p; uint i = 0; int err; if (initialised) return 0; num_instances = 0; p = start; while (p < end) { PIO_CHIP_T *chip = *p; PIO pio = chip->create_instance(chip, i); if (pio && !PIO_IS_ERR(pio)) { pio_instances[num_instances++] = pio; i++; } else { p++; i = 0; } } err = pthread_mutex_init(&pio_handle_lock, NULL); if (err) return err; initialised = true; return 0; } PIO pio_open(uint idx) { PIO pio = NULL; int err; err = pio_init(); if (err) return PIO_ERR(err); if (idx >= num_instances) return PIO_ERR(-EINVAL); pthread_mutex_lock(&pio_handle_lock); pio = pio_instances[idx]; if (pio) { if (pio->in_use) err = -EBUSY; else pio->in_use = 1; } pthread_mutex_unlock(&pio_handle_lock); if (err) return PIO_ERR(err); err = pio->chip->open_instance(pio); if (err) { pio->in_use = 0; return PIO_ERR(err); } pio_select(pio); return pio; } PIO pio_open_by_name(const char *name) { int err = -ENOENT; uint i; err = pio_init(); if (err) return PIO_ERR(err); for (i = 0; i < num_instances; i++) { PIO p = pio_instances[i]; if (!strcmp(name, p->chip->name)) break; } if (i == num_instances) return PIO_ERR(-ENOENT); return pio_open(i); } PIO pio_open_helper(uint idx) { PIO pio = pio_instances[idx]; if (!pio || !pio->in_use) { pio = pio_open(idx); if (PIO_IS_ERR(pio)) { printf("* Failed to open PIO device %d (error %d)\n", idx, PIO_ERR_VAL(pio)); exit(1); } } return pio; } void pio_close(PIO pio) { pio->chip->close_instance(pio); pthread_mutex_lock(&pio_handle_lock); pio->in_use = 0; pthread_mutex_unlock(&pio_handle_lock); } void pio_panic(const char *msg) { fprintf(stderr, "PANIC: %s\n", msg); exit(1); } void sleep_us(uint64_t us) { const struct timespec tv = { .tv_sec = (us / 1000000), .tv_nsec = 1000ull * (us % 1000000) }; nanosleep(&tv, NULL); } raspi-utils-20250514/raspinfo/000077500000000000000000000000001501106437300160535ustar00rootroot00000000000000raspi-utils-20250514/raspinfo/CMakeLists.txt000066400000000000000000000002731501106437300206150ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) #set project name project(raspinfo) #add executables install(PROGRAMS raspinfo DESTINATION ${CMAKE_INSTALL_BINDIR}) raspi-utils-20250514/raspinfo/README.md000066400000000000000000000001351501106437300173310ustar00rootroot00000000000000# raspinfo Short script to dump information about the Pi. Use it for annotating bug reports. raspi-utils-20250514/raspinfo/raspinfo000077500000000000000000000134601501106437300176260ustar00rootroot00000000000000#!/bin/bash # Some of the regex's used in sed # Catch basic IP6 address "s/\([0-9a-fA-F]\{1,4\}:\)\{7,7\}[0-9a-fA-F]\{1,4\}/y.y.y.y.y.y.y.y/g" # Catch y::y.y.y.y "s/[0-9a-fA-F]\{1,4\}:\(:[0-9a-fA-F]\{1,4\}\)\{1,4\}/y::y.y.y.y/g" # IP4 d.d.d.d decimal "s/\([0-9]\{1,3\}\.\)\{3,3\}[0-9]\{1,3\}/x.x.x.x/g" # mac address "s/\([0-9a-fA-F]\{2,2\}\:\)\{5,5\}[0-9a-fA-F]\{2,2\}/m.m.m.m/g" display_info_drm() { # If running X then can use xrandr, otherwise # dump the /sys/class entries for the displays if command -v xrandr > /dev/null && DISPLAY=${DISPLAY:-:0} xrandr --listmonitors &>/dev/null; then echo "Running (F)KMS and X" echo DISPLAY=${DISPLAY:-:0} xrandr --verbose else echo "Running (F)KMS, console" echo for card in /sys/class/drm/card[0-9]-*; do echo $card # if kmsprint isn't installed print basic mode info if ! command -v kmsprint > /dev/null; then if [ -f $card/modes ]; then cat $card/modes else echo "No modes found" fi fi if [ -f $card/edid ]; then base64 $card/edid else echo "No EDID found" fi echo done fi # kmsprint is more useful, but not always installed echo if command -v kmsprint > /dev/null; then kmsprint echo kmsprint -m else echo "kmsprint is not installed. Install with: sudo apt install kms++-utils" fi echo # dump the /sys/class entries for the displays cardfound=0 for card in `seq 0 9`; do if sudo test -f "/sys/kernel/debug/dri/${card}/state"; then for hdmi in 0 1; do if sudo test -f "/sys/kernel/debug/dri/${card}/hdmi${hdmi}_regs"; then echo "HDMI${hdmi}: $(sudo cat /sys/kernel/debug/dri/$card/hdmi${hdmi}_regs | grep HOTPLUG)" fi done echo echo "/sys/kernel/debug/dri/$card/state:" sudo cat "/sys/kernel/debug/dri/$card/state" echo cardfound=1 fi done if [ "$cardfound" == "0" ]; then echo "kms state not found" fi echo } display_info_legacy() { # Legacy mode echo "Running Legacy framebuffer" echo for card in `seq 0 9`; do F="/dev/fb${card}" if test -e $F; then echo Framebuffer: $F fbset -s -fb $F fi done disps=`tvservice -l | awk '/Display Number/{print substr($3,1,1)}'` tmp=$(mktemp) for display in $disps do echo echo "Display: " $display tvservice -v $display -s tvservice -v $display -n tvservice -v $display -m CEA tvservice -v $display -m DMT echo tvservice -v $display -d $tmp > /dev/null base64 $tmp done rm $tmp } display_info() { # Check if we are running a KMS/DRM system if [ -d "/dev/dri" ]; then display_info_drm else display_info_legacy fi } audio_info() { aplay -l echo aplay -L echo systemctl --user status pipewire.socket pipewire.service pulseaudio.service pulseaudio.socket echo if command -v pactl > /dev/null; then pactl info else echo pactl not installed fi } OUT=raspinfo.txt rm -f $OUT exec > >(tee -ia $OUT) echo "System Information" echo "------------------" echo cat /sys/firmware/devicetree/base/model | sed 's/\x0//g' echo cat /etc/os-release | head -4 echo cat /etc/rpi-issue echo uname -a cat /proc/cpuinfo | tail -3 echo "Throttled flag : "`vcgencmd get_throttled` echo "Camera : "`vcgencmd get_camera` echo echo "Videocore information" echo "---------------------" echo vcgencmd version echo vcgencmd mem_reloc_stats echo echo "Filesystem information" echo "----------------------" df echo cat /proc/swaps echo echo "Package version information" echo "---------------------------" apt-cache policy raspberrypi-ui-mods | head -2 apt-cache policy raspberrypi-sys-mods | head -2 apt-cache policy openbox | head -2 apt-cache policy lxpanel | head -2 apt-cache policy pcmanfm | head -2 apt-cache policy rpd-plym-splash | head -2 echo echo "Networking Information" echo "----------------------" echo ifconfig | sed -e "s/\([0-9a-fA-F]\{1,4\}:\)\{7,7\}[0-9a-fA-F]\{1,4\}/y.y.y.y.y.y.y.y/g" | sed -e "s/[0-9a-fA-F]\{1,4\}:\(:[0-9a-fA-F]\{1,4\}\)\{1,4\}/y::y.y.y.y/g" | sed -e "s/\([0-9]\{1,3\}\.\)\{3,3\}[0-9]\{1,3\}/x.x.x.x/g" | sed -e "s/\([0-9a-fA-F]\{2,2\}\:\)\{5,5\}[0-9a-fA-F]\{2,2\}/m.m.m.m/g" echo echo "USB Information" echo "---------------" echo lsusb -t echo echo "Display Information" echo "-------------------" echo display_info echo echo "Audio Information" echo "-------------------" echo audio_info echo echo "config.txt" echo "----------" echo #cat /boot/config.txt | egrep -v "^\s*(#|^$)" vcgencmd get_config int vcgencmd get_config str echo echo "cmdline.txt" echo "-----------" cat /proc/cmdline echo echo "pin configuration" echo "-----------------" echo if command -v pinctrl > /dev/null; then sudo pinctrl 2>&1 elif command -v raspi-gpio > /dev/null; then raspi-gpio get 2>&1 else echo "pinctrl/raspi-gpio not found" fi echo echo "vcdbg log messages" echo "------------------" echo if command -v vcdbg > /dev/null; then sudo vcdbg log msg 2>&1 elif command -v vclog > /dev/null; then sudo vclog --msg 2>&1 else echo "vcdbg not found" fi echo echo "dmesg log" echo "---------" echo sudo dmesg | sed -e "s/\([0-9a-fA-F]\{1,4\}:\)\{7,7\}[0-9a-fA-F]\{1,4\}/y.y.y.y.y.y.y.y/g" | sed -e "s/[0-9a-fA-F]\{1,4\}:\(:[0-9a-fA-F]\{1,4\}\)\{1,4\}/y::y.y.y.y/g" | sed -e "s/\([0-9a-fA-F]\{2,2\}\:\)\{5,5\}[0-9a-fA-F]\{2,2\}/m.m.m.m/g" if grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]1[13457][0-9a-fA-F]$" /proc/cpuinfo then echo echo "EEPROM" echo "------" echo sudo rpi-eeprom-update fi raspi-utils-20250514/vcgencmd/000077500000000000000000000000001501106437300160205ustar00rootroot00000000000000raspi-utils-20250514/vcgencmd/CMakeLists.txt000066400000000000000000000006271501106437300205650ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) #set project name project(vcgencmd) add_executable(vcgencmd vcgencmd.c) install(TARGETS vcgencmd RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES vcgencmd.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install(FILES vcgencmd-completion.bash RENAME vcgencmd DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/bash-completion/completions") raspi-utils-20250514/vcgencmd/vcgencmd-completion.bash000066400000000000000000000004311501106437300226120ustar00rootroot00000000000000_vcgencmd() { local cur prev words cword split _init_completion -s || return if ! ((cword == 1)); then return fi local cmds=$(vcgencmd commands | sed 's/^.*=//;s/[",]//g') COMPREPLY+=($(compgen -W "$cmds" -- $cur)) } complete -F _vcgencmd vcgencmd raspi-utils-20250514/vcgencmd/vcgencmd.1000066400000000000000000000142301501106437300176700ustar00rootroot00000000000000'\" t .TH VCGENCMD 1 . .SH NAME vcgencmd \- query the VideoCore for information . . .SH SYNOPSIS .SY vcgencmd .IR command \ [ params ] .YS . . .SH DESCRIPTION .B vcgencmd is a command line utility that can get various pieces of information from the VideoCore GPU on the Raspberry Pi. . . .SH COMMANDS To get a list of all the commands that .B vcgencmd supports, type .IR "vcgencmd\ commands" . Some of the more useful commands are described below. . .TP .BI vcos \ sub-command The .B vcos command has a number of sub-commands: .RS .TP .B version Displays the build date and version of the firmware on the VideoCore. .TP .B log status Displays the error log status of the various VideoCore software areas. .RE . .TP .B version Displays the build date and version of the firmware on the VideoCore. . .TP .B get_camera Displays the enabled and detected state of the official camera. 1 means yes, 0 means no. Whilst all firmware (except cutdown versions) will support the camera, this support needs to be enabled by using the .I start_x boot option .BR [BOOT] . . .TP .B get_throttled Returns the throttled state of the system. This is a bit pattern - a bit being set indicates the following meanings: .TS tab(|); l l . Bit|Meaning \_|\_ .T& n l . 0|Under-voltage detected 1|Arm frequency capped 2|Currently throttled 3|Soft temperature limit active 16|Under-voltage has occurred 17|Arm frequency capping has occurred 18|Throttling has occurred 19|Soft temperature limit has occurred .TE .IP A value of zero indicates that none of the above conditions is true. .IP To find if one of these bits has been set, convert the value returned to binary, then number each bit along the top. You can then see which bits are set. For example: .IP .EX 0x50000 = 0101 0000 0000 0000 0000 .EE .IP Adding the bit numbers along the top we get: .TS tab( ); n n n n n n n n n n n n n n n n n n n n . 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .TE .IP From this we can see that bits 18 and 16 are set, indicating that the Pi has previously been throttled due to under-voltage, but is not currently throttled for any reason. . .TP .B measure_temp Returns the temperature of the SoC as measured by the on-board temperature sensor. . .TP .BI measure_clock \ clock This returns the current frequency of the specified clock. The options are: .TS tab(|); l l . Clock|Description \_|\_ arm|ARM cores core|VC4 scaler cores h264|H.264 block isp|Image Signal Processor v3d|3D block uart|UART pwm|PWM block (analog audio output) emmc|SD card interface pixel|Pixel valve vec|Analog video encoder hdmi|HDMI dpi|Display Peripheral Interface .TE .IP For example, .IR "vcgencmd measure_clock arm" . . .TP .BI measure_volts \ block Displays the current voltages used by the specific block. .TS tab(|); l l . Block|Description \_|\_ core|VC4 core voltage sdram_c| sdram_i| sdram_p| .TE . .TP .B otp_dump Displays the content of the One Time Programmable (OTP) memory, which is part of the SoC. These are 32 bit values, indexed from 8 to 64. See the .BR raspi-otp (7) for more details. . .TP .BI get_mem \ type Reports on the amount of memory allocated to the ARM cores with .I vcgencmd get_mem arm or the VC4 with .IR "vcgencmd get_mem gpu" . .IP .B Note: On a Raspberry Pi 4 with greater than 1GB of RAM, the .I arm option is inaccurate. This is because the GPU firmware which implements this command is only aware of the first gigabyte of RAM on the system, so the .I arm setting will always return 1GB minus the .I gpu memory value. To get an accurate report of the amount of ARM memory, use one of the standard Linux commands, such as .I free or .IR "cat /proc/meminfo" . . .TP .BI codec_enabled \ type Reports whether the specified CODEC type is enabled. Possible options for type are AGIF, FLAC, H263, H264, MJPA, MJPB, MJPG, MPG2, MPG4, MVC0, PCM, THRA, VORB, VP6, VP8, WMV9, WVC1. .IP MPG2, WMV9, and WVC1 currently require a paid for licence (see the .B [FAQ] for more info), except on the Pi4, where these hardware codecs are disabled in preference to software decoding, which requires no licence. Note that because the H265 hardware block on the Raspberry Pi4 is not part of the VideoCore GPU, its status is not accessed via this command. . .TP .BI get_config \ type|name This returns all the configuration items of the specified type that have been set in config.txt, or a single configuration item. Possible values for type parameter are .IR int ", " str ", " or simply use the name of the configuration item. . .TP .B get_lcd_info Displays the resolution and colour depth of any attached display. . .TP .B mem_oom Displays statistics on any Out Of Memory events occuring in the VC4 memory space. . .TP .B mem_reloc_stats Displays statistics from the relocatable memory allocator on the VC4. . .TP .B read_ring_osc Returns the curent speed voltage and temperature of the ring oscillator. . .TP .B hdmi_timings Displays the current HDMI settings timings. See .B [VIDEO] for details of the values returned. . .TP .B dispmanx_list Dump a list of all dispmanx items currently being displayed. . .TP .BI display_power \ 0|1|-1 .TQ .BI display_power " 0|1|-1 display" Show current display power state, or set the display power state. .I vcgencmd display_power 0 will turn off power to the current display. .I vcgencmd display_power 1 will turn on power to the display. If no parameter is set, this will display the current power state. The final parameter is an optional display ID, as returned by .I tvservice -l or from the table below, which allows a specific display to be turned on or off. .IP .I vcgencmd display_power 0 7 will turn off power to display ID 7, which is HDMI 1 on a Raspberry Pi 4. .TS tab(|); l l . Display|ID \_|\_ .T& l n . Main LCD|0 Secondary LCD|1 HDMI 0|2 Composite|3 HDMI 1|7 .TE .IP To determine if a specific display ID is on or off, use -1 as the first parameter. .IP .I vcgencmd display_power -1 7 will return 0 if display ID 7 is off, 1 if display ID 7 is on, or -1 if display ID 7 is in an unknown state, for example undetected. . . .SH EXIT STATUS . .IP 0 Command completed successfully .IP -1 Problem with VHCI .IP -2 VideoCore returned an error . . .SH SEE ALSO .B [DOCS] https://www.raspberrypi.com/documentation/computers/os.html#vcgencmd . raspi-utils-20250514/vcgencmd/vcgencmd.c000066400000000000000000000113221501106437300177510ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * 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. */ /* ---- Include Files ---------------------------------------------------- */ #include #include #include #include #include #include #include #include /* ioctl */ #define DEVICE_FILE_NAME "/dev/vcio" #define MAJOR_NUM 100 #define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) #define MAX_STRING 1024 /* * use ioctl to send mbox property message */ static int mbox_property(int file_desc, void *buf) { int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf); if (ret_val < 0) { printf("ioctl_set_msg failed:%d\n", ret_val); } return ret_val; } static int mbox_open() { int file_desc; // open a char device file used for communicating with kernel mbox driver file_desc = open(DEVICE_FILE_NAME, 0); if (file_desc < 0) { printf("Can't open device file: %s\n", DEVICE_FILE_NAME); printf("Try creating a device file with: sudo mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM); exit(-1); } return file_desc; } static void mbox_close(int file_desc) { close(file_desc); } #define GET_GENCMD_RESULT 0x00030080 static unsigned gencmd(int file_desc, const char *command, char *result, int result_len) { int i=0; unsigned p[(MAX_STRING>>2) + 7]; int len = strlen(command); // maximum length for command or response if (len + 1 >= MAX_STRING) { fprintf(stderr, "gencmd length too long : %d\n", len); return -1; } p[i++] = 0; // size p[i++] = 0x00000000; // process request p[i++] = GET_GENCMD_RESULT; // (the tag id) p[i++] = MAX_STRING;// buffer_len p[i++] = 0; // request_len (set to response length) p[i++] = 0; // error repsonse memcpy(p+i, command, len + 1); i += MAX_STRING >> 2; p[i++] = 0x00000000; // end tag p[0] = i*sizeof *p; // actual size mbox_property(file_desc, p); result[0] = 0; strncat(result, (const char *)(p+6), result_len); return p[5]; } static void show_usage() { puts( "Usage: vcgencmd command [ params ]" ); puts( "Send a command to the VideoCore and print the result.\n" ); puts( "Without any argument this information is shown.\n" ); puts( "Use the command 'vcgencmd commands' to get a list of available commands\n" ); puts( "Exit status:" ); puts( " 0 command completed successfully" ); puts( " else VideoCore returned an error\n" ); puts( "For further documentation please see" ); puts( "https://www.raspberrypi.com/documentation/computers/os.html#vcgencmd\n" ); } int main(int argc, char *argv[]) { int mb = mbox_open(); int i; char command[MAX_STRING] = {}; char result[MAX_STRING] = {}; if ( argc == 1 ) { // no arguments passed, so show basic usage show_usage(); return 0; } for (i = 1; i < argc; i++) { char *c = command + strlen(command); if (c > command) { strncat(c, " ", command + sizeof command - c); c = command + strlen(command); } strncat(c, argv[i], command + sizeof command - c); } int ret = gencmd(mb, command, result, sizeof result); if (ret) printf( "vc_gencmd_read_response returned %d\n", ret ); mbox_close(mb); printf("%s\n", result); return ret; } raspi-utils-20250514/vclog/000077500000000000000000000000001501106437300153445ustar00rootroot00000000000000raspi-utils-20250514/vclog/CMakeLists.txt000066400000000000000000000004101501106437300200770ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -pedantic") #set project name project(vclog) #add executables add_executable(vclog vclog.c) install(TARGETS vclog RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) raspi-utils-20250514/vclog/README.md000066400000000000000000000027451501106437300166330ustar00rootroot00000000000000 # vclog Tool to fetch VideoCore logs ( assert or msg ) and display them on the terminal. The addresses where logs can be found are fetched from the device tree and then using headers found inside that region of memory, the code accesses the log type of interest and parses it. **Build Instructions** Install cmake with "sudo apt install cmake" - you need at least version 3.10. After cloning this repo 'git clone https://github.com/raspberrypi/utils' it is advised to create a binary folder seperate from the source ie 'build' for where CMake can generate the build pipeline: - *mkdir build* - *cd build* - *cmake ..* - *make* **Usage** * sudo ./vclog [-f] <-m|-a> * sudo ./vclog [--follow] <--msg|--assert> **Notes** * Structs describing how VC arranges it's data within the regions of interest must keep the same size values. Since the VC might have a different memory model from the host (32-bit vs 64-bit) structs values need to be of the same size. * memcpy load pair on aarch64 requires the src address to be aligned with 8 bit boundaries but also at (src + size of memory to copy). memcpy was used so that we could capture the current state of the logs for parsing, as logs are written to a circular buffer. * Logs are written to a circular buffer - so VideoCore keeps track of: A) Oldest message (first message user wants to read) which gets wiped if circular buffer wraps around back to start B) Next region in circular buffer where VideoCore will write its next message raspi-utils-20250514/vclog/vclog.c000066400000000000000000000337071501106437300166340ustar00rootroot00000000000000/******************************************************************************* Summary: Command line program for printing VideoCore log messages or assertion logs messages Licensing: Copyright (c) 2022-2023, Raspberry Pi Ltd. All rights reserved. *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum { LOG_UNKNOWN, LOG_MSG, LOG_ASSERT, }; typedef struct { uint32_t padding[8]; uint32_t assert_type; uint32_t assert_addr; uint32_t msg_type; uint32_t msg_addr; uint32_t task_type; uint32_t task_addr; } log_toc_t; typedef struct { uint32_t id; uint32_t start; // circular buffer start for this type of message uint32_t end; // circular buffer end uint32_t write; // where VC will write the next message uint32_t read; // the oldest valid message } log_hdr_t; typedef struct { uint32_t time; // time log produced in millseconds uint16_t seq_num; // if two entries have same timestamp then // this seq num differentiates them uint16_t size; // size of entire log entry = this header + payload } msg_hdr_t; struct fb_dmacopy { void *dst; uint32_t src; uint32_t length; }; enum { LOG_ID = 0x564c4f47, MSG_ID = 0x5353454d, TASK_ID = 0x3b534154, ASSERT_ID = 0x54525341, }; #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #define ARRAY_SIZE(_a) (sizeof(_a)/sizeof(_a[0])) #define FBIODMACOPY _IOW('z', 0x22, struct fb_dmacopy) #ifdef DEBUG #define DLOG(...) printf(__VA_ARGS__) #else #define DLOG(...) (void)0 #endif static int log_type = LOG_UNKNOWN; static int help = 0; static int follow_mode = 0; static const struct option long_options[] = { { "a", no_argument, &log_type, LOG_ASSERT }, { "assert", no_argument, &log_type, LOG_ASSERT }, { "m", no_argument, &log_type, LOG_MSG }, { "msg", no_argument, &log_type, LOG_MSG }, { "f", no_argument, &follow_mode, 1 }, { "follow", no_argument, &follow_mode, 1 }, { "h", no_argument, &help, 1 }, { "help", no_argument, &help, 1 }, { 0 } }; static uint32_t vc_map_base; static uint32_t vc_map_end; static int dma_fd = -1; static char *vc_map = MAP_FAILED; static bool find_logs(uint32_t *logs_start, uint32_t *logs_size); static bool prepare_vc_mapping(uint32_t vc_start, uint32_t vc_size); static void destroy_vc_mapping(void); static void read_vc_mem(uint32_t vc_addr, uint32_t size, void *dest); static uint32_t log_copy_wrap(const char *log_buffer, uint32_t log_size, uint32_t offset, uint32_t len, char *dest); static void die(const char *msg); int32_t main(int32_t argc, char *argv[]) { const struct timespec delay_10ms = {0, 10000000}; const struct timespec delay_1ms = {0, 1000000}; uint32_t logs_start_vc = 0; uint32_t logs_size = 0; char *payload_buffer = NULL; uint32_t payload_buffer_size = 0; uint32_t log_id; uint32_t log_addr; uint32_t log_size; char *log_buffer; log_toc_t toc; log_hdr_t log_hdr; uint32_t read_pos; uint32_t write_pos; uint32_t next_pos = 0; int corrupt_retries = 0; while (getopt_long_only(argc, argv, "", long_options, NULL) != -1) continue; if (help || log_type == LOG_UNKNOWN) { fprintf(stderr, "Usage:\n\t%s [-f] <-m|-a>\n\t%s [--follow] <--msg|--assert>\n", argv[0], argv[0]); return EXIT_FAILURE; } // find the address and size of the logs in VC memory if (!find_logs(&logs_start_vc, &logs_size)) die("Could not determine logs location from Device Tree"); if (!prepare_vc_mapping(logs_start_vc, logs_size)) die("Cannot access logs"); read_vc_mem(logs_start_vc, sizeof(toc), &toc); switch (log_type) { case LOG_ASSERT: log_addr = toc.assert_addr; log_id = ASSERT_ID; break; case LOG_MSG: log_addr = toc.msg_addr; log_id = MSG_ID; break; default: die("Invalid type"); } read_vc_mem(log_addr, sizeof(log_hdr), &log_hdr); if (log_hdr.id != log_id) die("Log ID incorrect"); log_size = log_hdr.end - log_hdr.start; log_buffer = malloc(log_size); if (!log_buffer) { fprintf(stderr, "Failed to allocate buffer for VC msgs\n"); goto cleanup; } read_vc_mem(log_hdr.start, log_size, log_buffer); read_pos = log_hdr.read - log_hdr.start; write_pos = log_hdr.write - log_hdr.start; if (read_pos >= log_size || write_pos >= log_size) die("Log pointers out of range"); while (1) { uint32_t new_write_pos; int new_data; // Print logs between read_pos and write_pos while (read_pos != write_pos) { msg_hdr_t msg; uint32_t payload_pos; uint32_t payload_len; msg.size = 0; payload_pos = log_copy_wrap(log_buffer, log_size, read_pos, sizeof(msg), (char *)&msg); payload_len = msg.size - sizeof(msg_hdr_t); if (payload_len > log_size) { DLOG("log corrupt 1\n"); corrupt_retries++; break; } if (payload_len > payload_buffer_size) { payload_buffer_size = MAX(payload_len, 100); // skip some churn payload_buffer = realloc(payload_buffer, payload_buffer_size); if (!payload_buffer) die("Out of memory"); } next_pos = log_copy_wrap(log_buffer, log_size, payload_pos, payload_len, payload_buffer); // Check read_pos hasn't overtaken write_pos if (next_pos > read_pos) { // Normal if (read_pos < write_pos && next_pos > write_pos) { DLOG("log corrupt 2\n"); corrupt_retries++; break; } } else { // This message wrapped if (read_pos < write_pos || next_pos > write_pos) { DLOG("log corrupt 3\n"); corrupt_retries++; break; } } if (log_type == LOG_MSG) { DLOG("%08x: ", read_pos); printf("%06i.%03i: %.*s\n", msg.time / 1000, msg.time % 1000, payload_len - 4, payload_buffer + 4); } else if (log_type == LOG_ASSERT) { size_t filename_len = strnlen(payload_buffer, payload_len); uint32_t cond_offset; uint32_t line_number; cond_offset = filename_len +1 + 4; if ((cond_offset + 1) >= payload_len) { DLOG("log corrupt 4\n"); corrupt_retries++; break; } memcpy(&line_number, payload_buffer + filename_len + 1, 4); printf("%06i.%03i: assert( %.*s ) failed; %s line %d\n", msg.time / 1000, msg.time % 1000, payload_len = cond_offset, payload_buffer + cond_offset, payload_buffer, line_number); printf("----------------\n"); } corrupt_retries = 0; read_pos = next_pos; } if (corrupt_retries) { // Apparent corruption is likely to be due to the effects of caches. // Rewind to re-copy the affected area a number of times before // giving up. if (corrupt_retries == 10) { DLOG("==== read %x, write %x, next %x, size %x ====\n", read_pos, write_pos, next_pos, log_size); die("Log corrupt"); } write_pos = read_pos; nanosleep(&delay_10ms, NULL); } if (!follow_mode) break; while (1) { // Refresh view of the log pointers read_vc_mem(log_addr, sizeof(log_hdr), &log_hdr); new_write_pos = log_hdr.write - log_hdr.start; if (new_write_pos != write_pos) break; nanosleep(&delay_1ms, NULL); } // More messages added new_data = new_write_pos - write_pos; if (new_data > 0) { read_vc_mem(log_hdr.start + write_pos, new_data, log_buffer + write_pos); } else { // The new data wraps around the end of the buffer read_vc_mem(log_hdr.start + write_pos, log_size - write_pos, log_buffer + write_pos); read_vc_mem(log_hdr.start, new_write_pos, log_buffer); } write_pos = new_write_pos; } free(payload_buffer); free(log_buffer); cleanup: destroy_vc_mapping(); return EXIT_SUCCESS; } static bool find_logs(uint32_t *logs_start, uint32_t *logs_size) { const char *const filename = "/proc/device-tree/chosen/log"; uint32_t vals[2]; FILE *fp; bool ret = false; if (!logs_start || !logs_size) goto exit; // VideoCore logs start and size can be found in the Device Tree fp = fopen(filename, "rb"); if (!fp) goto exit; if (fread(vals, sizeof(vals), 1, fp) != 1) goto cleanup; // Device Tree is stored in network order, i.e. big-endian *logs_start = ntohl(vals[0]); *logs_size = ntohl(vals[1]); ret = true; cleanup: fclose(fp); exit: return ret; } static bool prepare_vc_mapping(uint32_t vc_start, uint32_t vc_size) { const char *dma_filenames[] = { "/dev/vc-mem", "/dev/fb0" }; const char *mem_filename = "/dev/mem"; struct fb_dmacopy ioparam; uint32_t id; int err, fd, i; ioparam.dst = &id; ioparam.src = vc_start; ioparam.length = sizeof(id); for (i = 0; i < (int)ARRAY_SIZE(dma_filenames); i++) { if ((fd = open(dma_filenames[i], O_RDWR | O_SYNC)) >= 0) { err = ioctl(fd, FBIODMACOPY, &ioparam); if (err == 0 && id == LOG_ID) { dma_fd = fd; goto success; } close(fd); } } if ((fd = open(mem_filename, O_RDONLY)) >= 0) { long page_size = sysconf(_SC_PAGE_SIZE); /* find start and end addresses aligned down and up to pagesize respectively */ off_t mmap_start = (uintptr_t)vc_start & ~(page_size - 1); off_t mmap_end = ((uintptr_t)vc_start + vc_size + page_size - 1) & ~(page_size -1); vc_map = mmap(NULL, mmap_end - mmap_start, PROT_READ, MAP_PRIVATE, fd, (uintptr_t)mmap_start); close(fd); if (vc_map != MAP_FAILED) { id = *(uint32_t *)(vc_map + vc_start - mmap_start); if (id == LOG_ID) { vc_start = mmap_start; vc_size = mmap_end - mmap_start; goto success; } } } fprintf(stderr, "Could not map VC memory: %s\n", strerror(errno)); return false; success: vc_map_base = vc_start; vc_map_end = vc_start + vc_size; return true; } static void destroy_vc_mapping(void) { if (dma_fd != -1) { close(dma_fd); dma_fd = -1; } if (vc_map != MAP_FAILED) { munmap(vc_map, vc_map_end - vc_map_base); vc_map = MAP_FAILED; } } /********************************************************************************/ /* Note: gcc with -O2 or higher may replace most of this code with memcpy */ /* which causes a bus error when given an insufficiently aligned mmap-ed buffer */ /* Using volatile disables that optimisation */ /********************************************************************************/ static void memcpy_vc_memory(void *restrict dest, const volatile void *restrict src, size_t n) { if ((((uintptr_t)dest | (uintptr_t)src | n) & 3) == 0) { uint32_t *restrict d = (uint32_t *restrict )dest; const volatile uint32_t *restrict s = (const volatile uint32_t *restrict)src; while (n) *d++ = *s++, n -= 4; } else { uint8_t *restrict d = (uint8_t *restrict )dest; const volatile uint8_t *restrict s = (const volatile uint8_t *restrict)src; while (n--) *d++ = *s++; } } static void read_vc_mem(uint32_t vc_addr, uint32_t size, void *dest) { vc_addr &= 0x3fffffff; if (vc_addr < vc_map_base || vc_addr + size > vc_map_end) die("VC access out-of-bounds"); if (dma_fd != -1) { struct fb_dmacopy ioparam; ioparam.dst = dest; ioparam.src = vc_addr; ioparam.length = size; if (ioctl(dma_fd, FBIODMACOPY, &ioparam) != 0) die("Failed to copy VC memory"); } else { memcpy_vc_memory(dest, vc_map + vc_addr - vc_map_base, size); } } static uint32_t log_copy_wrap(const char *log_buffer, uint32_t log_size, uint32_t offset, uint32_t len, char *dest) { if (offset >= log_size) return ~0; if (offset + len < log_size) { memcpy(dest, log_buffer + offset, len); return offset + len; } else { uint32_t first_chunk = log_size - offset; memcpy(dest, log_buffer + offset, first_chunk); len -= first_chunk; if (len) memcpy(dest + first_chunk, log_buffer, len); return len; } } static void die(const char *msg) { fprintf(stderr, "Fatal error: %s\n", msg); exit(EXIT_FAILURE); } raspi-utils-20250514/vcmailbox/000077500000000000000000000000001501106437300162165ustar00rootroot00000000000000raspi-utils-20250514/vcmailbox/CMakeLists.txt000066400000000000000000000006311501106437300207560ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10...3.27) include(GNUInstallDirs) #set project name project(vcmailbox) add_executable(vcmailbox vcmailbox.c) target_link_libraries(vcmailbox) install(TARGETS vcmailbox RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES vcmailbox.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install(FILES vcmailbox.7 raspiotp.7 raspirev.7 DESTINATION ${CMAKE_INSTALL_MANDIR}/man7) raspi-utils-20250514/vcmailbox/raspiotp.7000066400000000000000000000117001501106437300201460ustar00rootroot00000000000000.TH RASPIOTP 7 . .SH NAME raspiotp \- the Raspberry Pi OTP register bits . . .SH DESCRIPTION . All SoCs used by the Raspberry Pi range have a inbuilt One-Time Programmable (OTP) memory block. It is 66 32-bit values long, although only a few locations have factory-programmed data. The .BR vcgencmd (1) utility can be used to display the contents of the OTP like so: .PP .EX $ \fBvcgencmd otp_dump\fR .EE . . .SH OTP REGISTERS This list contains the publicly available information on the registers. If a register or bit is not defined here, then it is not public. . .TP .B 17 bootmode register .RS .TP .B Bit 1 sets the oscillator frequency to 19.2MHz .TP .B Bit 3 enables pull ups on the SDIO pins .TP .B Bit 19 enables GPIO bootmode .TP .B Bit 20 sets the bank to check for GPIO bootmode .TP .B Bit 21 enables booting from SD card .TP .B Bit 22 sets the bank to boot from .TP .B Bit 28 enables USB device booting .TP .B Bit 29 enables USB host booting (ethernet and mass storage) .RE . .TP .B 18 copy of bootmode register . .TP .B 28 serial number . .TP .B 29 ~(serial number) . .TP .B 30 revision code; . .BR raspirev (7) .TP .B 36-43 customer OTP values (see .B INDUSTRIAL USE below) . .TP .B 45 MPG2 decode key . .TP .B 46 WVC1 decode key . .TP .B 64-65 MAC address; if set, system will use this in preference to the automatically generated address based on the serial number . .TP .B 66 advanced boot register .RS .TP .B Bits 0-6 GPIO for ETH_CLK output pin .TP .B Bit 7 enables ETH_CLK output .TP .B Bits 8-14 GPIO for LAN_RUN output pin .TP .B Bit 15 enables LAN_RUN output .TP .B Bit 24 extends USB HUB timeout parameter .TP .B Bit 25 ETH_CLK frequency: 0=25MHz, 1=24MHz .RE . . .SH INDUSTRIAL USE The Raspberry Pi is often used as part of another product. This section describes some extra facilities available to use other capabilities of the Pi. . .SS Customer OTP settings There are a number of OTP values that can be used. To see a list of all the OTP values, you can use: .PP .EX $ \fBvcgencmd otp_dump\fR .EE .PP In register locations 36 to 43 (inclusive), there are eight rows of 32 bits available for the customer (detailed in .B OTP REGISTERS above). .PP To program these bits, you will need to use the vcmailbox. This is a Linux driver interface to the firmware which will handle the programming of the rows. To do this, please refer to the documentation at [MAILBOX], and the .BR vcmailbox (1) example application. For example: .PP .EX $ \fBvcmailbox 0x00010004 8 8 0 0\fR 0x00000020 0x80000000 0x00010004 0x00000008 0x800000008 0xnnnnnnnn 0x00000000 0x00000000 .EE .PP The above uses the .B GET_BOARD_SERIAL tag (detailed under [MAILBOX]) with a request size of 8 bytes and response size of 8 bytes (sending two integers for the request 0, 0). The response to this will be two integers (0x00000020 and 0x80000000) followed by the tag code, the request length, the response length (with the 31st bit set to indicate that it is a response) then the 64 bit serial number (where the MS 32bits are always 0). .PP To set the customer OTP values you will need to use the .B SET_CUSTOMER_OTP (0x38021) tag as follows: .PP .EX $ \fBvcmailbox 0x00038021 \fI[8+rows*4] [8+rows*4] [start] [rows] [value]\fR ... .EE .TP .B start the first row to program from 0-7 .TP .B rows number of rows to program .TP .B value each value to program .PP So, to program OTP customer rows 4, 5, and 6 to 0x11111111, 0x22222222, 0x33333333 respectively, you would use: .PP .EX $ \fBvcmailbox 0x00038021 20 20 4 3 0x11111111 0x22222222 0x33333333\fR .EE .PP This will then program rows 40, 41, and 42. To read the values back, you can use: .PP .EX $ \fBvcmailbox 0x00030021 20 20 4 3 0 0 0\fR 0x0000002c 0x80000000 0x00030021 0x00000014 0x80000014 0x00000000 0x00000003 0x11111111 0x22222222 0x33333333 .EE .PP If you'd like to integrate this functionality into your own code, you should be able to achieve this by using the vcmailbox.c code as an example. . .SS Locking the OTP changes It is possible to lock the OTP changes to avoid them being edited again. This can be done using a special argument with the OTP write mailbox: .PP .EX $ \fBvcmailbox 0x00038021 8 8 0xffffffff 0xaffe0000\fR .EE .PP Once locked, the customer OTP values can no longer be altered. Note that this locking operation is irreversible. . .SS Making customer OTP bits unreadable It is possible to prevent the customer OTP bits from being read at all. This can be done using a special argument with the OTP write mailbox: .PP .EX $ \fBvcmailbox 0x00038021 8 8 0xffffffff 0xaffebabe\fR .EE .PP This operation is unlikely to be useful for the vast majority of users, and is irreversible. . . .SH SEE ALSO .BR vcgencmd (1), .BR raspirev (7), .B [SOURCE] . . .SH REFERENCES .TP .B [MAILBOX] https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface . .TP .B [SOURCE] https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#otp-register-and-bit-definitions and https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#industrial-use-of-the-raspberry-pi raspi-utils-20250514/vcmailbox/raspirev.7000066400000000000000000000100151501106437300201360ustar00rootroot00000000000000'\" t .TH RASPIREV 7 . .SH NAME raspirev \- Raspberry Pi revision codes . . .SH DESCRIPTION Each distinct Raspberry Pi model revision has a unique revision code. You can look up a Raspberry Pi's revision code by running: .PP .EX $ \fBcat /proc/cpuinfo\fR .EE .PP The last three lines show the hardware type, the revision code, and the Pi's unique serial number. For example: .PP .EX Hardware : BCM2835 Revision : a02082 Serial : 00000000765fc593 .EE .PP .B Note: As of the 4.9 kernel, all Pis report BCM2835, even those with BCM2836, BCM2837 and BCM2711 processors. You should not use this string to detect the processor. Decode the revision code using the information below, or read .I /sys/firmware/devicetree/base/model . . .SH OLD-STYLE REVISION CODES The first set of Raspberry Pi models were given sequential hex revision codes from 0002 to 0015: .TS tab(|); l l l l l . Code|Model|Revision|RAM|Manufacturer \_|\_|\_|\_|\_ 0002|B|1.0|256MB|Egoman 0003|B|1.0|256MB|Egoman 0004|B|2.0|256MB|Sony UK 0005|B|2.0|256MB|Qisda 0006|B|2.0|256MB|Egoman 0007|A|2.0|256MB|Egoman 0008|A|2.0|256MB|Sony UK 0009|A|2.0|256MB|Qisda 000d|B|2.0|512MB|Egoman 000e|B|2.0|512MB|Sony UK 000f|B|2.0|512MB|Egoman 0010|B+|1.2|512MB|Sony UK 0011|CM1|1.0|512MB|Sony UK 0012|A+|1.1|256MB|Sony UK 0013|B+|1.2|512MB|Embest 0014|CM1|1.0|512MB|Embest 0015|A+|1.1|256MB/512MB|Embest .TE . . .SH NEW-STYLE REVISION CODES With the launch of the Raspberry Pi 2, new-style revision codes were introduced. Rather than being sequential, each bit of the hex code represents a piece of information about the revision: .PP .EX NOQuuuWuFMMMCCCCPPPPTTTTTTTTRRRR .EE . .TP .B N Overvoltage .PD 0 .RS .TP .B 0 Overvoltage allowed .TP .B 1 Overvoltage disallowed .RE .PD . .TP .B O OTP Programming; see .BR raspiotp (7) .PD 0 .RS .TP .B 0 OTP programming allowed .TP .B 1 OTP programming disallowed .RE .PD . .TP .B Q OTP Reading; see .BR raspiotp (7) .PD 0 .RS .TP .B 0 OTP reading allowed .TP .B 1 OTP reading disallowed .RE .PD . .TP .BR uuu Unused . .TP .B W Warranty bit .PD 0 .RS .TP .B 0 Warranty is intact .TP .B 1 Warranty has been voided by overclocking .RE .PD . .TP .B u Unused . .TP .B F New flag .PD 0 .RS .TP .B 1 new-style revision .TP .B 0 old-style revision .RE .PD . .TP .B MMM Memory size .PD 0 .RS .TP .B 0 256MB .TP .B 1 512MB .TP .B 2 1GB .TP .B 3 2GB .TP .B 4 4GB .TP .B 5 8GB .RE .PD . .TP .B CCCC Manufacturer .PD 0 .RS .TP .B 0 Sony UK .TP .B 1 Egoman .TP .B 2 Embest .TP .B 3 Sony Japan .TP .B 4 Embest .TP .B 5 Stadium .RE .PD . .TP .B PPPP Processor .PD 0 .RS .TP .B 0 BCM2835 .TP .B 1 BCM2836 .TP .B 2 BCM2837 .TP .B 3 BCM2711 .RE .PD . .TP .B TTTTTTTT Type .PD 0 .RS .TP .B 0 A .TP .B 1 B .TP .B 2 A+ .TP .B 3 B+ .TP .B 4 2B .TP .B 5 Alpha (early prototype) .TP .B 6 CM1 .TP .B 8 3B .TP .B 9 Zero .TP .B a CM3 .TP .B c Zero W .TP .B d 3B+ .TP .B e 3A+ .TP .B f Internal use only .TP .B 10 CM3+ .TP .B 11 4B .TP .B 13 400 .TP .B 14 CM4 .RE .PD . .TP .B RRRR Revision (0, 1, 2, etc.) .PP New-style revision codes in use at the time of writing: .TS tab(|); l l l l l . Code|Model|Revision|RAM|Manufacturer \_|\_|\_|\_|\_ 900021|A+|1.1|512MB|Sony UK 900032|B+|1.2|512MB|Sony UK 900092|Zero|1.2|512MB|Sony UK 900093|Zero|1.3|512MB|Sony UK 9000c1|Zero W|1.1|512MB|Sony UK 9020e0|3A+|1.0|512MB|Sony UK 920092|Zero|1.2|512MB|Embest 920093|Zero|1.3|512MB|Embest 900061|CM|1.1|512MB|Sony UK a01040|2B|1.0|1GB|Sony UK a01041|2B|1.1|1GB|Sony UK a02082|3B|1.2|1GB|Sony UK a020a0|CM3|1.0|1GB|Sony UK a020d3|3B+|1.3|1GB|Sony UK a02042|2B (BCM2837)|1.2|1GB|Sony UK a21041|2B|1.1|1GB|Embest a22042|2B (BCM2837)|1.2|1GB|Embest a22082|3B|1.2|1GB|Embest a220a0|CM3|1.0|1GB|Embest a32082|3B|1.2|1GB|Sony Japan a52082|3B|1.2|1GB|Stadium a22083|3B|1.3|1GB|Embest a02100|CM3+|1.0|1GB|Sony UK a03111|4B|1.1|1GB|Sony UK b03111|4B|1.1|2GB|Sony UK b03112|4B|1.2|2GB|Sony UK c03111|4B|1.1|4GB|Sony UK c03112|4B|1.2|4GB|Sony UK d03114|4B|1.4|8GB|Sony UK .TE . . .SH SEE ALSO .BR raspiotp (7), .B [SOURCE] . . .SH REFERENCES .TP .B [SOURCE] https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-revision-codes raspi-utils-20250514/vcmailbox/vcmailbox.1000066400000000000000000000011351501106437300202640ustar00rootroot00000000000000.TH VCMAILBOX 1 . .SH NAME vcmailbox \- send messages to the VideoCore via the mailbox . . .SH SYNOPSIS .SY vcmailbox .RI [ word0 ] .RI [ word1 ] \|.\|.\|. . . .SH DESCRIPTION .B vcmailbox is a low-level utility for sending mailbox messages to the VideoCore. See .B [MAILBOX] or .BR vcmailbox (7) for more information on the available messages and their respective formats. and .BR raspiotp (7) for some examples of typical usage. . . .SH SEE ALSO .BR vcgencmd (1), .BR raspiotp (7), .B [MAILBOX] . . .SH REFERENCES .TP .B [MAILBOX] https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface raspi-utils-20250514/vcmailbox/vcmailbox.7000066400000000000000000000642651501106437300203070ustar00rootroot00000000000000'\" t .TH VCMAILBOX 7 . .SH NAME vcmailbox \- the VideoCore mailbox property interface . . .SH DESCRIPTION . The mailbox can be used by the ARM on the Raspberry Pi to communicate with the VideoCore processor. From the command line, the .BR vcmailbox (1) can be used for this purpose. The mailbox operates a simple request / response protocol: . .IP \(bu 3 The response overwrites the request. .RS .IP \(bu 3 The callee is not allowed to return a different buffer address, this allows the caller to make independent asynchronous requests. .RE . .IP \(bu The buffer itself is 16-byte aligned as only the upper 28 bits of the address can be passed via the mailbox. . .IP \(bu All u64/u32/u16 values are in host CPU endian order. . .IP \(bu Unknown tags are ignored (the response bit will not be set). . .IP \(bu Response may include unsolicited tags. . .IP \(bu Response tag lengths may be longer than expected if additional information is provided in a future format. .RS .IP \(bu 3 The response is truncated to the provided buffer length. .IP \(bu Incompatible changes require a new tag so that where the buffer is the size required by a previous version the truncated part should be readable as per the previous version. .IP \(bu Response length indicates the desired length even when it is longer than the buffer size filled. .IP \(bu Tag value length can be used to infer the version of the request/response. .RE . .IP \(bu Tags should be processed in order except where an interface requires multiple tags for a single operation (like the frame buffer). . . .SS Mailbox messages . .IP \(bu 3 The mailbox interface has 28 bits (MSB) available for the value and 4 bits (LSB) for the channel. .RS .IP \(bu 3 Request message: 28 bits (MSB) buffer address .IP \(bu Response message: 28 bits (MSB) buffer address .RE . .IP \(bu Channels 8 and 9 are used. .RS .IP \(bu 3 Channel 8: Request from ARM for response by VC .IP \(bu Channel 9: Request from VC for response by ARM (none currently defined) .RE . . .SS Buffer contents . . .TP .B u32 buffer size in bytes (including the header values, the end tag and padding) . .TP .B u32 buffer request/response code .RS .PP Request codes: .PD 0 .TP 12 .B 0x00000000 process request .TP .B \& All other values reserved .PD .PP Response codes: .PD 0 .TP 12 .B 0x80000000 request successful .TP .B 0x80000001 error parsing request buffer (partial response) .TP .B \& All other values reserved .PD .RE . .TP .B u8\|.\|.\|. sequence of concatenated tags . .TP .B u32 0x0 (end tag) . .TP .B u8\|.\|.\|. padding . . .SS Tag format . .TP .B u32 tag identifier . .TP .B u32 value buffer size in bytes . .TP .B u32 Request codes: .PD 0 .RS .TP 12 .B b31 clear request .TP .B b30-b0 reserved .RE .PD .IP Response codes: .PD 0 .RS .TP 12 .B b31 set response .TP .B b30-b0 value length in bytes .RE .PD . .TP .B u8\|.\|.\|. value buffer . .TP .B u8\|.\|.\|. padding to align the tag to 32 bits. . . .SH VIDEOCORE TAGS . . .SS Get firmware revision .PD 0 .TP 12 .B Tag: 0x00000001 .TP .B Request: 0 bytes .TP .B Response: 4 bytes .RS .TP .B u32 firmware revision .RE .PD . . .SH HARDWARE TAGS . .SS Get board model .PD 0 .TP 12 .B Tag: 0x00010001 .TP .B Request: 0 bytes .TP .B Response: 4 bytes .RS .TP .B u32 board model .RE .PD . .SS Get board revision .PD 0 .TP 12 .B Tag: 0x00010002 .TP .B Request: 0 bytes .TP .B Response: 4 bytes .RS .TP .B u32 board revision .RE .PD . .SS Get board MAC address .PD 0 .TP 12 .B Tag: 0x00010003 .TP .B Request: 0 bytes .TP .B Response: 6 bytes .RS .TP .B u8... MAC address in network byte order .RE .PD . .SS Get board serial .PD 0 .TP 12 .B Tag: 0x00010004 .TP .B Request: 0 bytes .TP .B Response: 8 bytes .RS .TP .B u64 board serial .RE .PD . .SS Get ARM memory .PD 0 .TP 12 .B Tag: 0x00010005 .TP .B Request: 0 bytes .TP .B Response: 8 bytes .RS .TP .B u32 base address in bytes .TP .B u32 size in bytes .RE .PD . .PP Future formats may specify multiple base+size combinations. . .SS Get VC memory .PD 0 .TP 12 .B Tag: 0x00010006 .TP .B Request: 0 bytes .TP .B Response: 8 bytes .RS .TP .B u32 base address in bytes .TP .B u32 size in bytes .RE .PD . .PP Future formats may specify multiple base+size combinations. . .SS Get clocks .PD 0 .TP 12 .B Tag: 0x00010007 .TP .B Request: 0 bytes .TP .B Response: variable bytes (multiple of 8) .RS .TP .B u32 parent clock id (0 for a root clock) .TP .B u32 clock id .TP .B (repeated) .RE .PD . .PP Returns all clocks that exist .IR "in top-down, breadth-first order" . Clocks that depend on another clock should be defined as children of that clock. Clocks that depend on no other clocks should have no parent. Clock IDs are as in the .B CLOCK TAGS section below. . . .SH CONFIG TAGS . .SS Get command line .PD 0 .TP 12 .B Tag: 0x00050001 .TP .B Request: 0 bytes .TP .B Response: variable bytes .RS .TP .B u8... ASCII command line string .RE .PD . .PP Caller should not assume the string is null terminated. . . .SH SHARED RESOURCE MANAGEMENT TAGS . .SS Get DMA channels .PD 0 .TP 12 .B Tag: 0x00060001 .TP .B Request: 0 bytes .TP .B Response: 4 bytes .RS .TP .B u32 mask .RS .TP 12 .B Bits 0-15 DMA channels 0-15 (0=do not use, 1=usable) .TP .B Bits 16-31 reserved for future use .RE .RE .PD . .PP Caller assumes that the VC has enabled all the usable DMA channels. . . .SH POWER TAGS . .SS Unique device IDs .PD 0 .TP 12 .B 0x00000000 SD Card .TP .B 0x00000001 UART0 .TP .B 0x00000002 UART1 .TP .B 0x00000003 USB HCD .TP .B 0x00000004 I2C0 .TP .B 0x00000005 I2C1 .TP .B 0x00000006 I2C2 .TP .B 0x00000007 SPI .TP .B 0x00000008 CCP2TX .TP .B 0x00000009 Unknown (RPi4) .TP .B 0x0000000a Unknown (RPi4) .PD . .SS Get power state .PD 0 .TP 12 .B Tag: 0x00020001 .TP .B Request: 4 bytes .RS .TP .B u32 device id .RE .TP .B Response: 8 bytes .RS .TP .B u32 device id .TP .B u32 state .RS .TP 12 .B Bit 0 0=off, 1=on .TP .B Bit 1 0=device exists, 1=device does not exist .TP .B Bits 2-31 reserved for future use .RE .RE .PD . .PP Response indicates current state. . .SS Get timing .PD 0 .TP 12 .B Tag: 0x00020002 .TP .B Request: 4 bytes .RS .TP .B u32 device id .RE .TP .B Response: 8 bytes .RS .TP .B u32 device id .TP .B u32 enable wait time in microseconds .RE .PD . .PP Response indicates wait time required after turning a device on before power is stable. Returns 0 wait time if the device does not exist. . .SS Set power state .PD 0 .TP 12 .B Tag: 0x00028001 .TP .B Request: 8 bytes .RS .TP .B u32 device id .TP .B u32 state .RS .TP 12 .B Bit 0 0=off, 1=on .TP .B Bit 1 0=do not wait, 1=wait .TP .B Bits 2-31 reserved for future use (set to 0) .RE .RE .TP .B Response: 8 bytes .RS .TP .B u32 device id .TP .B u32 state .RS .TP 12 .B Bit 0 0=off, 1=on .TP .B Bit 1 0=device exists, 1=device does not exist .TP .B Bits 2-31 reserved for future use .RE .RE .PD . .PP Response indicates new state, with/without waiting for the power to become stable. . . .SH CLOCK TAGS . .SS Unique clock IDs .PD 0 .TP 12 .B 0x00000000 reserved .TP .B 0x00000001 EMMC .TP .B 0x00000002 UART .TP .B 0x00000003 ARM .TP .B 0x00000004 CORE .TP .B 0x00000005 V3D .TP .B 0x00000006 H264 .TP .B 0x00000007 ISP .TP .B 0x00000008 SDRAM .TP .B 0x00000009 PIXEL .TP .B 0x0000000a PWM .TP .B 0x0000000b HEVC .TP .B 0x0000000c EMMC2 .TP .B 0x0000000d M2MC .TP .B 0x0000000e PIXEL_BVB .PD . .PP All clocks are the .I base clocks for those peripherals, e.g. 3MHz for UART, 50/100MHz for EMMC, not the dividers applied using the peripheral. . .SS Get clock state .PD 0 .TP 12 .B Tag: 0x00030001 .TP .B Request: 4 bytes .RS .TP .B u32 clock id .RE .TP .B Response: 8 bytes .RS .TP .B u32 clock id .TP .B u32 state .RS .TP 12 .B Bit 0 0=off, 1=on .TP .B Bit 1 0=clock exists, 1=clock does not exist .TP .B Bits 2-31 reserved for future use .RE .RE .PD . .SS Set clock state .PD 0 .TP 12 .B Tag: 0x00038001 .TP .B Request: 8 bytes .RS .TP .B u32 clock id .TP .B u32 state .RS .TP 12 .B Bit 0 0=off, 1=on .TP .B Bit 1 0=clock exists, 1=clock does not exist .TP .B Bits 2-31 reserved for future use (set to 0) .RE .RE .TP .B Response: 8 bytes .RS .TP .B u32 clock id .TP .B u32 state .RS .TP 12 .B Bit 0 0=off, 1=on .TP .B Bit 1 0=clock exists, 1=clock does not exist .TP .B Bits 2-31 reserved for future use .RE .RE .PD . .SS Get clock rate .PD 0 .TP 12 .B Tag: 0x00030002 .TP .B Request: 4 bytes .RS .TP .B u32 clock id .RE .TP .B Response: 8 bytes .RS .TP .B u32 clock id .TP .B u32 rate (in Hz) .RE .PD . .PP Next enable rate should be returned even if the clock is not running. A rate of 0 is returned if the clock does not exist. . .SS Set clock rate .PD 0 .TP 12 .B Tag: 0x00038002 .TP .B Request: 12 bytes .RS .TP .B u32 clock id .TP .B u32 rate (in Hz) .TP .B u32 skip setting turbo .RE .TP .B Response: 8 bytes .RS .TP .B u32 clock id .TP .B u32 rate (in Hz) .RE .PD . .PP Next supported enable rate should be returned even if the clock is not running. A rate of 0 is returned if the clock does not exist. The clock rate may be clamped to the supported range. . .PP By default when setting arm freq above default, other turbo settings will be enabled (e.g. voltage, sdram and gpu frequencies). You can disable this effect by setting "skip setting turbo" to 1. . .SS Get max clock rate .PD 0 .TP 12 .B Tag: 0x00030004 .TP .B Request: 4 bytes .RS .TP .B u32 clock id .RE .TP .B Response: 8 bytes .RS .TP .B u32 clock id .TP .B u32 rate (in Hz) .RE .PD . .PP Return the maximum supported clock rate for the given clock. Clocks should not be set higher than this. . .SS Get min clock rate .PD 0 .TP 12 .B Tag: 0x00030007 .TP .B Request: 4 bytes .RS .TP .B u32 clock id .RE .TP .B Response: 8 bytes .RS .TP .B u32 clock id .TP .B u32 rate (in Hz) .RE .PD . .PP Return the minimum supported clock rate for the given clock. This may be used when idle. . .SS Get turbo .PD 0 .TP 12 .B Tag: 0x00030009 .TP .B Request: 4 bytes .RS .TP .B u32 id .RE .TP .B Response: 8 bytes .RS .TP .B u32 id .TP .B u32 level .RE .PD . .PP Get the turbo state for index id. id should be 0. level will be zero for non-turbo and one for turbo. . .SS Set turbo .PD 0 .TP 12 .B Tag: 0x00038009 .TP .B Request: 8 bytes .RS .TP .B u32 id .TP .B u32 level .RE .TP .B Response: 8 bytes .RS .TP .B u32 id .TP .B u32 level .RE .PD . .PP Set the turbo state for index id. id should be zero. level will be zero for non-turbo and one for turbo. This will cause GPU clocks to be set to maximum when enabled and minimum when disabled. . . .SH VOLTAGE TAGS . .SS Unique voltage IDs .PD 0 .TP 12 .B 0x00000000 reserved .TP .B 0x00000001 Core .TP .B 0x00000002 SDRAM_C .TP .B 0x00000003 SDRAM_P .TP .B 0x00000004 SDRAM_I .PD . .SS Get voltage .PD 0 .TP 12 .B Tag: 0x00030003 .TP .B Request: 4 bytes .RS .TP .B u32 voltage id .RE .TP .B Response: 8 bytes .RS .TP .B u32 voltage id .TP .B u32 value (offset from 1.2V in units of 0.025V) .RE .PD . .PP The voltage value may be clamped to the supported range. A value of 0x80000000 means the id was not valid. . .SS Set voltage .PD 0 .TP 12 .B Tag: 0x00038003 .TP .B Request: 8 bytes .RS .TP .B u32 voltage id .TP .B u32 value (offset from 1.2V in units of 0.025V) .RE .TP .B Response: 8 bytes .RS .TP .B u32 voltage id .TP .B u32 value (offset from 1.2V in units of 0.025V) .RE .PD . .PP The voltage value may be clamped to the supported range. A value of 0x80000000 means the id was not valid. . .SS Get max voltage .PD 0 .TP 12 .B Tag: 0x00030005 .TP .B Request: 4 bytes .RS .TP .B u32 voltage id .RE .TP .B Response: 8 bytes .RS .TP .B u32 voltage id .TP .B u32 value (offset from 1.2V in units of 0.025V) .RE .PD . .PP Return the maximum supported voltage rate for the given id. Voltages should not be set higher than this. . .SS Get min voltage .PD 0 .TP 12 .B Tag: 0x00030008 .TP .B Request: 4 bytes .RS .TP .B u32 voltage id .RE .TP .B Response: 8 bytes .RS .TP .B u32 voltage id .TP .B u32 value (offset from 1.2V in units of 0.025V) .RE .PD . .PP Return the minimum supported voltage rate for the given id. This may be used when idle. . .SS Get temperature .PD 0 .TP 12 .B Tag: 0x00030006 .TP .B Request: 4 bytes .RS .TP .B u32 temperature id .RE .TP .B Response: 8 bytes .RS .TP .B u32 temperature id .TP .B u32 value .RE .PD . .PP Return the temperature of the SoC in thousandths of a degree C. id should be zero. . .SS Get max temperature .PD 0 .TP 12 .B Tag: 0x0003000a .TP .B Request: 4 bytes .RS .TP .B u32 temperature id .RE .TP .B Response: 8 bytes .RS .TP .B u32 temperature id .TP .B u32 value .RE .PD . .PP Return the maximum safe temperature of the SoC in thousandths of a degree C. id should be zero. Overclock may be disabled above this temperature. . .SS Allocate memory .PD 0 .TP 12 .B Tag: 0x0003000c .TP .B Request: 12 bytes .RS .TP .B u32 size .TP .B u32 alignment .TP .B u32 flags .RE .TP .B Response: 4 bytes .RS .TP .B u32 handle .RE .PD . .PP Allocates contiguous memory on the GPU. size and alignment are in bytes. flags contain: . .PP .TS l l l . Name Value Comments \_ \_ \_ MEM_FLAG_NORMAL 0 normal allocating alias; don't use from ARM MEM_FLAG_DISCARDABLE 1 << 0 can be resized to 0 at any time; use for cached data MEM_FLAG_DIRECT 1 << 2 0xC alias uncached MEM_FLAG_COHERENT 1 << 3 0x8 alias; non-allocating in L2 but coherent MEM_FLAG_L1_NONALLOCATING 3 << 2 Allocating in L2 MEM_FLAG_ZERO 1 << 4 Initialise buffer to all zeros MEM_FLAG_NO_INIT 1 << 5 Don't initialise; default is initialise to all ones MEM_FLAG_HINT_PERMALOCK 1 << 6 Likely to be locked for long periods of time .TE . .SS Lock memory .PD 0 .TP 12 .B Tag: 0x0003000d .TP .B Request: 4 bytes .RS .TP .B u32 handle .RE .TP .B Response: 4 bytes .RS .TP .B u32 bus address .RE .PD . .PP Lock buffer in place, and return a bus address. Must be done before memory can be accessed . .SS Unlock memory .PD 0 .TP 12 .B Tag: 0x0003000e .TP .B Request: 4 bytes .RS .TP .B u32 handle .RE .TP .B Response: 4 bytes .RS .TP .B u32 status .RE .PD . .PP Unlock buffer. It retains contents, but may move. Needs to be locked before next use. status=0 is success. . .SS Release Memory .PD 0 .TP 12 .B Tag: 0x0003000f .TP .B Request: 4 bytes .RS .TP .B u32 handle .RE .TP .B Response: 4 bytes .RS .TP .B u32 status .RE .PD . .PP Free the memory buffer. status=0 is success. . .SS Execute Code .PD 0 .TP 12 .B Tag: 0x00030010 .TP .B Request: 28 bytes .RS .TP .B u32 function pointer .TP .B u32 r0 .TP .B u32 r1 .TP .B u32 r2 .TP .B u32 r3 .TP .B u32 r4 .TP .B u32 r5 .RE .TP .B Response: 4 bytes .RS .TP .B u32 r0 .RE .PD . .PP Calls the function at given (bus) address and with arguments given. E.g. r0 = fn(r0, r1, r2, r3, r4, r5); It blocks until call completes. The (GPU) instruction cache is implicitly flushed. Setting the lsb of function pointer address will suppress the instruction cache flush if you know the buffer hasn't changed since last execution. . .SS Get Dispmanx Resource mem handle .PD 0 .TP 12 .B Tag: 0x00030014 .TP .B Request: 4 bytes .RS .TP .B u32 dispmanx resource handle .RE .TP .B Response: 8 bytes .RS .TP .B u32 0 is successful .TP .B u32 mem handle for resource .RE .PD . .PP Gets the mem_handle associated with a created dispmanx resource. This can be locked and the memory directly written from the arm to avoid having to copy the image data to GPU. . .SS Get EDID block .PD 0 .TP 12 .B Tag: 0x00030020 .TP .B Request: 4 bytes .RS .TP .B u32 block number .RE .TP .B Response: 136 bytes .RS .TP .B u32 block number .TP .B u32 status .TP .B u8\|.\|.\|. EDID block .RE .PD . .PP This reads the specified EDID block from attached HDMI/DVI device. There will always be at least one block of 128 bytes, but there may be additional blocks. You should keep requesting blocks (starting from 0) until the status returned is non-zero. . . .SH FRAME BUFFER TAGS . .IP \(bu 3 All tags in the request are processed in one operation. .IP \(bu It is not valid to mix Test tags with Get/Set tags in the same operation and no tags will be returned. .IP \(bu Get tags will be processed after all Set tags. .IP \(bu If an allocate buffer tag is omitted when setting parameters, then no change occurs unless it can be accommodated without changing the buffer base or size. .IP \(bu When an allocate buffer response is returned, the old buffer area (if the base or size has changed) is implicitly freed. . .PP For example: . .IP 1. 3 The current values/defaults are loaded into a temporary struct . .IP 2. The tags are used to overwrite some or all of the values . .IP 3. Validation of Test/Set tags occurs . .IP 4. The Set changes are applied and responses based on the requested Get/Test/Set tags are written to the buffer . Duplicating the same tag in one request/response is prohibited. The expected result is either an error or implementation specified undefined behaviour (such as only using the last instance of the tag). .PD . .SS Allocate buffer .PD 0 .TP 12 .B Tag: 0x00040001 .TP .B Request: 4 bytes .RS .TP .B u32 alignment in bytes .RE .TP .B Response: 8 bytes .RS .TP .B u32 frame buffer base address in bytes .TP .B u32 frame buffer size in bytes .RE .PD . .PP If the requested alignment is unsupported then the current base and size (which may be 0 if not allocated) is returned and no change occurs. . .SS Release buffer .PD 0 .TP 12 .B Tag: 0x00048001 .TP .B Request: 0 bytes .TP .B Response: 0 bytes .PD . .PP Releases and disables the frame buffer. . .SS Blank screen .PD 0 .TP 12 .B Tag: 0x00040002 .TP .B Request: 4 bytes .RS .TP .B u32 state .RS .TP 12 .B Bit 0 0=off, 1=on .TP .B Bits 1-31 reserved for future use (set to 0) .RE .RE .TP .B Response: 4 bytes .RS .TP .B u32 state .RS .TP 12 .B Bit 0 0=off, 1=on .TP .B Bits 1-31 reserved for future use .RE .RE .PD . .SS Get physical (display) width/height .PD 0 .TP 12 .B Tag: 0x00040003 .TP .B Request: 0 bytes .TP .B Response: 8 bytes .RS .TP .B u32 width in pixels .TP .B u32 height in pixels .RE .PD . .PP Note that the "physical (display)" size is the size of the allocated buffer in memory, not the resolution of the video signal sent to the display device. . .SS Test physical (display) width/height .PD 0 .TP 12 .B Tag: 0x00044003 .TP .B Request: 8 bytes .RS .TP .B u32 width in pixels .TP .B u32 height in pixels .RE .TP .B Response: 8 bytes .RS .TP .B u32 width in pixels .TP .B u32 height in pixels .RE .PD . .PP Response is the same as the request (or modified), to indicate if this configuration is supported (in combination with all the other settings). Does not modify the current hardware or frame buffer state. . .SS Set physical (display) width/height .PD 0 .TP 12 .B Tag: 0x00048003 .TP .B Request: 8 bytes .RS .TP .B u32 width in pixels .TP .B u32 height in pixels .RE .TP .B Response: 8 bytes .RS .TP .B u32 width in pixels .TP .B u32 height in pixels .RE .PD . .PP The response may not be the same as the request so it must be checked. May be the previous width/height or 0 for unsupported. . . .SS Get virtual (buffer) width/height .PD 0 .TP 12 .B Tag: 0x00040004 .TP .B Request: 0 bytes .TP .B Response: 8 bytes .RS .TP .B u32 width in pixels .TP .B u32 height in pixels .RE .PD . .PP Note that the "virtual (buffer)" size is the portion of buffer that is sent to the display device, not the resolution the buffer itself. This may be smaller than the allocated buffer size in order to implement panning. . .SS Test virtual (buffer) width/height .PD 0 .TP 12 .B Tag: 0x00044004 .TP .B Request: 8 bytes .RS .TP .B u32 width in pixels .TP .B u32 height in pixels .RE .TP .B Response: 8 bytes .RS .TP .B u32 width in pixels .TP .B u32 height in pixels .RE .PD . .PP Response is the same as the request (or modified), to indicate if this configuration is supported (in combination with all the other settings). Does not modify the current hardware or frame buffer state. . .SS Set virtual (buffer) width/height .PD 0 .TP 12 .B Tag: 0x00048004 .TP .B Request: 8 bytes .RS .TP .B u32 width in pixels .TP .B u32 height in pixels .RE .TP .B Response: 8 bytes .RS .TP .B u32 width in pixels .TP .B u32 height in pixels .RE .PD . .PP The response may not be the same as the request so it must be checked. May be the previous width/height or 0 for unsupported. . .SS Get depth .PD 0 .TP 12 .B Tag: 0x00040005 .TP .B Request: 0 bytes .TP .B Response: 4 bytes .RS .TP .B u32 bits per pixel .RE .PD . .PP Test depth .PD 0 .TP 12 .B Tag: 0x00044005 .TP .B Request: 4 bytes .RS .TP .B u32 bits per pixel .RE .TP .B Response: 4 bytes .RS .TP .B u32 bits per pixel .RE .PD . .PP Response is the same as the request (or modified), to indicate if this configuration is supported (in combination with all the other settings). Does not modify the current hardware or frame buffer state. . .SS Set depth .PD 0 .TP 12 .B Tag: 0x00048005 .TP .B Request: 4 bytes .RS .TP .B u32 bits per pixel .RE .TP .B Response: 4 bytes .RS .TP .B u32 bits per pixel .RE .PD . .PP The response may not be the same as the request so it must be checked. May be the previous depth or 0 for unsupported. . .SS Get pixel order .PD 0 .TP 12 .B Tag: 0x00040006 .TP .B Request: 0 bytes .TP .B Response: 4 bytes .RS .TP .B u32 state .RS .TP .B 0x0 BGR .TP .B 0x1 RGB .RE .RE .PD . .SS Test pixel order .PD 0 .TP 12 .B Tag: 0x00044006 .TP .B Request: 4 bytes .RS .TP .B u32 state (as above) .RE .TP .B Response: 4 bytes .RS .TP .B u32 state (as above) .RE .PD . .PP Response is the same as the request (or modified), to indicate if this configuration is supported (in combination with all the other settings). Does not modify the current hardware or frame buffer state. . .SS Set pixel order .PD 0 .TP 12 .B Tag: 0x00048006 .TP .B Request: 4 bytes .RS .TP .B u32 state (as above) .RE .TP .B Response: 4 bytes .RS .TP .B u32 state (as above) .RE .PD . .PP The response may not be the same as the request so it must be checked. . .SS Get alpha mode .PD 0 .TP 12 .B Tag: 0x00040007 .TP .B Request: 0 bytes .TP .B Response: 4 bytes .RS .TP .B u32 state .RS .TP .B 0x0 Alpha channel enabled (0 = fully opaque) .TP .B 0x1 Alpha channel reversed (0 = fully transparent) .TP .B 0x2 Alpha channel ignored .RE .RE .PD . .SS Test alpha mode .PD 0 .TP 12 .B Tag: 0x00044007 .TP .B Request: 4 bytes .RS .TP .B u32 state (as above) .RE .TP .B Response: 4 bytes .RS .TP .B u32 state (as above) .RE .PD . .PP Response is the same as the request (or modified), to indicate if this configuration is supported (in combination with all the other settings). Does not modify the current hardware or frame buffer state. . .SS Set alpha mode .PD 0 .TP 12 .B Tag: 0x00048007 .TP .B Request: 4 bytes .RS .TP .B u32 state (as above) .RE .TP .B Response: 4 bytes .RS .TP .B u32 state (as above) .RE .PD . .PP The response may not be the same as the request so it must be checked. . .SS Get pitch .PD 0 .TP 12 .B Tag: 0x00040008 .TP .B Request: 0 bytes .TP .B Response: 4 bytes .RS .TP .B u32 bytes per line .RE .PD . .SS Get virtual offset .PD 0 .TP 12 .B Tag: 0x00040009 .TP .B Request: 0 bytes .TP .B Response: 8 bytes .RS .TP .B u32 X in pixels .TP .B u32 Y in pixels .RE .PD . .SS Test virtual offset .PD 0 .TP 12 .B Tag: 0x00044009 .TP .B Request: 8 bytes .RS .TP .B u32 X in pixels .TP .B u32 Y in pixels .RE .TP .B Response: 8 bytes .RS .TP .B u32 X in pixels .TP .B u32 Y in pixels .RE .PD . .PP Response is the same as the request (or modified), to indicate if this configuration is supported (in combination with all the other settings). Does not modify the current hardware or frame buffer state. . .SS Set virtual offset .PD 0 .TP 12 .B Tag: 0x00048009 .TP .B Request: 8 bytes .RS .TP .B u32 X in pixels .TP .B u32 Y in pixels .RE .TP .B Response: 8 bytes .RS .TP .B u32 X in pixels .TP .B u32 Y in pixels .RE .PD . .PP The response may not be the same as the request so it must be checked. May be the previous offset or 0 for unsupported. . .SS Get overscan .PD 0 .TP 12 .B Tag: 0x0004000a .TP .B Request: 0 bytes .TP .B Response: 16 bytes .RS .TP .B u32 top in pixels .TP .B u32 bottom in pixels .TP .B u32 left in pixels .TP .B u32 right in pixels .RE .PD . .SS Test overscan .PD 0 .TP 12 .B Tag: 0x0004400a .TP .B Request: 16 bytes .RS .TP .B u32 top in pixels .TP .B u32 bottom in pixels .TP .B u32 left in pixels .TP .B u32 right in pixels .RE .TP .B Response: 16 bytes .RS .TP .B u32 top in pixels .TP .B u32 bottom in pixels .TP .B u32 left in pixels .TP .B u32 right in pixels .RE .PD . .PP Response is the same as the request (or modified), to indicate if this configuration is supported (in combination with all the other settings). Does not modify the current hardware or frame buffer state. . .SS Set overscan .PD 0 .TP 12 .B Tag: 0x0004800a .TP .B Request: 16 bytes .RS .TP .B u32 top in pixels .TP .B u32 bottom in pixels .TP .B u32 left in pixels .TP .B u32 right in pixels .RE .TP .B Response: 16 bytes .RS .TP .B u32 top in pixels .TP .B u32 bottom in pixels .TP .B u32 left in pixels .TP .B u32 right in pixels .RE .PD . .PP The response may not be the same as the request so it must be checked. May be the previous overscan or 0 for unsupported. . .SS Get palette .PD 0 .TP 12 .B Tag: 0x0004000b .TP .B Request: 0 bytes .TP .B Response: 1024 bytes .RS .TP .B u32... RGBA palette values (index 0 to 255) .RE .PD . .SS Test palette .PD 0 .TP 12 .B Tag: 0x0004400b .TP .B Request: 24 bytes..1032 bytes .RS .TP .B u32 offset: first palette index to set (0-255) .TP .B u32 length: number of palette entries to set (1-256) .TP .B u32... RGBA palette values (offset to offset+length-1) .RE .TP .B Response: 4 bytes .RS .TP .B u32 0=valid, 1=invalid .RE .PD . .PP Response is the same as the request (or modified), to indicate if this configuration is supported (in combination with all the other settings). Does not modify the current hardware or frame buffer state. . .SS Set palette .PD 0 .TP 12 .B Tag: 0x0004800b .TP .B Request: 24 bytes..1032 bytes .RS .TP .B u32 offset: first palette index to set (0-255) .TP .B u32 length: number of palette entries to set (1-256) .TP .B u32... RGBA palette values (offset to offset+length-1) .RE .TP .B Response: 4 bytes .RS .TP .B u32 0=valid, 1=invalid .RE .PD . .PP The response may not be the same as the request so it must be checked. Palette changes should not be partially applied. . .SS Set Cursor Info .PD 0 .TP 12 .B Tag: 0x00008010 .TP .B Request: 24 bytes .RS .TP .B u32 width .TP .B u32 height .TP .B u32 (unused) .TP .B u32 pointer to pixels .TP .B u32 hotspotX .TP .B u32 hotspotY .RE .TP .B Response: 4 bytes .RS .TP .B u32 0=valid, 1=invalid .RE .PD . .PP Format is 32bpp (ARGB). Width and height should be >= 16 and (width * height) <= 64. . .SS Set Cursor State .PD 0 .TP 12 .B Tag: 0x00008011 .TP .B Request: 16 bytes .RS .TP .B u32 enable (1=visible, 0=invisible) .TP .B u32 x .TP .B u32 y .TP .B u32 flags; 0=display coords, 1=framebuffer coords .RE .TP .B Response: 4 bytes .RS .TP .B u32 0=valid, 1=invalid .RE .PD . .PP if Set Cursor Info hasn't been called a default cursor will be used (64x64 with hotspot at 0,0). raspi-utils-20250514/vcmailbox/vcmailbox.c000077500000000000000000000062041501106437300203530ustar00rootroot00000000000000/* Copyright (c) 2015 Raspberry Pi (Trading) Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * 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. */ // Utility for driving mailbox interface to VideoCore #include #include #include #include #include #include #include #include #include /* ioctl */ #define DEVICE_FILE_NAME "/dev/vcio" #define MAJOR_NUM 100 #define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) /* * use ioctl to send mbox property message */ static int mbox_property(int file_desc, void *buf) { int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf); if (ret_val < 0) { printf("ioctl_set_msg failed:%d\n", ret_val); } return ret_val; } static int mbox_open() { int file_desc; // open a char device file used for communicating with kernel mbox driver file_desc = open(DEVICE_FILE_NAME, 0); if (file_desc < 0) { printf("Can't open device file: %s\n", DEVICE_FILE_NAME); printf("Try creating a device file with: sudo mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM); exit(-1); } return file_desc; } static void mbox_close(int file_desc) { close(file_desc); } #define MAX_WORDS 256 int main(int argc, char *argv[]) { int mb = mbox_open(); unsigned words[MAX_WORDS] = {}; int i; if (argc < 2 || argc+1 >= MAX_WORDS) { printf("Usage %s [word0] [word1] ...\n", argv[0]); return 1; } words[0] = (argc+2) * sizeof words[0]; words[1] = 0; // process request for (i=1; i