drivers/input/keyboard/bcm-keypad.c

Source file repositories/reference/linux-study-clean/drivers/input/keyboard/bcm-keypad.c

File Facts

System
Linux kernel
Corpus path
drivers/input/keyboard/bcm-keypad.c
Extension
.c
Size
10954 bytes
Lines
430
Domain
Driver Families
Bucket
drivers/input
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct bcm_kp {
	void __iomem *base;
	int irq;
	struct clk *clk;
	struct input_dev *input_dev;
	unsigned long last_state[2];
	unsigned int n_rows;
	unsigned int n_cols;
	u32 kpcr;
	u32 kpior;
	u32 kpemr;
	u32 imr0_val;
	u32 imr1_val;
};

/*
 * Returns the keycode from the input device keymap given the row and
 * column.
 */
static int bcm_kp_get_keycode(struct bcm_kp *kp, int row, int col)
{
	unsigned int row_shift = get_count_order(kp->n_cols);
	unsigned short *keymap = kp->input_dev->keycode;

	return keymap[MATRIX_SCAN_CODE(row, col, row_shift)];
}

static void bcm_kp_report_keys(struct bcm_kp *kp, int reg_num, int pull_mode)
{
	unsigned long state, change;
	int bit_nr;
	int key_press;
	int row, col;
	unsigned int keycode;

	/* Clear interrupts */
	writel(0xFFFFFFFF, kp->base + KPICRN_OFFSET(reg_num));

	state = readl(kp->base + KPSSRN_OFFSET(reg_num));
	change = kp->last_state[reg_num] ^ state;
	kp->last_state[reg_num] = state;

	for_each_set_bit(bit_nr, &change, BITS_PER_LONG) {
		key_press = state & BIT(bit_nr);
		/* The meaning of SSR register depends on pull mode. */
		key_press = pull_mode ? !key_press : key_press;
		row = BIT_TO_ROW_SSRN(bit_nr, reg_num);
		col = BIT_TO_COL(bit_nr);
		keycode = bcm_kp_get_keycode(kp, row, col);
		input_report_key(kp->input_dev, keycode, key_press);
	}
}

static irqreturn_t bcm_kp_isr_thread(int irq, void *dev_id)
{
	struct bcm_kp *kp = dev_id;
	int pull_mode = (kp->kpcr >> KPCR_MODE_SHIFT) & KPCR_MODE_MASK;
	int reg_num;

	for (reg_num = 0; reg_num <= 1; reg_num++)
		bcm_kp_report_keys(kp, reg_num, pull_mode);

	input_sync(kp->input_dev);

	return IRQ_HANDLED;
}

static int bcm_kp_start(struct bcm_kp *kp)
{
	int error;

	if (kp->clk) {
		error = clk_prepare_enable(kp->clk);
		if (error)
			return error;
	}

	writel(kp->kpior, kp->base + KPIOR_OFFSET);

	writel(kp->imr0_val, kp->base + KPIMR0_OFFSET);
	writel(kp->imr1_val, kp->base + KPIMR1_OFFSET);

	writel(kp->kpemr, kp->base + KPEMR0_OFFSET);
	writel(kp->kpemr, kp->base + KPEMR1_OFFSET);
	writel(kp->kpemr, kp->base + KPEMR2_OFFSET);
	writel(kp->kpemr, kp->base + KPEMR3_OFFSET);

	writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET);
	writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET);

Annotation

Implementation Notes