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 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 248return 0 2>/dev/null 249echo "gpio-ctrl is a library, not executed directly" >&2 250exit 1 251