Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | // SPDX-License-Identifier: GPL-2.0+ /* * Copyright(C) 2025 Svyatoslav Ryhel <clamor95@gmail.com> */ #include <stdlib.h> #include <dm.h> #include <input.h> #include <keyboard.h> #include <power/pmic.h> #include <power/cpcap.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/input.h> static const unsigned int cpcpap_to_reg[] = { CPCAP_REG_INT1, CPCAP_REG_INT2, CPCAP_REG_INT3, CPCAP_REG_INT4, }; /** * struct cpcap_pwrbutton_priv * * @bank: id of interrupt bank co-responding to an IRQ register * @id: id of interrupt pin co-responding to the bit in IRQ register * @keycode: linux key code * @old_state: holder of last button state * @skip: holder of keycode skip state. This is required since both pressing * and releasing generate same event and cause key send duplication. */ struct cpcap_pwrbutton_priv { u32 bank; u32 id; u32 keycode; bool old_state; bool skip; }; static int cpcap_pwrbutton_read_keys(struct input_config *input) { struct udevice *dev = input->dev; struct cpcap_pwrbutton_priv *priv = dev_get_priv(dev); u32 value, state_changed; bool state; value = pmic_reg_read(dev->parent, cpcpap_to_reg[priv->bank]) & BIT(priv->id); /* Interrupt bit is cleared by writing it to interrupt reg */ pmic_reg_write(dev->parent, cpcpap_to_reg[priv->bank], BIT(priv->id)); state = value >> priv->id; state_changed = state != priv->old_state; if (state_changed && !priv->skip) { priv->old_state = state; input_add_keycode(input, priv->keycode, state); } if (state) priv->skip = !priv->skip; return 0; } static int cpcap_pwrbutton_of_to_plat(struct udevice *dev) { struct cpcap_pwrbutton_priv *priv = dev_get_priv(dev); ofnode irq_parent; u32 irq_desc; int ret; /* Check interrupt parent, driver supports only CPCAP as parent */ irq_parent = ofnode_parse_phandle(dev_ofnode(dev), "interrupt-parent", 0); if (!ofnode_device_is_compatible(irq_parent, "motorola,cpcap")) return -EINVAL; ret = dev_read_u32(dev, "interrupts", &irq_desc); if (ret) return ret; /* IRQ registers are 16 bit wide */ priv->bank = irq_desc / 16; priv->id = irq_desc % 16; ret = dev_read_u32(dev, "linux,code", &priv->keycode); if (ret) return ret; priv->old_state = false; priv->skip = false; return 0; } static int cpcap_pwrbutton_probe(struct udevice *dev) { struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); struct stdio_dev *sdev = &uc_priv->sdev; struct input_config *input = &uc_priv->input; int ret; input_init(input, false); input_add_tables(input, false); /* Register the device */ input->dev = dev; input->read_keys = cpcap_pwrbutton_read_keys; strcpy(sdev->name, "cpcap-pwrbutton"); ret = input_stdio_register(sdev); if (ret) { log_debug("%s: input_stdio_register() failed\n", __func__); return ret; } return 0; } static const struct udevice_id cpcap_pwrbutton_ids[] = { { .compatible = "motorola,cpcap-pwrbutton" }, { } }; U_BOOT_DRIVER(cpcap_pwrbutton) = { .name = "cpcap_pwrbutton", .id = UCLASS_KEYBOARD, .of_match = cpcap_pwrbutton_ids, .of_to_plat = cpcap_pwrbutton_of_to_plat, .probe = cpcap_pwrbutton_probe, .priv_auto = sizeof(struct cpcap_pwrbutton_priv), }; |