diff options
Diffstat (limited to 'drivers/input/keyboard/sun4i-lradc-keys.c')
| -rw-r--r-- | drivers/input/keyboard/sun4i-lradc-keys.c | 119 |
1 files changed, 97 insertions, 22 deletions
diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c index 57272df34cd5..5730f08f82d7 100644 --- a/drivers/input/keyboard/sun4i-lradc-keys.c +++ b/drivers/input/keyboard/sun4i-lradc-keys.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Allwinner sun4i low res adc attached tablet keys driver * * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ /* @@ -23,15 +14,19 @@ * there are no boards known to use channel 1. */ +#include <linux/clk.h> #include <linux/err.h> #include <linux/init.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_platform.h> +#include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_wakeirq.h> +#include <linux/property.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/slab.h> #define LRADC_CTRL 0x00 @@ -46,6 +41,7 @@ #define CONTINUE_TIME_SEL(x) ((x) << 16) /* 4 bits */ #define KEY_MODE_SEL(x) ((x) << 12) /* 2 bits */ #define LEVELA_B_CNT(x) ((x) << 8) /* 4 bits */ +#define HOLD_KEY_EN(x) ((x) << 7) #define HOLD_EN(x) ((x) << 6) #define LEVELB_VOL(x) ((x) << 4) /* 2 bits */ #define SAMPLE_RATE(x) ((x) << 2) /* 2 bits */ @@ -63,6 +59,33 @@ #define CHAN0_KEYDOWN_IRQ BIT(1) #define CHAN0_DATA_IRQ BIT(0) +/* struct lradc_variant - Describe sun4i-a10-lradc-keys hardware variant + * @divisor_numerator: The numerator of lradc Vref internally divisor + * @divisor_denominator: The denominator of lradc Vref internally divisor + * @has_clock_reset: If the binding requires a clock and reset + */ +struct lradc_variant { + u8 divisor_numerator; + u8 divisor_denominator; + bool has_clock_reset; +}; + +static const struct lradc_variant lradc_variant_a10 = { + .divisor_numerator = 2, + .divisor_denominator = 3 +}; + +static const struct lradc_variant r_lradc_variant_a83t = { + .divisor_numerator = 3, + .divisor_denominator = 4 +}; + +static const struct lradc_variant lradc_variant_r329 = { + .divisor_numerator = 3, + .divisor_denominator = 4, + .has_clock_reset = true, +}; + struct sun4i_lradc_keymap { u32 voltage; u32 keycode; @@ -72,8 +95,11 @@ struct sun4i_lradc_data { struct device *dev; struct input_dev *input; void __iomem *base; + struct clk *clk; + struct reset_control *reset; struct regulator *vref_supply; struct sun4i_lradc_keymap *chan0_map; + const struct lradc_variant *variant; u32 chan0_map_count; u32 chan0_keycode; u32 vref; @@ -128,9 +154,17 @@ static int sun4i_lradc_open(struct input_dev *dev) if (error) return error; - /* lradc Vref internally is divided by 2/3 */ - lradc->vref = regulator_get_voltage(lradc->vref_supply) * 2 / 3; + error = reset_control_deassert(lradc->reset); + if (error) + goto err_disable_reg; + + error = clk_prepare_enable(lradc->clk); + if (error) + goto err_assert_reset; + lradc->vref = regulator_get_voltage(lradc->vref_supply) * + lradc->variant->divisor_numerator / + lradc->variant->divisor_denominator; /* * Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to * stabilize on press, wait (1 + 1) * 4 ms for key release @@ -141,6 +175,13 @@ static int sun4i_lradc_open(struct input_dev *dev) writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC); return 0; + +err_assert_reset: + reset_control_assert(lradc->reset); +err_disable_reg: + regulator_disable(lradc->vref_supply); + + return error; } static void sun4i_lradc_close(struct input_dev *dev) @@ -152,13 +193,15 @@ static void sun4i_lradc_close(struct input_dev *dev) SAMPLE_RATE(2), lradc->base + LRADC_CTRL); writel(0, lradc->base + LRADC_INTC); + clk_disable_unprepare(lradc->clk); + reset_control_assert(lradc->reset); regulator_disable(lradc->vref_supply); } static int sun4i_lradc_load_dt_keymap(struct device *dev, struct sun4i_lradc_data *lradc) { - struct device_node *np, *pp; + struct device_node *np; int i; int error; @@ -179,7 +222,7 @@ static int sun4i_lradc_load_dt_keymap(struct device *dev, return -ENOMEM; i = 0; - for_each_child_of_node(np, pp) { + for_each_child_of_node_scoped(np, pp) { struct sun4i_lradc_keymap *map = &lradc->chan0_map[i]; u32 channel; @@ -211,8 +254,7 @@ static int sun4i_lradc_probe(struct platform_device *pdev) { struct sun4i_lradc_data *lradc; struct device *dev = &pdev->dev; - int i; - int error; + int error, i, irq; lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL); if (!lradc) @@ -222,6 +264,22 @@ static int sun4i_lradc_probe(struct platform_device *pdev) if (error) return error; + lradc->variant = of_device_get_match_data(&pdev->dev); + if (!lradc->variant) { + dev_err(&pdev->dev, "Missing sun4i-a10-lradc-keys variant\n"); + return -EINVAL; + } + + if (lradc->variant->has_clock_reset) { + lradc->clk = devm_clk_get(dev, NULL); + if (IS_ERR(lradc->clk)) + return PTR_ERR(lradc->clk); + + lradc->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(lradc->reset)) + return PTR_ERR(lradc->reset); + } + lradc->vref_supply = devm_regulator_get(dev, "vref"); if (IS_ERR(lradc->vref_supply)) return PTR_ERR(lradc->vref_supply); @@ -246,13 +304,15 @@ static int sun4i_lradc_probe(struct platform_device *pdev) input_set_drvdata(lradc->input, lradc); - lradc->base = devm_ioremap_resource(dev, - platform_get_resource(pdev, IORESOURCE_MEM, 0)); + lradc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(lradc->base)) return PTR_ERR(lradc->base); - error = devm_request_irq(dev, platform_get_irq(pdev, 0), - sun4i_lradc_irq, 0, + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + error = devm_request_irq(dev, irq, sun4i_lradc_irq, 0, "sun4i-a10-lradc-keys", lradc); if (error) return error; @@ -261,11 +321,26 @@ static int sun4i_lradc_probe(struct platform_device *pdev) if (error) return error; + if (device_property_read_bool(dev, "wakeup-source")) { + error = dev_pm_set_wake_irq(dev, irq); + if (error) + dev_warn(dev, + "Failed to set IRQ %d as a wake IRQ: %d\n", + irq, error); + else + device_set_wakeup_capable(dev, true); + } + return 0; } static const struct of_device_id sun4i_lradc_of_match[] = { - { .compatible = "allwinner,sun4i-a10-lradc-keys", }, + { .compatible = "allwinner,sun4i-a10-lradc-keys", + .data = &lradc_variant_a10 }, + { .compatible = "allwinner,sun8i-a83t-r-lradc", + .data = &r_lradc_variant_a83t }, + { .compatible = "allwinner,sun50i-r329-lradc", + .data = &lradc_variant_r329 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match); |
