"Das U-Boot" Source Tree
at master 218 lines 5.0 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Qualcomm generic pmic gpio driver 4 * 5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> 6 * (C) Copyright 2023 Linaro Ltd. 7 */ 8 9#include <button.h> 10#include <dt-bindings/input/linux-event-codes.h> 11#include <dm.h> 12#include <dm/device-internal.h> 13#include <dm/lists.h> 14#include <log.h> 15#include <power/pmic.h> 16#include <spmi/spmi.h> 17#include <linux/bitops.h> 18#include <time.h> 19 20#define REG_TYPE 0x4 21#define REG_SUBTYPE 0x5 22 23struct qcom_pmic_btn_data { 24 char *compatible; 25 unsigned int status_bit; 26 int code; 27 char *label; 28}; 29 30struct qcom_pmic_btn_priv { 31 u32 base; 32 u32 status_bit; 33 int code; 34 struct udevice *pmic; 35 ulong last_release_time; 36}; 37 38#define PON_INT_RT_STS 0x10 39#define PON_KPDPWR_N_SET 0 40#define PON_RESIN_N_SET 1 41#define PON_GEN3_RESIN_N_SET 6 42#define PON_GEN3_KPDPWR_N_SET 7 43 44static enum button_state_t qcom_pwrkey_get_state(struct udevice *dev) 45{ 46 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev); 47 bool pressed; 48 int reg; 49 50 if (get_timer_us(0) - priv->last_release_time < 25000) 51 return BUTTON_OFF; 52 53 reg = pmic_reg_read(priv->pmic, priv->base + PON_INT_RT_STS); 54 if (reg < 0) 55 return 0; 56 57 pressed = !!(reg & BIT(priv->status_bit)); 58 if (!pressed) 59 priv->last_release_time = get_timer_us(0); 60 61 return pressed; 62} 63 64static int qcom_pwrkey_get_code(struct udevice *dev) 65{ 66 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev); 67 68 return priv->code; 69} 70 71static const struct qcom_pmic_btn_data qcom_pmic_btn_data_table[] = { 72 { 73 .compatible = "qcom,pm8941-pwrkey", 74 .status_bit = PON_KPDPWR_N_SET, 75 .code = KEY_ENTER, 76 .label = "pwrkey", 77 }, 78 { 79 .compatible = "qcom,pm8941-resin", 80 .status_bit = PON_RESIN_N_SET, 81 .code = KEY_DOWN, 82 .label = "vol_down", 83 }, 84 { 85 .compatible = "qcom,pmk8350-pwrkey", 86 .status_bit = PON_GEN3_KPDPWR_N_SET, 87 .code = KEY_ENTER, 88 .label = "pwrkey", 89 }, 90 { 91 .compatible = "qcom,pmk8350-resin", 92 .status_bit = PON_GEN3_RESIN_N_SET, 93 .code = KEY_DOWN, 94 .label = "vol_down", 95 }, 96}; 97 98static const struct qcom_pmic_btn_data *button_qcom_pmic_match(ofnode node) 99{ 100 int i; 101 102 for (i = 0; i < ARRAY_SIZE(qcom_pmic_btn_data_table); ++i) { 103 if (ofnode_device_is_compatible(node, 104 qcom_pmic_btn_data_table[i].compatible)) 105 return &qcom_pmic_btn_data_table[i]; 106 } 107 108 return NULL; 109} 110 111static int qcom_pwrkey_probe(struct udevice *dev) 112{ 113 struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); 114 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev); 115 const struct qcom_pmic_btn_data *btn_data; 116 ofnode node = dev_ofnode(dev); 117 int ret; 118 u64 base; 119 120 /* Ignore the top-level pon node */ 121 if (!uc_plat->label) 122 return 0; 123 124 /* Get the data for the node compatible */ 125 btn_data = button_qcom_pmic_match(node); 126 if (!btn_data) 127 return -EINVAL; 128 129 priv->status_bit = btn_data->status_bit; 130 priv->code = btn_data->code; 131 132 /* the pwrkey and resin nodes are children of the "pon" node, get the 133 * PMIC device to use in pmic_reg_* calls. 134 */ 135 priv->pmic = dev->parent->parent; 136 137 /* Get the address of the parent pon node */ 138 base = dev_read_addr(dev->parent); 139 if (base == FDT_ADDR_T_NONE) { 140 printf("%s: Can't find address\n", dev->name); 141 return -EINVAL; 142 } 143 144 priv->base = base; 145 146 /* Do a sanity check */ 147 ret = pmic_reg_read(priv->pmic, priv->base + REG_TYPE); 148 if (ret != 0x1 && ret != 0xb) { 149 printf("%s: unexpected PMIC function type %d\n", dev->name, ret); 150 return -ENXIO; 151 } 152 153 ret = pmic_reg_read(priv->pmic, priv->base + REG_SUBTYPE); 154 if (ret < 0 || (ret & 0x7) == 0) { 155 printf("%s: unexpected PMIC function subtype %d\n", dev->name, ret); 156 return -ENXIO; 157 } 158 159 return 0; 160} 161 162static int button_qcom_pmic_bind(struct udevice *parent) 163{ 164 struct udevice *dev; 165 ofnode node; 166 int ret; 167 168 dev_for_each_subnode(node, parent) { 169 const struct qcom_pmic_btn_data *btn_data; 170 struct button_uc_plat *uc_plat; 171 const char *label; 172 173 if (!ofnode_is_enabled(node)) 174 continue; 175 176 /* Get the data for the node compatible */ 177 btn_data = button_qcom_pmic_match(node); 178 if (!btn_data) { 179 debug("Unknown button node '%s'\n", ofnode_get_name(node)); 180 continue; 181 } 182 183 ret = device_bind_driver_to_node(parent, "qcom_pwrkey", 184 ofnode_get_name(node), 185 node, &dev); 186 if (ret) { 187 printf("Failed to bind %s! %d\n", label, ret); 188 return ret; 189 } 190 uc_plat = dev_get_uclass_plat(dev); 191 uc_plat->label = btn_data->label; 192 } 193 194 return 0; 195} 196 197static const struct button_ops button_qcom_pmic_ops = { 198 .get_state = qcom_pwrkey_get_state, 199 .get_code = qcom_pwrkey_get_code, 200}; 201 202static const struct udevice_id qcom_pwrkey_ids[] = { 203 { .compatible = "qcom,pm8916-pon" }, 204 { .compatible = "qcom,pm8941-pon" }, 205 { .compatible = "qcom,pm8998-pon" }, 206 { .compatible = "qcom,pmk8350-pon" }, 207 { } 208}; 209 210U_BOOT_DRIVER(qcom_pwrkey) = { 211 .name = "qcom_pwrkey", 212 .id = UCLASS_BUTTON, 213 .of_match = qcom_pwrkey_ids, 214 .bind = button_qcom_pmic_bind, 215 .probe = qcom_pwrkey_probe, 216 .ops = &button_qcom_pmic_ops, 217 .priv_auto = sizeof(struct qcom_pmic_btn_priv), 218};