// SPDX-License-Identifier: GPL-2.0+ /* * Copyright(C) 2025 Svyatoslav Ryhel */ #include #include #include #include #include #include #include #include #include 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), };