1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.hardware.deviceTree;
7
8 overlayType = types.submodule {
9 options = {
10 name = mkOption {
11 type = types.str;
12 description = ''
13 Name of this overlay
14 '';
15 };
16
17 dtsFile = mkOption {
18 type = types.nullOr types.path;
19 description = ''
20 Path to .dts overlay file, overlay is applied to
21 each .dtb file matching "compatible" of the overlay.
22 '';
23 default = null;
24 example = literalExpression "./dts/overlays.dts";
25 };
26
27 dtsText = mkOption {
28 type = types.nullOr types.str;
29 default = null;
30 description = ''
31 Literal DTS contents, overlay is applied to
32 each .dtb file matching "compatible" of the overlay.
33 '';
34 example = ''
35 /dts-v1/;
36 /plugin/;
37 / {
38 compatible = "raspberrypi";
39 fragment@0 {
40 target-path = "/soc";
41 __overlay__ {
42 pps {
43 compatible = "pps-gpio";
44 status = "okay";
45 };
46 };
47 };
48 };
49 '';
50 };
51
52 dtboFile = mkOption {
53 type = types.nullOr types.path;
54 default = null;
55 description = ''
56 Path to .dtbo compiled overlay file.
57 '';
58 };
59 };
60 };
61
62 # this requires kernel package
63 dtbsWithSymbols = pkgs.stdenv.mkDerivation {
64 name = "dtbs-with-symbols";
65 inherit (cfg.kernelPackage) src nativeBuildInputs depsBuildBuild;
66 patches = map (patch: patch.patch) cfg.kernelPackage.kernelPatches;
67 buildPhase = ''
68 patchShebangs scripts/*
69 substituteInPlace scripts/Makefile.lib \
70 --replace 'DTC_FLAGS += $(DTC_FLAGS_$(basetarget))' 'DTC_FLAGS += $(DTC_FLAGS_$(basetarget)) -@'
71 make ${pkgs.stdenv.hostPlatform.linux-kernel.baseConfig} ARCH="${pkgs.stdenv.hostPlatform.linuxArch}"
72 make dtbs ARCH="${pkgs.stdenv.hostPlatform.linuxArch}"
73 '';
74 installPhase = ''
75 make dtbs_install INSTALL_DTBS_PATH=$out/dtbs ARCH="${pkgs.stdenv.hostPlatform.linuxArch}"
76 '';
77 };
78
79 filterDTBs = src: if isNull cfg.filter
80 then "${src}/dtbs"
81 else
82 pkgs.runCommand "dtbs-filtered" {} ''
83 mkdir -p $out
84 cd ${src}/dtbs
85 find . -type f -name '${cfg.filter}' -print0 \
86 | xargs -0 cp -v --no-preserve=mode --target-directory $out --parents
87 '';
88
89 # Compile single Device Tree overlay source
90 # file (.dts) into its compiled variant (.dtbo)
91 compileDTS = name: f: pkgs.callPackage({ dtc }: pkgs.stdenv.mkDerivation {
92 name = "${name}-dtbo";
93
94 nativeBuildInputs = [ dtc ];
95
96 buildCommand = ''
97 dtc -I dts ${f} -O dtb -@ -o $out
98 '';
99 }) {};
100
101 # Fill in `dtboFile` for each overlay if not set already.
102 # Existence of one of these is guarded by assertion below
103 withDTBOs = xs: flip map xs (o: o // { dtboFile =
104 if isNull o.dtboFile then
105 if !isNull o.dtsFile then compileDTS o.name o.dtsFile
106 else compileDTS o.name (pkgs.writeText "dts" o.dtsText)
107 else o.dtboFile; } );
108
109in
110{
111 imports = [
112 (mkRemovedOptionModule [ "hardware" "deviceTree" "base" ] "Use hardware.deviceTree.kernelPackage instead")
113 ];
114
115 options = {
116 hardware.deviceTree = {
117 enable = mkOption {
118 default = pkgs.stdenv.hostPlatform.linux-kernel.DTB or false;
119 type = types.bool;
120 description = ''
121 Build device tree files. These are used to describe the
122 non-discoverable hardware of a system.
123 '';
124 };
125
126 kernelPackage = mkOption {
127 default = config.boot.kernelPackages.kernel;
128 defaultText = literalExpression "config.boot.kernelPackages.kernel";
129 example = literalExpression "pkgs.linux_latest";
130 type = types.path;
131 description = ''
132 Kernel package containing the base device-tree (.dtb) to boot. Uses
133 device trees bundled with the Linux kernel by default.
134 '';
135 };
136
137 name = mkOption {
138 default = null;
139 example = "some-dtb.dtb";
140 type = types.nullOr types.str;
141 description = ''
142 The name of an explicit dtb to be loaded, relative to the dtb base.
143 Useful in extlinux scenarios if the bootloader doesn't pick the
144 right .dtb file from FDTDIR.
145 '';
146 };
147
148 filter = mkOption {
149 type = types.nullOr types.str;
150 default = null;
151 example = "*rpi*.dtb";
152 description = ''
153 Only include .dtb files matching glob expression.
154 '';
155 };
156
157 overlays = mkOption {
158 default = [];
159 example = literalExpression ''
160 [
161 { name = "pps"; dtsFile = ./dts/pps.dts; }
162 { name = "spi";
163 dtsText = "...";
164 }
165 { name = "precompiled"; dtboFile = ./dtbos/example.dtbo; }
166 ]
167 '';
168 type = types.listOf (types.coercedTo types.path (path: {
169 name = baseNameOf path;
170 dtboFile = path;
171 }) overlayType);
172 description = ''
173 List of overlays to apply to base device-tree (.dtb) files.
174 '';
175 };
176
177 package = mkOption {
178 default = null;
179 type = types.nullOr types.path;
180 internal = true;
181 description = ''
182 A path containing the result of applying `overlays` to `kernelPackage`.
183 '';
184 };
185 };
186 };
187
188 config = mkIf (cfg.enable) {
189
190 assertions = let
191 invalidOverlay = o: isNull o.dtsFile && isNull o.dtsText && isNull o.dtboFile;
192 in lib.singleton {
193 assertion = lib.all (o: !invalidOverlay o) cfg.overlays;
194 message = ''
195 deviceTree overlay needs one of dtsFile, dtsText or dtboFile set.
196 Offending overlay(s):
197 ${toString (map (o: o.name) (builtins.filter invalidOverlay cfg.overlays))}
198 '';
199 };
200
201 hardware.deviceTree.package = if (cfg.overlays != [])
202 then pkgs.deviceTree.applyOverlays (filterDTBs dtbsWithSymbols) (withDTBOs cfg.overlays)
203 else (filterDTBs cfg.kernelPackage);
204 };
205}