drivers/input/serio/sun4i-ps2.c

Source file repositories/reference/linux-study-clean/drivers/input/serio/sun4i-ps2.c

File Facts

System
Linux kernel
Corpus path
drivers/input/serio/sun4i-ps2.c
Extension
.c
Size
8614 bytes
Lines
333
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 sun4i_ps2data {
	struct serio *serio;
	struct device *dev;

	/* IO mapping base */
	void __iomem	*reg_base;

	/* clock management */
	struct clk	*clk;

	/* irq */
	spinlock_t	lock;
	int		irq;
};

static irqreturn_t sun4i_ps2_interrupt(int irq, void *dev_id)
{
	struct sun4i_ps2data *drvdata = dev_id;
	u32 intr_status;
	u32 fifo_status;
	unsigned char byte;
	unsigned int rxflags = 0;
	u32 rval;

	guard(spinlock)(&drvdata->lock);

	/* Get the PS/2 interrupts and clear them */
	intr_status  = readl(drvdata->reg_base + PS2_REG_LSTS);
	fifo_status  = readl(drvdata->reg_base + PS2_REG_FSTS);

	/* Check line status register */
	if (intr_status & PS2_LINE_ERROR_BIT) {
		rxflags = (intr_status & PS2_LINE_ERROR_BIT) ? SERIO_FRAME : 0;
		rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_PARITY : 0;
		rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_TIMEOUT : 0;

		rval = PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR |
			PS2_LSTS_PARERR | PS2_LSTS_RXTDO;
		writel(rval, drvdata->reg_base + PS2_REG_LSTS);
	}

	/* Check FIFO status register */
	if (fifo_status & PS2_FIFO_ERROR_BIT) {
		rval = PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_TXRDY |
			PS2_FSTS_RXUF | PS2_FSTS_RXOF | PS2_FSTS_RXRDY;
		writel(rval, drvdata->reg_base + PS2_REG_FSTS);
	}

	rval = (fifo_status >> 16) & 0x3;
	while (rval--) {
		byte = readl(drvdata->reg_base + PS2_REG_DATA) & 0xff;
		serio_interrupt(drvdata->serio, byte, rxflags);
	}

	writel(intr_status, drvdata->reg_base + PS2_REG_LSTS);
	writel(fifo_status, drvdata->reg_base + PS2_REG_FSTS);

	return IRQ_HANDLED;
}

static int sun4i_ps2_open(struct serio *serio)
{
	struct sun4i_ps2data *drvdata = serio->port_data;
	u32 src_clk = 0;
	u32 clk_scdf;
	u32 clk_pcdf;
	u32 rval;

	/* Set line control and enable interrupt */
	rval = PS2_LCTL_STOPERREN | PS2_LCTL_ACKERREN
		| PS2_LCTL_PARERREN | PS2_LCTL_RXDTOEN;
	writel(rval, drvdata->reg_base + PS2_REG_LCTL);

	/* Reset FIFO */
	rval = PS2_FCTL_TXRST | PS2_FCTL_RXRST | PS2_FCTL_TXUFIEN
		| PS2_FCTL_TXOFIEN | PS2_FCTL_RXUFIEN
		| PS2_FCTL_RXOFIEN | PS2_FCTL_RXRDYIEN;

	writel(rval, drvdata->reg_base + PS2_REG_FCTL);

	src_clk = clk_get_rate(drvdata->clk);
	/* Set clock divider register */
	clk_scdf = src_clk / PS2_SAMPLE_CLK - 1;
	clk_pcdf = PS2_SAMPLE_CLK / PS2_SCLK - 1;
	rval = (clk_scdf << 8) | clk_pcdf;
	writel(rval, drvdata->reg_base + PS2_REG_CLKDR);

	/* Set global control register */
	rval = PS2_GCTL_RESET | PS2_GCTL_INTEN | PS2_GCTL_MASTER
		| PS2_GCTL_BUSEN;

Annotation

Implementation Notes