blob: 13c624b2f27550a6aaee436ff9f806522490595b [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
34 source "$conf"
35done
36
37declare -A gpio_sysfs_lookup_cache=()
38declare -A gpio_lookup_cache=()
39
40declare -A gpio_init=()
41
42##################################################
43# Looks up the sysfs GPIO number
44# Arguments:
45# $1: GPIO name
46# Return:
47# 0 if success, non-zero if error
48# stdout: The GPIO number
49##################################################
50gpio_name_to_num() {
51 local name="$1"
52
53 if [ -n "${gpio_lookup_cache["$name"]+1}" ]; then
54 echo "${gpio_lookup_cache["$name"]}"
55 return 0
56 fi
57
58 local scm="${GPIO_NAMES_TO_SCM["$name"]-}"
59 if [ -z "$scm" ]; then
60 echo "Missing gpio definition: $name" >&2
61 return 1
62 fi
63 local id="${scm% *}"
64 local type="${id%=*}"
65 local val="${id#*=}"
66 local offset="${scm#* }"
67
68 local sysfs
69 if [ -n "${gpio_sysfs_lookup_cache["$id"]+1}" ]; then
70 sysfs="${gpio_sysfs_lookup_cache["$id"]}"
71 else
72 case "$type" in
73 label)
74 if ! sysfs="$(grep -xl "$val" /sys/class/gpio/gpiochip*/label)"; then
75 echo "Failed to find gpiochip: $val" >&2
76 return 1
77 fi
78 sysfs="${sysfs%/label}"
79 ;;
80 of_node)
81 for sysfs in $(echo /sys/class/gpio/gpiochip*); do
82 local link
83 # Ignore errors because not all devices have of_nodes
84 link="$(readlink -f "$sysfs/device/of_node" 2>/dev/null)" || continue
85 [ "${link#/sys/firmware/devicetree/base}" = "$val" ] && break
86 sysfs=
87 done
88 if [ -z "$sysfs" ]; then
89 echo "Failed to find gpiochip: $val" >&2
90 return 1
91 fi
92 ;;
93 *)
94 echo "Invalid GPIO type $type" >&2
95 return 1
96 ;;
97 esac
98 gpio_sysfs_lookup_cache["$id"]="$sysfs"
99 fi
100
101 local ngpio=$(cat "$sysfs"/ngpio)
102 if (( ngpio <= offset )); then
103 echo "$name with gpiochip $sysfs only has $ngpio but wants $offset" >&2
104 return 1
105 fi
106
107 gpio_lookup_cache["$name"]=$(( $(cat "$sysfs"/base) + offset ))
108 echo "${gpio_lookup_cache["$name"]}"
109}
110
111
112##################################################
113# Populates the GPIO lookup cache
114# Most calls to gpio_name_to_num that would
115# normally cache the sysfs lookups for gpios run
116# inside subshells. This prevents them from
117# populating a global cache and greatly speeding
118# up future lookups. This call allows scripts to
119# populate the cache prior to looking up gpios.
120##################################################
121gpio_build_cache() {
122 local timeout="${1-0}"
123 shift
124 local gpios=("$@")
125
126 if (( ${#gpios[@]} == 0 )); then
127 gpios="${!GPIO_NAMES_TO_SCM[@]}"
128 fi
129
130 local deadline=$(( timeout + SECONDS ))
131 local name
132 for name in "${gpios[@]}"; do
133 while true; do
134 gpio_name_to_num "$name" >/dev/null && break
135 if (( deadline <= SECONDS )); then
136 echo "Timed out building gpio cache" >&2
137 return 1
138 fi
139 done
140 done
141}
142
143##################################################
144# Initializes the gpio state
145# This operation is idempotent and can be applied
146# repeatedly to the same gpio. It will make sure the
147# gpio ends up in the initialized state even if it
148# was.
149# Arguments:
150# $1: GPIO name
151# Return:
152# 0 if success, non-zero if error
153##################################################
154gpio_init_() {
155 local name="$1"
156 local num="$2"
157
158 if [ -n "${gpio_init["$name"]+1}" ]; then
159 return 0
160 fi
161
162 if [ ! -e "/sys/class/gpio/gpio$num" ]; then
163 if ! echo "$num" >'/sys/class/gpio/export'; then
164 echo "Failed to export $name gpio$num" >&2
165 return 1
166 fi
167 fi
168 local active_low=0
169 if [[ "${name%_N}" != "$name" ]]; then
170 active_low=1
171 fi
172 if ! echo "$active_low" >"/sys/class/gpio/gpio$num/active_low"; then
173 echo "Failed to set active_low for $name gpio$num" >&2
174 return 1
175 fi
176 gpio_init["$name"]=1
177}
178gpio_init() {
179 local name="$1"
180
181 # Ensure the cache is updated by not running in a subshell
182 gpio_name_to_num "$name" >/dev/null || return
183
184 gpio_init_ "$name" "$(gpio_name_to_num "$name")"
185}
186
187##################################################
188# Sets the output GPIO value.
189# Arguments:
190# $1: GPIO name
191# $2: GPIO value, "1" or "0"
192# Return:
193# 0 if success, non-zero if error
194##################################################
195gpio_set_value_() {
196 local name="$1"
197 local num="$2"
198 local val="$3"
199
200 gpio_init_ "$name" "$num" || return
201 if ! echo out >"/sys/class/gpio/gpio$num/direction"; then
202 echo "Failed to set output for $name gpio$num" >&2
203 return 1
204 fi
205 if ! echo "$val" >"/sys/class/gpio/gpio$num/value"; then
206 echo "Failed to set $name gpio$num = $val" >&2
207 return 1
208 fi
209}
210gpio_set_value() {
211 local name="$1"
212 local val="$2"
213
214 # Ensure the cache is updated by not running in a subshell
215 gpio_name_to_num "$name" >/dev/null || return
216
217 gpio_set_value_ "$name" "$(gpio_name_to_num "$name")" "$val"
218}
219
220##################################################
221# Get GPIO value
222# Arguments:
223# $1: GPIO name
224# Return:
225# 0 if success, non-zero if error
226# stdout: The value of the gpio
227##################################################
228gpio_get_value_() {
229 local name="$1"
230 local num="$2"
231
232 gpio_init_ "$name" "$num" || return
233 if ! cat "/sys/class/gpio/gpio$num/value"; then
234 echo "Failed to get $name gpio$num value" >&2
235 return 1
236 fi
237}
238gpio_get_value() {
239 local name="$1"
240
241 # Ensure the cache is updated by not running in a subshell
242 gpio_name_to_num "$name" >/dev/null || return
243
244 gpio_get_value_ "$name" "$(gpio_name_to_num "$name")"
245}
246
247gpio_ctrl_init=1