linux/drivers/input/keyboard/clps711x-keypad.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Cirrus Logic CLPS711X Keypad driver
   4 *
   5 * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
   6 */
   7
   8#include <linux/input.h>
   9#include <linux/input-polldev.h>
  10#include <linux/module.h>
  11#include <linux/of_gpio.h>
  12#include <linux/platform_device.h>
  13#include <linux/regmap.h>
  14#include <linux/sched.h>
  15#include <linux/input/matrix_keypad.h>
  16#include <linux/mfd/syscon.h>
  17#include <linux/mfd/syscon/clps711x.h>
  18
  19#define CLPS711X_KEYPAD_COL_COUNT       8
  20
  21struct clps711x_gpio_data {
  22        struct gpio_desc *desc;
  23        DECLARE_BITMAP(last_state, CLPS711X_KEYPAD_COL_COUNT);
  24};
  25
  26struct clps711x_keypad_data {
  27        struct regmap                   *syscon;
  28        int                             row_count;
  29        unsigned int                    row_shift;
  30        struct clps711x_gpio_data       *gpio_data;
  31};
  32
  33static void clps711x_keypad_poll(struct input_polled_dev *dev)
  34{
  35        const unsigned short *keycodes = dev->input->keycode;
  36        struct clps711x_keypad_data *priv = dev->private;
  37        bool sync = false;
  38        int col, row;
  39
  40        for (col = 0; col < CLPS711X_KEYPAD_COL_COUNT; col++) {
  41                /* Assert column */
  42                regmap_update_bits(priv->syscon, SYSCON_OFFSET,
  43                                   SYSCON1_KBDSCAN_MASK,
  44                                   SYSCON1_KBDSCAN(8 + col));
  45
  46                /* Scan rows */
  47                for (row = 0; row < priv->row_count; row++) {
  48                        struct clps711x_gpio_data *data = &priv->gpio_data[row];
  49                        bool state, state1;
  50
  51                        /* Read twice for protection against fluctuations */
  52                        do {
  53                                state = gpiod_get_value_cansleep(data->desc);
  54                                cond_resched();
  55                                state1 = gpiod_get_value_cansleep(data->desc);
  56                        } while (state != state1);
  57
  58                        if (test_bit(col, data->last_state) != state) {
  59                                int code = MATRIX_SCAN_CODE(row, col,
  60                                                            priv->row_shift);
  61
  62                                if (state) {
  63                                        set_bit(col, data->last_state);
  64                                        input_event(dev->input, EV_MSC,
  65                                                    MSC_SCAN, code);
  66                                } else {
  67                                        clear_bit(col, data->last_state);
  68                                }
  69
  70                                if (keycodes[code])
  71                                        input_report_key(dev->input,
  72                                                         keycodes[code], state);
  73                                sync = true;
  74                        }
  75                }
  76
  77                /* Set all columns to low */
  78                regmap_update_bits(priv->syscon, SYSCON_OFFSET,
  79                                   SYSCON1_KBDSCAN_MASK, SYSCON1_KBDSCAN(1));
  80        }
  81
  82        if (sync)
  83                input_sync(dev->input);
  84}
  85
  86static int clps711x_keypad_probe(struct platform_device *pdev)
  87{
  88        struct clps711x_keypad_data *priv;
  89        struct device *dev = &pdev->dev;
  90        struct device_node *np = dev->of_node;
  91        struct input_polled_dev *poll_dev;
  92        u32 poll_interval;
  93        int i, err;
  94
  95        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  96        if (!priv)
  97                return -ENOMEM;
  98
  99        priv->syscon =
 100                syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1");
 101        if (IS_ERR(priv->syscon))
 102                return PTR_ERR(priv->syscon);
 103
 104        priv->row_count = of_gpio_named_count(np, "row-gpios");
 105        if (priv->row_count < 1)
 106                return -EINVAL;
 107
 108        priv->gpio_data = devm_kcalloc(dev,
 109                                priv->row_count, sizeof(*priv->gpio_data),
 110                                GFP_KERNEL);
 111        if (!priv->gpio_data)
 112                return -ENOMEM;
 113
 114        priv->row_shift = get_count_order(CLPS711X_KEYPAD_COL_COUNT);
 115
 116        for (i = 0; i < priv->row_count; i++) {
 117                struct clps711x_gpio_data *data = &priv->gpio_data[i];
 118
 119                data->desc = devm_gpiod_get_index(dev, "row", i, GPIOD_IN);
 120                if (IS_ERR(data->desc))
 121                        return PTR_ERR(data->desc);
 122        }
 123
 124        err = of_property_read_u32(np, "poll-interval", &poll_interval);
 125        if (err)
 126                return err;
 127
 128        poll_dev = input_allocate_polled_device();
 129        if (!poll_dev)
 130                return -ENOMEM;
 131
 132        poll_dev->private               = priv;
 133        poll_dev->poll                  = clps711x_keypad_poll;
 134        poll_dev->poll_interval         = poll_interval;
 135        poll_dev->input->name           = pdev->name;
 136        poll_dev->input->dev.parent     = dev;
 137        poll_dev->input->id.bustype     = BUS_HOST;
 138        poll_dev->input->id.vendor      = 0x0001;
 139        poll_dev->input->id.product     = 0x0001;
 140        poll_dev->input->id.version     = 0x0100;
 141
 142        err = matrix_keypad_build_keymap(NULL, NULL, priv->row_count,
 143                                         CLPS711X_KEYPAD_COL_COUNT,
 144                                         NULL, poll_dev->input);
 145        if (err)
 146                goto out_err;
 147
 148        input_set_capability(poll_dev->input, EV_MSC, MSC_SCAN);
 149        if (of_property_read_bool(np, "autorepeat"))
 150                __set_bit(EV_REP, poll_dev->input->evbit);
 151
 152        platform_set_drvdata(pdev, poll_dev);
 153
 154        /* Set all columns to low */
 155        regmap_update_bits(priv->syscon, SYSCON_OFFSET, SYSCON1_KBDSCAN_MASK,
 156                           SYSCON1_KBDSCAN(1));
 157
 158        err = input_register_polled_device(poll_dev);
 159        if (err)
 160                goto out_err;
 161
 162        return 0;
 163
 164out_err:
 165        input_free_polled_device(poll_dev);
 166        return err;
 167}
 168
 169static int clps711x_keypad_remove(struct platform_device *pdev)
 170{
 171        struct input_polled_dev *poll_dev = platform_get_drvdata(pdev);
 172
 173        input_unregister_polled_device(poll_dev);
 174        input_free_polled_device(poll_dev);
 175
 176        return 0;
 177}
 178
 179static const struct of_device_id clps711x_keypad_of_match[] = {
 180        { .compatible = "cirrus,ep7209-keypad", },
 181        { }
 182};
 183MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match);
 184
 185static struct platform_driver clps711x_keypad_driver = {
 186        .driver = {
 187                .name           = "clps711x-keypad",
 188                .of_match_table = clps711x_keypad_of_match,
 189        },
 190        .probe  = clps711x_keypad_probe,
 191        .remove = clps711x_keypad_remove,
 192};
 193module_platform_driver(clps711x_keypad_driver);
 194
 195MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
 196MODULE_DESCRIPTION("Cirrus Logic CLPS711X Keypad driver");
 197MODULE_LICENSE("GPL");
 198