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