1#!/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 # shellcheck source=/dev/null 35 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 102 local ngpio 103 ngpio=$(<"$sysfs"/ngpio) 104 if (( ngpio <= offset )); then 105 echo "$name with gpiochip $sysfs only has $ngpio but wants $offset" >&2 106 return 1 107 fi 108 109 gpio_lookup_cache["$name"]=$(( $(<"$sysfs"/base) + offset )) 110 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 129 gpios=("${!GPIO_NAMES_TO_SCM[@]}") 130 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 250