├── tests ├── data │ ├── dup-propname.dts │ ├── imx7d-sdb.dtb │ ├── dup-nodename.dts │ ├── dup-phandle.dts │ ├── stringlist.dts │ ├── appendprop.dts │ ├── addresses.dts │ ├── sized_cells.dts │ ├── comments.dts │ ├── base.dts │ ├── fdtdump.dts │ ├── subnode_iterate.dts │ └── imx7d-sdb.dts ├── conftest.py ├── test_fdt_parser.py ├── test_fdt_class.py ├── test_cli_tool.py └── test_items_api.py ├── .travis.yml ├── .github └── workflows │ ├── pythonpublish.yml │ └── pythonpackage.yml ├── .gitignore ├── setup.py ├── fdt ├── misc.py ├── header.py ├── __main__.py ├── items.py └── __init__.py ├── README.md └── LICENSE /tests/data/dup-propname.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | prop; 5 | prop; 6 | }; 7 | -------------------------------------------------------------------------------- /tests/data/imx7d-sdb.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molejar/pyFDT/HEAD/tests/data/imx7d-sdb.dtb -------------------------------------------------------------------------------- /tests/data/dup-nodename.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | node { 5 | }; 6 | node { 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /tests/data/dup-phandle.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | node1 { 5 | linux,phandle = <1>; 6 | }; 7 | node2 { 8 | linux,phandle = <1>; 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /tests/data/stringlist.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | compatible = "test-strings"; 5 | #address-cells = <2>; 6 | #size-cells = <2>; 7 | 8 | device { 9 | compatible = "foo", "bar"; 10 | big-endian; 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /tests/data/appendprop.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | prop-str = "hello world", "nastystring: \a\b\t\n\v\f\r\\\""; 5 | //prop-int64 = /bits/ 64 <0xdeadbeef01abcdef 0xdeadbeef01abcdef>; 6 | prop-int = <0xdeadbeef 123456789>; 7 | prop-bytes = [00010203040001020304]; 8 | }; 9 | -------------------------------------------------------------------------------- /tests/data/addresses.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | compatible = "test_addresses"; 5 | #address-cells = <2>; 6 | #size-cells = <2>; 7 | 8 | identity-bus@0 { 9 | }; 10 | 11 | simple-bus@1000000 { 12 | #address-cells = <2>; 13 | #size-cells = <1>; 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.5" 4 | - "3.6" 5 | - "3.7" 6 | before_install: 7 | - pip install pytest pytest-console-scripts 8 | - pip install pytest-cov 9 | - pip install coveralls 10 | install: 11 | - pip install -e . 12 | script: 13 | - py.test --cov=fdt tests/* 14 | after_success: 15 | - coveralls -------------------------------------------------------------------------------- /tests/data/sized_cells.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | cells-8b = /bits/ 8 <'\r' 'b' '\0' '\'' '\xff' 0xde>; 5 | cells-16b = /bits/ 16 <'\r' 'b' '\0' '\'' '\xff' 0xdead>; 6 | cells-32b = /bits/ 32 <'\r' 'b' '\0' '\'' '\xff' 0xdeadbeef>; 7 | cells-64b = /bits/ 64 <'\r' 'b' '\0' '\'' '\xff' 0xdeadbeef00000000>; 8 | 9 | cells-one-16b = /bits/ 16 <0x1234 0x5678 0x0 0xffff>; 10 | cells-one-32b = <0x12345678 0x0000ffff>; 11 | }; 12 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | 4 | 5 | @pytest.fixture(scope="module") 6 | def data_dir(): 7 | data_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data') 8 | if not os.path.exists(data_dir): 9 | raise Exception("Directory doesnt exist: {}".format(data_dir)) 10 | return data_dir 11 | 12 | 13 | @pytest.fixture(scope="module") 14 | def temp_dir(): 15 | temp_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'temp') 16 | os.makedirs(temp_dir, exist_ok=True) 17 | return temp_dir 18 | -------------------------------------------------------------------------------- /tests/data/comments.dts: -------------------------------------------------------------------------------- 1 | /* regexps for lexing comments are.. tricky. Check if we've actually 2 | * got it right */ 3 | /dts-v1/; 4 | 5 | / { 6 | // line comment 7 | prop1; 8 | /* comment */ 9 | prop2; 10 | /* multiline 11 | 12 | notaprop1; 13 | 14 | comment */ 15 | prop3; 16 | /**/ 17 | prop4; 18 | /***/ 19 | prop5; 20 | /****/ 21 | prop6; 22 | /* another 23 | * multiline 24 | * comment */ 25 | prop7; 26 | /* yet 27 | * another 28 | * multline 29 | * comment 30 | */ 31 | prop8; 32 | /** try this */ 33 | prop9; 34 | /* and this **/ 35 | prop10; 36 | child /* finally */ { 37 | }; 38 | }; 39 | /* final comment */ 40 | -------------------------------------------------------------------------------- /.github/workflows/pythonpublish.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Set up Python 13 | uses: actions/setup-python@v1 14 | with: 15 | python-version: '3.x' 16 | - name: Install dependencies 17 | run: | 18 | python -m pip install --upgrade pip 19 | pip install setuptools wheel twine 20 | - name: Build and publish 21 | env: 22 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 23 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 24 | run: | 25 | python setup.py sdist bdist_wheel 26 | twine upload dist/* 27 | -------------------------------------------------------------------------------- /tests/data/base.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | model = "SomeModel"; 5 | compatible = "Nothing"; 6 | #address-cells = <2>; 7 | #size-cells = <2>; 8 | 9 | memory@0 { 10 | device_type = "memory"; 11 | reg = <0x00000000 0x00000000 0x00000000 0x20000000>; 12 | }; 13 | 14 | cpus { 15 | #address-cells = <1>; 16 | #size-cells = <0>; 17 | d10 = <10>; // hex: 0xa 18 | d23 = <23>; // hex: 0x17 19 | b101 = <0x5>; // hex: 0x5 20 | o17 = <017>; // hex: 0xf 21 | hd00d = <0xd00d>; // hex: 0xd00d 22 | 23 | // hex: 0x4d2 0x163e 0x2334 0xd80 24 | stuff = < 1234 5678 9012 3456>; 25 | 26 | 27 | bad-d-1 = <0>; // Hrm. 0 28 | bad-d-2 = <123456789012345>; 29 | bad-o-1 = <00>; 30 | bad-o-2 = <0123456123456>; 31 | }; 32 | 33 | }; 34 | -------------------------------------------------------------------------------- /.github/workflows/pythonpackage.yml: -------------------------------------------------------------------------------- 1 | name: Python package 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Set up Python ${{ matrix.python-version }} 16 | uses: actions/setup-python@v1 17 | with: 18 | python-version: ${{ matrix.python-version }} 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip 22 | pip install wheel 23 | pip install pytest 24 | pip install pytest-console-scripts 25 | pip install pytest-cov 26 | pip install coveralls 27 | - name: Test with pytest 28 | run: | 29 | pip install -e . 30 | pytest --cov=fdt tests/* 31 | #coveralls 32 | -------------------------------------------------------------------------------- /tests/data/fdtdump.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | /memreserve/ 0 0xe; 4 | / { 5 | model = "MyBoardName"; 6 | compatible = "MyBoardName", "MyBoardFamilyName"; 7 | #address-cells = <0x00000002>; 8 | #size-cells = <0x00000002>; 9 | cpus { 10 | linux,phandle = <0x00000001>; 11 | #address-cells = <0x00000001>; 12 | #size-cells = <0x00000000>; 13 | PowerPC,970@0 { 14 | device_type = "cpu"; 15 | reg = <0x00000000>; 16 | linux,boot-cpu; 17 | }; 18 | PowerPC,970@1 { 19 | device_type = "cpu"; 20 | reg = <0x00000001>; 21 | }; 22 | }; 23 | randomnode { 24 | string = "foo", "stuff"; 25 | bytes = [61 62 63 64 65]; 26 | nbytes = [80 ff]; 27 | child { 28 | }; 29 | }; 30 | memory@0 { 31 | device_type = "memory"; 32 | reg = <0x00000000 0x00000123 0x00000456 0x87654321>; 33 | }; 34 | chosen { 35 | bootargs = "root=/dev/sda2"; 36 | linux,platform = <0x00000600>; 37 | }; 38 | }; 39 | -------------------------------------------------------------------------------- /tests/data/subnode_iterate.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | #address-cells = <1>; 5 | #size-cells = <0>; 6 | 7 | test1 { 8 | subnodes = <2>; 9 | linux,phandle = <0x1>; 10 | #address-cells = <1>; 11 | #size-cells = <0>; 12 | PowerPC,970@0 { 13 | name = "PowerPC,970"; 14 | device_type = "cpu"; 15 | reg = <0x00000000>; 16 | clock-frequency = <1600000000>; 17 | timebase-frequency = <33333333>; 18 | linux,boot-cpu; 19 | i-cache-size = <65536>; 20 | d-cache-size = <32768>; 21 | another-sub-node { 22 | should-be-ignored; 23 | yet-another { 24 | should-also-be-ignored; 25 | }; 26 | }; 27 | }; 28 | 29 | PowerPC,970@1 { 30 | name = "PowerPC,970"; 31 | device_type = "cpu"; 32 | reg = <0x00000001>; 33 | clock-frequency = <1600000000>; 34 | timebase-frequency = <33333333>; 35 | i-cache-size = <65536>; 36 | d-cache-size = <32768>; 37 | }; 38 | }; 39 | 40 | test2 { 41 | subnodes = <0>; 42 | }; 43 | }; 44 | 45 | -------------------------------------------------------------------------------- /tests/test_fdt_parser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import fdt 3 | import pytest 4 | 5 | 6 | def test_01(data_dir): 7 | with open(os.path.join(data_dir, "addresses.dts")) as f: 8 | data = f.read() 9 | 10 | fdt_obj = fdt.parse_dts(data) 11 | assert fdt_obj.get_property('compatible').value == "test_addresses" 12 | assert fdt_obj.get_property('#address-cells').value == 2 13 | assert fdt_obj.get_property('#size-cells').value == 2 14 | assert fdt_obj.get_node('identity-bus@0') == fdt.Node('identity-bus@0') 15 | assert fdt_obj.get_node('simple-bus@1000000') == fdt.Node('simple-bus@1000000', 16 | fdt.PropWords('#address-cells', 2), 17 | fdt.PropWords('#size-cells', 1)) 18 | with pytest.raises(Exception): 19 | _ = fdt_obj.to_dtb() 20 | 21 | data = fdt_obj.to_dtb(17) 22 | assert isinstance(data, bytes) 23 | assert len(data) == 254 24 | 25 | 26 | def test_02(data_dir): 27 | with open(os.path.join(data_dir, "comments.dts")) as f: 28 | data = f.read() 29 | 30 | fdt_obj = fdt.parse_dts(data) 31 | assert len(fdt_obj.root.props) == 10 32 | assert len(fdt_obj.root.nodes) == 1 33 | 34 | 35 | def test_03(data_dir): 36 | with open(os.path.join(data_dir, "appendprop.dts")) as f: 37 | data = f.read() 38 | 39 | with pytest.raises(AssertionError): 40 | _ = fdt.parse_dts(data) 41 | 42 | 43 | def test_04(data_dir): 44 | with open(os.path.join(data_dir, "base.dts")) as f: 45 | data = f.read() 46 | 47 | with pytest.raises(AssertionError): 48 | _ = fdt.parse_dts(data) 49 | 50 | -------------------------------------------------------------------------------- /tests/test_fdt_class.py: -------------------------------------------------------------------------------- 1 | import os 2 | import fdt 3 | import pytest 4 | 5 | 6 | def test_fdt_constructor(): 7 | fdt_obj = fdt.FDT() 8 | 9 | assert isinstance(fdt_obj, fdt.FDT) 10 | assert fdt_obj.root == fdt.Node('/') 11 | 12 | 13 | def test_fdt_methods(): 14 | fdt_obj = fdt.FDT() 15 | 16 | assert isinstance(fdt_obj, fdt.FDT) 17 | 18 | fdt_obj.add_item(fdt.Property('prop')) 19 | fdt_obj.set_property("prop", 10, path="/node1") 20 | fdt_obj.set_property("prop", [10, 20], path="/node1/node2") 21 | fdt_obj.set_property("prop_bytes", b"\x10\x20", path="/node1/node2") 22 | 23 | assert fdt_obj.exist_node("node1") 24 | assert fdt_obj.exist_property("prop", "/node1") 25 | assert fdt_obj.exist_property("prop", "/node1/node2") 26 | 27 | items = fdt_obj.search("") 28 | assert len(items) == 7 29 | 30 | fdt_obj.remove_property("prop") 31 | 32 | items = fdt_obj.search("") 33 | assert len(items) == 6 34 | items = fdt_obj.search("", itype=fdt.ItemType.NODE) 35 | assert len(items) == 3 36 | items = fdt_obj.search("", itype=fdt.ItemType.PROP) 37 | assert len(items) == 3 38 | items = fdt_obj.search("", itype=fdt.ItemType.PROP_BASE) 39 | assert len(items) == 0 40 | items = fdt_obj.search("", itype=fdt.ItemType.PROP_WORDS) 41 | assert len(items) == 2 42 | items = fdt_obj.search("", itype=fdt.ItemType.PROP_BYTES) 43 | assert len(items) == 1 44 | items = fdt_obj.search("", itype=fdt.ItemType.PROP_STRINGS) 45 | assert len(items) == 0 46 | 47 | fdt_obj.update_phandles() 48 | 49 | items = fdt_obj.search("phandle", itype=fdt.ItemType.PROP, path="node1", recursive=False) 50 | assert len(items) == 1 51 | 52 | fdt_obj.remove_node("node1") 53 | 54 | assert len(fdt_obj.search("prop")) == 0 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # pyCharm 107 | .idea 108 | 109 | # VSCode 110 | .vscode 111 | 112 | # other 113 | temp 114 | -------------------------------------------------------------------------------- /tests/test_cli_tool.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | 4 | 5 | @pytest.mark.script_launch_mode('subprocess') 6 | def test_pydtc_pack(script_runner, data_dir, temp_dir): 7 | src_file = os.path.join(data_dir, 'imx7d-sdb.dts') 8 | out_file = os.path.join(temp_dir, 'imx7d-sdb.dtb') 9 | 10 | ret = script_runner.run('pydtc', 'pack', '-p', '-o ' + out_file, src_file) 11 | assert ret.success 12 | assert ret.stderr == '' 13 | 14 | ret = script_runner.run('pydtc', 'pack', '-p', '-o ' + out_file) 15 | assert not ret.success 16 | assert 'error' in ret.stderr 17 | 18 | ret = script_runner.run('pydtc', 'pack', '-p', '-o ' + out_file, 'not_valid.dts') 19 | assert not ret.success 20 | assert ret.stderr == 'File doesnt exist: not_valid.dts\n' 21 | 22 | 23 | @pytest.mark.script_launch_mode('subprocess') 24 | def test_pydtc_unpack(script_runner, data_dir, temp_dir): 25 | src_file = os.path.join(data_dir, 'imx7d-sdb.dtb') 26 | out_file = os.path.join(temp_dir, 'imx7d-sdb.dts') 27 | 28 | ret = script_runner.run('pydtc', 'unpack', '-o ' + out_file, src_file) 29 | assert ret.success 30 | assert ret.stderr == '' 31 | 32 | 33 | @pytest.mark.script_launch_mode('subprocess') 34 | def test_pydtc_merge(script_runner, data_dir, temp_dir): 35 | in1_file = os.path.join(data_dir, 'fdtdump.dts') 36 | in2_file = os.path.join(data_dir, 'addresses.dts') 37 | out_file = os.path.join(temp_dir, 'merged.dts') 38 | 39 | ret = script_runner.run('pydtc', 'merge', out_file, in1_file, in2_file) 40 | assert ret.success 41 | assert ret.stderr == '' 42 | 43 | 44 | @pytest.mark.script_launch_mode('subprocess') 45 | def test_pydtc_diff(script_runner, data_dir, temp_dir): 46 | in1_file = os.path.join(data_dir, 'fdtdump.dts') 47 | in2_file = os.path.join(data_dir, 'addresses.dts') 48 | out_dir = os.path.join(temp_dir, 'diff_out') 49 | 50 | ret = script_runner.run('pydtc', 'diff', '-o ' + out_dir, in1_file, in2_file) 51 | assert ret.success 52 | assert ret.stderr == '' 53 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2017 Martin Olejar 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from os import path 18 | from setuptools import setup 19 | from fdt import __version__, __license__, __author__, __contact__ 20 | 21 | 22 | def get_long_description(): 23 | with open(path.join(path.dirname(path.abspath(__file__)), 'README.md'), encoding='utf8') as fp: 24 | return fp.read() 25 | 26 | 27 | setup( 28 | name='fdt', 29 | author=__author__, 30 | version=__version__, 31 | license=__license__, 32 | author_email=__contact__, 33 | url='https://github.com/molejar/pyFDT', 34 | description='Flattened Device Tree Python Module', 35 | long_description=get_long_description(), 36 | long_description_content_type='text/markdown', 37 | python_requires='>=3.5', 38 | packages=['fdt'], 39 | classifiers=[ 40 | 'Programming Language :: Python :: 3.5', 41 | 'Programming Language :: Python :: 3.6', 42 | 'Programming Language :: Python :: 3.7', 43 | 'Programming Language :: Python :: 3.8', 44 | 'Programming Language :: Python :: 3.9', 45 | 'Programming Language :: Python :: 3.10', 46 | 'Operating System :: OS Independent', 47 | 'Environment :: Console', 48 | 'License :: OSI Approved :: Apache Software License', 49 | 'Topic :: Scientific/Engineering', 50 | 'Topic :: Software Development :: Embedded Systems', 51 | 'Topic :: Utilities' 52 | ], 53 | entry_points={ 54 | 'console_scripts': [ 55 | 'pydtc = fdt.__main__:main' 56 | ], 57 | } 58 | ) 59 | -------------------------------------------------------------------------------- /fdt/misc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Martin Olejar 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import re 16 | from string import printable 17 | 18 | 19 | def is_string(data): 20 | """ Check property string validity """ 21 | if not len(data): 22 | return None 23 | if data[-1] != 0: 24 | return None 25 | pos = 0 26 | while pos < len(data): 27 | posi = pos 28 | while pos < len(data) and \ 29 | data[pos] != 0 and \ 30 | data[pos] in printable.encode() and \ 31 | data[pos] not in (ord('\r'), ord('\n')): 32 | pos += 1 33 | if data[pos] != 0 or pos == posi: 34 | return None 35 | pos += 1 36 | return True 37 | 38 | 39 | def extract_string(data, offset=0): 40 | """ Extract string """ 41 | str_end = offset 42 | while data[str_end] != 0: 43 | str_end += 1 44 | return data[offset:str_end].decode("ascii") 45 | 46 | 47 | def line_offset(tabsize, offset, string): 48 | offset = " " * (tabsize * offset) 49 | return offset + string 50 | 51 | 52 | def get_version_info(text): 53 | ret = dict() 54 | for line in text.split('\n'): 55 | line = line.rstrip('\0') 56 | if line and line.startswith('/ {'): 57 | break 58 | if line and line.startswith('//'): 59 | line = line.replace('//', '').replace(':', '') 60 | line = line.split() 61 | if line[0] in ('version', 'last_comp_version', 'boot_cpuid_phys'): 62 | ret[line[0]] = int(line[1], 0) 63 | return ret 64 | 65 | 66 | def strip_comments(text): 67 | text = re.sub(r'//.*?(\r\n?|\n)|/\*.*?\*/', '\n', text, flags=re.S) 68 | return text 69 | 70 | 71 | def split_to_lines(text): 72 | lines = [] 73 | mline = str() 74 | for line in text.split('\n'): 75 | line = line.replace('\t', ' ') 76 | line = line.rstrip('\0') 77 | line = line.rstrip(' ') 78 | line = line.lstrip(' ') 79 | if not line or line.startswith('/dts-'): 80 | continue 81 | if line.endswith('{') or line.endswith(';'): 82 | line = line.replace(';', '') 83 | lines.append(mline + line) 84 | mline = str() 85 | else: 86 | mline += line + ' ' 87 | 88 | return lines 89 | 90 | -------------------------------------------------------------------------------- /fdt/header.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Martin Olejar 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from struct import unpack_from, pack 16 | 17 | 18 | ######################################################################################################################## 19 | # Binary Blob Constants 20 | ######################################################################################################################## 21 | 22 | DTB_BEGIN_NODE = 0x1 23 | DTB_END_NODE = 0x2 24 | DTB_PROP = 0x3 25 | DTB_NOP = 0x4 26 | DTB_END = 0x9 27 | 28 | 29 | ######################################################################################################################## 30 | # Header Class 31 | ######################################################################################################################## 32 | 33 | class Header: 34 | 35 | MIN_SIZE = 4 * 7 36 | MAX_SIZE = 4 * 10 37 | 38 | MAX_VERSION = 17 39 | 40 | MAGIC_NUMBER = 0xD00DFEED 41 | 42 | @property 43 | def version(self): 44 | return self._version 45 | 46 | @version.setter 47 | def version(self, value): 48 | if value > self.MAX_VERSION: 49 | raise ValueError("Invalid Version {}, use: 0 - 17 !".format(value)) 50 | # update size and padding 51 | self._size = self.MIN_SIZE 52 | if value >= 2: 53 | self._size += 4 54 | if value >= 3: 55 | self._size += 4 56 | if value >= 17: 57 | self._size += 4 58 | self._padding = 8 - (self._size % 8) if self._size % 8 != 0 else 0 59 | self._version = value 60 | self.last_comp_version = value - 1 61 | 62 | @property 63 | def size(self): 64 | return self._size + self._padding 65 | 66 | @property 67 | def padding(self): 68 | return self._padding 69 | 70 | def __init__(self): 71 | # private variables 72 | self._version = None 73 | self._size = 0 74 | self._padding = 0 75 | # public variables 76 | self.total_size = 0 77 | self.off_dt_struct = 0 78 | self.off_dt_strings = 0 79 | self.off_mem_rsvmap = 0 80 | self.last_comp_version = 0 81 | # version depend variables 82 | self.boot_cpuid_phys = 0 83 | self.size_dt_strings = None 84 | self.size_dt_struct = None 85 | 86 | def __str__(self): 87 | return ''.format(self.version, self.size) 88 | 89 | def info(self): 90 | nfo = 'FDT Header:' 91 | nfo += '- Version: {}'.format(self.version) 92 | nfo += '- Size: {}'.format(self.size) 93 | return nfo 94 | 95 | def export(self) -> bytes: 96 | """ 97 | 98 | :return: 99 | """ 100 | if self.version is None: 101 | raise Exception("Header version must be specified !") 102 | 103 | blob = pack('>7I', self.MAGIC_NUMBER, self.total_size, self.off_dt_struct, self.off_dt_strings, 104 | self.off_mem_rsvmap, self.version, self.last_comp_version) 105 | if self.version >= 2: 106 | blob += pack('>I', self.boot_cpuid_phys) 107 | if self.version >= 3: 108 | blob += pack('>I', self.size_dt_strings) 109 | if self.version >= 17: 110 | blob += pack('>I', self.size_dt_struct) 111 | if self.padding: 112 | blob += bytes([0] * self.padding) 113 | 114 | return blob 115 | 116 | @classmethod 117 | def parse(cls, data: bytes, offset: int = 0): 118 | """ 119 | 120 | :param data: 121 | :param offset: 122 | """ 123 | if len(data) < (offset + cls.MIN_SIZE): 124 | raise ValueError('Data size too small !') 125 | 126 | header = cls() 127 | (magic_number, 128 | header.total_size, 129 | header.off_dt_struct, 130 | header.off_dt_strings, 131 | header.off_mem_rsvmap, 132 | header.version, 133 | header.last_comp_version) = unpack_from('>7I', data, offset) 134 | offset += cls.MIN_SIZE 135 | 136 | if magic_number != cls.MAGIC_NUMBER: 137 | raise Exception('Invalid Magic Number') 138 | if header.last_comp_version > cls.MAX_VERSION - 1: 139 | raise Exception('Invalid last compatible Version {}'.format(header.last_comp_version)) 140 | 141 | if header.version >= 2: 142 | header.boot_cpuid_phys = unpack_from('>I', data, offset)[0] 143 | offset += 4 144 | 145 | if header.version >= 3: 146 | header.size_dt_strings = unpack_from('>I', data, offset)[0] 147 | offset += 4 148 | 149 | if header.version >= 17: 150 | header.size_dt_struct = unpack_from('>I', data, offset)[0] 151 | offset += 4 152 | 153 | return header 154 | -------------------------------------------------------------------------------- /tests/test_items_api.py: -------------------------------------------------------------------------------- 1 | import fdt 2 | import struct 3 | import pytest 4 | 5 | 6 | def test_header(): 7 | header = fdt.Header() 8 | header.version = fdt.Header.MAX_VERSION 9 | 10 | assert header.version == fdt.Header.MAX_VERSION 11 | assert header.size == fdt.Header.MAX_SIZE 12 | 13 | with pytest.raises(ValueError): 14 | header.version = fdt.Header.MAX_VERSION + 1 15 | 16 | blob = struct.pack('>7I', fdt.Header.MAGIC_NUMBER, 0, 0, 0, 0, 1, 1) 17 | header = fdt.Header.parse(blob) 18 | 19 | assert header.version == 1 20 | assert header.size == 32 21 | 22 | 23 | def test_base_property(): 24 | prop = fdt.Property('prop') 25 | 26 | assert isinstance(prop, fdt.Property) 27 | assert prop.name == 'prop' 28 | 29 | with pytest.raises(AssertionError): 30 | _ = fdt.Property('prop\0') 31 | with pytest.raises(AssertionError): 32 | _ = fdt.Property(5) 33 | 34 | prop1 = fdt.Property('prop1') 35 | assert prop1 != prop 36 | 37 | prop1 = fdt.Property('prop') 38 | assert prop1 == prop 39 | 40 | str_data = prop.to_dts() 41 | assert str_data == 'prop;\n' 42 | 43 | blob_data, str_data, pos = prop.to_dtb('') 44 | assert blob_data == struct.pack('>III', 0x03, 0, 0) 45 | assert str_data == 'prop\0' 46 | assert pos == 12 47 | 48 | 49 | def test_strings_property(): 50 | prop = fdt.PropStrings('prop', 'test', 'test') 51 | 52 | assert isinstance(prop, fdt.PropStrings) 53 | assert prop.name == 'prop' 54 | assert len(prop) == 2 55 | assert prop.data == ['test', 'test'] 56 | 57 | with pytest.raises(AssertionError): 58 | prop.append('test\0') 59 | with pytest.raises(AssertionError): 60 | prop.append(5) 61 | 62 | prop1 = fdt.PropStrings('prop', 'test', 'test', 'test') 63 | assert prop1 != prop 64 | 65 | prop.append("test") 66 | assert len(prop) == 3 67 | assert prop1 == prop 68 | 69 | str_data = prop.to_dts() 70 | assert str_data == 'prop = "test", "test", "test";\n' 71 | 72 | 73 | def test_words_property(): 74 | prop = fdt.PropWords('prop', 0x11111111, 0x55555555) 75 | 76 | assert isinstance(prop, fdt.PropWords) 77 | assert prop.name == 'prop' 78 | assert len(prop) == 2 79 | assert prop.data == [0x11111111, 0x55555555] 80 | 81 | with pytest.raises(AssertionError): 82 | prop.append('test') 83 | with pytest.raises(AssertionError): 84 | prop.append(0x5555555555) 85 | 86 | prop1 = fdt.PropWords('prop', 0x11111111, 0x55555555, 0x00) 87 | assert prop1 != prop 88 | 89 | prop.append(0x00) 90 | assert len(prop) == 3 91 | assert prop1 == prop 92 | 93 | str_data = prop.to_dts() 94 | assert str_data == 'prop = <0x11111111 0x55555555 0x0>;\n' 95 | 96 | 97 | def test_bytes_property(): 98 | prop = fdt.PropBytes('prop', 0x10, 0x50) 99 | 100 | assert isinstance(prop, fdt.PropBytes) 101 | assert prop.name == 'prop' 102 | assert len(prop) == 2 103 | assert prop.data == b"\x10\x50" 104 | 105 | with pytest.raises(AssertionError): 106 | prop.append('test') 107 | with pytest.raises(AssertionError): 108 | prop.append(256) 109 | 110 | prop1 = fdt.PropBytes('prop', data=b"\x10\x50\x00") 111 | assert prop1 != prop 112 | 113 | prop.append(0x00) 114 | assert len(prop) == 3 115 | assert prop1 == prop 116 | 117 | str_data = prop.to_dts() 118 | assert str_data == 'prop = [10 50 00];\n' 119 | 120 | 121 | def test_node(): 122 | # create node object 123 | node = fdt.Node('/') 124 | node.append(fdt.Property('prop')) 125 | node.append(fdt.PropStrings('prop_str', 'test', 'test')) 126 | node.append(fdt.PropWords('prop_word', 0x11111111, 0x55555555)) 127 | node.append(fdt.PropBytes('prop_byte', 0x10, 0x50)) 128 | subnode0 = fdt.Node('subnode0') 129 | subnode0.append(fdt.Property('prop0')) 130 | subnode0.append(fdt.PropStrings('prop_str0', 'test')) 131 | subnode1 = fdt.Node('subnode1') 132 | subnode1.append(fdt.Property('prop1')) 133 | subnode1.append(fdt.PropWords('prop_word1', 0x11111111)) 134 | subnode0.append(subnode1) 135 | node.append(subnode0) 136 | 137 | assert isinstance(node, fdt.Node) 138 | assert node.name == '/' 139 | assert len(node.props) == 4 140 | assert len(node.nodes) == 1 141 | 142 | # Use only node constructor 143 | new_node = fdt.Node('/', 144 | fdt.Property('prop'), 145 | fdt.PropStrings('prop_str', 'test', 'test'), 146 | fdt.PropWords('prop_word', 0x11111111, 0x55555555), 147 | fdt.PropBytes('prop_byte', 0x10, 0x50), 148 | fdt.Node('subnode0', 149 | fdt.Property('prop0'), 150 | fdt.PropStrings('prop_str0', 'test'), 151 | fdt.Node('subnode1', 152 | fdt.Property('prop1'), 153 | fdt.PropWords('prop_word1', 0x11111111) 154 | ) 155 | ) 156 | ) 157 | 158 | assert node == new_node 159 | 160 | with pytest.raises(AssertionError): 161 | node.append('test') 162 | with pytest.raises(Exception): 163 | node.append(256) 164 | with pytest.raises(Exception): 165 | node.append(fdt.Property('prop')) 166 | 167 | copy_node = node.copy() 168 | assert copy_node == node 169 | 170 | copy_node.set_property('prop_word', [0x10, 0x50]) 171 | assert copy_node != node 172 | 173 | # export to dts 174 | str_data = node.to_dts() 175 | out = "/ {\n" 176 | out += " prop;\n" 177 | out += " prop_str = \"test\", \"test\";\n" 178 | out += " prop_word = <0x11111111 0x55555555>;\n" 179 | out += " prop_byte = [10 50];\n" 180 | out += " subnode0 {\n" 181 | out += " prop0;\n" 182 | out += " prop_str0 = \"test\";\n" 183 | out += " subnode1 {\n" 184 | out += " prop1;\n" 185 | out += " prop_word1 = <0x11111111>;\n" 186 | out += " };\n" 187 | out += " };\n" 188 | out += "};\n" 189 | 190 | assert str_data == out 191 | 192 | 193 | def test_node_set_property(): 194 | root_node = fdt.Node('/') 195 | 196 | # add properties into node by set_property method 197 | root_node.set_property('prop', None) 198 | root_node.set_property('int_prop', 1000) 199 | root_node.set_property('list_int_prop', [1, 2]) 200 | root_node.set_property('str_prop', 'value') 201 | root_node.set_property('list_str_prop', ['value1', 'value2']) 202 | root_node.set_property('bytes_prop', b'\x00\x55\x66') 203 | 204 | # validate types and values 205 | assert len(root_node.props) == 6 206 | assert isinstance(root_node.get_property('prop'), fdt.Property) 207 | assert isinstance(root_node.get_property('int_prop'), fdt.PropWords) 208 | assert root_node.get_property('int_prop').value == 1000 209 | assert isinstance(root_node.get_property('list_int_prop'), fdt.PropWords) 210 | assert root_node.get_property('list_int_prop').data == [1, 2] 211 | assert isinstance(root_node.get_property('str_prop'), fdt.PropStrings) 212 | assert root_node.get_property('str_prop').value == 'value' 213 | assert isinstance(root_node.get_property('list_str_prop'), fdt.PropStrings) 214 | assert root_node.get_property('list_str_prop').data == ['value1', 'value2'] 215 | assert isinstance(root_node.get_property('bytes_prop'), fdt.PropBytes) 216 | assert root_node.get_property('bytes_prop').data == b'\x00\x55\x66' 217 | 218 | # update property in node by set_property method 219 | root_node.set_property('list_int_prop', [1, 2, 3]) 220 | 221 | # validate property value 222 | assert root_node.get_property('list_int_prop').data == [1, 2, 3] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flattened Device Tree Python Module 2 | 3 | [![Build Status](https://travis-ci.org/molejar/pyFDT.svg?branch=master)](https://travis-ci.org/molejar/pyFDT) 4 | [![Coverage Status](https://coveralls.io/repos/github/molejar/pyFDT/badge.svg)](https://coveralls.io/github/molejar/pyFDT) 5 | [![PyPI Status](https://img.shields.io/pypi/v/fdt.svg)](https://pypi.python.org/pypi/fdt) 6 | [![Python Version](https://img.shields.io/pypi/pyversions/fdt.svg)](https://www.python.org) 7 | 8 | This python module is usable for manipulation with [Device Tree Data](https://www.devicetree.org/) and primary was 9 | created for [imxsb tool](https://github.com/molejar/imxsb) 10 | 11 | > Some parts in this module have been inspired from: https://github.com/superna9999/pyfdt project. 12 | 13 | ## Installation 14 | 15 | ```bash 16 | pip install fdt 17 | ``` 18 | 19 | To install the latest version from master branch execute in shell following command: 20 | 21 | ```bash 22 | pip install -U https://github.com/molejar/pyFDT/archive/master.zip 23 | ``` 24 | 25 | In case of development, install it from cloned sources: 26 | 27 | ```bash 28 | git clone https://github.com/molejar/pyFDT.git 29 | cd pyFDT 30 | pip install -U -e . 31 | ``` 32 | 33 | **NOTE:** You may run into a permissions issues running these commands. Here are a few options how to fix it: 34 | 35 | 1. Run with `sudo` to install `fdt` and dependencies globally 36 | 2. Specify the `--user` option to install locally into your home directory (export "~/.local/bin" into PATH variable if haven't). 37 | 3. Run the command in a [virtualenv](https://virtualenv.pypa.io/en/latest/) local to a specific project working set. 38 | 39 | ## Usage 40 | 41 | **fdt** module has intuitive and self describing API, what is presented in following example. Many of general requirements 42 | for manipulation with FDT Nodes, Properties and dts/dtb files are already implemented. 43 | 44 | ```python 45 | import fdt 46 | 47 | #----------------------------------------------- 48 | # convert *.dtb to *.dts 49 | # ---------------------------------------------- 50 | with open("example.dtb", "rb") as f: 51 | dtb_data = f.read() 52 | 53 | dt1 = fdt.parse_dtb(dtb_data) 54 | 55 | with open("example.dts", "w") as f: 56 | f.write(dt1.to_dts()) 57 | 58 | #----------------------------------------------- 59 | # convert *.dts to *.dtb 60 | # ---------------------------------------------- 61 | with open("example.dts", "r") as f: 62 | dts_text = f.read() 63 | 64 | dt2 = fdt.parse_dts(dts_text) 65 | 66 | with open("example.dtb", "wb") as f: 67 | f.write(dt2.to_dtb(version=17)) 68 | 69 | #----------------------------------------------- 70 | # add new Node into dt2 71 | # ---------------------------------------------- 72 | # create node instance 73 | node = fdt.Node('test_node1') 74 | 75 | # add properties 76 | node.append(fdt.Property('basic_property')) 77 | node.append(fdt.PropStrings('string_property', 'value1', 'value2')) 78 | node.append(fdt.PropWords('words_property', 0x80000000)) 79 | node.append(fdt.PropBytes('bytes_property', 0x00, 0x01, 0x02)) 80 | 81 | # PropBytes constructor take also complex data object as bytes() or bytearray() 82 | node.append(fdt.PropBytes('bytes_property2', data=b"\x00\x01\x02")) 83 | 84 | # add created node into root path of dt2 85 | dt2.add_item(node) 86 | 87 | # use set_property method to update or create new property 88 | dt2.set_property('words_property', [0, 1], path='/test_node1') 89 | dt2.set_property('bytes_property', b"\x00\x01", path='/test_node1') 90 | dt2.set_property('string_property', ['value1', 'value2', 'value3'], path='/test_node1') 91 | 92 | # use search method for find all string properties and then update it 93 | items = dt2.search("", itype=fdt.ItemType.PROP_STRINGS, path="/test_node1") 94 | for item in items: 95 | item.data = ['value1', 'value2'] 96 | 97 | #----------------------------------------------- 98 | # merge dt2 into dt1 99 | # ---------------------------------------------- 100 | dt1.merge(dt2) 101 | 102 | with open("merged.dtb", "wb") as f: 103 | f.write(dt1.to_dtb(version=17)) 104 | 105 | #----------------------------------------------- 106 | # diff two fdt objects 107 | # ---------------------------------------------- 108 | out = fdt.diff(dt1, dt2) 109 | 110 | print(out[0]) # same in dt1 and dt2 111 | print(out[1]) # specific for dt1 112 | print(out[2]) # specific for dt2 113 | ``` 114 | 115 | ## [ pydtc ] Tool 116 | 117 | The python device tree converter **pydtc** is a tool for conversion *.dts to *.dtb and vice versa. Is distributed 118 | together with **fdt** module. This tool can be in some cases used as replacement of [device tree compiler](https://git.kernel.org/pub/scm/utils/dtc/dtc.git). 119 | 120 | ```bash 121 | $ pydtc -h 122 | 123 | usage: pydtc [-h] [-v] {pack,unpack,merge,diff} ... 124 | 125 | Flat Device Tree (FDT) tool for manipulation with *.dtb and *.dts files 126 | 127 | positional arguments: 128 | {pack,unpack,merge,diff} 129 | pack Pack *.dts into binary blob (*.dtb) 130 | unpack Unpack *.dtb into readable format (*.dts) 131 | merge Merge more files in *.dtb or *.dts format 132 | diff Compare two files in *.dtb or *.dts format 133 | 134 | optional arguments: 135 | -h, --help show this help message and exit 136 | -v, --version show program's version number and exit 137 | 138 | ``` 139 | 140 | #### $ pydtc unpack [-h] [-s TAB_SIZE] [-o DTS_FILE] dtb_file 141 | 142 | Unpack Device Tree from binary blob *.dtb into readable text file *.dts 143 | 144 | **dtb_file** - Single DTB file with `dtb` extension 145 | 146 | ##### optional arguments: 147 | * **-h, --help** - Show this help message and exit 148 | * **-s TAB_SIZE** - Tabulator Size 149 | * **-o DTS_FILE** - Output path/file name (*.dts) 150 | 151 | ##### Example: 152 | 153 | ```bash 154 | pydtc unpack test.dtb 155 | 156 | DTS saved as: test.dts 157 | ``` 158 | 159 | #### $ pydtc pack [-h] [-v VERSION] [-l LC_VERSION] [-c CPU_ID] [-p] [-o DTB_FILE] dts_file 160 | 161 | 162 | Pack Device Tree from readable text file *.dts into binary blob *.dtb 163 | 164 | **dts_file** - Single DTS file as *.dts 165 | 166 | ##### optional arguments: 167 | * **-h, --help** - Show this help message and exit 168 | * **-v VERSION** - DTB Version 169 | * **-l LC_VERSION** - DTB Last Compatible Version 170 | * **-c CPU_ID** - Boot CPU ID 171 | * **-p** - Update phandle 172 | * **-o DTB_FILE** - Output path/file name (*.dtb) 173 | 174 | ##### Example: 175 | 176 | ``` bash 177 | pydtc pack -v 17 test.dts 178 | 179 | DTB saved as: test.dtb 180 | ``` 181 | 182 | #### $ pydtc merge [-h] [-t {auto,dts,dtb}] [-s TAB_SIZE] out_file in_files [in_files ...] 183 | 184 | 185 | Merge two and more *.dtb or *.dts files into one *.dts file 186 | 187 | **out_file** - The output file name with *.dts extension
188 | **in_files** - Two or more input files with *.dtb or *.dts extension 189 | 190 | ##### optional arguments: 191 | * **-h, --help** - Show this help message and exit 192 | * **-t {auto,dts,dtb}** - Input file type: 'auto', 'dts', 'dtb' (default: auto) 193 | * **-s TAB_SIZE** - Tabulator Size 194 | 195 | ##### Example: 196 | 197 | ```bash 198 | pydtc merge out.dts test1.dtb test2.dtb 199 | 200 | Output saved as: out.dts 201 | ``` 202 | 203 | #### $ pydtc diff [-h] [-t {auto,dts,dtb}] [-o OUT_DIR] in_file1 in_file2 204 | 205 | Compare two dtb/dts files and generate 3 dts files (same in 1 and 2, specific for 1, specific for 2) 206 | 207 | **in_file1** - Input file 1
208 | **in_file2** - Input file 2 209 | 210 | ##### optional arguments: 211 | * **-h, --help** - Show this help message and exit 212 | * **-t {auto,dts,dtb}** - Input file type: 'auto', 'dts', 'dtb' (default: auto) 213 | * **-o OUT_DIR** - Output directory (default: diff_out) 214 | 215 | ##### Example: 216 | 217 | ```bash 218 | pydtc diff test1.dtb test2.dtb 219 | 220 | Output saved into: diff_out 221 | ``` 222 | -------------------------------------------------------------------------------- /fdt/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2017 Martin Olejar 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import os 18 | import sys 19 | import fdt 20 | import argparse 21 | 22 | 23 | ######################################################################################################################## 24 | # Helper Functions 25 | ######################################################################################################################## 26 | def parse_fdt(file_path: str, file_type: str): 27 | """ 28 | Parse *.dtb ot *.dts input file and return FDT object 29 | 30 | :param file_path: The path to input file 31 | :param file_type: File type 'dtb', 'dts' or 'auto' 32 | """ 33 | 34 | if not os.path.exists(file_path): 35 | raise Exception('File doesnt exist: {}'.format(file_path)) 36 | 37 | if file_type == 'auto': 38 | if file_path.endswith(".dtb"): 39 | file_type = 'dtb' 40 | elif file_path.endswith(".dts"): 41 | file_type = 'dts' 42 | else: 43 | raise Exception('Not supported file extension: {}'.format(file_path)) 44 | 45 | if file_type == 'dtb': 46 | with open(file_path, 'rb') as f: 47 | obj = fdt.parse_dtb(f.read()) 48 | else: 49 | with open(file_path, 'r') as f: 50 | obj = fdt.parse_dts(f.read(), os.path.dirname(file_path)) 51 | 52 | return obj 53 | 54 | 55 | ######################################################################################################################## 56 | # Commands Functions 57 | ######################################################################################################################## 58 | def pack(in_file: str, out_file: str, version: int, lc_version: int, cpu_id: int, update_phandles: bool): 59 | """ 60 | The implementation of pack command. 61 | 62 | :param in_file: Input File Path 63 | :param out_file: Output File Path 64 | :param version: DTB version 65 | :param lc_version: DTB Last Compatible Version 66 | :param cpu_id: Boot CPU ID 67 | :param update_phandles: If True phandles will be updated 68 | """ 69 | 70 | if version is not None and version > fdt.Header.MAX_VERSION: 71 | raise Exception("DTB Version must be lover or equal {} !".format(fdt.Header.MAX_VERSION)) 72 | 73 | fdt_obj = parse_fdt(in_file, 'dts') 74 | if update_phandles: 75 | fdt_obj.update_phandles() 76 | raw_data = fdt_obj.to_dtb(version, lc_version, cpu_id) 77 | 78 | with open(out_file, 'wb') as f: 79 | f.write(raw_data) 80 | 81 | print(" DTB saved as: {}".format(out_file)) 82 | 83 | 84 | def unpack(in_file: str, out_file: str, tab_size): 85 | """ 86 | The implementation of unpack command. 87 | 88 | :param in_file: Input File Path 89 | :param out_file: Output File Path 90 | :param tab_size: Tabulator size in count of spaces 91 | """ 92 | fdt_obj = parse_fdt(in_file, 'dtb') 93 | 94 | with open(out_file, 'w') as f: 95 | f.write(fdt_obj.to_dts(tab_size)) 96 | 97 | print(" DTS saved as: {}".format(out_file)) 98 | 99 | 100 | def merge(out_file: str, in_files: list, file_type: str, tab_size: int): 101 | """ 102 | The implementation of merge command. 103 | 104 | :param out_file: Output File Path 105 | :param in_files: Input Files Path 106 | :param file_type: The type of input files 107 | :param tab_size: Tabulator size in count of spaces 108 | """ 109 | fdt_obj = None 110 | 111 | for file in in_files: 112 | obj = parse_fdt(file, file_type) 113 | if fdt_obj is None: 114 | fdt_obj = obj 115 | else: 116 | fdt_obj.merge(obj) 117 | 118 | with open(out_file, 'w') as f: 119 | f.write(fdt_obj.to_dts(tab_size)) 120 | 121 | print(" Output saved as: {}".format(out_file)) 122 | 123 | 124 | def diff(in_file1: str, in_file2: str, file_type: str, out_dir: str): 125 | """ 126 | The implementation of diff command. 127 | 128 | :param in_file1: Input File1 Path 129 | :param in_file2: Input File2 Path 130 | :param file_type: The type of input files 131 | :param out_dir: Path to output directory 132 | """ 133 | # load input files 134 | fdt1 = parse_fdt(in_file1, file_type) 135 | fdt2 = parse_fdt(in_file2, file_type) 136 | 137 | # compare it 138 | diff = fdt.diff(fdt1, fdt2) 139 | if diff[0].empty: 140 | print(" Input files are completely different !") 141 | sys.exit() 142 | 143 | # create output directory 144 | os.makedirs(out_dir, exist_ok=True) 145 | 146 | # get names for output files 147 | file_name = ( 148 | "same.dts", 149 | os.path.splitext(os.path.basename(in_file1))[0] + ".dts", 150 | os.path.splitext(os.path.basename(in_file2))[0] + ".dts") 151 | 152 | # save output files 153 | for index, obj in enumerate(diff): 154 | if not obj.empty: 155 | with open(os.path.join(out_dir, file_name[index]), 'w') as f: 156 | f.write(obj.to_dts()) 157 | 158 | print(" Diff output saved into: {}".format(out_dir)) 159 | 160 | 161 | ######################################################################################################################## 162 | # Main 163 | ######################################################################################################################## 164 | def main(): 165 | # cli interface 166 | parser = argparse.ArgumentParser( 167 | prog="pydtc", 168 | description="Flat Device Tree (FDT) tool for manipulation with *.dtb and *.dts files") 169 | parser.add_argument('-v', '--version', action='version', version=fdt.__version__) 170 | subparsers = parser.add_subparsers(dest='command') 171 | 172 | # pack command 173 | pack_parser = subparsers.add_parser('pack', help='Pack *.dts into binary blob (*.dtb)') 174 | pack_parser.add_argument('dts_file', nargs=1, help='Path to *.dts file') 175 | pack_parser.add_argument('-v', dest='version', type=int, help='DTB Version') 176 | pack_parser.add_argument('-l', dest='lc_version', type=int, help='DTB Last Compatible Version') 177 | pack_parser.add_argument('-c', dest='cpu_id', type=int, help='Boot CPU ID') 178 | pack_parser.add_argument('-p', dest='phandles', action='store_true', help='Update phandles') 179 | pack_parser.add_argument('-o', dest='dtb_file', type=str, help='Output path with file name (*.dtb)') 180 | 181 | # unpack command 182 | unpack_parser = subparsers.add_parser('unpack', help='Unpack *.dtb into readable format (*.dts)') 183 | unpack_parser.add_argument('dtb_file', nargs=1, help='Path to *.dtb file') 184 | unpack_parser.add_argument('-s', dest='tab_size', type=int, default=4, help='Tabulator Size') 185 | unpack_parser.add_argument('-o', dest='dts_file', type=str, help='Output path with file name (*.dts)') 186 | 187 | # merge command 188 | merge_parser = subparsers.add_parser('merge', help='Merge more files in *.dtb or *.dts format') 189 | merge_parser.add_argument('out_file', nargs=1, help='Output path with file name (*.dts or *.dtb)') 190 | merge_parser.add_argument('in_files', nargs='+', help='Path to input files') 191 | merge_parser.add_argument('-t', dest='type', type=str, choices=['auto', 'dts', 'dtb'], help='Input file type') 192 | merge_parser.add_argument('-s', dest='tab_size', type=int, default=4, help='Tabulator Size for dts') 193 | 194 | # diff command 195 | diff_parser = subparsers.add_parser('diff', help='Compare two files in *.dtb or *.dts format') 196 | diff_parser.add_argument('in_file1', nargs=1, help='Path to dts or dtb file') 197 | diff_parser.add_argument('in_file2', nargs=1, help='Path to dts or dtb file') 198 | diff_parser.add_argument('-t', dest='type', type=str, choices=['auto', 'dts', 'dtb'], help='Input file type') 199 | diff_parser.add_argument('-o', dest='out_dir', type=str, help='Output directory') 200 | 201 | args = parser.parse_args() 202 | 203 | try: 204 | if args.command == 'pack': 205 | in_file = args.dts_file[0] 206 | if args.dtb_file is None: 207 | out_file = os.path.splitext(os.path.basename(in_file))[0] + ".dtb" 208 | else: 209 | out_file = args.dtb_file.lstrip() 210 | pack(in_file, out_file, args.version, args.lc_version, args.cpu_id, args.phandles) 211 | 212 | elif args.command == 'unpack': 213 | in_file = args.dtb_file[0] 214 | if args.dts_file is None: 215 | out_file = os.path.splitext(os.path.basename(in_file))[0] + ".dts" 216 | else: 217 | out_file = args.dts_file.lstrip() 218 | unpack(in_file, out_file, args.tab_size) 219 | 220 | elif args.command == 'merge': 221 | merge(args.out_file[0], args.in_files, args.type, args.tab_size) 222 | 223 | elif args.command == 'diff': 224 | out_dir = args.out_dir if args.out_dir else os.path.join(os.getcwd(), 'diff_out') 225 | diff(args.in_file1[0], args.in_file2[0], args.type, out_dir.lstrip()) 226 | 227 | else: 228 | parser.print_help() 229 | 230 | except Exception as e: 231 | print("[pydtc] Execution Error !") 232 | print(str(e) if str(e) else "Unknown Error", file=sys.stderr) 233 | sys.exit(1) 234 | 235 | 236 | if __name__ == '__main__': 237 | main() 238 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /fdt/items.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Martin Olejar 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from struct import pack, Struct 16 | from string import printable 17 | 18 | from .header import Header, DTB_PROP, DTB_BEGIN_NODE, DTB_END_NODE 19 | from .misc import is_string, line_offset 20 | 21 | BIGENDIAN_WORD = Struct(">I") 22 | 23 | ######################################################################################################################## 24 | # Helper methods 25 | ######################################################################################################################## 26 | 27 | def new_property(name: str, raw_value: bytes) -> object: 28 | """ 29 | Instantiate property with raw value type 30 | 31 | :param name: Property name 32 | :param raw_value: Property raw data 33 | """ 34 | if is_string(raw_value): 35 | obj = PropStrings(name) 36 | # Extract strings from raw value 37 | for st in raw_value.decode('ascii').split('\0'): 38 | if st: 39 | obj.append(st) 40 | return obj 41 | 42 | elif len(raw_value) and len(raw_value) % 4 == 0: 43 | obj = PropWords(name) 44 | # Extract words from raw value 45 | obj.data = [BIGENDIAN_WORD.unpack(raw_value[i:i + 4])[0] for i in range(0, len(raw_value), 4)] 46 | return obj 47 | 48 | elif len(raw_value): 49 | return PropBytes(name, data=raw_value) 50 | 51 | else: 52 | return Property(name) 53 | 54 | 55 | ######################################################################################################################## 56 | # Base Class 57 | ######################################################################################################################## 58 | 59 | class BaseItem: 60 | 61 | @property 62 | def name(self): 63 | return self._name 64 | 65 | @property 66 | def label(self): 67 | return self._label 68 | 69 | @property 70 | def parent(self): 71 | return self._parent 72 | 73 | @property 74 | def path(self): 75 | node = self._parent 76 | path = "" 77 | while node: 78 | if node.name == '/': break 79 | path = '/' + node.name + path 80 | node = node.parent 81 | return path if path else '/' 82 | 83 | def __init__(self, name: str): 84 | """ 85 | BaseItem constructor 86 | 87 | :param name: Item name 88 | """ 89 | assert isinstance(name, str) 90 | assert all(c in printable for c in name), "The value must contain just printable chars !" 91 | self._name = name 92 | self._label = None 93 | self._parent = None 94 | 95 | def __str__(self): 96 | """ String representation """ 97 | return "{}".format(self.name) 98 | 99 | def set_name(self, value: str): 100 | """ 101 | Set item name 102 | 103 | :param value: The name in string format 104 | """ 105 | assert isinstance(value, str) 106 | assert all(c in printable for c in value), "The value must contain just printable chars !" 107 | self._name = value 108 | 109 | def set_label(self, value: str): 110 | """ 111 | Set item label 112 | 113 | :param value: The label in string format 114 | """ 115 | assert isinstance(value, str) 116 | assert all(c in printable for c in value), "The value must contain just printable chars !" 117 | self._label = value 118 | 119 | 120 | def set_parent(self, value): 121 | """ 122 | Set item parent 123 | 124 | :param value: The parent node 125 | """ 126 | assert isinstance(value, Node) 127 | self._parent = value 128 | 129 | def to_dts(self, tabsize: int = 4, depth: int = 0): 130 | raise NotImplementedError() 131 | 132 | def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION): 133 | raise NotImplementedError() 134 | 135 | 136 | ######################################################################################################################## 137 | # Property Classes 138 | ######################################################################################################################## 139 | 140 | class Property(BaseItem): 141 | 142 | def __getitem__(self, value): 143 | """ Returns No Items """ 144 | return None 145 | 146 | def __eq__(self, obj): 147 | """ Check Property object equality """ 148 | return isinstance(obj, Property) and self.name == obj.name 149 | 150 | def copy(self): 151 | """ Get object copy """ 152 | return Property(self.name) 153 | 154 | def to_dts(self, tabsize: int = 4, depth: int = 0): 155 | """ 156 | Get string representation 157 | 158 | :param tabsize: Tabulator size in count of spaces 159 | :param depth: Start depth for line 160 | """ 161 | return line_offset(tabsize, depth, '{};\n'.format(self.name)) 162 | 163 | def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION): 164 | """ 165 | Get binary blob representation 166 | 167 | :param strings: 168 | :param pos: 169 | :param version: 170 | """ 171 | strpos = strings.find(self.name + '\0') 172 | if strpos < 0: 173 | strpos = len(strings) 174 | strings += self.name + '\0' 175 | pos += 12 176 | return pack('>III', DTB_PROP, 0, strpos), strings, pos 177 | 178 | 179 | class PropStrings(Property): 180 | """Property with strings as value""" 181 | 182 | @property 183 | def value(self): 184 | return self.data[0] if self.data else None 185 | 186 | def __init__(self, name: str, *args): 187 | """ 188 | PropStrings constructor 189 | 190 | :param name: Property name 191 | :param args: str1, str2, ... 192 | """ 193 | super().__init__(name) 194 | self.data = [] 195 | for arg in args: 196 | self.append(arg) 197 | 198 | def __str__(self): 199 | """ String representation """ 200 | return "{} = {}".format(self.name, self.data) 201 | 202 | def __len__(self): 203 | """ Get strings count """ 204 | return len(self.data) 205 | 206 | def __getitem__(self, index): 207 | """ Get string by index """ 208 | return self.data[index] 209 | 210 | def __eq__(self, obj): 211 | """ Check PropStrings object equality """ 212 | if not isinstance(obj, PropStrings) or self.name != obj.name or len(self) != len(obj): 213 | return False 214 | for index in range(len(self)): 215 | if self.data[index] != obj[index]: 216 | return False 217 | return True 218 | 219 | def copy(self): 220 | """ Get object copy """ 221 | return PropStrings(self.name, *self.data) 222 | 223 | def append(self, value: str): 224 | assert isinstance(value, str) 225 | assert len(value) > 0, "Invalid strings value" 226 | assert all(c in printable or c in ('\r', '\n') for c in value), "Invalid chars in strings value" 227 | self.data.append(value) 228 | 229 | def pop(self, index: int): 230 | assert 0 <= index < len(self.data), "Index out of range" 231 | return self.data.pop(index) 232 | 233 | def clear(self): 234 | self.data.clear() 235 | 236 | def to_dts(self, tabsize: int = 4, depth: int = 0): 237 | """ 238 | Get string representation 239 | 240 | :param tabsize: Tabulator size in count of spaces 241 | :param depth: Start depth for line 242 | """ 243 | result = line_offset(tabsize, depth, self.name) 244 | result += ' = "' 245 | result += '", "'.join([item.replace('"', '\\"') for item in self.data]) 246 | result += '";\n' 247 | return result 248 | 249 | def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION): 250 | """ 251 | Get blob representation 252 | 253 | :param strings: 254 | :param pos: 255 | :param version: 256 | """ 257 | blob = pack('') 258 | for chars in self.data: 259 | blob += chars.encode('ascii') + pack('b', 0) 260 | blob_len = len(blob) 261 | if version < 16 and (pos + 12) % 8 != 0: 262 | blob = pack('b', 0) * (8 - ((pos + 12) % 8)) + blob 263 | if blob_len % 4: 264 | blob += pack('b', 0) * (4 - (blob_len % 4)) 265 | strpos = strings.find(self.name + '\0') 266 | if strpos < 0: 267 | strpos = len(strings) 268 | strings += self.name + '\0' 269 | blob = pack('>III', DTB_PROP, blob_len, strpos) + blob 270 | pos += len(blob) 271 | return blob, strings, pos 272 | 273 | 274 | class PropWords(Property): 275 | """Property with words as value""" 276 | 277 | @property 278 | def value(self): 279 | return self.data[0] if self.data else None 280 | 281 | def __init__(self, name, *args): 282 | """ 283 | PropWords constructor 284 | 285 | :param name: Property name 286 | :param args: word1, word2, ... 287 | """ 288 | super().__init__(name) 289 | self.data = [] 290 | self.word_size = 32 291 | for val in args: 292 | self.append(val) 293 | 294 | def __str__(self): 295 | """ String representation """ 296 | return "{} = {}".format(self.name, self.data) 297 | 298 | def __getitem__(self, index): 299 | """ Get word by index """ 300 | return self.data[index] 301 | 302 | def __len__(self): 303 | """ Get words count """ 304 | return len(self.data) 305 | 306 | def __eq__(self, prop): 307 | """ Check PropWords object equality """ 308 | if not isinstance(prop, PropWords): 309 | return False 310 | if self.name != prop.name: 311 | return False 312 | if len(self) != len(prop): 313 | return False 314 | for index in range(len(self)): 315 | if self.data[index] != prop[index]: 316 | return False 317 | return True 318 | 319 | def copy(self): 320 | return PropWords(self.name, *self.data) 321 | 322 | def append(self, value): 323 | assert isinstance(value, int), "Invalid object type" 324 | assert 0 <= value < 2**self.word_size, "Invalid word value {}, use <0x0 - 0x{:X}>".format( 325 | value, 2**self.word_size - 1) 326 | self.data.append(value) 327 | 328 | def pop(self, index): 329 | assert 0 <= index < len(self.data), "Index out of range" 330 | return self.data.pop(index) 331 | 332 | def clear(self): 333 | self.data.clear() 334 | 335 | def to_dts(self, tabsize: int = 4, depth: int = 0): 336 | """ 337 | Get string representation 338 | 339 | :param tabsize: Tabulator size in count of spaces 340 | :param depth: Start depth for line 341 | """ 342 | result = line_offset(tabsize, depth, self.name) 343 | result += ' = <' 344 | result += ' '.join(["0x{:X}".format(word) for word in self.data]) 345 | result += ">;\n" 346 | return result 347 | 348 | def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION): 349 | """ 350 | Get blob representation 351 | 352 | :param strings: 353 | :param pos: 354 | :param version: 355 | """ 356 | strpos = strings.find(self.name + '\0') 357 | if strpos < 0: 358 | strpos = len(strings) 359 | strings += self.name + '\0' 360 | blob = pack('>III', DTB_PROP, len(self.data) * 4, strpos) 361 | blob += bytes().join([BIGENDIAN_WORD.pack(word) for word in self.data]) 362 | pos += len(blob) 363 | return blob, strings, pos 364 | 365 | 366 | class PropBytes(Property): 367 | """Property with bytes as value""" 368 | 369 | def __init__(self, name, *args, data=None): 370 | """ 371 | PropBytes constructor 372 | 373 | :param name: Property name 374 | :param args: byte0, byte1, ... 375 | :param data: Data as list, bytes or bytearray 376 | """ 377 | super().__init__(name) 378 | self.data = bytearray(args) 379 | if data: 380 | assert isinstance(data, (list, bytes, bytearray)) 381 | self.data += bytearray(data) 382 | 383 | def __str__(self): 384 | """ String representation """ 385 | return "{} = {}".format(self.name, self.data) 386 | 387 | def __getitem__(self, index): 388 | """Get byte by index """ 389 | return self.data[index] 390 | 391 | def __len__(self): 392 | """ Get bytes count """ 393 | return len(self.data) 394 | 395 | def __eq__(self, prop): 396 | """ Check PropBytes object equality """ 397 | if not isinstance(prop, PropBytes): 398 | return False 399 | if self.name != prop.name: 400 | return False 401 | if len(self) != len(prop): 402 | return False 403 | for index in range(len(self)): 404 | if self.data[index] != prop[index]: 405 | return False 406 | return True 407 | 408 | def copy(self): 409 | """ Create a copy of object """ 410 | return PropBytes(self.name, data=self.data) 411 | 412 | def append(self, value): 413 | assert isinstance(value, int), "Invalid object type" 414 | assert 0 <= value <= 0xFF, "Invalid byte value {}, use <0 - 255>".format(value) 415 | self.data.append(value) 416 | 417 | def pop(self, index): 418 | assert 0 <= index < len(self.data), "Index out of range" 419 | return self.data.pop(index) 420 | 421 | def clear(self): 422 | self.data = bytearray() 423 | 424 | def to_dts(self, tabsize: int = 4, depth: int = 0): 425 | """ 426 | Get string representation 427 | 428 | :param tabsize: Tabulator size in count of spaces 429 | :param depth: Start depth for line 430 | """ 431 | result = line_offset(tabsize, depth, self.name) 432 | result += ' = [' 433 | result += ' '.join(["{:02X}".format(byte) for byte in self.data]) 434 | result += '];\n' 435 | return result 436 | 437 | def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION): 438 | """ 439 | Get blob representation 440 | 441 | :param strings: 442 | :param pos: 443 | :param version: 444 | """ 445 | strpos = strings.find(self.name + '\0') 446 | if strpos < 0: 447 | strpos = len(strings) 448 | strings += self.name + '\0' 449 | blob = pack('>III', DTB_PROP, len(self.data), strpos) 450 | blob += bytes(self.data) 451 | if len(blob) % 4: 452 | blob += bytes([0] * (4 - (len(blob) % 4))) 453 | pos += len(blob) 454 | return blob, strings, pos 455 | 456 | 457 | class PropIncBin(PropBytes): 458 | """Property with bytes as value""" 459 | 460 | def __init__(self, name, data=None, file_name=None, rpath=None): 461 | """ 462 | PropIncBin constructor 463 | 464 | :param name: Property name 465 | :param data: Data as list, bytes or bytearray 466 | :param file_name: File name 467 | :param rpath: Relative path 468 | """ 469 | super().__init__(name, data=data) 470 | self.file_name = file_name 471 | self.relative_path = rpath 472 | 473 | def __eq__(self, prop): 474 | """ Check PropIncBin object equality """ 475 | if not isinstance(prop, PropIncBin): 476 | return False 477 | if self.name != prop.name: 478 | return False 479 | if self.file_name != prop.file_name: 480 | return False 481 | if self.relative_path != prop.relative_path: 482 | return False 483 | if self.data != prop.data: 484 | return False 485 | return True 486 | 487 | def copy(self): 488 | """ Create a copy of object """ 489 | return PropIncBin(self.name, self.data, self.file_name, self.relative_path) 490 | 491 | def to_dts(self, tabsize: int = 4, depth: int = 0): 492 | """ 493 | Get string representation 494 | 495 | :param tabsize: Tabulator size in count of spaces 496 | :param depth: Start depth for line 497 | """ 498 | file_path = self.file_name 499 | if self.relative_path is not None: 500 | file_path = "{}/{}".format(self.relative_path, self.file_name) 501 | result = line_offset(tabsize, depth, self.name) 502 | result += " = /incbin/(\"{}\");\n".format(file_path) 503 | return result 504 | 505 | 506 | ######################################################################################################################## 507 | # Node Class 508 | ######################################################################################################################## 509 | 510 | class Node(BaseItem): 511 | """Node representation""" 512 | 513 | @property 514 | def props(self): 515 | return self._props 516 | 517 | @property 518 | def nodes(self): 519 | return self._nodes 520 | 521 | @property 522 | def empty(self): 523 | return False if self.nodes or self.props else True 524 | 525 | def __init__(self, name, *args): 526 | """ 527 | Node constructor 528 | 529 | :param name: Node name 530 | :param args: List of properties and subnodes 531 | """ 532 | super().__init__(name) 533 | self._props = [] 534 | self._nodes = [] 535 | for item in args: 536 | self.append(item) 537 | 538 | def __str__(self): 539 | """ String representation """ 540 | return "< {}: {} props, {} nodes >".format(self.name, len(self.props), len(self.nodes)) 541 | 542 | def __eq__(self, node): 543 | """ Check node equality """ 544 | if not isinstance(node, Node): 545 | return False 546 | if self.name != node.name or \ 547 | len(self.props) != len(node.props) or \ 548 | len(self.nodes) != len(node.nodes): 549 | return False 550 | for p in self.props: 551 | if p not in node.props: 552 | return False 553 | for n in self.nodes: 554 | if n not in node.nodes: 555 | return False 556 | return True 557 | 558 | def copy(self): 559 | """ Create a copy of Node object """ 560 | node = Node(self.name) 561 | for p in self.props: 562 | node.append(p.copy()) 563 | for n in self.nodes: 564 | node.append(n.copy()) 565 | return node 566 | 567 | def get_property(self, name): 568 | """ 569 | Get property object by its name 570 | 571 | :param name: Property name 572 | """ 573 | for p in self.props: 574 | if p.name == name: 575 | return p 576 | return None 577 | 578 | def set_property(self, name, value): 579 | """ 580 | Set property 581 | 582 | :param name: Property name 583 | :param value: Property value 584 | """ 585 | if value is None: 586 | new_prop = Property(name) 587 | elif isinstance(value, int): 588 | new_prop = PropWords(name, value) 589 | elif isinstance(value, str): 590 | new_prop = PropStrings(name, value) 591 | elif isinstance(value, list) and isinstance(value[0], int): 592 | new_prop = PropWords(name, *value) 593 | elif isinstance(value, list) and isinstance(value[0], str): 594 | new_prop = PropStrings(name, *value) 595 | elif isinstance(value, (bytes, bytearray)): 596 | new_prop = PropBytes(name, data=value) 597 | else: 598 | raise TypeError('Value type not supported') 599 | new_prop.set_parent(self) 600 | old_prop = self.get_property(name) 601 | if old_prop is None: 602 | self.props.append(new_prop) 603 | else: 604 | index = self.props.index(old_prop) 605 | self.props[index] = new_prop 606 | 607 | def get_subnode(self, name: str): 608 | """ 609 | Get subnode object by name 610 | 611 | :param name: Subnode name 612 | """ 613 | for n in self.nodes: 614 | if n.name == name: 615 | return n 616 | return None 617 | 618 | def exist_property(self, name: str) -> bool: 619 | """ 620 | Check if property exist and return True if exist else False 621 | 622 | :param name: Property name 623 | """ 624 | return False if self.get_property(name) is None else True 625 | 626 | def exist_subnode(self, name: str) -> bool: 627 | """ 628 | Check if subnode exist and return True if exist else False 629 | 630 | :param name: Subnode name 631 | """ 632 | return False if self.get_subnode(name) is None else True 633 | 634 | def remove_property(self, name: str): 635 | """ 636 | Remove property object by its name. 637 | 638 | :param name: Property name 639 | """ 640 | item = self.get_property(name) 641 | if item is not None: 642 | self.props.remove(item) 643 | 644 | def remove_subnode(self, name: str): 645 | """ 646 | Remove subnode object by its name. 647 | 648 | :param name: Subnode name 649 | """ 650 | item = self.get_subnode(name) 651 | if item is not None: 652 | self.nodes.remove(item) 653 | 654 | def append(self, item): 655 | """ 656 | Append node or property 657 | 658 | :param item: The node or property object 659 | """ 660 | assert isinstance(item, (Node, Property)), "Invalid object type, use \"Node\" or \"Property\"" 661 | 662 | if isinstance(item, Property): 663 | if self.get_property(item.name) is not None: 664 | raise Exception("{}: \"{}\" property already exists".format(self, item.name)) 665 | item.set_parent(self) 666 | self.props.append(item) 667 | 668 | else: 669 | if self.get_subnode(item.name) is not None: 670 | raise Exception("{}: \"{}\" node already exists".format(self, item.name)) 671 | if item is self: 672 | raise Exception("{}: append the same node {}".format(self, item.name)) 673 | item.set_parent(self) 674 | self.nodes.append(item) 675 | 676 | def merge(self, node_obj, replace: bool = True): 677 | """ 678 | Merge two nodes 679 | 680 | :param node_obj: Node object 681 | :param replace: If True, replace current properties with the given properties 682 | """ 683 | assert isinstance(node_obj, Node), "Invalid object type" 684 | 685 | def get_property_index(name): 686 | for i, p in enumerate(self.props): 687 | if p.name == name: 688 | return i 689 | return None 690 | 691 | def get_subnode_index(name): 692 | for i, n in enumerate(self.nodes): 693 | if n.name == name: 694 | return i 695 | return None 696 | 697 | for prop in node_obj.props: 698 | index = get_property_index(prop.name) 699 | if index is None: 700 | self.append(prop.copy()) 701 | elif prop in self._props: 702 | continue 703 | elif replace: 704 | new_prop = prop.copy() 705 | new_prop.set_parent(self) 706 | self._props[index] = new_prop 707 | else: 708 | pass 709 | 710 | for sub_node in node_obj.nodes: 711 | index = get_subnode_index(sub_node.name) 712 | if index is None: 713 | self.append(sub_node.copy()) 714 | elif sub_node in self._nodes: 715 | continue 716 | else: 717 | self._nodes[index].merge(sub_node, replace) 718 | 719 | def to_dts(self, tabsize: int = 4, depth: int = 0) -> str: 720 | """ 721 | Get string representation of NODE object 722 | 723 | :param tabsize: Tabulator size in count of spaces 724 | :param depth: Start depth for line 725 | """ 726 | if self._label is not None: 727 | dts = line_offset(tabsize, depth, self._label + ': ' + self.name + ' {\n') 728 | else: 729 | dts = line_offset(tabsize, depth, self.name + ' {\n') 730 | # phantom properties which maintain reference state info 731 | # have names ending with _with_references 732 | # don't write those out to dts file 733 | dts += ''.join( 734 | prop.to_dts(tabsize, depth + 1) 735 | for prop in self._props if prop.name.endswith('_with_references') is False) 736 | dts += ''.join(node.to_dts(tabsize, depth + 1) for node in self._nodes) 737 | dts += line_offset(tabsize, depth, "};\n") 738 | return dts 739 | 740 | def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION) -> tuple: 741 | """ 742 | Get NODE in binary blob representation 743 | 744 | :param strings: 745 | :param pos: 746 | :param version: 747 | """ 748 | if self.name == '/': 749 | blob = pack('>II', DTB_BEGIN_NODE, 0) 750 | else: 751 | blob = pack('>I', DTB_BEGIN_NODE) 752 | blob += self.name.encode('ascii') + b'\0' 753 | if len(blob) % 4: 754 | blob += pack('b', 0) * (4 - (len(blob) % 4)) 755 | pos += len(blob) 756 | for prop in self._props: 757 | # phantom property too maintain reference state should 758 | # not write out to dtb file 759 | if prop.name.endswith('_with_references') is False: 760 | (data, strings, pos) = prop.to_dtb(strings, pos, version) 761 | blob += data 762 | for node in self._nodes: 763 | (data, strings, pos) = node.to_dtb(strings, pos, version) 764 | blob += data 765 | pos += 4 766 | blob += pack('>I', DTB_END_NODE) 767 | return blob, strings, pos 768 | -------------------------------------------------------------------------------- /fdt/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Martin Olejar 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | 17 | from .header import Header, DTB_BEGIN_NODE, DTB_END_NODE, DTB_PROP, DTB_END, DTB_NOP 18 | from .items import new_property, Property, PropBytes, PropWords, PropStrings, PropIncBin, Node 19 | from .misc import strip_comments, split_to_lines, get_version_info, extract_string 20 | 21 | __author__ = "Martin Olejar" 22 | __contact__ = "martin.olejar@gmail.com" 23 | __version__ = "0.3.3" 24 | __license__ = "Apache 2.0" 25 | __status__ = "Development" 26 | __all__ = [ 27 | # FDT Classes 28 | 'FDT', 29 | 'Node', 30 | 'Header', 31 | # properties 32 | 'Property', 33 | 'PropBytes', 34 | 'PropWords', 35 | 'PropStrings', 36 | 'PropIncBin', 37 | # core methods 38 | 'parse_dts', 39 | 'parse_dtb', 40 | 'diff' 41 | ] 42 | 43 | 44 | class ItemType: 45 | NODE = 0 46 | PROP = 1 47 | # Specific property type 48 | PROP_BASE = 5 49 | PROP_WORDS = 6 50 | PROP_BYTES = 7 51 | PROP_STRINGS = 8 52 | # All types 53 | ALL = 100 54 | 55 | 56 | class FDT: 57 | """ Flattened Device Tree Class """ 58 | 59 | @property 60 | def empty(self): 61 | return self.root.empty 62 | 63 | def __init__(self, header=None, entries=[]): 64 | """ 65 | FDT class constructor 66 | 67 | :param header: 68 | """ 69 | self.entries = entries 70 | self.header = Header() if header is None else header 71 | self.root = Node('/') 72 | self.last_handle = 0 73 | self.label_to_handle = {} 74 | self.handle_to_label = {} 75 | 76 | 77 | def __str__(self): 78 | """ String representation """ 79 | return self.info() 80 | 81 | def info(self): 82 | """ Return object info in human readable format """ 83 | msg = "FDT Content:\n" 84 | for path, nodes, props in self.walk(): 85 | msg += "{} [{}N, {}P]\n".format(path, len(nodes), len(props)) 86 | return msg 87 | 88 | def get_node(self, path: str, create: bool = False) -> Node: 89 | """ 90 | Get node object from specified path 91 | 92 | :param path: Path as string 93 | :param create: If True, not existing nodes will be created 94 | """ 95 | assert isinstance(path, str), "Node path must be a string type !" 96 | 97 | node = self.root 98 | path = path.lstrip('/') 99 | if path: 100 | names = path.split('/') 101 | for name in names: 102 | item = node.get_subnode(name) 103 | if item is None: 104 | if create: 105 | item = Node(name) 106 | node.append(item) 107 | else: 108 | raise ValueError("Path \"{}\" doesn't exists".format(path)) 109 | node = item 110 | 111 | return node 112 | 113 | def get_property(self, name: str, path: str = '') -> Property: 114 | """ 115 | Get property object by name from specified path 116 | 117 | :param name: Property name 118 | :param path: Path to sub-node 119 | """ 120 | return self.get_node(path).get_property(name) 121 | 122 | def set_property(self, name: str, value, path: str = '', create: bool = True): 123 | """ 124 | Set property object by name 125 | 126 | :param name: Property name 127 | :param value: Property value 128 | :param path: Path to subnode 129 | :param create: If True, not existing nodes will be created 130 | """ 131 | self.get_node(path, create).set_property(name, value) 132 | 133 | def exist_node(self, path: str) -> bool: 134 | """ 135 | Check if /node exist and return True 136 | 137 | :param path: path/node name 138 | :return True if /node exist else False 139 | """ 140 | try: 141 | self.get_node(path) 142 | except ValueError: 143 | return False 144 | else: 145 | return True 146 | 147 | def exist_property(self, name: str, path: str = '') -> bool: 148 | """ 149 | Check if property exist 150 | 151 | :param name: Property name 152 | :param path: The path 153 | """ 154 | return self.get_node(path).exist_property(name) if self.exist_node(path) else False 155 | 156 | def remove_node(self, name: str, path: str = ''): 157 | """ 158 | Remove node obj by path/name. Raises ValueError if path/name doesn't exist 159 | 160 | :param name: Node name 161 | :param path: Path to sub-node 162 | """ 163 | self.get_node(path).remove_subnode(name) 164 | 165 | def remove_property(self, name: str, path: str = ''): 166 | """ 167 | Remove property obj by name. Raises ValueError if path/name doesn't exist 168 | 169 | :param name: Property name 170 | :param path: Path to subnode 171 | """ 172 | self.get_node(path).remove_property(name) 173 | 174 | def add_item(self, obj, path: str = '', create: bool = True): 175 | """ 176 | Add sub-node or property at specified path. Raises ValueError if path doesn't exist 177 | 178 | :param obj: The node or property object 179 | :param path: The path to subnode 180 | :param create: If True, not existing nodes will be created 181 | """ 182 | self.get_node(path, create).append(obj) 183 | 184 | def add_label(self, label): 185 | ''' track labels/references to convert to phandles 186 | adds label with incrmenting handle to dictionary if not alread present 187 | returns handle for which can be used to replace the reference''' 188 | if label in self.label_to_handle: 189 | return self.label_to_handle[label] 190 | self.last_handle += 1 191 | self.label_to_handle[label] = self.last_handle 192 | self.handle_to_label[self.last_handle] = label 193 | return self.last_handle 194 | 195 | def search(self, name: str, itype: int = ItemType.ALL, path: str = '', recursive: bool = True) -> list: 196 | """ 197 | Search properties and/or nodes with specified name. Return list of founded items 198 | 199 | :param name: The Property or Node name. If empty "", all nodes or properties will selected 200 | :param itype: Item type - NODE, PROP, PROP_BASE, PROP_WORDS, PROP_BYTES, PROP_STRINGS or ALL 201 | :param path: Path to root node 202 | :param recursive: Search in all sub-nodes (default: True) 203 | """ 204 | assert isinstance(name, str), "Property name must be a string type !" 205 | 206 | node = self.get_node(path) 207 | nodes = [] 208 | items = [] 209 | pclss = { 210 | ItemType.PROP_BASE: Property, 211 | ItemType.PROP_BYTES: PropBytes, 212 | ItemType.PROP_WORDS: PropWords, 213 | ItemType.PROP_STRINGS: PropStrings 214 | } 215 | while True: 216 | nodes += node.nodes 217 | if itype == ItemType.NODE or itype == ItemType.ALL: 218 | if not name or node.name == name: 219 | items.append(node) 220 | if itype != ItemType.NODE or itype == ItemType.ALL: 221 | for p in node.props: 222 | if name and p.name != name: 223 | continue 224 | if itype in pclss and type(p) is not pclss[itype]: 225 | continue 226 | items.append(p) 227 | if not recursive or not nodes: 228 | break 229 | node = nodes.pop() 230 | 231 | return items 232 | 233 | def walk(self, path: str = '', relative: bool = False) -> list: 234 | """ 235 | Walk trough nodes and return relative/absolute path with list of sub-nodes and properties 236 | 237 | :param path: The path to root node 238 | :param relative: True for relative or False for absolute return path 239 | """ 240 | all_nodes = [] 241 | 242 | node = self.get_node(path) 243 | while True: 244 | all_nodes += node.nodes 245 | current_path = "{}/{}".format(node.path, node.name) 246 | current_path = current_path.replace('///', '/') 247 | current_path = current_path.replace('//', '/') 248 | if path and relative: 249 | current_path = current_path.replace(path, '').lstrip('/') 250 | yield current_path, node.nodes, node.props 251 | if not all_nodes: 252 | break 253 | node = all_nodes.pop() 254 | 255 | def merge(self, fdt_obj, replace: bool = True): 256 | """ 257 | Merge external FDT object into this object. 258 | 259 | :param fdt_obj: The FDT object which will be merged into this 260 | :param replace: True for replace existing items or False for keep old items 261 | """ 262 | assert isinstance(fdt_obj, FDT) 263 | if self.header.version is None: 264 | self.header = fdt_obj.header 265 | else: 266 | if fdt_obj.header.version is not None and \ 267 | fdt_obj.header.version > self.header.version: 268 | self.header.version = fdt_obj.header.version 269 | if fdt_obj.entries: 270 | for in_entry in fdt_obj.entries: 271 | exist = False 272 | for index in range(len(self.entries)): 273 | if self.entries[index]['address'] == in_entry['address']: 274 | self.entries[index]['size'] = in_entry['size'] 275 | exist = True 276 | break 277 | if not exist: 278 | self.entries.append(in_entry) 279 | 280 | self.root.merge(fdt_obj.get_node('/'), replace) 281 | 282 | def update_phandles(self): 283 | all_nodes = [] 284 | no_phandle_nodes = [] 285 | 286 | node = self.root 287 | all_nodes += self.root.nodes 288 | while all_nodes: 289 | props = (node.get_property('phandle'), node.get_property('linux,phandle')) 290 | value = None 291 | for i, p in enumerate(props): 292 | if isinstance(p, PropWords) and isinstance(p.value, int): 293 | value = None if i == 1 and p.value != value else p.value 294 | if value is None: 295 | no_phandle_nodes.append(node) 296 | # ... 297 | node = all_nodes.pop() 298 | all_nodes += node.nodes 299 | 300 | for node in no_phandle_nodes: 301 | if node.name != '/': 302 | if node.path == '/': 303 | phandle_value = self.add_label(node.name) 304 | else: 305 | phandle_value = self.add_label(node.path+'/'+node.name) 306 | node.set_property('linux,phandle', phandle_value) 307 | node.set_property('phandle', phandle_value) 308 | 309 | 310 | def to_dts(self, tabsize: int = 4) -> str: 311 | """ 312 | Store FDT Object into string format (DTS) 313 | 314 | :param tabsize: 315 | """ 316 | result = "/dts-v1/;\n" 317 | if self.header.version is not None: 318 | result += "// version: {}\n".format(self.header.version) 319 | result += "// last_comp_version: {}\n".format(self.header.last_comp_version) 320 | if self.header.version >= 2: 321 | result += "// boot_cpuid_phys: 0x{:X}\n".format(self.header.boot_cpuid_phys) 322 | result += '\n' 323 | if self.entries: 324 | for entry in self.entries: 325 | result += "/memreserve/ " 326 | result += "{:#x} ".format(entry['address']) if entry['address'] else "0 " 327 | result += "{:#x}".format(entry['size']) if entry['size'] else "0" 328 | result += ";\n" 329 | if self.root is not None: 330 | result += self.root.to_dts(tabsize) 331 | return result 332 | 333 | def to_dtb(self, version: int = None, last_comp_version: int = None, boot_cpuid_phys: int = None, strings: str = None, padding: int = 0) -> bytes: 334 | """ 335 | Export FDT Object into Binary Blob format (DTB) 336 | 337 | :param version: 338 | :param last_comp_version: 339 | :param boot_cpuid_phys: 340 | :param strings: 341 | :param padding: 342 | 343 | The strings param is useful (only) when manipulating a signed itb or dtb. The signature includes 344 | the strings buffer in the dtb _in order_. C executables write the strings out in a surprising order. 345 | The argument is used as an initial version of the strings buffer, so that all strings in the input 346 | file are included (and in the same order) in the output file. Usage: 347 | 348 | # Read and parse dtb 349 | with open(input, 'rb') as file: 350 | data = file.read() 351 | dtree = fdt.parse_dtb(data) 352 | 353 | # Read strings buffer (Assumes version >= 3) 354 | strings_start = dtree.header.off_dt_strings 355 | strings_end = strings_start + dtree.header.size_dt_strings 356 | strings = data[strings_start:strings_end].decode("ascii") 357 | 358 | # Serialize dtb and write to output 359 | data = dtree.to_dtb(strings=strings) 360 | with open(output, 'wb') as file: 361 | file.write(data) 362 | 363 | """ 364 | if self.root is None: 365 | return b'' 366 | 367 | from struct import pack 368 | 369 | if version is not None: 370 | self.header.version = version 371 | if padding < 0: 372 | raise Exception("DTB padding must be >= 0 !") 373 | if last_comp_version is not None: 374 | self.header.last_comp_version = last_comp_version 375 | if boot_cpuid_phys is not None: 376 | self.header.boot_cpuid_phys = boot_cpuid_phys 377 | if self.header.version is None: 378 | raise Exception("DTB Version must be specified !") 379 | if strings is None: 380 | strings = '' 381 | 382 | blob_entries = bytes() 383 | if self.entries: 384 | for entry in self.entries: 385 | blob_entries += pack('>QQ', entry['address'], entry['size']) 386 | blob_entries += pack('>QQ', 0, 0) 387 | blob_data_start = self.header.size + len(blob_entries) 388 | (blob_data, blob_strings, data_pos) = self.root.to_dtb(strings, blob_data_start, self.header.version) 389 | blob_data += pack('>I', DTB_END) 390 | self.header.size_dt_strings = len(blob_strings) 391 | self.header.size_dt_struct = len(blob_data) 392 | self.header.off_mem_rsvmap = self.header.size 393 | self.header.off_dt_struct = blob_data_start 394 | self.header.off_dt_strings = blob_data_start + len(blob_data) 395 | self.header.total_size = blob_data_start + len(blob_data) + len(blob_strings) + padding 396 | blob_header = self.header.export() 397 | return blob_header + blob_entries + blob_data + blob_strings.encode('ascii') + (b'\x00' * padding) 398 | 399 | 400 | def parse_dts(text: str, root_dir: str = '') -> FDT: 401 | """ 402 | Parse DTS text file and create FDT Object 403 | 404 | :param text: 405 | :param root_dir: 406 | """ 407 | ver = get_version_info(text) 408 | text = strip_comments(text) 409 | dts_lines = split_to_lines(text) 410 | fdt_obj = FDT() 411 | if 'version' in ver: 412 | fdt_obj.header.version = ver['version'] 413 | if 'last_comp_version' in ver: 414 | fdt_obj.header.last_comp_version = ver['last_comp_version'] 415 | if 'boot_cpuid_phys' in ver: 416 | fdt_obj.header.boot_cpuid_phys = ver['boot_cpuid_phys'] 417 | # parse entries 418 | fdt_obj.entries = [] 419 | for line in dts_lines: 420 | if line.endswith('{'): 421 | break 422 | if line.startswith('/memreserve/'): 423 | line = line.strip(';') 424 | line = line.split() 425 | if len(line) != 3 : 426 | raise Exception() 427 | fdt_obj.entries.append({'address': int(line[1], 0), 'size': int(line[2], 0)}) 428 | # parse nodes 429 | curnode = None 430 | fdt_obj.root = None 431 | for line in dts_lines: 432 | if line.endswith('{'): 433 | # start node 434 | if ':' in line: #indicates the present of a label 435 | label, rest = line.split(':') 436 | node_name = rest.split()[0] 437 | new_node = Node(node_name) 438 | new_node.set_label(label) 439 | 440 | 441 | else: 442 | node_name = line.split()[0] 443 | new_node = Node(node_name) 444 | if fdt_obj.root is None: 445 | fdt_obj.root = new_node 446 | if curnode is not None: 447 | curnode.append(new_node) 448 | curnode = new_node 449 | elif line.endswith('}'): 450 | # end node 451 | if curnode is not None: 452 | if curnode.get_property('phandle') is None: 453 | if curnode.label is not None: 454 | handle = fdt_obj.add_label(curnode.label) 455 | curnode.set_property('phandle', handle) 456 | curnode = curnode.parent 457 | else: 458 | # properties 459 | if line.find('=') == -1: 460 | prop_name = line 461 | prop_obj = Property(prop_name) 462 | else: 463 | line = line.split('=', maxsplit=1) 464 | prop_name = line[0].rstrip(' ') 465 | prop_value = line[1].lstrip(' ') 466 | if prop_value.startswith('<'): 467 | prop_obj = PropWords(prop_name) 468 | prop_value = prop_value.replace('<', '').replace('>', '') 469 | # ['interrupts ' = ' <0 5 4>, <0 6 4>'] 470 | # just change ',' to ' ' -- to concatenate the values into single array 471 | if ',' in prop_value: 472 | prop_value = prop_value.replace(',', ' ') 473 | 474 | # keep the orginal references for phandles as a phantom 475 | # property 476 | if "&" in prop_value: 477 | phantom_obj = PropStrings(prop_name+'_with_references') 478 | phantom_obj.append(line[1].lstrip(' ')) 479 | if curnode is not None: 480 | curnode.append(phantom_obj) 481 | for prop in prop_value.split(): 482 | if prop.startswith('0x'): 483 | prop_obj.append(int(prop, 16)) 484 | elif prop.startswith('0b'): 485 | prop_obj.append(int(prop, 2)) 486 | elif prop.startswith('0'): 487 | prop_obj.append(int(prop, 8)) 488 | elif prop.startswith('&'): 489 | prop_obj.append(fdt_obj.add_label(prop[1:])) 490 | else: 491 | prop_obj.append(int(prop)) 492 | elif prop_value.startswith('['): 493 | prop_obj = PropBytes(prop_name) 494 | prop_value = prop_value.replace('[', '').replace(']', '') 495 | for prop in prop_value.split(): 496 | prop_obj.append(int(prop, 16)) 497 | elif prop_value.startswith('/incbin/'): 498 | prop_value = prop_value.replace('/incbin/("', '').replace('")', '') 499 | prop_value = prop_value.split(',') 500 | file_path = os.path.join(root_dir, prop_value[0].strip()) 501 | file_offset = int(prop_value.strip(), 0) if len(prop_value) > 1 else 0 502 | file_size = int(prop_value.strip(), 0) if len(prop_value) > 2 else 0 503 | if file_path is None or not os.path.exists(file_path): 504 | raise Exception("File path doesn't exist: {}".format(file_path)) 505 | with open(file_path, "rb") as f: 506 | f.seek(file_offset) 507 | prop_data = f.read(file_size) if file_size > 0 else f.read() 508 | prop_obj = PropIncBin(prop_name, prop_data, os.path.split(file_path)[1]) 509 | elif prop_value.startswith('/plugin/'): 510 | raise NotImplementedError("Not implemented property value: /plugin/") 511 | elif prop_value.startswith('/bits/'): 512 | raise NotImplementedError("Not implemented property value: /bits/") 513 | else: 514 | prop_obj = PropStrings(prop_name) 515 | expect_open = True 516 | in_prop = False 517 | prop = '' 518 | for c in prop_value: 519 | if c == '"' and not in_prop and expect_open: 520 | prop = '' 521 | in_prop = True 522 | elif c == '"' and in_prop: 523 | if not len(prop) > 0: 524 | raise ValueError('Empty string') 525 | prop_obj.append(prop) 526 | in_prop = False 527 | expect_open = False 528 | elif in_prop: 529 | prop += c 530 | elif c == ',' and not expect_open: 531 | expect_open = True 532 | elif c == ' ': 533 | continue 534 | else: 535 | raise ValueError(f'Invalid char: {c}') 536 | 537 | if expect_open: 538 | raise ValueError('Expected string after ,') 539 | if curnode is not None: 540 | curnode.append(prop_obj) 541 | 542 | return fdt_obj 543 | 544 | 545 | def parse_dtb(data: bytes, offset: int = 0) -> FDT: 546 | """ 547 | Parse FDT Binary Blob and create FDT Object 548 | 549 | :param data: FDT Binary Blob in bytes 550 | :param offset: The offset of input data 551 | """ 552 | assert isinstance(data, (bytes, bytearray)), "Invalid argument type" 553 | 554 | from struct import unpack_from 555 | 556 | fdt_obj = FDT() 557 | # parse header 558 | fdt_obj.header = Header.parse(data) 559 | # parse entries 560 | index = fdt_obj.header.off_mem_rsvmap 561 | while True: 562 | entrie = dict(zip(('address', 'size'), unpack_from(">QQ", data, offset + index))) 563 | index += 16 564 | if entrie['address'] == 0 and entrie['size'] == 0: 565 | break 566 | fdt_obj.entries.append(entrie) 567 | # parse nodes 568 | current_node = None 569 | fdt_obj.root = None 570 | index = fdt_obj.header.off_dt_struct 571 | while True: 572 | if len(data) < (offset + index + 4): 573 | raise Exception("Index out of range !") 574 | tag = unpack_from(">I", data, offset + index)[0] 575 | index += 4 576 | if tag == DTB_BEGIN_NODE: 577 | node_name = extract_string(data, offset + index) 578 | index = ((index + len(node_name) + 4) & ~3) 579 | if not node_name: node_name = '/' 580 | new_node = Node(node_name) 581 | if fdt_obj.root is None: 582 | fdt_obj.root = new_node 583 | if current_node is not None: 584 | current_node.append(new_node) 585 | current_node = new_node 586 | elif tag == DTB_END_NODE: 587 | if current_node is not None: 588 | current_node = current_node.parent 589 | elif tag == DTB_PROP: 590 | prop_size, prop_string_pos, = unpack_from(">II", data, offset + index) 591 | prop_start = index + 8 592 | if fdt_obj.header.version < 16 and prop_size >= 8: 593 | prop_start = ((prop_start + 7) & ~0x7) 594 | prop_name = extract_string(data, fdt_obj.header.off_dt_strings + prop_string_pos) 595 | prop_raw_value = data[offset + prop_start : offset + prop_start + prop_size] 596 | index = prop_start + prop_size 597 | index = ((index + 3) & ~0x3) 598 | if current_node is not None: 599 | current_node.append(new_property(prop_name, prop_raw_value)) 600 | elif tag == DTB_END: 601 | break 602 | else: 603 | raise Exception("Unknown Tag: {}".format(tag)) 604 | 605 | return fdt_obj 606 | 607 | 608 | def diff(fdt1: FDT, fdt2: FDT) -> tuple: 609 | """ 610 | Compare two flattened device tree objects and return list of 3 objects (same in 1 and 2, specific for 1, specific for 2) 611 | 612 | :param fdt1: The object 1 of FDT 613 | :param fdt2: The object 2 of FDT 614 | """ 615 | assert isinstance(fdt1, FDT), "Invalid argument type" 616 | assert isinstance(fdt2, FDT), "Invalid argument type" 617 | 618 | fdt_a = FDT(fdt1.header) 619 | fdt_b = FDT(fdt2.header) 620 | 621 | if fdt1.header.version is not None and fdt2.header.version is not None: 622 | fdt_same = FDT(fdt1.header if fdt1.header.version > fdt2.header.version else fdt2.header) 623 | else: 624 | fdt_same = FDT(fdt1.header) 625 | 626 | if fdt1.entries and fdt2.entries: 627 | for entry_a in fdt1.entries: 628 | for entry_b in fdt2.entries: 629 | if entry_a['address'] == entry_b['address'] and entry_a['size'] == entry_b['size']: 630 | fdt_same.entries.append(entry_a) 631 | break 632 | 633 | for entry_a in fdt1.entries: 634 | found = False 635 | for entry_s in fdt_same.entries: 636 | if entry_a['address'] == entry_s['address'] and entry_a['size'] == entry_s['size']: 637 | found = True 638 | break 639 | if not found: 640 | fdt_a.entries.append(entry_a) 641 | 642 | for entry_b in fdt2.entries: 643 | found = False 644 | for entry_s in fdt_same.entries: 645 | if entry_b['address'] == entry_s['address'] and entry_b['size'] == entry_s['size']: 646 | found = True 647 | break 648 | if not found: 649 | fdt_b.entries.append(entry_b) 650 | 651 | for path, nodes, props in fdt1.walk(): 652 | try: 653 | rnode = fdt2.get_node(path) 654 | except: 655 | rnode = None 656 | 657 | for node_b in nodes: 658 | if rnode is None or rnode.get_subnode(node_b.name) is None: 659 | fdt_a.add_item(Node(node_b.name), path) 660 | else: 661 | fdt_same.add_item(Node(node_b.name), path) 662 | 663 | for prop_a in props: 664 | if rnode is not None and prop_a == rnode.get_property(prop_a.name): 665 | fdt_same.add_item(prop_a.copy(), path) 666 | else: 667 | fdt_a.add_item(prop_a.copy(), path) 668 | 669 | for path, nodes, props in fdt2.walk(): 670 | try: 671 | rnode = fdt_same.get_node(path) 672 | except: 673 | rnode = None 674 | 675 | for node_b in nodes: 676 | if rnode is None or rnode.get_subnode(node_b.name) is None: 677 | fdt_b.add_item(Node(node_b.name), path) 678 | 679 | for prop_b in props: 680 | if rnode is None or prop_b != rnode.get_property(prop_b.name): 681 | fdt_b.add_item(prop_b.copy(), path) 682 | 683 | return fdt_same, fdt_a, fdt_b 684 | -------------------------------------------------------------------------------- /tests/data/imx7d-sdb.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | // version: 17 3 | // last_comp_version: 16 4 | // boot_cpuid_phys: 0x0 5 | 6 | / { 7 | #address-cells = <0x1>; 8 | #size-cells = <0x1>; 9 | model = "Freescale i.MX7D SabreSD Board"; 10 | compatible = "fsl,imx7d-sdb", "fsl,imx7d"; 11 | chosen { 12 | }; 13 | aliases { 14 | gpio0 = "/soc/aips-bus@30000000/gpio@30200000"; 15 | gpio1 = "/soc/aips-bus@30000000/gpio@30210000"; 16 | gpio2 = "/soc/aips-bus@30000000/gpio@30220000"; 17 | gpio3 = "/soc/aips-bus@30000000/gpio@30230000"; 18 | gpio4 = "/soc/aips-bus@30000000/gpio@30240000"; 19 | gpio5 = "/soc/aips-bus@30000000/gpio@30250000"; 20 | gpio6 = "/soc/aips-bus@30000000/gpio@30260000"; 21 | i2c0 = "/soc/aips-bus@30800000/i2c@30a20000"; 22 | i2c1 = "/soc/aips-bus@30800000/i2c@30a30000"; 23 | i2c2 = "/soc/aips-bus@30800000/i2c@30a40000"; 24 | i2c3 = "/soc/aips-bus@30800000/i2c@30a50000"; 25 | mmc0 = "/soc/aips-bus@30800000/usdhc@30b40000"; 26 | mmc1 = "/soc/aips-bus@30800000/usdhc@30b50000"; 27 | mmc2 = "/soc/aips-bus@30800000/usdhc@30b60000"; 28 | serial0 = "/soc/aips-bus@30800000/spba-bus@30800000/serial@30860000"; 29 | serial1 = "/soc/aips-bus@30800000/spba-bus@30800000/serial@30890000"; 30 | serial2 = "/soc/aips-bus@30800000/spba-bus@30800000/serial@30880000"; 31 | serial3 = "/soc/aips-bus@30800000/serial@30a60000"; 32 | serial4 = "/soc/aips-bus@30800000/serial@30a70000"; 33 | serial5 = "/soc/aips-bus@30800000/serial@30a80000"; 34 | serial6 = "/soc/aips-bus@30800000/serial@30a90000"; 35 | spi0 = "/soc/aips-bus@30800000/spba-bus@30800000/ecspi@30820000"; 36 | spi1 = "/soc/aips-bus@30800000/spba-bus@30800000/ecspi@30830000"; 37 | spi2 = "/soc/aips-bus@30800000/spba-bus@30800000/ecspi@30840000"; 38 | spi3 = "/soc/aips-bus@30400000/ecspi@30630000"; 39 | }; 40 | memory { 41 | device_type = "memory"; 42 | reg = <0x80000000 0x80000000>; 43 | }; 44 | cpus { 45 | #address-cells = <0x1>; 46 | #size-cells = <0x0>; 47 | cpu@0 { 48 | compatible = "arm,cortex-a7"; 49 | device_type = "cpu"; 50 | reg = <0x0>; 51 | operating-points = <0xF32A0 0x106738 0xC15C0 0xEE098>; 52 | clock-latency = <0xEE6C>; 53 | clocks = <0x1 0x3E 0x1 0x3F 0x1 0x2 0x1 0x6>; 54 | clock-names = "arm", "arm_root_src", "pll_arm", "pll_sys_main"; 55 | arm-supply = <0x2>; 56 | linux,phandle = <0xF>; 57 | phandle = <0xF>; 58 | }; 59 | cpu@1 { 60 | compatible = "arm,cortex-a7"; 61 | device_type = "cpu"; 62 | reg = <0x1>; 63 | linux,phandle = <0x11>; 64 | phandle = <0x11>; 65 | }; 66 | }; 67 | interrupt-controller@31001000 { 68 | compatible = "arm,cortex-a7-gic"; 69 | #interrupt-cells = <0x3>; 70 | interrupt-controller; 71 | reg = <0x31001000 0x1000 0x31002000 0x100>; 72 | interrupt-parent = <0x3>; 73 | linux,phandle = <0x3>; 74 | phandle = <0x3>; 75 | }; 76 | clocks { 77 | #address-cells = <0x1>; 78 | #size-cells = <0x0>; 79 | clock@0 { 80 | compatible = "fixed-clock"; 81 | reg = <0x0>; 82 | #clock-cells = <0x0>; 83 | clock-frequency = <0x8000>; 84 | clock-output-names = "ckil"; 85 | linux,phandle = <0x1F>; 86 | phandle = <0x1F>; 87 | }; 88 | clock@1 { 89 | compatible = "fixed-clock"; 90 | reg = <0x1>; 91 | #clock-cells = <0x0>; 92 | clock-frequency = <0x16E3600>; 93 | clock-output-names = "osc"; 94 | linux,phandle = <0x20>; 95 | phandle = <0x20>; 96 | }; 97 | }; 98 | timer { 99 | compatible = "arm,armv7-timer"; 100 | arm,cpu-registers-not-fw-configured; 101 | interrupts = <0x1 0xD 0xF08 0x1 0xE 0xF08>; 102 | interrupt-parent = <0x3>; 103 | clock-frequency = <0x7A1200>; 104 | }; 105 | etr@0,30086000 { 106 | compatible = "arm,coresight-tmc", "arm,primecell"; 107 | reg = <0x30086000 0x1000>; 108 | coresight-default-sink; 109 | clocks = <0x1 0x4A>; 110 | clock-names = "apb_pclk"; 111 | port { 112 | endpoint { 113 | slave-mode; 114 | remote-endpoint = <0x4>; 115 | linux,phandle = <0x6>; 116 | phandle = <0x6>; 117 | }; 118 | }; 119 | }; 120 | tpiu@0,30087000 { 121 | compatible = "arm,coresight-tpiu", "arm,primecell"; 122 | reg = <0x30087000 0x1000>; 123 | clocks = <0x1 0x4A>; 124 | clock-names = "apb_pclk"; 125 | port { 126 | endpoint@0 { 127 | slave-mode; 128 | remote-endpoint = <0x4>; 129 | linux,phandle = <0x5>; 130 | phandle = <0x5>; 131 | }; 132 | }; 133 | }; 134 | replicator { 135 | compatible = "arm,coresight-replicator"; 136 | ports { 137 | #address-cells = <0x1>; 138 | #size-cells = <0x0>; 139 | port@0 { 140 | reg = <0x0>; 141 | endpoint { 142 | remote-endpoint = <0x5>; 143 | }; 144 | }; 145 | port@1 { 146 | reg = <0x1>; 147 | endpoint { 148 | remote-endpoint = <0x6>; 149 | linux,phandle = <0x4>; 150 | phandle = <0x4>; 151 | }; 152 | }; 153 | port@2 { 154 | reg = <0x0>; 155 | endpoint { 156 | slave-mode; 157 | remote-endpoint = <0x7>; 158 | linux,phandle = <0x9>; 159 | phandle = <0x9>; 160 | }; 161 | }; 162 | }; 163 | }; 164 | etf@0,30084000 { 165 | compatible = "arm,coresight-tmc", "arm,primecell"; 166 | reg = <0x30084000 0x1000>; 167 | clocks = <0x1 0x4A>; 168 | clock-names = "apb_pclk"; 169 | ports { 170 | #address-cells = <0x1>; 171 | #size-cells = <0x0>; 172 | port@0 { 173 | reg = <0x0>; 174 | endpoint { 175 | slave-mode; 176 | remote-endpoint = <0x8>; 177 | linux,phandle = <0xB>; 178 | phandle = <0xB>; 179 | }; 180 | }; 181 | port@1 { 182 | reg = <0x0>; 183 | endpoint { 184 | remote-endpoint = <0x9>; 185 | linux,phandle = <0x7>; 186 | phandle = <0x7>; 187 | }; 188 | }; 189 | }; 190 | }; 191 | funnel@1,30083000 { 192 | compatible = "arm,coresight-funnel", "arm,primecell"; 193 | reg = <0x30083000 0x1000>; 194 | clocks = <0x1 0x4A>; 195 | clock-names = "apb_pclk"; 196 | ports { 197 | #address-cells = <0x1>; 198 | #size-cells = <0x0>; 199 | port@0 { 200 | reg = <0x0>; 201 | endpoint { 202 | slave-mode; 203 | remote-endpoint = <0xA>; 204 | linux,phandle = <0xE>; 205 | phandle = <0xE>; 206 | }; 207 | }; 208 | port@1 { 209 | reg = <0x1>; 210 | endpoint { 211 | slave-mode; 212 | }; 213 | }; 214 | port@2 { 215 | reg = <0x0>; 216 | endpoint { 217 | remote-endpoint = <0xB>; 218 | linux,phandle = <0x8>; 219 | phandle = <0x8>; 220 | }; 221 | }; 222 | }; 223 | }; 224 | funnel@0,30041000 { 225 | compatible = "arm,coresight-funnel", "arm,primecell"; 226 | reg = <0x30041000 0x1000>; 227 | clocks = <0x1 0x4A>; 228 | clock-names = "apb_pclk"; 229 | ports { 230 | #address-cells = <0x1>; 231 | #size-cells = <0x0>; 232 | port@0 { 233 | reg = <0x0>; 234 | endpoint { 235 | slave-mode; 236 | remote-endpoint = <0xC>; 237 | linux,phandle = <0x10>; 238 | phandle = <0x10>; 239 | }; 240 | }; 241 | port@1 { 242 | reg = <0x1>; 243 | endpoint { 244 | slave-mode; 245 | remote-endpoint = <0xD>; 246 | linux,phandle = <0x12>; 247 | phandle = <0x12>; 248 | }; 249 | }; 250 | port@2 { 251 | reg = <0x0>; 252 | endpoint { 253 | remote-endpoint = <0xE>; 254 | linux,phandle = <0xA>; 255 | phandle = <0xA>; 256 | }; 257 | }; 258 | }; 259 | }; 260 | etm@0,3007c000 { 261 | compatible = "arm,coresight-etm3x", "arm,primecell"; 262 | reg = <0x3007C000 0x1000>; 263 | cpu = <0xF>; 264 | clocks = <0x1 0x4A>; 265 | clock-names = "apb_pclk"; 266 | port { 267 | endpoint { 268 | remote-endpoint = <0x10>; 269 | linux,phandle = <0xC>; 270 | phandle = <0xC>; 271 | }; 272 | }; 273 | }; 274 | etm@1,3007d000 { 275 | compatible = "arm,coresight-etm3x", "arm,primecell"; 276 | reg = <0x3007D000 0x1000>; 277 | arm,primecell-periphid = <0xBB956>; 278 | cpu = <0x11>; 279 | clocks = <0x1 0x4A>; 280 | clock-names = "apb_pclk"; 281 | port { 282 | endpoint { 283 | remote-endpoint = <0x12>; 284 | linux,phandle = <0xD>; 285 | phandle = <0xD>; 286 | }; 287 | }; 288 | }; 289 | reserved-memory { 290 | #address-cells = <0x1>; 291 | #size-cells = <0x1>; 292 | ranges; 293 | linux,cma { 294 | compatible = "shared-dma-pool"; 295 | reusable; 296 | size = <0x14000000>; 297 | linux,cma-default; 298 | }; 299 | }; 300 | soc { 301 | #address-cells = <0x1>; 302 | #size-cells = <0x1>; 303 | compatible = "simple-bus"; 304 | interrupt-parent = <0x13>; 305 | ranges; 306 | busfreq { 307 | compatible = "fsl,imx_busfreq"; 308 | fsl,max_ddr_freq = <0x1FC4EF40>; 309 | clocks = <0x1 0x0 0x1 0x4B 0x1 0x5B 0x1 0xF 0x1 0x63 0x1 0x6B 0x1 0x30 0x1 0x6A 0x1 0x15 0x1 0x12 0x1 0x5A 0x1 0x4D>; 310 | clock-names = "osc", "axi_sel", "ahb_sel", "pfd0_392m", "dram_root", "dram_alt_sel", "pll_dram", "dram_alt_root", "pfd2_270m", "pfd1_332m", "ahb", "axi"; 311 | interrupts = <0x0 0x70 0x4 0x0 0x71 0x4>; 312 | interrupt-names = "irq_busfreq_0", "irq_busfreq_1"; 313 | }; 314 | caam-sm@00100000 { 315 | compatible = "fsl,imx7d-caam-sm", "fsl,imx6q-caam-sm"; 316 | reg = <0x100000 0x3FFF>; 317 | }; 318 | caam_secvio { 319 | compatible = "fsl,imx7d-caam-secvio", "fsl,imx6q-caam-secvio"; 320 | interrupts = <0x0 0x14 0x4>; 321 | jtag-tamper = "disabled"; 322 | watchdog-tamper = "enabled"; 323 | internal-boot-tamper = "enabled"; 324 | external-pin-tamper = "disabled"; 325 | }; 326 | pmu { 327 | compatible = "arm,cortex-a7-pmu"; 328 | interrupts = <0x0 0x5C 0x4>; 329 | status = "disabled"; 330 | }; 331 | sram@00900000 { 332 | compatible = "fsl,ddr-lpm-sram"; 333 | reg = <0x900000 0x1000>; 334 | clocks = <0x1 0x19F>; 335 | }; 336 | sram@901000 { 337 | compatible = "mmio-sram"; 338 | reg = <0x901000 0x1F000>; 339 | clocks = <0x1 0x19F>; 340 | linux,phandle = <0x5F>; 341 | phandle = <0x5F>; 342 | }; 343 | sram@00180000 { 344 | compatible = "fsl,lpm-sram"; 345 | reg = <0x180000 0x8000>; 346 | clocks = <0x1 0x1A0>; 347 | status = "disabled"; 348 | }; 349 | sram-mf@00900000 { 350 | compatible = "fsl,mega-fast-sram"; 351 | reg = <0x900000 0x20000>; 352 | clocks = <0x1 0x19F>; 353 | }; 354 | dma-apbh@33000000 { 355 | compatible = "fsl,imx7d-dma-apbh", "fsl,imx28-dma-apbh"; 356 | reg = <0x33000000 0x2000>; 357 | interrupts = <0x0 0xC 0x4 0x0 0xC 0x4 0x0 0xC 0x4 0x0 0xC 0x4>; 358 | interrupt-names = "gpmi0", "gpmi1", "gpmi2", "gpmi3"; 359 | #dma-cells = <0x1>; 360 | dma-channels = <0x4>; 361 | clocks = <0x1 0x56 0x1 0xB6>; 362 | clock-names = "dma_apbh_bch", "dma_apbh_io"; 363 | linux,phandle = <0x14>; 364 | phandle = <0x14>; 365 | }; 366 | gpmi-nand@33002000 { 367 | compatible = "fsl,imx7d-gpmi-nand"; 368 | #address-cells = <0x1>; 369 | #size-cells = <0x1>; 370 | reg = <0x33002000 0x2000 0x33004000 0x4000>; 371 | reg-names = "gpmi-nand", "bch"; 372 | interrupts = <0x0 0xE 0x4>; 373 | interrupt-names = "bch"; 374 | clocks = <0x1 0xB6 0x1 0x56>; 375 | clock-names = "gpmi_io", "gpmi_bch_apb"; 376 | dmas = <0x14 0x0>; 377 | dma-names = "rx-tx"; 378 | status = "disabled"; 379 | pinctrl-names = "default"; 380 | pinctrl-0 = <0x15>; 381 | nand-on-flash-bbt; 382 | }; 383 | aips-bus@30000000 { 384 | compatible = "fsl,aips-bus", "simple-bus"; 385 | #address-cells = <0x1>; 386 | #size-cells = <0x1>; 387 | reg = <0x30000000 0x400000>; 388 | ranges; 389 | gpio@30200000 { 390 | compatible = "fsl,imx7d-gpio", "fsl,imx35-gpio"; 391 | reg = <0x30200000 0x10000>; 392 | interrupts = <0x0 0x40 0x4 0x0 0x41 0x4>; 393 | gpio-controller; 394 | #gpio-cells = <0x2>; 395 | interrupt-controller; 396 | #interrupt-cells = <0x2>; 397 | linux,phandle = <0x2A>; 398 | phandle = <0x2A>; 399 | }; 400 | gpio@30210000 { 401 | compatible = "fsl,imx7d-gpio", "fsl,imx35-gpio"; 402 | reg = <0x30210000 0x10000>; 403 | interrupts = <0x0 0x42 0x4 0x0 0x43 0x4>; 404 | gpio-controller; 405 | #gpio-cells = <0x2>; 406 | interrupt-controller; 407 | #interrupt-cells = <0x2>; 408 | linux,phandle = <0x39>; 409 | phandle = <0x39>; 410 | }; 411 | gpio@30220000 { 412 | compatible = "fsl,imx7d-gpio", "fsl,imx35-gpio"; 413 | reg = <0x30220000 0x10000>; 414 | interrupts = <0x0 0x44 0x4 0x0 0x45 0x4>; 415 | gpio-controller; 416 | #gpio-cells = <0x2>; 417 | interrupt-controller; 418 | #interrupt-cells = <0x2>; 419 | }; 420 | gpio@30230000 { 421 | compatible = "fsl,imx7d-gpio", "fsl,imx35-gpio"; 422 | reg = <0x30230000 0x10000>; 423 | interrupts = <0x0 0x46 0x4 0x0 0x47 0x4>; 424 | gpio-controller; 425 | #gpio-cells = <0x2>; 426 | interrupt-controller; 427 | #interrupt-cells = <0x2>; 428 | linux,phandle = <0x45>; 429 | phandle = <0x45>; 430 | }; 431 | gpio@30240000 { 432 | compatible = "fsl,imx7d-gpio", "fsl,imx35-gpio"; 433 | reg = <0x30240000 0x10000>; 434 | interrupts = <0x0 0x48 0x4 0x0 0x49 0x4>; 435 | gpio-controller; 436 | #gpio-cells = <0x2>; 437 | interrupt-controller; 438 | #interrupt-cells = <0x2>; 439 | linux,phandle = <0x37>; 440 | phandle = <0x37>; 441 | }; 442 | gpio@30250000 { 443 | compatible = "fsl,imx7d-gpio", "fsl,imx35-gpio"; 444 | reg = <0x30250000 0x10000>; 445 | interrupts = <0x0 0x4A 0x4 0x0 0x4B 0x4>; 446 | gpio-controller; 447 | #gpio-cells = <0x2>; 448 | interrupt-controller; 449 | #interrupt-cells = <0x2>; 450 | }; 451 | gpio@30260000 { 452 | compatible = "fsl,imx7d-gpio", "fsl,imx35-gpio"; 453 | reg = <0x30260000 0x10000>; 454 | interrupts = <0x0 0x4C 0x4 0x0 0x4D 0x4>; 455 | gpio-controller; 456 | #gpio-cells = <0x2>; 457 | interrupt-controller; 458 | #interrupt-cells = <0x2>; 459 | }; 460 | lpsr-gpr@30270000 { 461 | compatible = "fsl,imx7d-lpsr-gpr"; 462 | reg = <0x30270000 0x10000>; 463 | }; 464 | wdog@30280000 { 465 | compatible = "fsl,imx7d-wdt", "fsl,imx21-wdt"; 466 | reg = <0x30280000 0x10000>; 467 | interrupts = <0x0 0x4E 0x4>; 468 | clocks = <0x1 0x142>; 469 | pinctrl-names = "default"; 470 | pinctrl-0 = <0x16>; 471 | fsl,wdog_b; 472 | }; 473 | wdog@30290000 { 474 | compatible = "fsl,imx7d-wdt", "fsl,imx21-wdt"; 475 | reg = <0x30290000 0x10000>; 476 | interrupts = <0x0 0x4F 0x4>; 477 | clocks = <0x1 0x1A1>; 478 | status = "disabled"; 479 | }; 480 | wdog@302a0000 { 481 | compatible = "fsl,imx7d-wdt", "fsl,imx21-wdt"; 482 | reg = <0x302A0000 0x10000>; 483 | interrupts = <0x0 0xA 0x4>; 484 | clocks = <0x1 0x1A2>; 485 | status = "disabled"; 486 | }; 487 | wdog@302b0000 { 488 | compatible = "fsl,imx7d-wdt", "fsl,imx21-wdt"; 489 | reg = <0x302B0000 0x10000>; 490 | interrupts = <0x0 0x6D 0x4>; 491 | clocks = <0x1 0x1A3>; 492 | status = "disabled"; 493 | }; 494 | iomuxc-lpsr@302c0000 { 495 | compatible = "fsl,imx7d-iomuxc-lpsr"; 496 | reg = <0x302C0000 0x10000>; 497 | fsl,input-sel = <0x17>; 498 | pinctrl-names = "default"; 499 | pinctrl-0 = <0x18 0x19>; 500 | imx7d-sdb { 501 | hoggrp-2 { 502 | fsl,pins = <0x14 0x44 0x0 0x0 0x0 0x14>; 503 | linux,phandle = <0x18>; 504 | phandle = <0x18>; 505 | }; 506 | pwm1grp { 507 | fsl,pins = <0x4 0x34 0x0 0x1 0x0 0x30>; 508 | linux,phandle = <0x24>; 509 | phandle = <0x24>; 510 | }; 511 | usbotg2-2 { 512 | fsl,pins = <0x1C 0x4C 0x0 0x0 0x0 0x14>; 513 | linux,phandle = <0x19>; 514 | phandle = <0x19>; 515 | }; 516 | wdoggrp { 517 | fsl,pins = <0x0 0x30 0x0 0x3 0x0 0x74>; 518 | linux,phandle = <0x16>; 519 | phandle = <0x16>; 520 | }; 521 | enet2_epdc0_grp { 522 | fsl,pins = <0x10 0x40 0x0 0x0 0x0 0x59>; 523 | linux,phandle = <0x26>; 524 | phandle = <0x26>; 525 | }; 526 | sai3grp_mclk { 527 | fsl,pins = <0xC 0x3C 0x0 0x3 0x0 0x1F>; 528 | linux,phandle = <0x3D>; 529 | phandle = <0x3D>; 530 | }; 531 | }; 532 | }; 533 | gpt@302d0000 { 534 | compatible = "fsl,imx7d-gpt", "fsl,imx31-gpt"; 535 | reg = <0x302D0000 0x10000>; 536 | interrupts = <0x0 0x37 0x4>; 537 | clocks = <0x1 0x12E 0x1 0x12E 0x1 0x19E>; 538 | clock-names = "ipg", "per", "osc_per"; 539 | }; 540 | gpt@302e0000 { 541 | compatible = "fsl,imx7d-gpt", "fsl,imx31-gpt"; 542 | reg = <0x302E0000 0x10000>; 543 | interrupts = <0x0 0x36 0x4>; 544 | clocks = <0x1 0x19D 0x1 0x132>; 545 | clock-names = "ipg", "per"; 546 | status = "disabled"; 547 | }; 548 | gpt@302f0000 { 549 | compatible = "fsl,imx7d-gpt", "fsl,imx31-gpt"; 550 | reg = <0x302F0000 0x10000>; 551 | interrupts = <0x0 0x35 0x4>; 552 | clocks = <0x1 0x19D 0x1 0x136>; 553 | clock-names = "ipg", "per"; 554 | status = "disabled"; 555 | }; 556 | gpt@30300000 { 557 | compatible = "fsl,imx7d-gpt", "fsl,imx31-gpt"; 558 | reg = <0x30300000 0x10000>; 559 | interrupts = <0x0 0x34 0x4>; 560 | clocks = <0x1 0x19D 0x1 0x13A>; 561 | clock-names = "ipg", "per"; 562 | status = "disabled"; 563 | }; 564 | kpp@30320000 { 565 | compatible = "fsl,imx7d-kpp", "fsl,imx21-kpp"; 566 | reg = <0x30320000 0x10000>; 567 | interrupts = <0x0 0x50 0x4>; 568 | clocks = <0x1 0x19D>; 569 | status = "disabled"; 570 | }; 571 | iomuxc@30330000 { 572 | compatible = "fsl,imx7d-iomuxc"; 573 | reg = <0x30330000 0x10000>; 574 | pinctrl-names = "default"; 575 | pinctrl-0 = <0x1A>; 576 | linux,phandle = <0x17>; 577 | phandle = <0x17>; 578 | imx7d-sdb { 579 | hoggrp-1 { 580 | fsl,pins = <0x184 0x3F4 0x0 0x5 0x0 0x19 0xA4 0x314 0x0 0x5 0x0 0x59>; 581 | linux,phandle = <0x1A>; 582 | phandle = <0x1A>; 583 | }; 584 | epdc_elan_touch_grp { 585 | fsl,pins = <0x200 0x470 0x0 0x5 0x0 0x59 0x20C 0x47C 0x0 0x5 0x0 0x1B 0x204 0x474 0x0 0x5 0x0 0x80000000>; 586 | }; 587 | mipi_dsi_reset_grp { 588 | fsl,pins = <0x20C 0x47C 0x0 0x5 0x0 0x1B>; 589 | }; 590 | ecspi3_cs_grp { 591 | fsl,pins = <0x1AC 0x41C 0x0 0x5 0x0 0x80000000>; 592 | linux,phandle = <0x36>; 593 | phandle = <0x36>; 594 | }; 595 | ecspi3grp { 596 | fsl,pins = <0x21C 0x48C 0x548 0x1 0x1 0x2 0x220 0x490 0x54C 0x1 0x1 0x2 0x224 0x494 0x544 0x1 0x1 0x2>; 597 | linux,phandle = <0x35>; 598 | phandle = <0x35>; 599 | }; 600 | enet1grp { 601 | fsl,pins = <0x1C 0x274 0x568 0x2 0x0 0x3 0x20 0x278 0x0 0x2 0x0 0x3 0x258 0x4C8 0x0 0x0 0x0 0x1 0x244 0x4B4 0x0 0x0 0x0 0x1 0x248 0x4B8 0x0 0x0 0x0 0x1 0x24C 0x4BC 0x0 0x0 0x0 0x1 0x250 0x4C0 0x0 0x0 0x0 0x1 0x254 0x4C4 0x0 0x0 0x0 0x1 0x240 0x4B0 0x0 0x0 0x0 0x1 0x22C 0x49C 0x0 0x0 0x0 0x1 0x230 0x4A0 0x0 0x0 0x0 0x1 0x234 0x4A4 0x0 0x0 0x0 0x1 0x238 0x4A8 0x0 0x0 0x0 0x1 0x23C 0x4AC 0x0 0x0 0x0 0x1>; 602 | linux,phandle = <0x60>; 603 | phandle = <0x60>; 604 | }; 605 | enet2grp { 606 | fsl,pins = <0xA0 0x310 0x0 0x2 0x0 0x1 0x8C 0x2FC 0x0 0x2 0x0 0x1 0x90 0x300 0x0 0x2 0x0 0x1 0x94 0x304 0x0 0x2 0x0 0x1 0x98 0x308 0x0 0x2 0x0 0x1 0x9C 0x30C 0x0 0x2 0x0 0x1 0x88 0x2F8 0x578 0x2 0x0 0x1 0x74 0x2E4 0x0 0x2 0x0 0x1 0x78 0x2E8 0x0 0x2 0x0 0x1 0x7C 0x2EC 0x0 0x2 0x0 0x1 0x80 0x2F0 0x0 0x2 0x0 0x1 0x84 0x2F4 0x0 0x2 0x0 0x1>; 607 | linux,phandle = <0x62>; 608 | phandle = <0x62>; 609 | }; 610 | epdcgrp0 { 611 | fsl,pins = <0x34 0x2A4 0x0 0x0 0x0 0x2 0x38 0x2A8 0x0 0x0 0x0 0x2 0x3C 0x2AC 0x0 0x0 0x0 0x2 0x40 0x2B0 0x0 0x0 0x0 0x2 0x44 0x2B4 0x0 0x0 0x0 0x2 0x48 0x2B8 0x0 0x0 0x0 0x2 0x4C 0x2BC 0x0 0x0 0x0 0x2 0x50 0x2C0 0x0 0x0 0x0 0x2 0x54 0x2C4 0x0 0x0 0x0 0x2 0x58 0x2C8 0x0 0x0 0x0 0x2 0x5C 0x2CC 0x0 0x0 0x0 0x2 0x60 0x2D0 0x0 0x0 0x0 0x2 0x64 0x2D4 0x0 0x0 0x0 0x2 0x68 0x2D8 0x0 0x0 0x0 0x2 0x6C 0x2DC 0x0 0x0 0x0 0x2 0x70 0x2E0 0x0 0x0 0x0 0x2 0x74 0x2E4 0x0 0x0 0x0 0x2 0x78 0x2E8 0x0 0x0 0x0 0x2 0x7C 0x2EC 0x0 0x0 0x0 0x2 0x80 0x2F0 0x0 0x0 0x0 0x2 0x84 0x2F4 0x0 0x0 0x0 0x2 0x88 0x2F8 0x0 0x0 0x0 0x2 0x94 0x304 0x0 0x0 0x0 0x2 0x98 0x308 0x0 0x0 0x0 0x2 0x9C 0x30C 0x0 0x0 0x0 0x2 0xA0 0x310 0x0 0x0 0x0 0x2>; 612 | linux,phandle = <0x25>; 613 | phandle = <0x25>; 614 | }; 615 | flexcan2grp { 616 | fsl,pins = <0x2C 0x284 0x4E0 0x3 0x0 0x59 0x30 0x288 0x0 0x3 0x0 0x59 0x6C 0x2DC 0x0 0x5 0x0 0x59>; 617 | linux,phandle = <0x3E>; 618 | phandle = <0x3E>; 619 | }; 620 | gpmi-nand-1 { 621 | fsl,pins = <0x1D0 0x440 0x0 0x1 0x0 0x71 0x1D4 0x444 0x0 0x1 0x0 0x71 0x218 0x488 0x0 0x1 0x0 0x71 0x204 0x474 0x0 0x1 0x0 0x71 0x200 0x470 0x0 0x1 0x0 0x71 0x20C 0x47C 0x0 0x1 0x0 0x74 0x1F8 0x468 0x0 0x1 0x0 0x71 0x1FC 0x46C 0x0 0x1 0x0 0x71 0x1D8 0x448 0x0 0x1 0x0 0x71 0x1DC 0x44C 0x0 0x1 0x0 0x71 0x1E0 0x450 0x0 0x1 0x0 0x71 0x1E4 0x454 0x0 0x1 0x0 0x71 0x1E8 0x458 0x0 0x1 0x0 0x71 0x1EC 0x45C 0x0 0x1 0x0 0x71 0x1F0 0x460 0x0 0x1 0x0 0x71 0x1F4 0x464 0x0 0x1 0x0 0x71>; 622 | linux,phandle = <0x15>; 623 | phandle = <0x15>; 624 | }; 625 | i2c1grp { 626 | fsl,pins = <0x14C 0x3BC 0x5D8 0x0 0x1 0x4000007F 0x148 0x3B8 0x5D4 0x0 0x1 0x4000007F>; 627 | linux,phandle = <0x40>; 628 | phandle = <0x40>; 629 | }; 630 | i2c2grp { 631 | fsl,pins = <0x154 0x3C4 0x5E0 0x0 0x1 0x4000007F 0x150 0x3C0 0x5DC 0x0 0x1 0x4000007F>; 632 | linux,phandle = <0x41>; 633 | phandle = <0x41>; 634 | }; 635 | i2c3grp { 636 | fsl,pins = <0x15C 0x3CC 0x5E8 0x0 0x2 0x4000007F 0x158 0x3C8 0x5E4 0x0 0x2 0x4000007F>; 637 | linux,phandle = <0x42>; 638 | phandle = <0x42>; 639 | }; 640 | i2c4grp { 641 | fsl,pins = <0x214 0x484 0x5F0 0x3 0x3 0x4000007F 0x210 0x480 0x5EC 0x3 0x3 0x4000007F>; 642 | linux,phandle = <0x46>; 643 | phandle = <0x46>; 644 | }; 645 | lcdifdatgrp { 646 | fsl,pins = <0xC8 0x338 0x638 0x0 0x2 0x79 0xCC 0x33C 0x63C 0x0 0x2 0x79 0xD0 0x340 0x640 0x0 0x2 0x79 0xD4 0x344 0x644 0x0 0x2 0x79 0xD8 0x348 0x648 0x0 0x2 0x79 0xDC 0x34C 0x64C 0x0 0x2 0x79 0xE0 0x350 0x650 0x0 0x2 0x79 0xE4 0x354 0x654 0x0 0x2 0x79 0xE8 0x358 0x658 0x0 0x2 0x79 0xEC 0x35C 0x65C 0x0 0x2 0x79 0xF0 0x360 0x660 0x0 0x2 0x79 0xF4 0x364 0x664 0x0 0x2 0x79 0xF8 0x368 0x668 0x0 0x2 0x79 0xFC 0x36C 0x66C 0x0 0x1 0x79 0x100 0x370 0x670 0x0 0x1 0x79 0x104 0x374 0x674 0x0 0x1 0x79 0x108 0x378 0x678 0x0 0x2 0x79 0x10C 0x37C 0x67C 0x0 0x2 0x79 0x110 0x380 0x680 0x0 0x2 0x79 0x114 0x384 0x684 0x0 0x2 0x79 0x118 0x388 0x688 0x0 0x2 0x79 0x11C 0x38C 0x68C 0x0 0x2 0x79 0x120 0x390 0x690 0x0 0x2 0x79 0x124 0x394 0x694 0x0 0x2 0x79>; 647 | linux,phandle = <0x2C>; 648 | phandle = <0x2C>; 649 | }; 650 | lcdifctrlgrp { 651 | fsl,pins = <0xB4 0x324 0x0 0x0 0x0 0x79 0xB8 0x328 0x0 0x0 0x0 0x79 0xC0 0x330 0x698 0x0 0x2 0x79 0xBC 0x32C 0x0 0x0 0x0 0x79 0xC4 0x334 0x0 0x0 0x0 0x79>; 652 | linux,phandle = <0x2D>; 653 | phandle = <0x2D>; 654 | }; 655 | max17135grp-1 { 656 | fsl,pins = <0xB0 0x320 0x0 0x5 0x0 0x80000000 0x160 0x3D0 0x0 0x5 0x0 0x80000000 0x90 0x300 0x0 0x5 0x0 0x80000000 0xAC 0x31C 0x0 0x5 0x0 0x80000000 0x8C 0x2FC 0x0 0x5 0x0 0x80000000>; 657 | linux,phandle = <0x44>; 658 | phandle = <0x44>; 659 | }; 660 | sai1grp { 661 | fsl,pins = <0x218 0x488 0x0 0x0 0x0 0x1F 0x260 0x4D0 0x6A8 0x2 0x1 0x1F 0x264 0x4D4 0x6AC 0x2 0x1 0x1F 0x268 0x4D8 0x0 0x2 0x0 0x30 0x25C 0x4CC 0x6A0 0x2 0x1 0x1F>; 662 | linux,phandle = <0x3B>; 663 | phandle = <0x3B>; 664 | }; 665 | sai2grp { 666 | fsl,pins = <0x220 0x490 0x6BC 0x0 0x1 0x1F 0x21C 0x48C 0x6C0 0x0 0x1 0x1F 0x228 0x498 0x0 0x0 0x0 0x30 0x224 0x494 0x6B4 0x0 0x1 0x1F>; 667 | }; 668 | sai3grp { 669 | fsl,pins = <0x13C 0x3AC 0x6D0 0x2 0x0 0x1F 0x144 0x3B4 0x6D4 0x2 0x0 0x1F 0x140 0x3B0 0x0 0x2 0x0 0x30>; 670 | linux,phandle = <0x3C>; 671 | phandle = <0x3C>; 672 | }; 673 | spi1grp { 674 | fsl,pins = <0x18 0x270 0x0 0x0 0x0 0x59 0x24 0x27C 0x0 0x0 0x0 0x59 0x28 0x280 0x0 0x0 0x0 0x59>; 675 | linux,phandle = <0x6A>; 676 | phandle = <0x6A>; 677 | }; 678 | hdmigrp-1 { 679 | fsl,pins = <0x68 0x2D8 0x0 0x5 0x0 0x59>; 680 | linux,phandle = <0x43>; 681 | phandle = <0x43>; 682 | }; 683 | sim1grp-1 { 684 | fsl,pins = <0x5C 0x2CC 0x0 0x1 0x0 0x77 0x64 0x2D4 0x6E0 0x1 0x0 0x77 0x60 0x2D0 0x0 0x1 0x0 0x77 0x58 0x2C8 0x0 0x1 0x0 0x73 0x54 0x2C4 0x6E4 0x1 0x0 0x73>; 685 | linux,phandle = <0x5E>; 686 | phandle = <0x5E>; 687 | }; 688 | tsc2046_pendown { 689 | fsl,pins = <0xA8 0x318 0x0 0x5 0x0 0x59>; 690 | linux,phandle = <0x38>; 691 | phandle = <0x38>; 692 | }; 693 | uart1grp { 694 | fsl,pins = <0x12C 0x39C 0x0 0x0 0x0 0x79 0x128 0x398 0x6F4 0x0 0x0 0x79>; 695 | linux,phandle = <0x3A>; 696 | phandle = <0x3A>; 697 | }; 698 | uart5grp { 699 | fsl,pins = <0x204 0x474 0x0 0x2 0x0 0x79 0x200 0x470 0x714 0x2 0x2 0x79>; 700 | linux,phandle = <0x49>; 701 | phandle = <0x49>; 702 | }; 703 | uart5dtegrp { 704 | fsl,pins = <0x204 0x474 0x714 0x2 0x3 0x79 0x200 0x470 0x0 0x2 0x0 0x79>; 705 | }; 706 | uart6grp { 707 | fsl,pins = <0x16C 0x3DC 0x0 0x1 0x0 0x79 0x168 0x3D8 0x71C 0x1 0x2 0x79 0x174 0x3E4 0x0 0x1 0x0 0x79 0x170 0x3E0 0x718 0x1 0x2 0x79>; 708 | linux,phandle = <0x4A>; 709 | phandle = <0x4A>; 710 | }; 711 | usdhc1_gpiogrp { 712 | fsl,pins = <0x188 0x3F8 0x0 0x5 0x0 0x59 0x18C 0x3FC 0x0 0x5 0x0 0x59 0x190 0x400 0x0 0x5 0x0 0x59 0x14 0x26C 0x0 0x1 0x0 0x59>; 713 | linux,phandle = <0x54>; 714 | phandle = <0x54>; 715 | }; 716 | usbotg2-1 { 717 | fsl,pins = <0x144 0x3B4 0x0 0x5 0x0 0x14>; 718 | }; 719 | usdhc1grp { 720 | fsl,pins = <0x198 0x408 0x0 0x0 0x0 0x59 0x194 0x404 0x0 0x0 0x0 0x19 0x19C 0x40C 0x0 0x0 0x0 0x59 0x1A0 0x410 0x0 0x0 0x0 0x59 0x1A4 0x414 0x0 0x0 0x0 0x59 0x1A8 0x418 0x0 0x0 0x0 0x59>; 721 | linux,phandle = <0x53>; 722 | phandle = <0x53>; 723 | }; 724 | usdhc1grp_100mhz { 725 | fsl,pins = <0x198 0x408 0x0 0x0 0x0 0x5A 0x194 0x404 0x0 0x0 0x0 0x1A 0x19C 0x40C 0x0 0x0 0x0 0x5A 0x1A0 0x410 0x0 0x0 0x0 0x5A 0x1A4 0x414 0x0 0x0 0x0 0x5A 0x1A8 0x418 0x0 0x0 0x0 0x5A>; 726 | linux,phandle = <0x55>; 727 | phandle = <0x55>; 728 | }; 729 | usdhc1grp_200mhz { 730 | fsl,pins = <0x198 0x408 0x0 0x0 0x0 0x5B 0x194 0x404 0x0 0x0 0x0 0x1B 0x19C 0x40C 0x0 0x0 0x0 0x5B 0x1A0 0x410 0x0 0x0 0x0 0x5B 0x1A4 0x414 0x0 0x0 0x0 0x5B 0x1A8 0x418 0x0 0x0 0x0 0x5B>; 731 | linux,phandle = <0x56>; 732 | phandle = <0x56>; 733 | }; 734 | usdhc2grp { 735 | fsl,pins = <0x1BC 0x42C 0x0 0x0 0x0 0x59 0x1B8 0x428 0x0 0x0 0x0 0x19 0x1C0 0x430 0x0 0x0 0x0 0x59 0x1C4 0x434 0x0 0x0 0x0 0x59 0x1C8 0x438 0x0 0x0 0x0 0x59 0x1CC 0x43C 0x0 0x0 0x0 0x59 0x17C 0x3EC 0x0 0x5 0x0 0x19 0x178 0x3E8 0x0 0x5 0x0 0x19>; 736 | linux,phandle = <0x58>; 737 | phandle = <0x58>; 738 | }; 739 | usdhc2grp_100mhz { 740 | fsl,pins = <0x1BC 0x42C 0x0 0x0 0x0 0x5A 0x1B8 0x428 0x0 0x0 0x0 0x1A 0x1C0 0x430 0x0 0x0 0x0 0x5A 0x1C4 0x434 0x0 0x0 0x0 0x5A 0x1C8 0x438 0x0 0x0 0x0 0x5A 0x1CC 0x43C 0x0 0x0 0x0 0x5A>; 741 | linux,phandle = <0x59>; 742 | phandle = <0x59>; 743 | }; 744 | usdhc2grp_200mhz { 745 | fsl,pins = <0x1BC 0x42C 0x0 0x0 0x0 0x5B 0x1B8 0x428 0x0 0x0 0x0 0x1B 0x1C0 0x430 0x0 0x0 0x0 0x5B 0x1C4 0x434 0x0 0x0 0x0 0x5B 0x1C8 0x438 0x0 0x0 0x0 0x5B 0x1CC 0x43C 0x0 0x0 0x0 0x5B>; 746 | linux,phandle = <0x5A>; 747 | phandle = <0x5A>; 748 | }; 749 | usdhc3grp { 750 | fsl,pins = <0x1D4 0x444 0x0 0x0 0x0 0x59 0x1D0 0x440 0x0 0x0 0x0 0x19 0x1D8 0x448 0x0 0x0 0x0 0x59 0x1DC 0x44C 0x0 0x0 0x0 0x59 0x1E0 0x450 0x0 0x0 0x0 0x59 0x1E4 0x454 0x0 0x0 0x0 0x59 0x1E8 0x458 0x0 0x0 0x0 0x59 0x1EC 0x45C 0x0 0x0 0x0 0x59 0x1F0 0x460 0x0 0x0 0x0 0x59 0x1F4 0x464 0x0 0x0 0x0 0x59 0x1F8 0x468 0x0 0x0 0x0 0x19>; 751 | linux,phandle = <0x5B>; 752 | phandle = <0x5B>; 753 | }; 754 | usdhc3grp_100mhz { 755 | fsl,pins = <0x1D4 0x444 0x0 0x0 0x0 0x5A 0x1D0 0x440 0x0 0x0 0x0 0x1A 0x1D8 0x448 0x0 0x0 0x0 0x5A 0x1DC 0x44C 0x0 0x0 0x0 0x5A 0x1E0 0x450 0x0 0x0 0x0 0x5A 0x1E4 0x454 0x0 0x0 0x0 0x5A 0x1E8 0x458 0x0 0x0 0x0 0x5A 0x1EC 0x45C 0x0 0x0 0x0 0x5A 0x1F0 0x460 0x0 0x0 0x0 0x5A 0x1F4 0x464 0x0 0x0 0x0 0x5A 0x1F8 0x468 0x0 0x0 0x0 0x1A>; 756 | linux,phandle = <0x5C>; 757 | phandle = <0x5C>; 758 | }; 759 | usdhc3grp_200mhz { 760 | fsl,pins = <0x1D4 0x444 0x0 0x0 0x0 0x5B 0x1D0 0x440 0x0 0x0 0x0 0x1B 0x1D8 0x448 0x0 0x0 0x0 0x5B 0x1DC 0x44C 0x0 0x0 0x0 0x5B 0x1E0 0x450 0x0 0x0 0x0 0x5B 0x1E4 0x454 0x0 0x0 0x0 0x5B 0x1E8 0x458 0x0 0x0 0x0 0x5B 0x1EC 0x45C 0x0 0x0 0x0 0x5B 0x1F0 0x460 0x0 0x0 0x0 0x5B 0x1F4 0x464 0x0 0x0 0x0 0x5B 0x1F8 0x468 0x0 0x0 0x0 0x1B>; 761 | linux,phandle = <0x5D>; 762 | phandle = <0x5D>; 763 | }; 764 | }; 765 | }; 766 | iomuxc-gpr@30340000 { 767 | compatible = "fsl,imx7d-iomuxc-gpr", "syscon"; 768 | reg = <0x30340000 0x10000>; 769 | linux,phandle = <0x1B>; 770 | phandle = <0x1B>; 771 | }; 772 | mqs { 773 | compatible = "fsl,imx6sx-mqs"; 774 | gpr = <0x1B>; 775 | status = "disabled"; 776 | }; 777 | ocotp-ctrl@30350000 { 778 | compatible = "fsl,imx7d-ocotp", "syscon"; 779 | reg = <0x30350000 0x10000>; 780 | clocks = <0x1 0x1B5>; 781 | linux,phandle = <0x1D>; 782 | phandle = <0x1D>; 783 | }; 784 | anatop@30360000 { 785 | compatible = "fsl,imx7d-anatop", "fsl,imx6q-anatop", "syscon", "simple-bus"; 786 | reg = <0x30360000 0x10000>; 787 | interrupts = <0x0 0x31 0x4 0x0 0x33 0x4>; 788 | linux,phandle = <0x1C>; 789 | phandle = <0x1C>; 790 | regulator-vdd1p0d@210 { 791 | compatible = "fsl,anatop-regulator"; 792 | regulator-name = "vdd1p0d"; 793 | regulator-min-microvolt = <0xC3500>; 794 | regulator-max-microvolt = <0x124F80>; 795 | anatop-reg-offset = <0x210>; 796 | anatop-vol-bit-shift = <0x8>; 797 | anatop-vol-bit-width = <0x5>; 798 | anatop-min-bit-val = <0x8>; 799 | anatop-min-voltage = <0xC3500>; 800 | anatop-max-voltage = <0x124F80>; 801 | anatop-enable-bit = <0x0>; 802 | linux,phandle = <0x21>; 803 | phandle = <0x21>; 804 | }; 805 | regulator-vdd1p2@220 { 806 | compatible = "fsl,anatop-regulator"; 807 | regulator-name = "vdd1p2"; 808 | regulator-min-microvolt = <0x10C8E0>; 809 | regulator-max-microvolt = <0x13D620>; 810 | anatop-reg-offset = <0x220>; 811 | anatop-vol-bit-shift = <0x8>; 812 | anatop-vol-bit-width = <0x5>; 813 | anatop-min-bit-val = <0x8>; 814 | anatop-min-voltage = <0x10C8E0>; 815 | anatop-max-voltage = <0x13D620>; 816 | anatop-enable-bit = <0x1F>; 817 | linux,phandle = <0x22>; 818 | phandle = <0x22>; 819 | }; 820 | }; 821 | tempmon { 822 | compatible = "fsl,imx7d-tempmon"; 823 | interrupts = <0x0 0x31 0x4>; 824 | fsl,tempmon = <0x1C>; 825 | fsl,tempmon-data = <0x1D>; 826 | clocks = <0x1 0x6>; 827 | }; 828 | caam-snvs@30370000 { 829 | compatible = "fsl,imx6q-caam-snvs"; 830 | reg = <0x30370000 0x10000>; 831 | }; 832 | snvs@30370000 { 833 | compatible = "fsl,sec-v4.0-mon", "syscon", "simple-mfd"; 834 | reg = <0x30370000 0x10000>; 835 | linux,phandle = <0x1E>; 836 | phandle = <0x1E>; 837 | snvs-rtc-lp { 838 | compatible = "fsl,sec-v4.0-mon-rtc-lp"; 839 | regmap = <0x1E>; 840 | offset = <0x34>; 841 | interrupts = <0x0 0x13 0x4 0x0 0x14 0x4>; 842 | }; 843 | snvs-powerkey { 844 | compatible = "fsl,sec-v4.0-pwrkey"; 845 | regmap = <0x1E>; 846 | interrupts = <0x0 0x4 0x4>; 847 | linux,keycode = <0x74>; 848 | wakeup; 849 | }; 850 | snvs-poweroff { 851 | compatible = "syscon-poweroff"; 852 | regmap = <0x1E>; 853 | offset = <0x38>; 854 | mask = <0x61>; 855 | }; 856 | }; 857 | ccm@30380000 { 858 | compatible = "fsl,imx7d-ccm"; 859 | reg = <0x30380000 0x10000>; 860 | interrupts = <0x0 0x55 0x4 0x0 0x56 0x4>; 861 | #clock-cells = <0x1>; 862 | clocks = <0x1F 0x20>; 863 | clock-names = "ckil", "osc"; 864 | assigned-clocks = <0x1 0x1AE>; 865 | assigned-clock-rates = <0x34BC0000>; 866 | linux,phandle = <0x1>; 867 | phandle = <0x1>; 868 | }; 869 | src@30390000 { 870 | compatible = "fsl,imx7d-src", "fsl,imx51-src", "syscon"; 871 | reg = <0x30390000 0x10000>; 872 | interrupts = <0x0 0x59 0x4>; 873 | #reset-cells = <0x1>; 874 | linux,phandle = <0x31>; 875 | phandle = <0x31>; 876 | }; 877 | gpc@303a0000 { 878 | compatible = "fsl,imx7d-gpc"; 879 | reg = <0x303A0000 0x10000>; 880 | interrupt-controller; 881 | interrupts = <0x0 0x57 0x4>; 882 | #interrupt-cells = <0x3>; 883 | interrupt-parent = <0x3>; 884 | fsl,mf-mix-wakeup-irq = <0x54010000 0xC00 0x0 0x1040640>; 885 | mipi-phy-supply = <0x21>; 886 | pcie-phy-supply = <0x21>; 887 | vcc-supply = <0x22>; 888 | linux,phandle = <0x13>; 889 | phandle = <0x13>; 890 | }; 891 | }; 892 | aips-bus@30400000 { 893 | compatible = "fsl,aips-bus", "simple-bus"; 894 | #address-cells = <0x1>; 895 | #size-cells = <0x1>; 896 | reg = <0x30400000 0x400000>; 897 | ranges; 898 | adc@30610000 { 899 | compatible = "fsl,imx7d-adc"; 900 | reg = <0x30610000 0x10000>; 901 | interrupts = <0x0 0x62 0x4>; 902 | clocks = <0x1 0x1B6>; 903 | num-channels = <0x4>; 904 | clock-names = "adc"; 905 | status = "okay"; 906 | vref-supply = <0x23>; 907 | }; 908 | adc@30620000 { 909 | compatible = "fsl,imx7d-adc"; 910 | reg = <0x30620000 0x10000>; 911 | interrupts = <0x0 0x63 0x4>; 912 | clocks = <0x1 0x1B6>; 913 | num-channels = <0x4>; 914 | clock-names = "adc"; 915 | status = "okay"; 916 | vref-supply = <0x23>; 917 | }; 918 | ecspi@30630000 { 919 | #address-cells = <0x1>; 920 | #size-cells = <0x0>; 921 | compatible = "fsl,imx7d-ecspi", "fsl,imx6sx-ecspi", "fsl,imx51-ecspi"; 922 | reg = <0x30630000 0x10000>; 923 | interrupts = <0x0 0x22 0x4>; 924 | clocks = <0x1 0x10A 0x1 0x10A>; 925 | clock-names = "ipg", "per"; 926 | status = "disabled"; 927 | }; 928 | flextimer@30640000 { 929 | compatible = "fsl,imx7d-flextimer"; 930 | reg = <0x30640000 0x10000>; 931 | interrupts = <0x0 0x11 0x4>; 932 | status = "disabled"; 933 | }; 934 | flextimer@30650000 { 935 | compatible = "fsl,imx7d-flextimer"; 936 | reg = <0x30650000 0x10000>; 937 | interrupts = <0x0 0x12 0x4>; 938 | status = "disabled"; 939 | }; 940 | pwm@30660000 { 941 | compatible = "fsl,imx7d-pwm", "fsl,imx27-pwm"; 942 | reg = <0x30660000 0x10000>; 943 | interrupts = <0x0 0x51 0x4>; 944 | clocks = <0x1 0x10E 0x1 0x10E>; 945 | clock-names = "ipg", "per"; 946 | #pwm-cells = <0x2>; 947 | status = "okay"; 948 | pinctrl-names = "default"; 949 | pinctrl-0 = <0x24>; 950 | linux,phandle = <0x64>; 951 | phandle = <0x64>; 952 | }; 953 | pwm@30670000 { 954 | compatible = "fsl,imx7d-pwm", "fsl,imx27-pwm"; 955 | reg = <0x30670000 0x10000>; 956 | interrupts = <0x0 0x52 0x4>; 957 | clocks = <0x1 0x112 0x1 0x112>; 958 | clock-names = "ipg", "per"; 959 | #pwm-cells = <0x2>; 960 | status = "disabled"; 961 | }; 962 | pwm@30680000 { 963 | compatible = "fsl,imx7d-pwm", "fsl,imx27-pwm"; 964 | reg = <0x30680000 0x10000>; 965 | interrupts = <0x0 0x53 0x4>; 966 | clocks = <0x1 0x116 0x1 0x116>; 967 | clock-names = "ipg", "per"; 968 | #pwm-cells = <0x2>; 969 | status = "disabled"; 970 | }; 971 | pwm@30690000 { 972 | compatible = "fsl,imx7d-pwm", "fsl,imx27-pwm"; 973 | reg = <0x30690000 0x10000>; 974 | interrupts = <0x0 0x54 0x4>; 975 | clocks = <0x1 0x11A 0x1 0x11A>; 976 | clock-names = "ipg", "per"; 977 | #pwm-cells = <0x2>; 978 | status = "disabled"; 979 | }; 980 | system-counter-rd@306a0000 { 981 | compatible = "fsl,imx7d-system-counter-rd"; 982 | reg = <0x306A0000 0x10000>; 983 | status = "disabled"; 984 | }; 985 | system-counter-cmp@306b0000 { 986 | compatible = "fsl,imx7d-system-counter-cmp"; 987 | reg = <0x306B0000 0x10000>; 988 | status = "disabled"; 989 | }; 990 | system-counter-ctrl@306c0000 { 991 | compatible = "fsl,imx7d-system-counter-ctrl"; 992 | reg = <0x306C0000 0x10000>; 993 | interrupts = <0x0 0x2F 0x4 0x0 0x30 0x4>; 994 | status = "disabled"; 995 | }; 996 | epdc@306f0000 { 997 | compatible = "fsl,imx7d-epdc"; 998 | interrupts = <0x0 0x75 0x4>; 999 | reg = <0x306F0000 0x10000>; 1000 | clocks = <0x1 0x19D 0x1 0x7A>; 1001 | clock-names = "epdc_axi", "epdc_pix"; 1002 | epdc-ram = <0x1B 0x4 0x1E>; 1003 | status = "disabled"; 1004 | pinctrl-names = "default"; 1005 | pinctrl-0 = <0x25 0x26>; 1006 | V3P3-supply = <0x27>; 1007 | VCOM-supply = <0x28>; 1008 | DISPLAY-supply = <0x29>; 1009 | en-gpios = <0x2A 0x4 0x0>; 1010 | }; 1011 | epxp@30700000 { 1012 | compatible = "fsl,imx7d-pxp-dma"; 1013 | interrupts = <0x0 0x8 0x4 0x0 0x2E 0x4>; 1014 | reg = <0x30700000 0x10000>; 1015 | clocks = <0x1 0x1B7 0x1 0x1B8>; 1016 | clock-names = "pxp_ipg", "pxp_axi"; 1017 | status = "okay"; 1018 | }; 1019 | csi@30710000 { 1020 | compatible = "fsl,imx7d-csi", "fsl,imx6s-csi"; 1021 | reg = <0x30710000 0x10000>; 1022 | interrupts = <0x0 0x7 0x4>; 1023 | clocks = <0x1 0x19D 0x1 0x146 0x1 0x19D>; 1024 | clock-names = "disp-axi", "csi_mclk", "disp_dcic"; 1025 | status = "okay"; 1026 | csi-mux-mipi = <0x1B 0x14 0x4>; 1027 | port { 1028 | endpoint { 1029 | remote-endpoint = <0x2B>; 1030 | linux,phandle = <0x33>; 1031 | phandle = <0x33>; 1032 | }; 1033 | }; 1034 | }; 1035 | lcdif@30730000 { 1036 | compatible = "fsl,imx7d-lcdif", "fsl,imx28-lcdif"; 1037 | reg = <0x30730000 0x10000>; 1038 | interrupts = <0x0 0x5 0x4>; 1039 | clocks = <0x1 0x7E 0x1 0x19D 0x1 0x19D>; 1040 | clock-names = "pix", "axi", "disp_axi"; 1041 | status = "okay"; 1042 | pinctrl-names = "default"; 1043 | pinctrl-0 = <0x2C 0x2D>; 1044 | enable-gpio = <0x2E 0x7 0x1>; 1045 | display = <0x2F>; 1046 | display { 1047 | bits-per-pixel = <0x10>; 1048 | bus-width = <0x18>; 1049 | linux,phandle = <0x2F>; 1050 | phandle = <0x2F>; 1051 | display-timings { 1052 | native-mode = <0x30>; 1053 | timing0 { 1054 | clock-frequency = <0x8C6180>; 1055 | hactive = <0x1E0>; 1056 | vactive = <0x110>; 1057 | hfront-porch = <0x8>; 1058 | hback-porch = <0x4>; 1059 | hsync-len = <0x29>; 1060 | vback-porch = <0x2>; 1061 | vfront-porch = <0x4>; 1062 | vsync-len = <0xA>; 1063 | hsync-active = <0x0>; 1064 | vsync-active = <0x0>; 1065 | de-active = <0x1>; 1066 | pixelclk-active = <0x0>; 1067 | linux,phandle = <0x30>; 1068 | phandle = <0x30>; 1069 | }; 1070 | }; 1071 | }; 1072 | }; 1073 | mipi-csi@30750000 { 1074 | compatible = "fsl,imx7d-mipi-csi"; 1075 | reg = <0x30750000 0x10000>; 1076 | interrupts = <0x0 0x19 0x4>; 1077 | clocks = <0x1 0x86 0x1 0x8A>; 1078 | clock-names = "mipi_clk", "phy_clk"; 1079 | mipi-phy-supply = <0x21>; 1080 | csis-phy-reset = <0x31 0x28 0x2>; 1081 | bus-width = <0x4>; 1082 | status = "okay"; 1083 | clock-frequency = <0xE4E1C00>; 1084 | port { 1085 | endpoint1 { 1086 | remote-endpoint = <0x32>; 1087 | data-lanes = <0x2>; 1088 | csis-hs-settle = <0xD>; 1089 | csis-clk-settle = <0x2>; 1090 | csis-wclk; 1091 | linux,phandle = <0x48>; 1092 | phandle = <0x48>; 1093 | }; 1094 | endpoint2 { 1095 | remote-endpoint = <0x33>; 1096 | linux,phandle = <0x2B>; 1097 | phandle = <0x2B>; 1098 | }; 1099 | }; 1100 | }; 1101 | mipi-dsi@30760000 { 1102 | compatible = "fsl,imx7d-mipi-dsi"; 1103 | reg = <0x30760000 0x10000>; 1104 | interrupts = <0x0 0x29 0x4>; 1105 | clocks = <0x1 0x82 0x1 0x8A>; 1106 | clock-names = "mipi_cfg_clk", "mipi_pllref_clk"; 1107 | mipi-phy-supply = <0x21>; 1108 | status = "disabled"; 1109 | }; 1110 | ddrc@307a0000 { 1111 | compatible = "fsl,imx7-ddrc"; 1112 | reg = <0x307A0000 0x10000>; 1113 | }; 1114 | }; 1115 | aips-bus@30800000 { 1116 | compatible = "fsl,aips-bus", "simple-bus"; 1117 | #address-cells = <0x1>; 1118 | #size-cells = <0x1>; 1119 | reg = <0x30800000 0x400000>; 1120 | ranges; 1121 | spba-bus@30800000 { 1122 | compatible = "fsl,spba-bus", "simple-bus"; 1123 | #address-cells = <0x1>; 1124 | #size-cells = <0x1>; 1125 | reg = <0x30800000 0x100000>; 1126 | ranges; 1127 | ecspi@30820000 { 1128 | #address-cells = <0x1>; 1129 | #size-cells = <0x0>; 1130 | compatible = "fsl,imx7d-ecspi", "fsl,imx6sx-ecspi", "fsl,imx51-ecspi"; 1131 | reg = <0x30820000 0x10000>; 1132 | interrupts = <0x0 0x1F 0x4>; 1133 | clocks = <0x1 0xFE 0x1 0xFE>; 1134 | clock-names = "ipg", "per"; 1135 | dmas = <0x34 0x0 0x7 0x1 0x34 0x1 0x7 0x2>; 1136 | dma-names = "rx", "tx"; 1137 | status = "disabled"; 1138 | }; 1139 | ecspi@30830000 { 1140 | #address-cells = <0x1>; 1141 | #size-cells = <0x0>; 1142 | compatible = "fsl,imx7d-ecspi", "fsl,imx6sx-ecspi", "fsl,imx51-ecspi"; 1143 | reg = <0x30830000 0x10000>; 1144 | interrupts = <0x0 0x20 0x4>; 1145 | clocks = <0x1 0x102 0x1 0x102>; 1146 | clock-names = "ipg", "per"; 1147 | dmas = <0x34 0x2 0x7 0x1 0x34 0x3 0x7 0x2>; 1148 | dma-names = "rx", "tx"; 1149 | status = "disabled"; 1150 | }; 1151 | ecspi@30840000 { 1152 | #address-cells = <0x1>; 1153 | #size-cells = <0x0>; 1154 | compatible = "fsl,imx7d-ecspi", "fsl,imx6sx-ecspi", "fsl,imx51-ecspi"; 1155 | reg = <0x30840000 0x10000>; 1156 | interrupts = <0x0 0x21 0x4>; 1157 | clocks = <0x1 0x106 0x1 0x106>; 1158 | clock-names = "ipg", "per"; 1159 | dmas = <0x34 0x4 0x7 0x1 0x34 0x5 0x7 0x2>; 1160 | dma-names = "rx", "tx"; 1161 | status = "okay"; 1162 | fsl,spi-num-chipselects = <0x1>; 1163 | pinctrl-names = "default"; 1164 | pinctrl-0 = <0x35 0x36>; 1165 | cs-gpios = <0x37 0x9 0x0>; 1166 | tsc2046 { 1167 | compatible = "ti,tsc2046"; 1168 | reg = <0x0>; 1169 | spi-max-frequency = <0xF4240>; 1170 | pinctrl-names = "default"; 1171 | pinctrl-0 = <0x38>; 1172 | interrupt-parent = <0x39>; 1173 | interrupts = <0x1D 0x0>; 1174 | pendown-gpio = <0x39 0x1D 0x0>; 1175 | ti,x-min = [00 00]; 1176 | ti,x-max = [00 00]; 1177 | ti,y-min = [00 00]; 1178 | ti,y-max = [00 00]; 1179 | ti,pressure-max = [00 00]; 1180 | ti,x-plat-ohms = [01 90]; 1181 | linux,wakeup; 1182 | }; 1183 | }; 1184 | serial@30860000 { 1185 | compatible = "fsl,imx7d-uart", "fsl,imx6q-uart", "fsl,imx21-uart"; 1186 | reg = <0x30860000 0x10000>; 1187 | interrupts = <0x0 0x1A 0x4>; 1188 | clocks = <0x1 0xE2 0x1 0xE2>; 1189 | clock-names = "ipg", "per"; 1190 | dmas = <0x34 0x16 0x4 0x0 0x34 0x17 0x4 0x0>; 1191 | dma-names = "rx", "tx"; 1192 | status = "okay"; 1193 | pinctrl-names = "default"; 1194 | pinctrl-0 = <0x3A>; 1195 | assigned-clocks = <0x1 0xE3>; 1196 | assigned-clock-parents = <0x1 0x0>; 1197 | }; 1198 | serial@30890000 { 1199 | compatible = "fsl,imx7d-uart", "fsl,imx6q-uart", "fsl,imx21-uart"; 1200 | reg = <0x30890000 0x10000>; 1201 | interrupts = <0x0 0x1B 0x4>; 1202 | clocks = <0x1 0xE6 0x1 0xE6>; 1203 | clock-names = "ipg", "per"; 1204 | dmas = <0x34 0x18 0x4 0x0 0x34 0x19 0x4 0x0>; 1205 | dma-names = "rx", "tx"; 1206 | status = "disabled"; 1207 | }; 1208 | serial@30880000 { 1209 | compatible = "fsl,imx7d-uart", "fsl,imx6q-uart", "fsl,imx21-uart"; 1210 | reg = <0x30880000 0x10000>; 1211 | interrupts = <0x0 0x1C 0x4>; 1212 | clocks = <0x1 0xEA 0x1 0xEA>; 1213 | clock-names = "ipg", "per"; 1214 | dmas = <0x34 0x1A 0x4 0x0 0x34 0x1B 0x4 0x0>; 1215 | dma-names = "rx", "tx"; 1216 | status = "disabled"; 1217 | }; 1218 | sai@308a0000 { 1219 | #sound-dai-cells = <0x0>; 1220 | compatible = "fsl,imx7d-sai", "fsl,imx6sx-sai"; 1221 | reg = <0x308A0000 0x10000>; 1222 | interrupts = <0x0 0x5F 0x4>; 1223 | clocks = <0x1 0x1AA 0x1 0x19D 0x1 0x8E 0x1 0x19D 0x1 0x19D>; 1224 | clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3"; 1225 | dma-names = "rx", "tx"; 1226 | dmas = <0x34 0x8 0x18 0x0 0x34 0x9 0x18 0x0>; 1227 | status = "okay"; 1228 | pinctrl-names = "default"; 1229 | pinctrl-0 = <0x3B>; 1230 | assigned-clocks = <0x1 0x8F 0x1 0x8E>; 1231 | assigned-clock-parents = <0x1 0x1AE>; 1232 | assigned-clock-rates = <0x0 0x2328000>; 1233 | linux,phandle = <0x66>; 1234 | phandle = <0x66>; 1235 | }; 1236 | sai@308b0000 { 1237 | #sound-dai-cells = <0x0>; 1238 | compatible = "fsl,imx7d-sai", "fsl,imx6sx-sai"; 1239 | reg = <0x308B0000 0x10000>; 1240 | interrupts = <0x0 0x60 0x4>; 1241 | clocks = <0x1 0x1AB 0x1 0x19D 0x1 0x92 0x1 0x19D 0x1 0x19D>; 1242 | clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3"; 1243 | dma-names = "rx", "tx"; 1244 | dmas = <0x34 0xA 0x18 0x0 0x34 0xB 0x18 0x0>; 1245 | status = "disabled"; 1246 | }; 1247 | sai@308c0000 { 1248 | #sound-dai-cells = <0x0>; 1249 | compatible = "fsl,imx7d-sai", "fsl,imx6sx-sai"; 1250 | reg = <0x308C0000 0x10000>; 1251 | interrupts = <0x0 0x32 0x4>; 1252 | clocks = <0x1 0x1AC 0x1 0x19D 0x1 0x96 0x1 0x19D 0x1 0x19D>; 1253 | clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3"; 1254 | dma-names = "rx", "tx"; 1255 | dmas = <0x34 0xC 0x18 0x0 0x34 0xD 0x18 0x0>; 1256 | status = "okay"; 1257 | pinctrl-names = "default"; 1258 | pinctrl-0 = <0x3C 0x3D>; 1259 | assigned-clocks = <0x1 0x97 0x1 0x96>; 1260 | assigned-clock-parents = <0x1 0x1AE>; 1261 | assigned-clock-rates = <0x0 0x2328000>; 1262 | linux,phandle = <0x68>; 1263 | phandle = <0x68>; 1264 | }; 1265 | }; 1266 | caam@30900000 { 1267 | compatible = "fsl,imx7d-caam", "fsl,sec-v4.0"; 1268 | #address-cells = <0x1>; 1269 | #size-cells = <0x1>; 1270 | reg = <0x30900000 0x40000>; 1271 | ranges = <0x0 0x30900000 0x40000>; 1272 | interrupts = <0x0 0x5B 0x4>; 1273 | clocks = <0x1 0x1B4 0x1 0x5A>; 1274 | clock-names = "caam_ipg", "caam_aclk"; 1275 | jr0@1000 { 1276 | compatible = "fsl,sec-v4.0-job-ring"; 1277 | reg = <0x1000 0x1000>; 1278 | interrupts = <0x0 0x69 0x4>; 1279 | }; 1280 | jr1@2000 { 1281 | compatible = "fsl,sec-v4.0-job-ring"; 1282 | reg = <0x2000 0x1000>; 1283 | interrupts = <0x0 0x6A 0x4>; 1284 | }; 1285 | jr2@3000 { 1286 | compatible = "fsl,sec-v4.0-job-ring"; 1287 | reg = <0x3000 0x1000>; 1288 | interrupts = <0x0 0x72 0x4>; 1289 | }; 1290 | }; 1291 | can@30a00000 { 1292 | compatible = "fsl,imx7d-flexcan", "fsl,imx6q-flexcan"; 1293 | reg = <0x30A00000 0x10000>; 1294 | interrupts = <0x0 0x6E 0x4>; 1295 | clocks = <0x1 0x19D 0x1 0xCA>; 1296 | clock-names = "ipg", "per"; 1297 | stop-mode = <0x1B 0x10 0x1 0x10 0x11>; 1298 | status = "disabled"; 1299 | }; 1300 | can@30a10000 { 1301 | compatible = "fsl,imx7d-flexcan", "fsl,imx6q-flexcan"; 1302 | reg = <0x30A10000 0x10000>; 1303 | interrupts = <0x0 0x6F 0x4>; 1304 | clocks = <0x1 0x19D 0x1 0xCE>; 1305 | clock-names = "ipg", "per"; 1306 | stop-mode = <0x1B 0x10 0x2 0x10 0x12>; 1307 | status = "okay"; 1308 | pinctrl-names = "default"; 1309 | pinctrl-0 = <0x3E>; 1310 | xceiver-supply = <0x3F>; 1311 | }; 1312 | i2c@30a20000 { 1313 | #address-cells = <0x1>; 1314 | #size-cells = <0x0>; 1315 | compatible = "fsl,imx7d-i2c", "fsl,imx21-i2c"; 1316 | reg = <0x30A20000 0x10000>; 1317 | interrupts = <0x0 0x23 0x4>; 1318 | clocks = <0x1 0xD2>; 1319 | status = "okay"; 1320 | clock-frequency = <0x186A0>; 1321 | pinctrl-names = "default"; 1322 | pinctrl-0 = <0x40>; 1323 | pfuze3000@08 { 1324 | compatible = "fsl,pfuze3000"; 1325 | reg = <0x8>; 1326 | regulators { 1327 | sw1a { 1328 | regulator-min-microvolt = <0xAAE60>; 1329 | regulator-max-microvolt = <0x325AA0>; 1330 | regulator-boot-on; 1331 | regulator-always-on; 1332 | regulator-ramp-delay = <0x186A>; 1333 | linux,phandle = <0x2>; 1334 | phandle = <0x2>; 1335 | }; 1336 | sw1b { 1337 | regulator-min-microvolt = <0xAAE60>; 1338 | regulator-max-microvolt = <0x1681B8>; 1339 | regulator-boot-on; 1340 | regulator-always-on; 1341 | regulator-ramp-delay = <0x186A>; 1342 | }; 1343 | sw2 { 1344 | regulator-min-microvolt = <0x16E360>; 1345 | regulator-max-microvolt = <0x1C3A90>; 1346 | regulator-boot-on; 1347 | regulator-always-on; 1348 | }; 1349 | sw3 { 1350 | regulator-min-microvolt = <0xDBBA0>; 1351 | regulator-max-microvolt = <0x192D50>; 1352 | regulator-boot-on; 1353 | regulator-always-on; 1354 | }; 1355 | swbst { 1356 | regulator-min-microvolt = <0x4C4B40>; 1357 | regulator-max-microvolt = <0x4E9530>; 1358 | }; 1359 | vsnvs { 1360 | regulator-min-microvolt = <0xF4240>; 1361 | regulator-max-microvolt = <0x2DC6C0>; 1362 | regulator-boot-on; 1363 | regulator-always-on; 1364 | }; 1365 | vrefddr { 1366 | regulator-boot-on; 1367 | regulator-always-on; 1368 | }; 1369 | vldo1 { 1370 | regulator-min-microvolt = <0x1B7740>; 1371 | regulator-max-microvolt = <0x325AA0>; 1372 | regulator-always-on; 1373 | }; 1374 | vldo2 { 1375 | regulator-min-microvolt = <0xC3500>; 1376 | regulator-max-microvolt = <0x17A6B0>; 1377 | regulator-always-on; 1378 | }; 1379 | vccsd { 1380 | regulator-min-microvolt = <0x2B7CD0>; 1381 | regulator-max-microvolt = <0x325AA0>; 1382 | regulator-always-on; 1383 | }; 1384 | v33 { 1385 | regulator-min-microvolt = <0x2B7CD0>; 1386 | regulator-max-microvolt = <0x325AA0>; 1387 | regulator-always-on; 1388 | }; 1389 | vldo3 { 1390 | regulator-min-microvolt = <0x1B7740>; 1391 | regulator-max-microvolt = <0x325AA0>; 1392 | regulator-always-on; 1393 | }; 1394 | vldo4 { 1395 | regulator-min-microvolt = <0x1B7740>; 1396 | regulator-max-microvolt = <0x325AA0>; 1397 | regulator-always-on; 1398 | linux,phandle = <0x47>; 1399 | phandle = <0x47>; 1400 | }; 1401 | }; 1402 | }; 1403 | }; 1404 | i2c@30a30000 { 1405 | #address-cells = <0x1>; 1406 | #size-cells = <0x0>; 1407 | compatible = "fsl,imx7d-i2c", "fsl,imx21-i2c"; 1408 | reg = <0x30A30000 0x10000>; 1409 | interrupts = <0x0 0x24 0x4>; 1410 | clocks = <0x1 0xD6>; 1411 | status = "okay"; 1412 | clock-frequency = <0x186A0>; 1413 | pinctrl-names = "default"; 1414 | pinctrl-0 = <0x41>; 1415 | fxas2100x@20 { 1416 | compatible = "fsl,fxas2100x"; 1417 | reg = <0x20>; 1418 | }; 1419 | fxos8700@1e { 1420 | compatible = "fsl,fxos8700"; 1421 | reg = <0x1E>; 1422 | }; 1423 | mpl3115@60 { 1424 | compatible = "fsl,mpl3115"; 1425 | reg = <0x60>; 1426 | }; 1427 | }; 1428 | i2c@30a40000 { 1429 | #address-cells = <0x1>; 1430 | #size-cells = <0x0>; 1431 | compatible = "fsl,imx7d-i2c", "fsl,imx21-i2c"; 1432 | reg = <0x30A40000 0x10000>; 1433 | interrupts = <0x0 0x25 0x4>; 1434 | clocks = <0x1 0xDA>; 1435 | status = "okay"; 1436 | clock-frequency = <0x186A0>; 1437 | pinctrl-names = "default"; 1438 | pinctrl-0 = <0x42>; 1439 | sii902x@39 { 1440 | compatible = "SiI,sii902x"; 1441 | pinctrl-names = "default"; 1442 | pinctrl-0 = <0x43>; 1443 | interrupt-parent = <0x39>; 1444 | interrupts = <0xD 0x2>; 1445 | mode_str = "1280x720M@60"; 1446 | bits-per-pixel = <0x10>; 1447 | reg = <0x39>; 1448 | status = "okay"; 1449 | linux,phandle = <0x69>; 1450 | phandle = <0x69>; 1451 | }; 1452 | max17135@48 { 1453 | pinctrl-names = "default"; 1454 | pinctrl-0 = <0x44>; 1455 | compatible = "maxim,max17135"; 1456 | reg = <0x48>; 1457 | status = "disabled"; 1458 | vneg_pwrup = <0x1>; 1459 | gvee_pwrup = <0x2>; 1460 | vpos_pwrup = <0xA>; 1461 | gvdd_pwrup = <0xC>; 1462 | gvdd_pwrdn = <0x1>; 1463 | vpos_pwrdn = <0x2>; 1464 | gvee_pwrdn = <0x8>; 1465 | vneg_pwrdn = <0xA>; 1466 | gpio_pmic_pwrgood = <0x39 0x1F 0x0>; 1467 | gpio_pmic_vcom_ctrl = <0x45 0xE 0x0>; 1468 | gpio_pmic_wakeup = <0x39 0x17 0x0>; 1469 | gpio_pmic_v3p3 = <0x39 0x1E 0x0>; 1470 | gpio_pmic_intr = <0x39 0x16 0x0>; 1471 | regulators { 1472 | DISPLAY { 1473 | regulator-name = "DISPLAY"; 1474 | linux,phandle = <0x29>; 1475 | phandle = <0x29>; 1476 | }; 1477 | GVDD { 1478 | regulator-name = "GVDD"; 1479 | }; 1480 | GVEE { 1481 | regulator-name = "GVEE"; 1482 | }; 1483 | HVINN { 1484 | regulator-name = "HVINN"; 1485 | }; 1486 | HVINP { 1487 | regulator-name = "HVINP"; 1488 | }; 1489 | VCOM { 1490 | regulator-name = "VCOM"; 1491 | regulator-min-microvolt = <0xFFBE0178>; 1492 | regulator-max-microvolt = <0xFFF85EE0>; 1493 | linux,phandle = <0x28>; 1494 | phandle = <0x28>; 1495 | }; 1496 | VNEG { 1497 | regulator-name = "VNEG"; 1498 | }; 1499 | VPOS { 1500 | regulator-name = "VPOS"; 1501 | }; 1502 | V3P3 { 1503 | regulator-name = "V3P3"; 1504 | linux,phandle = <0x27>; 1505 | phandle = <0x27>; 1506 | }; 1507 | }; 1508 | }; 1509 | }; 1510 | i2c@30a50000 { 1511 | #address-cells = <0x1>; 1512 | #size-cells = <0x0>; 1513 | compatible = "fsl,imx7d-i2c", "fsl,imx21-i2c"; 1514 | reg = <0x30A50000 0x10000>; 1515 | interrupts = <0x0 0x26 0x4>; 1516 | clocks = <0x1 0xDE>; 1517 | status = "okay"; 1518 | clock-frequency = <0x186A0>; 1519 | pinctrl-names = "default"; 1520 | pinctrl-0 = <0x46>; 1521 | wm8960@1a { 1522 | compatible = "wlf,wm8960"; 1523 | reg = <0x1A>; 1524 | clocks = <0x1 0x14A>; 1525 | clock-names = "mclk"; 1526 | wlf,shared-lrclk; 1527 | linux,phandle = <0x67>; 1528 | phandle = <0x67>; 1529 | }; 1530 | ov5640_mipi@3c { 1531 | compatible = "ovti,ov5640_mipi"; 1532 | reg = <0x3C>; 1533 | clocks = <0x1 0x19D>; 1534 | clock-names = "csi_mclk"; 1535 | csi_id = <0x0>; 1536 | pwn-gpios = <0x2E 0x6 0x0>; 1537 | AVDD-supply = <0x47>; 1538 | mclk = <0x16E3600>; 1539 | mclk_source = <0x0>; 1540 | port { 1541 | endpoint { 1542 | remote-endpoint = <0x48>; 1543 | linux,phandle = <0x32>; 1544 | phandle = <0x32>; 1545 | }; 1546 | }; 1547 | }; 1548 | }; 1549 | serial@30a60000 { 1550 | compatible = "fsl,imx7d-uart", "fsl,imx6q-uart", "fsl,imx21-uart"; 1551 | reg = <0x30A60000 0x10000>; 1552 | interrupts = <0x0 0x1D 0x4>; 1553 | clocks = <0x1 0xEE 0x1 0xEE>; 1554 | clock-names = "ipg", "per"; 1555 | dmas = <0x34 0x1C 0x4 0x0 0x34 0x1D 0x4 0x0>; 1556 | dma-names = "rx", "tx"; 1557 | status = "disabled"; 1558 | }; 1559 | serial@30a70000 { 1560 | compatible = "fsl,imx7d-uart", "fsl,imx6q-uart", "fsl,imx21-uart"; 1561 | reg = <0x30A70000 0x10000>; 1562 | interrupts = <0x0 0x1E 0x4>; 1563 | clocks = <0x1 0xF2 0x1 0xF2>; 1564 | clock-names = "ipg", "per"; 1565 | dmas = <0x34 0x1E 0x4 0x0 0x34 0x1F 0x4 0x0>; 1566 | dma-names = "rx", "tx"; 1567 | status = "okay"; 1568 | pinctrl-names = "default"; 1569 | pinctrl-0 = <0x49>; 1570 | assigned-clocks = <0x1 0xF3>; 1571 | assigned-clock-parents = <0x1 0xD>; 1572 | }; 1573 | serial@30a80000 { 1574 | compatible = "fsl,imx7d-uart", "fsl,imx6q-uart", "fsl,imx21-uart"; 1575 | reg = <0x30A80000 0x10000>; 1576 | interrupts = <0x0 0x10 0x4>; 1577 | clocks = <0x1 0xF6 0x1 0xF6>; 1578 | clock-names = "ipg", "per"; 1579 | dmas = <0x34 0x20 0x4 0x0 0x34 0x21 0x4 0x0>; 1580 | dma-names = "rx", "tx"; 1581 | status = "okay"; 1582 | pinctrl-names = "default"; 1583 | pinctrl-0 = <0x4A>; 1584 | assigned-clocks = <0x1 0xF7>; 1585 | assigned-clock-parents = <0x1 0xD>; 1586 | fsl,uart-has-rtscts; 1587 | }; 1588 | serial@30a90000 { 1589 | compatible = "fsl,imx7d-uart", "fsl,imx6q-uart", "fsl,imx21-uart"; 1590 | reg = <0x30A90000 0x10000>; 1591 | interrupts = <0x0 0x7E 0x4>; 1592 | clocks = <0x1 0xFA 0x1 0xFA>; 1593 | clock-names = "ipg", "per"; 1594 | dmas = <0x34 0x22 0x4 0x0 0x34 0x23 0x4 0x0>; 1595 | dma-names = "rx", "tx"; 1596 | status = "disabled"; 1597 | }; 1598 | mu@30aa0000 { 1599 | compatible = "fsl,imx7d-mu", "fsl,imx6sx-mu"; 1600 | reg = <0x30AA0000 0x10000>; 1601 | interrupts = <0x0 0x58 0x4>; 1602 | clocks = <0x1 0x1B1>; 1603 | clock-names = "mu"; 1604 | status = "okay"; 1605 | }; 1606 | rpmsg { 1607 | compatible = "fsl,imx7d-rpmsg"; 1608 | status = "disabled"; 1609 | }; 1610 | sema4@30ac0000 { 1611 | compatible = "fsl,imx7d-sema4"; 1612 | reg = <0x30AC0000 0x10000>; 1613 | interrupts = <0x0 0x74 0x4>; 1614 | clocks = <0x1 0x1B2>; 1615 | clock-names = "sema4"; 1616 | status = "okay"; 1617 | }; 1618 | usb@30b10000 { 1619 | compatible = "fsl,imx7d-usb", "fsl,imx27-usb"; 1620 | reg = <0x30B10000 0x200>; 1621 | interrupts = <0x0 0x2B 0x4>; 1622 | clocks = <0x1 0x1A6>; 1623 | fsl,usbphy = <0x4B>; 1624 | fsl,usbmisc = <0x4C 0x0>; 1625 | phy-clkgate-delay-us = <0x190>; 1626 | status = "okay"; 1627 | vbus-supply = <0x4D>; 1628 | srp-disable; 1629 | hnp-disable; 1630 | adp-disable; 1631 | }; 1632 | usb@30b20000 { 1633 | compatible = "fsl,imx7d-usb", "fsl,imx27-usb"; 1634 | reg = <0x30B20000 0x200>; 1635 | interrupts = <0x0 0x2A 0x4>; 1636 | clocks = <0x1 0x1A6>; 1637 | fsl,usbphy = <0x4E>; 1638 | fsl,usbmisc = <0x4F 0x0>; 1639 | phy-clkgate-delay-us = <0x190>; 1640 | status = "okay"; 1641 | vbus-supply = <0x50>; 1642 | dr_mode = "host"; 1643 | }; 1644 | usb@30b30000 { 1645 | compatible = "fsl,imx7d-usb", "fsl,imx27-usb"; 1646 | reg = <0x30B30000 0x200>; 1647 | interrupts = <0x0 0x28 0x4>; 1648 | clocks = <0x1 0x1A6>; 1649 | fsl,usbphy = <0x51>; 1650 | fsl,usbmisc = <0x52 0x0>; 1651 | phy_type = "hsic"; 1652 | dr_mode = "host"; 1653 | phy-clkgate-delay-us = <0x190>; 1654 | status = "disabled"; 1655 | }; 1656 | usbmisc@30b10200 { 1657 | #index-cells = <0x1>; 1658 | compatible = "fsl,imx7d-usbmisc", "fsl,imx6q-usbmisc"; 1659 | reg = <0x30B10200 0x200>; 1660 | linux,phandle = <0x4C>; 1661 | phandle = <0x4C>; 1662 | }; 1663 | usbmisc@30b20200 { 1664 | #index-cells = <0x1>; 1665 | compatible = "fsl,imx7d-usbmisc", "fsl,imx6q-usbmisc"; 1666 | reg = <0x30B20200 0x200>; 1667 | linux,phandle = <0x4F>; 1668 | phandle = <0x4F>; 1669 | }; 1670 | usbmisc@30b30200 { 1671 | #index-cells = <0x1>; 1672 | compatible = "fsl,imx7d-usbmisc", "fsl,imx6q-usbmisc"; 1673 | reg = <0x30B30200 0x200>; 1674 | linux,phandle = <0x52>; 1675 | phandle = <0x52>; 1676 | }; 1677 | usbphy_nop1 { 1678 | compatible = "usb-nop-xceiv"; 1679 | clocks = <0x1 0x1A7>; 1680 | clock-names = "main_clk"; 1681 | linux,phandle = <0x4B>; 1682 | phandle = <0x4B>; 1683 | }; 1684 | usbphy_nop2 { 1685 | compatible = "usb-nop-xceiv"; 1686 | clocks = <0x1 0x1A8>; 1687 | clock-names = "main_clk"; 1688 | linux,phandle = <0x4E>; 1689 | phandle = <0x4E>; 1690 | }; 1691 | usbphy_nop3 { 1692 | compatible = "usb-nop-xceiv"; 1693 | clocks = <0x1 0x6E>; 1694 | clock-names = "main_clk"; 1695 | vcc-supply = <0x22>; 1696 | linux,phandle = <0x51>; 1697 | phandle = <0x51>; 1698 | }; 1699 | usdhc@30b40000 { 1700 | compatible = "fsl,imx7d-usdhc", "fsl,imx6sl-usdhc"; 1701 | reg = <0x30B40000 0x10000>; 1702 | interrupts = <0x0 0x16 0x4>; 1703 | clocks = <0x1 0x1A9 0x1 0x56 0x1 0xBE>; 1704 | clock-names = "ipg", "ahb", "per"; 1705 | fsl,tuning-start-tap = <0x14>; 1706 | fsl,tuning-step = <0x2>; 1707 | bus-width = <0x4>; 1708 | status = "okay"; 1709 | pinctrl-names = "default", "state_100mhz", "state_200mhz"; 1710 | pinctrl-0 = <0x53 0x54>; 1711 | pinctrl-1 = <0x55 0x54>; 1712 | pinctrl-2 = <0x56 0x54>; 1713 | cd-gpios = <0x37 0x0 0x1>; 1714 | wp-gpios = <0x37 0x1 0x0>; 1715 | vmmc-supply = <0x57>; 1716 | enable-sdio-wakeup; 1717 | keep-power-in-suspend; 1718 | }; 1719 | usdhc@30b50000 { 1720 | compatible = "fsl,imx7d-usdhc", "fsl,imx6sx-usdhc"; 1721 | reg = <0x30B50000 0x10000>; 1722 | interrupts = <0x0 0x17 0x4>; 1723 | clocks = <0x1 0x1A9 0x1 0x56 0x1 0xC2>; 1724 | clock-names = "ipg", "ahb", "per"; 1725 | fsl,tuning-start-tap = <0x14>; 1726 | fsl,tuning-step = <0x2>; 1727 | bus-width = <0x4>; 1728 | status = "okay"; 1729 | pinctrl-names = "default", "state_100mhz", "state_200mhz"; 1730 | pinctrl-0 = <0x58>; 1731 | pinctrl-1 = <0x59>; 1732 | pinctrl-2 = <0x5A>; 1733 | enable-sdio-wakeup; 1734 | keep-power-in-suspend; 1735 | non-removable; 1736 | cd-post; 1737 | pm-ignore-notify; 1738 | wifi-host; 1739 | }; 1740 | usdhc@30b60000 { 1741 | compatible = "fsl,imx7d-usdhc", "fsl,imx6sx-usdhc"; 1742 | reg = <0x30B60000 0x10000>; 1743 | interrupts = <0x0 0x18 0x4>; 1744 | clocks = <0x1 0x1A9 0x1 0x56 0x1 0xC6>; 1745 | clock-names = "ipg", "ahb", "per"; 1746 | fsl,tuning-start-tap = <0x14>; 1747 | fsl,tuning-step = <0x2>; 1748 | bus-width = <0x8>; 1749 | status = "okay"; 1750 | pinctrl-names = "default", "state_100mhz", "state_200mhz"; 1751 | pinctrl-0 = <0x5B>; 1752 | pinctrl-1 = <0x5C>; 1753 | pinctrl-2 = <0x5D>; 1754 | assigned-clocks = <0x1 0xC6>; 1755 | assigned-clock-rates = <0x17D78400>; 1756 | non-removable; 1757 | }; 1758 | sim@30b90000 { 1759 | compatible = "fsl,imx7d-sim"; 1760 | reg = <0x30B90000 0x10000>; 1761 | interrupts = <0x0 0x5A 0x4>; 1762 | clocks = <0x1 0x126>; 1763 | clock-names = "sim"; 1764 | status = "okay"; 1765 | pinctrl-names = "default"; 1766 | pinctrl-0 = <0x5E>; 1767 | port = <0x0>; 1768 | sven_low_active; 1769 | }; 1770 | sim@30ba0000 { 1771 | compatible = "fsl,imx7d-sim"; 1772 | reg = <0x30BA0000 0x10000>; 1773 | interrupts = <0x0 0x6 0x4>; 1774 | status = "disabled"; 1775 | }; 1776 | qspi@30bb0000 { 1777 | #address-cells = <0x1>; 1778 | #size-cells = <0x0>; 1779 | compatible = "fsl,imx7d-qspi"; 1780 | reg = <0x30BB0000 0x10000 0x60000000 0x10000000>; 1781 | reg-names = "QuadSPI", "QuadSPI-memory"; 1782 | interrupts = <0x0 0x6B 0x4>; 1783 | clocks = <0x1 0xBA 0x1 0xBA>; 1784 | clock-names = "qspi_en", "qspi"; 1785 | status = "disabled"; 1786 | }; 1787 | weim@30bc0000 { 1788 | compatible = "fsl,imx7d-weim", "fsl,imx6sx-weim", "fsl,imx6q-weim"; 1789 | reg = <0x30BC0000 0x10000>; 1790 | interrupts = <0x0 0xD 0x4>; 1791 | clocks = <0x1 0xB2>; 1792 | status = "disabled"; 1793 | }; 1794 | sdma@30bd0000 { 1795 | compatible = "fsl,imx7d-sdma", "fsl,imx35-sdma"; 1796 | reg = <0x30BD0000 0x10000>; 1797 | interrupts = <0x0 0x2 0x4>; 1798 | clocks = <0x1 0x1A4 0x1 0x5A>; 1799 | clock-names = "ipg", "ahb"; 1800 | #dma-cells = <0x3>; 1801 | iram = <0x5F>; 1802 | fsl,sdma-ram-script-name = "imx/sdma/sdma-imx7d.bin"; 1803 | status = "okay"; 1804 | linux,phandle = <0x34>; 1805 | phandle = <0x34>; 1806 | }; 1807 | ethernet@30be0000 { 1808 | compatible = "fsl,imx7d-fec", "fsl,imx6sx-fec"; 1809 | reg = <0x30BE0000 0x10000>; 1810 | interrupts = <0x0 0x76 0x4 0x0 0x77 0x4 0x0 0x78 0x4>; 1811 | clocks = <0x1 0x52 0x1 0x52 0x1 0xA2 0x1 0x2A 0x1 0xAE>; 1812 | clock-names = "ipg", "ahb", "ptp", "enet_clk_ref", "enet_out"; 1813 | stop-mode = <0x1B 0x10 0x3>; 1814 | fsl,num-tx-queues = <0x3>; 1815 | fsl,num-rx-queues = <0x3>; 1816 | fsl,wakeup_irq = <0x2>; 1817 | status = "okay"; 1818 | pinctrl-names = "default"; 1819 | pinctrl-0 = <0x60>; 1820 | pinctrl-assert-gpios = <0x2E 0x5 0x0>; 1821 | assigned-clocks = <0x1 0xA3 0x1 0xA2>; 1822 | assigned-clock-parents = <0x1 0x2B>; 1823 | assigned-clock-rates = <0x0 0x5F5E100>; 1824 | phy-mode = "rgmii"; 1825 | phy-handle = <0x61>; 1826 | fsl,magic-packet; 1827 | mdio { 1828 | #address-cells = <0x1>; 1829 | #size-cells = <0x0>; 1830 | ethernet-phy@0 { 1831 | compatible = "ethernet-phy-ieee802.3-c22"; 1832 | reg = <0x0>; 1833 | linux,phandle = <0x61>; 1834 | phandle = <0x61>; 1835 | }; 1836 | ethernet-phy@1 { 1837 | compatible = "ethernet-phy-ieee802.3-c22"; 1838 | reg = <0x1>; 1839 | linux,phandle = <0x63>; 1840 | phandle = <0x63>; 1841 | }; 1842 | }; 1843 | }; 1844 | ethernet@30bf0000 { 1845 | compatible = "fsl,imx7d-fec", "fsl,imx6sx-fec"; 1846 | reg = <0x30BF0000 0x10000>; 1847 | interrupts = <0x0 0x64 0x4 0x0 0x65 0x4 0x0 0x66 0x4>; 1848 | clocks = <0x1 0x52 0x1 0x52 0x1 0xAA 0x1 0x2A 0x1 0xAE>; 1849 | clock-names = "ipg", "ahb", "ptp", "enet_clk_ref", "enet_out"; 1850 | stop-mode = <0x1B 0x10 0x4>; 1851 | fsl,num-tx-queues = <0x3>; 1852 | fsl,num-rx-queues = <0x3>; 1853 | fsl,wakeup_irq = <0x2>; 1854 | status = "okay"; 1855 | pinctrl-names = "default"; 1856 | pinctrl-0 = <0x62 0x26>; 1857 | pinctrl-assert-gpios = <0x2A 0x4 0x1>; 1858 | assigned-clocks = <0x1 0xAB 0x1 0xAA>; 1859 | assigned-clock-parents = <0x1 0x2B>; 1860 | assigned-clock-rates = <0x0 0x5F5E100>; 1861 | phy-mode = "rgmii"; 1862 | phy-handle = <0x63>; 1863 | fsl,magic-packet; 1864 | }; 1865 | }; 1866 | pcie@0x33800000 { 1867 | compatible = "fsl,imx7d-pcie", "snps,dw-pcie"; 1868 | reg = <0x33800000 0x4000 0x4FF00000 0x80000>; 1869 | reg-names = "dbi", "config"; 1870 | #address-cells = <0x3>; 1871 | #size-cells = <0x2>; 1872 | device_type = "pci"; 1873 | ranges = <0x81000000 0x0 0x0 0x4FF80000 0x0 0x10000 0x82000000 0x0 0x40000000 0x40000000 0x0 0xFF00000>; 1874 | num-lanes = <0x1>; 1875 | interrupts = <0x0 0x7A 0x4>; 1876 | interrupt-names = "msi"; 1877 | #interrupt-cells = <0x1>; 1878 | interrupt-map-mask = <0x0 0x0 0x0 0x7>; 1879 | interrupt-map = <0x0 0x0 0x0 0x1 0x3 0x0 0x7D 0x4 0x0 0x0 0x0 0x2 0x3 0x0 0x7C 0x4 0x0 0x0 0x0 0x3 0x3 0x0 0x7B 0x4 0x0 0x0 0x0 0x4 0x3 0x0 0x7A 0x4>; 1880 | clocks = <0x1 0x72 0x1 0x2B 0x1 0x76>; 1881 | clock-names = "pcie", "pcie_bus", "pcie_phy"; 1882 | pcie-phy-supply = <0x21>; 1883 | status = "okay"; 1884 | pinctrl-names = "default"; 1885 | reset-gpio = <0x2E 0x1 0x1>; 1886 | disable-gpio = <0x2E 0x0 0x1>; 1887 | }; 1888 | }; 1889 | backlight { 1890 | compatible = "pwm-backlight"; 1891 | pwms = <0x64 0x0 0x4C4B40>; 1892 | brightness-levels = <0x0 0x4 0x8 0x10 0x20 0x40 0x80 0xFF>; 1893 | default-brightness-level = <0x6>; 1894 | status = "okay"; 1895 | }; 1896 | bcmdhd_wlan@0 { 1897 | compatible = "android,bcmdhd_wlan"; 1898 | gpios = <0x45 0x14 0x0>; 1899 | wlreg_on-supply = <0x65>; 1900 | }; 1901 | pxp_v4l2_out { 1902 | compatible = "fsl,imx7d-pxp-v4l2", "fsl,imx6sx-pxp-v4l2", "fsl,imx6sl-pxp-v4l2"; 1903 | status = "okay"; 1904 | }; 1905 | sound { 1906 | compatible = "fsl,imx7d-evk-wm8960", "fsl,imx-audio-wm8960"; 1907 | model = "wm8960-audio"; 1908 | cpu-dai = <0x66>; 1909 | audio-codec = <0x67>; 1910 | codec-master; 1911 | hp-det = <0x2 0x0>; 1912 | hp-det-gpios = <0x39 0x1C 0x0>; 1913 | audio-routing = "Headphone Jack", "HP_L", "Headphone Jack", "HP_R", "Ext Spk", "SPK_LP", "Ext Spk", "SPK_LN", "Ext Spk", "SPK_RP", "Ext Spk", "SPK_RN", "LINPUT1", "Main MIC", "Main MIC", "MICB"; 1914 | assigned-clocks = <0x1 0x14B 0x1 0x14A>; 1915 | assigned-clock-parents = <0x1 0x1AE>; 1916 | assigned-clock-rates = <0x0 0xBB8000>; 1917 | }; 1918 | sound-hdmi { 1919 | compatible = "fsl,imx7d-sdb-sii902x", "fsl,imx-audio-sii902x"; 1920 | model = "sii902x-audio"; 1921 | cpu-dai = <0x68>; 1922 | hdmi-controler = <0x69>; 1923 | }; 1924 | spi4 { 1925 | compatible = "spi-gpio"; 1926 | pinctrl-names = "default"; 1927 | pinctrl-0 = <0x6A>; 1928 | status = "okay"; 1929 | gpio-sck = <0x2A 0xD 0x0>; 1930 | gpio-mosi = <0x2A 0x9 0x0>; 1931 | cs-gpios = <0x2A 0xC 0x0>; 1932 | num-chipselects = <0x1>; 1933 | #address-cells = <0x1>; 1934 | #size-cells = <0x0>; 1935 | gpio_spi@0 { 1936 | compatible = "fairchild,74hc595"; 1937 | gpio-controller; 1938 | #gpio-cells = <0x2>; 1939 | reg = <0x0>; 1940 | registers-number = <0x1>; 1941 | registers-default = [74]; 1942 | spi-max-frequency = <0x186A0>; 1943 | linux,phandle = <0x2E>; 1944 | phandle = <0x2E>; 1945 | }; 1946 | }; 1947 | regulators { 1948 | compatible = "simple-bus"; 1949 | #address-cells = <0x1>; 1950 | #size-cells = <0x0>; 1951 | regulator@0 { 1952 | compatible = "regulator-fixed"; 1953 | reg = <0x0>; 1954 | regulator-name = "usb_otg1_vbus"; 1955 | regulator-min-microvolt = <0x4C4B40>; 1956 | regulator-max-microvolt = <0x4C4B40>; 1957 | gpio = <0x2A 0x5 0x0>; 1958 | enable-active-high; 1959 | linux,phandle = <0x4D>; 1960 | phandle = <0x4D>; 1961 | }; 1962 | regulator@1 { 1963 | compatible = "regulator-fixed"; 1964 | reg = <0x1>; 1965 | regulator-name = "usb_otg2_vbus"; 1966 | regulator-min-microvolt = <0x4C4B40>; 1967 | regulator-max-microvolt = <0x4C4B40>; 1968 | gpio = <0x2A 0x7 0x0>; 1969 | enable-active-high; 1970 | linux,phandle = <0x50>; 1971 | phandle = <0x50>; 1972 | }; 1973 | regulator@3 { 1974 | compatible = "regulator-fixed"; 1975 | regulator-name = "VDD_SD1"; 1976 | regulator-min-microvolt = <0x325AA0>; 1977 | regulator-max-microvolt = <0x325AA0>; 1978 | gpio = <0x37 0x2 0x0>; 1979 | startup-delay-us = <0x30D40>; 1980 | enable-active-high; 1981 | linux,phandle = <0x57>; 1982 | phandle = <0x57>; 1983 | }; 1984 | regulator@4 { 1985 | compatible = "regulator-fixed"; 1986 | reg = <0x4>; 1987 | regulator-name = "can2-3v3"; 1988 | regulator-min-microvolt = <0x325AA0>; 1989 | regulator-max-microvolt = <0x325AA0>; 1990 | gpio = <0x39 0xE 0x1>; 1991 | linux,phandle = <0x3F>; 1992 | phandle = <0x3F>; 1993 | }; 1994 | regulator@5 { 1995 | compatible = "regulator-fixed"; 1996 | reg = <0x5>; 1997 | regulator-name = "vref-1v8"; 1998 | regulator-min-microvolt = <0x1B7740>; 1999 | regulator-max-microvolt = <0x1B7740>; 2000 | linux,phandle = <0x23>; 2001 | phandle = <0x23>; 2002 | }; 2003 | fixedregulator@100 { 2004 | compatible = "regulator-fixed"; 2005 | regulator-min-microvolt = <0x4C4B40>; 2006 | regulator-max-microvolt = <0x4C4B40>; 2007 | regulator-name = "wlreg_on"; 2008 | gpio = <0x45 0x15 0x0>; 2009 | startup-delay-us = <0x64>; 2010 | enable-active-high; 2011 | linux,phandle = <0x65>; 2012 | phandle = <0x65>; 2013 | }; 2014 | }; 2015 | }; 2016 | --------------------------------------------------------------------------------