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 25 struct clps711x_gpio_data { 26 struct gpio_desc *desc; 27 DECLARE_BITMAP(last_state, CLPS711X_KEYPAD_COL_COUNT); 28 }; 29 30 struct 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 37 static 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 90 static 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); 124 if (!data->desc) 125 return -EINVAL; 126 127 if (IS_ERR(data->desc)) 128 return PTR_ERR(data->desc); 129 130 gpiod_direction_input(data->desc); 131 } 132 133 err = of_property_read_u32(np, "poll-interval", &poll_interval); 134 if (err) 135 return err; 136 137 poll_dev = input_allocate_polled_device(); 138 if (!poll_dev) 139 return -ENOMEM; 140 141 poll_dev->private = priv; 142 poll_dev->poll = clps711x_keypad_poll; 143 poll_dev->poll_interval = poll_interval; 144 poll_dev->input->name = pdev->name; 145 poll_dev->input->dev.parent = dev; 146 poll_dev->input->id.bustype = BUS_HOST; 147 poll_dev->input->id.vendor = 0x0001; 148 poll_dev->input->id.product = 0x0001; 149 poll_dev->input->id.version = 0x0100; 150 151 err = matrix_keypad_build_keymap(NULL, NULL, priv->row_count, 152 CLPS711X_KEYPAD_COL_COUNT, 153 NULL, poll_dev->input); 154 if (err) 155 goto out_err; 156 157 input_set_capability(poll_dev->input, EV_MSC, MSC_SCAN); 158 if (of_property_read_bool(np, "autorepeat")) 159 __set_bit(EV_REP, poll_dev->input->evbit); 160 161 platform_set_drvdata(pdev, poll_dev); 162 163 /* Set all columns to low */ 164 regmap_update_bits(priv->syscon, SYSCON_OFFSET, SYSCON1_KBDSCAN_MASK, 165 SYSCON1_KBDSCAN(1)); 166 167 err = input_register_polled_device(poll_dev); 168 if (err) 169 goto out_err; 170 171 return 0; 172 173 out_err: 174 input_free_polled_device(poll_dev); 175 return err; 176 } 177 178 static int clps711x_keypad_remove(struct platform_device *pdev) 179 { 180 struct input_polled_dev *poll_dev = platform_get_drvdata(pdev); 181 182 input_unregister_polled_device(poll_dev); 183 input_free_polled_device(poll_dev); 184 185 return 0; 186 } 187 188 static const struct of_device_id clps711x_keypad_of_match[] = { 189 { .compatible = "cirrus,clps711x-keypad", }, 190 { } 191 }; 192 MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match); 193 194 static struct platform_driver clps711x_keypad_driver = { 195 .driver = { 196 .name = "clps711x-keypad", 197 .owner = THIS_MODULE, 198 .of_match_table = clps711x_keypad_of_match, 199 }, 200 .probe = clps711x_keypad_probe, 201 .remove = clps711x_keypad_remove, 202 }; 203 module_platform_driver(clps711x_keypad_driver); 204 205 MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); 206 MODULE_DESCRIPTION("Cirrus Logic CLPS711X Keypad driver"); 207 MODULE_LICENSE("GPL"); 208