linux/drivers/input/keyboard/sun4i-lradc-keys.c
<<
>>
Prefs
   1/*
   2 * Allwinner sun4i low res adc attached tablet keys driver
   3 *
   4 * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17/*
  18 * Allwinnner sunxi SoCs have a lradc which is specifically designed to have
  19 * various (tablet) keys (ie home, back, search, etc). attached to it using
  20 * a resistor network. This driver is for the keys on such boards.
  21 *
  22 * There are 2 channels, currently this driver only supports channel 0 since
  23 * there are no boards known to use channel 1.
  24 */
  25
  26#include <linux/err.h>
  27#include <linux/init.h>
  28#include <linux/input.h>
  29#include <linux/interrupt.h>
  30#include <linux/io.h>
  31#include <linux/module.h>
  32#include <linux/of_platform.h>
  33#include <linux/platform_device.h>
  34#include <linux/regulator/consumer.h>
  35#include <linux/slab.h>
  36
  37#define LRADC_CTRL              0x00
  38#define LRADC_INTC              0x04
  39#define LRADC_INTS              0x08
  40#define LRADC_DATA0             0x0c
  41#define LRADC_DATA1             0x10
  42
  43/* LRADC_CTRL bits */
  44#define FIRST_CONVERT_DLY(x)    ((x) << 24) /* 8 bits */
  45#define CHAN_SELECT(x)          ((x) << 22) /* 2 bits */
  46#define CONTINUE_TIME_SEL(x)    ((x) << 16) /* 4 bits */
  47#define KEY_MODE_SEL(x)         ((x) << 12) /* 2 bits */
  48#define LEVELA_B_CNT(x)         ((x) << 8)  /* 4 bits */
  49#define HOLD_EN(x)              ((x) << 6)
  50#define LEVELB_VOL(x)           ((x) << 4)  /* 2 bits */
  51#define SAMPLE_RATE(x)          ((x) << 2)  /* 2 bits */
  52#define ENABLE(x)               ((x) << 0)
  53
  54/* LRADC_INTC and LRADC_INTS bits */
  55#define CHAN1_KEYUP_IRQ         BIT(12)
  56#define CHAN1_ALRDY_HOLD_IRQ    BIT(11)
  57#define CHAN1_HOLD_IRQ          BIT(10)
  58#define CHAN1_KEYDOWN_IRQ       BIT(9)
  59#define CHAN1_DATA_IRQ          BIT(8)
  60#define CHAN0_KEYUP_IRQ         BIT(4)
  61#define CHAN0_ALRDY_HOLD_IRQ    BIT(3)
  62#define CHAN0_HOLD_IRQ          BIT(2)
  63#define CHAN0_KEYDOWN_IRQ       BIT(1)
  64#define CHAN0_DATA_IRQ          BIT(0)
  65
  66struct sun4i_lradc_keymap {
  67        u32 voltage;
  68        u32 keycode;
  69};
  70
  71struct sun4i_lradc_data {
  72        struct device *dev;
  73        struct input_dev *input;
  74        void __iomem *base;
  75        struct regulator *vref_supply;
  76        struct sun4i_lradc_keymap *chan0_map;
  77        u32 chan0_map_count;
  78        u32 chan0_keycode;
  79        u32 vref;
  80};
  81
  82static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id)
  83{
  84        struct sun4i_lradc_data *lradc = dev_id;
  85        u32 i, ints, val, voltage, diff, keycode = 0, closest = 0xffffffff;
  86
  87        ints  = readl(lradc->base + LRADC_INTS);
  88
  89        /*
  90         * lradc supports only one keypress at a time, release does not give
  91         * any info as to which key was released, so we cache the keycode.
  92         */
  93
  94        if (ints & CHAN0_KEYUP_IRQ) {
  95                input_report_key(lradc->input, lradc->chan0_keycode, 0);
  96                lradc->chan0_keycode = 0;
  97        }
  98
  99        if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) {
 100                val = readl(lradc->base + LRADC_DATA0) & 0x3f;
 101                voltage = val * lradc->vref / 63;
 102
 103                for (i = 0; i < lradc->chan0_map_count; i++) {
 104                        diff = abs(lradc->chan0_map[i].voltage - voltage);
 105                        if (diff < closest) {
 106                                closest = diff;
 107                                keycode = lradc->chan0_map[i].keycode;
 108                        }
 109                }
 110
 111                lradc->chan0_keycode = keycode;
 112                input_report_key(lradc->input, lradc->chan0_keycode, 1);
 113        }
 114
 115        input_sync(lradc->input);
 116
 117        writel(ints, lradc->base + LRADC_INTS);
 118
 119        return IRQ_HANDLED;
 120}
 121
 122static int sun4i_lradc_open(struct input_dev *dev)
 123{
 124        struct sun4i_lradc_data *lradc = input_get_drvdata(dev);
 125        int error;
 126
 127        error = regulator_enable(lradc->vref_supply);
 128        if (error)
 129                return error;
 130
 131        /* lradc Vref internally is divided by 2/3 */
 132        lradc->vref = regulator_get_voltage(lradc->vref_supply) * 2 / 3;
 133
 134        /*
 135         * Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to
 136         * stabilize on press, wait (1 + 1) * 4 ms for key release
 137         */
 138        writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
 139                SAMPLE_RATE(0) | ENABLE(1), lradc->base + LRADC_CTRL);
 140
 141        writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC);
 142
 143        return 0;
 144}
 145
 146static void sun4i_lradc_close(struct input_dev *dev)
 147{
 148        struct sun4i_lradc_data *lradc = input_get_drvdata(dev);
 149
 150        /* Disable lradc, leave other settings unchanged */
 151        writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
 152                SAMPLE_RATE(2), lradc->base + LRADC_CTRL);
 153        writel(0, lradc->base + LRADC_INTC);
 154
 155        regulator_disable(lradc->vref_supply);
 156}
 157
 158static int sun4i_lradc_load_dt_keymap(struct device *dev,
 159                                      struct sun4i_lradc_data *lradc)
 160{
 161        struct device_node *np, *pp;
 162        int i;
 163        int error;
 164
 165        np = dev->of_node;
 166        if (!np)
 167                return -EINVAL;
 168
 169        lradc->chan0_map_count = of_get_child_count(np);
 170        if (lradc->chan0_map_count == 0) {
 171                dev_err(dev, "keymap is missing in device tree\n");
 172                return -EINVAL;
 173        }
 174
 175        lradc->chan0_map = devm_kmalloc_array(dev, lradc->chan0_map_count,
 176                                              sizeof(struct sun4i_lradc_keymap),
 177                                              GFP_KERNEL);
 178        if (!lradc->chan0_map)
 179                return -ENOMEM;
 180
 181        i = 0;
 182        for_each_child_of_node(np, pp) {
 183                struct sun4i_lradc_keymap *map = &lradc->chan0_map[i];
 184                u32 channel;
 185
 186                error = of_property_read_u32(pp, "channel", &channel);
 187                if (error || channel != 0) {
 188                        dev_err(dev, "%s: Inval channel prop\n", pp->name);
 189                        return -EINVAL;
 190                }
 191
 192                error = of_property_read_u32(pp, "voltage", &map->voltage);
 193                if (error) {
 194                        dev_err(dev, "%s: Inval voltage prop\n", pp->name);
 195                        return -EINVAL;
 196                }
 197
 198                error = of_property_read_u32(pp, "linux,code", &map->keycode);
 199                if (error) {
 200                        dev_err(dev, "%s: Inval linux,code prop\n", pp->name);
 201                        return -EINVAL;
 202                }
 203
 204                i++;
 205        }
 206
 207        return 0;
 208}
 209
 210static int sun4i_lradc_probe(struct platform_device *pdev)
 211{
 212        struct sun4i_lradc_data *lradc;
 213        struct device *dev = &pdev->dev;
 214        int i;
 215        int error;
 216
 217        lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL);
 218        if (!lradc)
 219                return -ENOMEM;
 220
 221        error = sun4i_lradc_load_dt_keymap(dev, lradc);
 222        if (error)
 223                return error;
 224
 225        lradc->vref_supply = devm_regulator_get(dev, "vref");
 226        if (IS_ERR(lradc->vref_supply))
 227                return PTR_ERR(lradc->vref_supply);
 228
 229        lradc->dev = dev;
 230        lradc->input = devm_input_allocate_device(dev);
 231        if (!lradc->input)
 232                return -ENOMEM;
 233
 234        lradc->input->name = pdev->name;
 235        lradc->input->phys = "sun4i_lradc/input0";
 236        lradc->input->open = sun4i_lradc_open;
 237        lradc->input->close = sun4i_lradc_close;
 238        lradc->input->id.bustype = BUS_HOST;
 239        lradc->input->id.vendor = 0x0001;
 240        lradc->input->id.product = 0x0001;
 241        lradc->input->id.version = 0x0100;
 242
 243        __set_bit(EV_KEY, lradc->input->evbit);
 244        for (i = 0; i < lradc->chan0_map_count; i++)
 245                __set_bit(lradc->chan0_map[i].keycode, lradc->input->keybit);
 246
 247        input_set_drvdata(lradc->input, lradc);
 248
 249        lradc->base = devm_ioremap_resource(dev,
 250                              platform_get_resource(pdev, IORESOURCE_MEM, 0));
 251        if (IS_ERR(lradc->base))
 252                return PTR_ERR(lradc->base);
 253
 254        error = devm_request_irq(dev, platform_get_irq(pdev, 0),
 255                                 sun4i_lradc_irq, 0,
 256                                 "sun4i-a10-lradc-keys", lradc);
 257        if (error)
 258                return error;
 259
 260        error = input_register_device(lradc->input);
 261        if (error)
 262                return error;
 263
 264        platform_set_drvdata(pdev, lradc);
 265        return 0;
 266}
 267
 268static const struct of_device_id sun4i_lradc_of_match[] = {
 269        { .compatible = "allwinner,sun4i-a10-lradc-keys", },
 270        { /* sentinel */ }
 271};
 272MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);
 273
 274static struct platform_driver sun4i_lradc_driver = {
 275        .driver = {
 276                .name   = "sun4i-a10-lradc-keys",
 277                .of_match_table = of_match_ptr(sun4i_lradc_of_match),
 278        },
 279        .probe  = sun4i_lradc_probe,
 280};
 281
 282module_platform_driver(sun4i_lradc_driver);
 283
 284MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver");
 285MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 286MODULE_LICENSE("GPL");
 287