162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/input/matrix_keypad.h> 762306a36Sopenharmony_ci#include <linux/platform_device.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/input.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define KEYPAD_SCAN_MODE 0x00 1862306a36Sopenharmony_ci#define KEYPAD_CNTL 0x04 1962306a36Sopenharmony_ci#define KEYPAD_INT 0x08 2062306a36Sopenharmony_ci#define KEYPAD_INTMSK 0x0C 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define KEYPAD_DATA 0x10 2362306a36Sopenharmony_ci#define KEYPAD_GPIO 0x30 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define KEYPAD_UNKNOWN_INT 0x40 2662306a36Sopenharmony_ci#define KEYPAD_UNKNOWN_INT_STS 0x44 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define KEYPAD_BITMASK_COLS 11 2962306a36Sopenharmony_ci#define KEYPAD_BITMASK_ROWS 8 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct nspire_keypad { 3262306a36Sopenharmony_ci void __iomem *reg_base; 3362306a36Sopenharmony_ci u32 int_mask; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci struct input_dev *input; 3662306a36Sopenharmony_ci struct clk *clk; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci struct matrix_keymap_data *keymap; 3962306a36Sopenharmony_ci int row_shift; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* Maximum delay estimated assuming 33MHz APB */ 4262306a36Sopenharmony_ci u32 scan_interval; /* In microseconds (~2000us max) */ 4362306a36Sopenharmony_ci u32 row_delay; /* In microseconds (~500us max) */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci u16 state[KEYPAD_BITMASK_ROWS]; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci bool active_low; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic irqreturn_t nspire_keypad_irq(int irq, void *dev_id) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct nspire_keypad *keypad = dev_id; 5362306a36Sopenharmony_ci struct input_dev *input = keypad->input; 5462306a36Sopenharmony_ci unsigned short *keymap = input->keycode; 5562306a36Sopenharmony_ci unsigned int code; 5662306a36Sopenharmony_ci int row, col; 5762306a36Sopenharmony_ci u32 int_sts; 5862306a36Sopenharmony_ci u16 state[8]; 5962306a36Sopenharmony_ci u16 bits, changed; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci int_sts = readl(keypad->reg_base + KEYPAD_INT) & keypad->int_mask; 6262306a36Sopenharmony_ci if (!int_sts) 6362306a36Sopenharmony_ci return IRQ_NONE; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci memcpy_fromio(state, keypad->reg_base + KEYPAD_DATA, sizeof(state)); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci for (row = 0; row < KEYPAD_BITMASK_ROWS; row++) { 6862306a36Sopenharmony_ci bits = state[row]; 6962306a36Sopenharmony_ci if (keypad->active_low) 7062306a36Sopenharmony_ci bits = ~bits; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci changed = bits ^ keypad->state[row]; 7362306a36Sopenharmony_ci if (!changed) 7462306a36Sopenharmony_ci continue; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci keypad->state[row] = bits; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci for (col = 0; col < KEYPAD_BITMASK_COLS; col++) { 7962306a36Sopenharmony_ci if (!(changed & (1U << col))) 8062306a36Sopenharmony_ci continue; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); 8362306a36Sopenharmony_ci input_event(input, EV_MSC, MSC_SCAN, code); 8462306a36Sopenharmony_ci input_report_key(input, keymap[code], 8562306a36Sopenharmony_ci bits & (1U << col)); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci input_sync(input); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci writel(0x3, keypad->reg_base + KEYPAD_INT); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return IRQ_HANDLED; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int nspire_keypad_open(struct input_dev *input) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct nspire_keypad *keypad = input_get_drvdata(input); 9962306a36Sopenharmony_ci unsigned long val = 0, cycles_per_us, delay_cycles, row_delay_cycles; 10062306a36Sopenharmony_ci int error; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci error = clk_prepare_enable(keypad->clk); 10362306a36Sopenharmony_ci if (error) 10462306a36Sopenharmony_ci return error; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci cycles_per_us = (clk_get_rate(keypad->clk) / 1000000); 10762306a36Sopenharmony_ci if (cycles_per_us == 0) 10862306a36Sopenharmony_ci cycles_per_us = 1; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci delay_cycles = cycles_per_us * keypad->scan_interval; 11162306a36Sopenharmony_ci WARN_ON(delay_cycles >= (1 << 16)); /* Overflow */ 11262306a36Sopenharmony_ci delay_cycles &= 0xffff; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci row_delay_cycles = cycles_per_us * keypad->row_delay; 11562306a36Sopenharmony_ci WARN_ON(row_delay_cycles >= (1 << 14)); /* Overflow */ 11662306a36Sopenharmony_ci row_delay_cycles &= 0x3fff; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci val |= 3 << 0; /* Set scan mode to 3 (continuous scan) */ 11962306a36Sopenharmony_ci val |= row_delay_cycles << 2; /* Delay between scanning each row */ 12062306a36Sopenharmony_ci val |= delay_cycles << 16; /* Delay between scans */ 12162306a36Sopenharmony_ci writel(val, keypad->reg_base + KEYPAD_SCAN_MODE); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci val = (KEYPAD_BITMASK_ROWS & 0xff) | (KEYPAD_BITMASK_COLS & 0xff)<<8; 12462306a36Sopenharmony_ci writel(val, keypad->reg_base + KEYPAD_CNTL); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Enable interrupts */ 12762306a36Sopenharmony_ci keypad->int_mask = 1 << 1; 12862306a36Sopenharmony_ci writel(keypad->int_mask, keypad->reg_base + KEYPAD_INTMSK); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void nspire_keypad_close(struct input_dev *input) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct nspire_keypad *keypad = input_get_drvdata(input); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Disable interrupts */ 13862306a36Sopenharmony_ci writel(0, keypad->reg_base + KEYPAD_INTMSK); 13962306a36Sopenharmony_ci /* Acknowledge existing interrupts */ 14062306a36Sopenharmony_ci writel(~0, keypad->reg_base + KEYPAD_INT); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci clk_disable_unprepare(keypad->clk); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int nspire_keypad_probe(struct platform_device *pdev) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci const struct device_node *of_node = pdev->dev.of_node; 14862306a36Sopenharmony_ci struct nspire_keypad *keypad; 14962306a36Sopenharmony_ci struct input_dev *input; 15062306a36Sopenharmony_ci struct resource *res; 15162306a36Sopenharmony_ci int irq; 15262306a36Sopenharmony_ci int error; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 15562306a36Sopenharmony_ci if (irq < 0) 15662306a36Sopenharmony_ci return -EINVAL; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci keypad = devm_kzalloc(&pdev->dev, sizeof(struct nspire_keypad), 15962306a36Sopenharmony_ci GFP_KERNEL); 16062306a36Sopenharmony_ci if (!keypad) { 16162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate keypad memory\n"); 16262306a36Sopenharmony_ci return -ENOMEM; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci keypad->row_shift = get_count_order(KEYPAD_BITMASK_COLS); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci error = of_property_read_u32(of_node, "scan-interval", 16862306a36Sopenharmony_ci &keypad->scan_interval); 16962306a36Sopenharmony_ci if (error) { 17062306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get scan-interval\n"); 17162306a36Sopenharmony_ci return error; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci error = of_property_read_u32(of_node, "row-delay", 17562306a36Sopenharmony_ci &keypad->row_delay); 17662306a36Sopenharmony_ci if (error) { 17762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get row-delay\n"); 17862306a36Sopenharmony_ci return error; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci keypad->active_low = of_property_read_bool(of_node, "active-low"); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci keypad->clk = devm_clk_get(&pdev->dev, NULL); 18462306a36Sopenharmony_ci if (IS_ERR(keypad->clk)) { 18562306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to get clock\n"); 18662306a36Sopenharmony_ci return PTR_ERR(keypad->clk); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci keypad->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 19062306a36Sopenharmony_ci if (IS_ERR(keypad->reg_base)) 19162306a36Sopenharmony_ci return PTR_ERR(keypad->reg_base); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci keypad->input = input = devm_input_allocate_device(&pdev->dev); 19462306a36Sopenharmony_ci if (!input) { 19562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate input device\n"); 19662306a36Sopenharmony_ci return -ENOMEM; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci error = clk_prepare_enable(keypad->clk); 20062306a36Sopenharmony_ci if (error) { 20162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to enable clock\n"); 20262306a36Sopenharmony_ci return error; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Disable interrupts */ 20662306a36Sopenharmony_ci writel(0, keypad->reg_base + KEYPAD_INTMSK); 20762306a36Sopenharmony_ci /* Acknowledge existing interrupts */ 20862306a36Sopenharmony_ci writel(~0, keypad->reg_base + KEYPAD_INT); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Disable GPIO interrupts to prevent hanging on touchpad */ 21162306a36Sopenharmony_ci /* Possibly used to detect touchpad events */ 21262306a36Sopenharmony_ci writel(0, keypad->reg_base + KEYPAD_UNKNOWN_INT); 21362306a36Sopenharmony_ci /* Acknowledge existing GPIO interrupts */ 21462306a36Sopenharmony_ci writel(~0, keypad->reg_base + KEYPAD_UNKNOWN_INT_STS); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci clk_disable_unprepare(keypad->clk); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci input_set_drvdata(input, keypad); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci input->id.bustype = BUS_HOST; 22162306a36Sopenharmony_ci input->name = "nspire-keypad"; 22262306a36Sopenharmony_ci input->open = nspire_keypad_open; 22362306a36Sopenharmony_ci input->close = nspire_keypad_close; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci __set_bit(EV_KEY, input->evbit); 22662306a36Sopenharmony_ci __set_bit(EV_REP, input->evbit); 22762306a36Sopenharmony_ci input_set_capability(input, EV_MSC, MSC_SCAN); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci error = matrix_keypad_build_keymap(NULL, NULL, 23062306a36Sopenharmony_ci KEYPAD_BITMASK_ROWS, 23162306a36Sopenharmony_ci KEYPAD_BITMASK_COLS, 23262306a36Sopenharmony_ci NULL, input); 23362306a36Sopenharmony_ci if (error) { 23462306a36Sopenharmony_ci dev_err(&pdev->dev, "building keymap failed\n"); 23562306a36Sopenharmony_ci return error; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci error = devm_request_irq(&pdev->dev, irq, nspire_keypad_irq, 0, 23962306a36Sopenharmony_ci "nspire_keypad", keypad); 24062306a36Sopenharmony_ci if (error) { 24162306a36Sopenharmony_ci dev_err(&pdev->dev, "allocate irq %d failed\n", irq); 24262306a36Sopenharmony_ci return error; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci error = input_register_device(input); 24662306a36Sopenharmony_ci if (error) { 24762306a36Sopenharmony_ci dev_err(&pdev->dev, 24862306a36Sopenharmony_ci "unable to register input device: %d\n", error); 24962306a36Sopenharmony_ci return error; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci dev_dbg(&pdev->dev, 25362306a36Sopenharmony_ci "TI-NSPIRE keypad at %pR (scan_interval=%uus, row_delay=%uus%s)\n", 25462306a36Sopenharmony_ci res, keypad->row_delay, keypad->scan_interval, 25562306a36Sopenharmony_ci keypad->active_low ? ", active_low" : ""); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic const struct of_device_id nspire_keypad_dt_match[] = { 26162306a36Sopenharmony_ci { .compatible = "ti,nspire-keypad" }, 26262306a36Sopenharmony_ci { }, 26362306a36Sopenharmony_ci}; 26462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, nspire_keypad_dt_match); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic struct platform_driver nspire_keypad_driver = { 26762306a36Sopenharmony_ci .driver = { 26862306a36Sopenharmony_ci .name = "nspire-keypad", 26962306a36Sopenharmony_ci .of_match_table = nspire_keypad_dt_match, 27062306a36Sopenharmony_ci }, 27162306a36Sopenharmony_ci .probe = nspire_keypad_probe, 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cimodule_platform_driver(nspire_keypad_driver); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 27762306a36Sopenharmony_ciMODULE_DESCRIPTION("TI-NSPIRE Keypad Driver"); 278