blob: a2db0d633f503f975259a7f605656c3c0a62e3f6 [file] [log] [blame]
Michael Shencf7d39d2021-08-05 10:14:46 +08001#!/bin/bash
2# Copyright 2021 Google LLC
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16# This is intended to be used as a library for managing gpio line values.
17# Executing this directly will do nothing.
18[ -n "${gpio_ctrl_init-}" ] && return
19
20# Map names of GPIOs to GPIO number
21# This maps a schematic name to a gpiochip and line at a specific offset
22declare -A GPIO_NAMES_TO_SCM=(
23 # Examples
24 #['SYS_RESET_N']='label=/pinctrl@f0800000/gpio@f0012000 21'
25 #['PWRBTN_N']='label=/pinctrl@f0800000/gpio@f0012000 23'
26 #['PCH_SLP_S5_R_N']='of_node=/ahb/apb/i2c@8e000/cpu_seq@6b 9'
27 #['PCH_PWRGOOD_R']='of_node=/ahb/apb/i2c@8e000/cpu_seq@6b 6'
28)
29
30# Load configurations from a known location in the filesystem to populate
31# named GPIOs
32shopt -s nullglob
33for conf in /usr/share/gpio-ctrl/conf.d/*.sh; do
William A. Kennington III650fb612023-06-02 16:27:24 -070034 # shellcheck source=/dev/null
Michael Shencf7d39d2021-08-05 10:14:46 +080035 source "$conf"
36done
37
38declare -A gpio_sysfs_lookup_cache=()
39declare -A gpio_lookup_cache=()
40
41declare -A gpio_init=()
42
43##################################################
44# Looks up the sysfs GPIO number
45# Arguments:
46# $1: GPIO name
47# Return:
48# 0 if success, non-zero if error
49# stdout: The GPIO number
50##################################################
51gpio_name_to_num() {
52 local name="$1"
53
54 if [ -n "${gpio_lookup_cache["$name"]+1}" ]; then
55 echo "${gpio_lookup_cache["$name"]}"
56 return 0
57 fi
58
59 local scm="${GPIO_NAMES_TO_SCM["$name"]-}"
60 if [ -z "$scm" ]; then
61 echo "Missing gpio definition: $name" >&2
62 return 1
63 fi
64 local id="${scm% *}"
65 local type="${id%=*}"
66 local val="${id#*=}"
67 local offset="${scm#* }"
68
69 local sysfs
70 if [ -n "${gpio_sysfs_lookup_cache["$id"]+1}" ]; then
71 sysfs="${gpio_sysfs_lookup_cache["$id"]}"
72 else
73 case "$type" in
74 label)
75 if ! sysfs="$(grep -xl "$val" /sys/class/gpio/gpiochip*/label)"; then
76 echo "Failed to find gpiochip: $val" >&2
77 return 1
78 fi
79 sysfs="${sysfs%/label}"
80 ;;
81 of_node)
82 for sysfs in $(echo /sys/class/gpio/gpiochip*); do
83 local link
84 # Ignore errors because not all devices have of_nodes
85 link="$(readlink -f "$sysfs/device/of_node" 2>/dev/null)" || continue
86 [ "${link#/sys/firmware/devicetree/base}" = "$val" ] && break
87 sysfs=
88 done
89 if [ -z "$sysfs" ]; then
90 echo "Failed to find gpiochip: $val" >&2
91 return 1
92 fi
93 ;;
94 *)
95 echo "Invalid GPIO type $type" >&2
96 return 1
97 ;;
98 esac
99 gpio_sysfs_lookup_cache["$id"]="$sysfs"
100 fi
101
William A. Kennington III650fb612023-06-02 16:27:24 -0700102 local ngpio
103 ngpio=$(<"$sysfs"/ngpio)
Michael Shencf7d39d2021-08-05 10:14:46 +0800104 if (( ngpio <= offset )); then
105 echo "$name with gpiochip $sysfs only has $ngpio but wants $offset" >&2
106 return 1
107 fi
108
William A. Kennington III650fb612023-06-02 16:27:24 -0700109 gpio_lookup_cache["$name"]=$(( $(<"$sysfs"/base) + offset ))
Michael Shencf7d39d2021-08-05 10:14:46 +0800110 echo "${gpio_lookup_cache["$name"]}"
111}
112
113
114##################################################
115# Populates the GPIO lookup cache
116# Most calls to gpio_name_to_num that would
117# normally cache the sysfs lookups for gpios run
118# inside subshells. This prevents them from
119# populating a global cache and greatly speeding
120# up future lookups. This call allows scripts to
121# populate the cache prior to looking up gpios.
122##################################################
123gpio_build_cache() {
124 local timeout="${1-0}"
125 shift
126 local gpios=("$@")
127
128 if (( ${#gpios[@]} == 0 )); then
William A. Kennington III650fb612023-06-02 16:27:24 -0700129 gpios=("${!GPIO_NAMES_TO_SCM[@]}")
Michael Shencf7d39d2021-08-05 10:14:46 +0800130 fi
131
132 local deadline=$(( timeout + SECONDS ))
133 local name
134 for name in "${gpios[@]}"; do
135 while true; do
136 gpio_name_to_num "$name" >/dev/null && break
137 if (( deadline <= SECONDS )); then
138 echo "Timed out building gpio cache" >&2
139 return 1
140 fi
141 done
142 done
143}
144
145##################################################
146# Initializes the gpio state
147# This operation is idempotent and can be applied
148# repeatedly to the same gpio. It will make sure the
149# gpio ends up in the initialized state even if it
150# was.
151# Arguments:
152# $1: GPIO name
153# Return:
154# 0 if success, non-zero if error
155##################################################
156gpio_init_() {
157 local name="$1"
158 local num="$2"
159
160 if [ -n "${gpio_init["$name"]+1}" ]; then
161 return 0
162 fi
163
164 if [ ! -e "/sys/class/gpio/gpio$num" ]; then
165 if ! echo "$num" >'/sys/class/gpio/export'; then
166 echo "Failed to export $name gpio$num" >&2
167 return 1
168 fi
169 fi
170 local active_low=0
171 if [[ "${name%_N}" != "$name" ]]; then
172 active_low=1
173 fi
174 if ! echo "$active_low" >"/sys/class/gpio/gpio$num/active_low"; then
175 echo "Failed to set active_low for $name gpio$num" >&2
176 return 1
177 fi
178 gpio_init["$name"]=1
179}
180gpio_init() {
181 local name="$1"
182
183 # Ensure the cache is updated by not running in a subshell
184 gpio_name_to_num "$name" >/dev/null || return
185
186 gpio_init_ "$name" "$(gpio_name_to_num "$name")"
187}
188
189##################################################
190# Sets the output GPIO value.
191# Arguments:
192# $1: GPIO name
193# $2: GPIO value, "1" or "0"
194# Return:
195# 0 if success, non-zero if error
196##################################################
197gpio_set_value_() {
198 local name="$1"
199 local num="$2"
200 local val="$3"
201
202 gpio_init_ "$name" "$num" || return
203 if ! echo out >"/sys/class/gpio/gpio$num/direction"; then
204 echo "Failed to set output for $name gpio$num" >&2
205 return 1
206 fi
207 if ! echo "$val" >"/sys/class/gpio/gpio$num/value"; then
208 echo "Failed to set $name gpio$num = $val" >&2
209 return 1
210 fi
211}
212gpio_set_value() {
213 local name="$1"
214 local val="$2"
215
216 # Ensure the cache is updated by not running in a subshell
217 gpio_name_to_num "$name" >/dev/null || return
218
219 gpio_set_value_ "$name" "$(gpio_name_to_num "$name")" "$val"
220}
221
222##################################################
223# Get GPIO value
224# Arguments:
225# $1: GPIO name
226# Return:
227# 0 if success, non-zero if error
228# stdout: The value of the gpio
229##################################################
230gpio_get_value_() {
231 local name="$1"
232 local num="$2"
233
234 gpio_init_ "$name" "$num" || return
235 if ! cat "/sys/class/gpio/gpio$num/value"; then
236 echo "Failed to get $name gpio$num value" >&2
237 return 1
238 fi
239}
240gpio_get_value() {
241 local name="$1"
242
243 # Ensure the cache is updated by not running in a subshell
244 gpio_name_to_num "$name" >/dev/null || return
245
246 gpio_get_value_ "$name" "$(gpio_name_to_num "$name")"
247}
248
249gpio_ctrl_init=1