Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/input/keyboard/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 6 kB image not shown  

Quelle  nspire-keypad.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
 */


#include <linux/input/matrix_keypad.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>

#define KEYPAD_SCAN_MODE 0x00
#define KEYPAD_CNTL  0x04
#define KEYPAD_INT  0x08
#define KEYPAD_INTMSK  0x0C

#define KEYPAD_DATA  0x10
#define KEYPAD_GPIO  0x30

#define KEYPAD_UNKNOWN_INT 0x40
#define KEYPAD_UNKNOWN_INT_STS 0x44

#define KEYPAD_BITMASK_COLS 11
#define KEYPAD_BITMASK_ROWS 8

struct nspire_keypad {
 void __iomem *reg_base;
 u32 int_mask;

 struct input_dev *input;
 struct clk *clk;

 struct matrix_keymap_data *keymap;
 int row_shift;

 /* Maximum delay estimated assuming 33MHz APB */
 u32 scan_interval; /* In microseconds (~2000us max) */
 u32 row_delay;  /* In microseconds (~500us max) */

 u16 state[KEYPAD_BITMASK_ROWS];

 bool active_low;
};

static irqreturn_t nspire_keypad_irq(int irq, void *dev_id)
{
 struct nspire_keypad *keypad = dev_id;
 struct input_dev *input = keypad->input;
 unsigned short *keymap = input->keycode;
 unsigned int code;
 int row, col;
 u32 int_sts;
 u16 state[8];
 u16 bits, changed;

 int_sts = readl(keypad->reg_base + KEYPAD_INT) & keypad->int_mask;
 if (!int_sts)
  return IRQ_NONE;

 memcpy_fromio(state, keypad->reg_base + KEYPAD_DATA, sizeof(state));

 for (row = 0; row < KEYPAD_BITMASK_ROWS; row++) {
  bits = state[row];
  if (keypad->active_low)
   bits = ~bits;

  changed = bits ^ keypad->state[row];
  if (!changed)
   continue;

  keypad->state[row] = bits;

  for (col = 0; col < KEYPAD_BITMASK_COLS; col++) {
   if (!(changed & (1U << col)))
    continue;

   code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
   input_event(input, EV_MSC, MSC_SCAN, code);
   input_report_key(input, keymap[code],
      bits & (1U << col));
  }
 }

 input_sync(input);

 writel(0x3, keypad->reg_base + KEYPAD_INT);

 return IRQ_HANDLED;
}

static int nspire_keypad_open(struct input_dev *input)
{
 struct nspire_keypad *keypad = input_get_drvdata(input);
 unsigned long val = 0, cycles_per_us, delay_cycles, row_delay_cycles;
 int error;

 error = clk_prepare_enable(keypad->clk);
 if (error)
  return error;

 cycles_per_us = (clk_get_rate(keypad->clk) / 1000000);
 if (cycles_per_us == 0)
  cycles_per_us = 1;

 delay_cycles = cycles_per_us * keypad->scan_interval;
 WARN_ON(delay_cycles >= (1 << 16)); /* Overflow */
 delay_cycles &= 0xffff;

 row_delay_cycles = cycles_per_us * keypad->row_delay;
 WARN_ON(row_delay_cycles >= (1 << 14)); /* Overflow */
 row_delay_cycles &= 0x3fff;

 val |= 3 << 0; /* Set scan mode to 3 (continuous scan) */
 val |= row_delay_cycles << 2; /* Delay between scanning each row */
 val |= delay_cycles << 16; /* Delay between scans */
 writel(val, keypad->reg_base + KEYPAD_SCAN_MODE);

 val = (KEYPAD_BITMASK_ROWS & 0xff) | (KEYPAD_BITMASK_COLS & 0xff)<<8;
 writel(val, keypad->reg_base + KEYPAD_CNTL);

 /* Enable interrupts */
 keypad->int_mask = 1 << 1;
 writel(keypad->int_mask, keypad->reg_base + KEYPAD_INTMSK);

 return 0;
}

static void nspire_keypad_close(struct input_dev *input)
{
 struct nspire_keypad *keypad = input_get_drvdata(input);

 /* Disable interrupts */
 writel(0, keypad->reg_base + KEYPAD_INTMSK);
 /* Acknowledge existing interrupts */
 writel(~0, keypad->reg_base + KEYPAD_INT);

 clk_disable_unprepare(keypad->clk);
}

static int nspire_keypad_probe(struct platform_device *pdev)
{
 const struct device_node *of_node = pdev->dev.of_node;
 struct nspire_keypad *keypad;
 struct input_dev *input;
 struct resource *res;
 int irq;
 int error;

 irq = platform_get_irq(pdev, 0);
 if (irq < 0)
  return -EINVAL;

 keypad = devm_kzalloc(&pdev->dev, sizeof(struct nspire_keypad),
         GFP_KERNEL);
 if (!keypad) {
  dev_err(&pdev->dev, "failed to allocate keypad memory\n");
  return -ENOMEM;
 }

 keypad->row_shift = get_count_order(KEYPAD_BITMASK_COLS);

 error = of_property_read_u32(of_node, "scan-interval",
         &keypad->scan_interval);
 if (error) {
  dev_err(&pdev->dev, "failed to get scan-interval\n");
  return error;
 }

 error = of_property_read_u32(of_node, "row-delay",
         &keypad->row_delay);
 if (error) {
  dev_err(&pdev->dev, "failed to get row-delay\n");
  return error;
 }

 keypad->active_low = of_property_read_bool(of_node, "active-low");

 keypad->clk = devm_clk_get(&pdev->dev, NULL);
 if (IS_ERR(keypad->clk)) {
  dev_err(&pdev->dev, "unable to get clock\n");
  return PTR_ERR(keypad->clk);
 }

 keypad->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
 if (IS_ERR(keypad->reg_base))
  return PTR_ERR(keypad->reg_base);

 keypad->input = input = devm_input_allocate_device(&pdev->dev);
 if (!input) {
  dev_err(&pdev->dev, "failed to allocate input device\n");
  return -ENOMEM;
 }

 error = clk_prepare_enable(keypad->clk);
 if (error) {
  dev_err(&pdev->dev, "failed to enable clock\n");
  return error;
 }

 /* Disable interrupts */
 writel(0, keypad->reg_base + KEYPAD_INTMSK);
 /* Acknowledge existing interrupts */
 writel(~0, keypad->reg_base + KEYPAD_INT);

 /* Disable GPIO interrupts to prevent hanging on touchpad */
 /* Possibly used to detect touchpad events */
 writel(0, keypad->reg_base + KEYPAD_UNKNOWN_INT);
 /* Acknowledge existing GPIO interrupts */
 writel(~0, keypad->reg_base + KEYPAD_UNKNOWN_INT_STS);

 clk_disable_unprepare(keypad->clk);

 input_set_drvdata(input, keypad);

 input->id.bustype = BUS_HOST;
 input->name = "nspire-keypad";
 input->open = nspire_keypad_open;
 input->close = nspire_keypad_close;

 __set_bit(EV_KEY, input->evbit);
 __set_bit(EV_REP, input->evbit);
 input_set_capability(input, EV_MSC, MSC_SCAN);

 error = matrix_keypad_build_keymap(NULL, NULL,
        KEYPAD_BITMASK_ROWS,
        KEYPAD_BITMASK_COLS,
        NULL, input);
 if (error) {
  dev_err(&pdev->dev, "building keymap failed\n");
  return error;
 }

 error = devm_request_irq(&pdev->dev, irq, nspire_keypad_irq, 0,
     "nspire_keypad", keypad);
 if (error) {
  dev_err(&pdev->dev, "allocate irq %d failed\n", irq);
  return error;
 }

 error = input_register_device(input);
 if (error) {
  dev_err(&pdev->dev,
   "unable to register input device: %d\n", error);
  return error;
 }

 dev_dbg(&pdev->dev,
  "TI-NSPIRE keypad at %pR (scan_interval=%uus, row_delay=%uus%s)\n",
  res, keypad->row_delay, keypad->scan_interval,
  keypad->active_low ? ", active_low" : "");

 return 0;
}

static const struct of_device_id nspire_keypad_dt_match[] = {
 { .compatible = "ti,nspire-keypad" },
 { },
};
MODULE_DEVICE_TABLE(of, nspire_keypad_dt_match);

static struct platform_driver nspire_keypad_driver = {
 .driver = {
  .name = "nspire-keypad",
  .of_match_table = nspire_keypad_dt_match,
 },
 .probe = nspire_keypad_probe,
};

module_platform_driver(nspire_keypad_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("TI-NSPIRE Keypad Driver");

Messung V0.5
C=94 H=93 G=93

¤ Dauer der Verarbeitung: 0.3 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.