drivers/pinctrl/samsung/pinctrl-exynos.c

Source file repositories/reference/linux-study-clean/drivers/pinctrl/samsung/pinctrl-exynos.c

File Facts

System
Linux kernel
Corpus path
drivers/pinctrl/samsung/pinctrl-exynos.c
Extension
.c
Size
31536 bytes
Lines
1106
Domain
Driver Families
Bucket
drivers/pinctrl
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 exynos_irq_chip {
	struct irq_chip chip;

	u32 eint_con;
	u32 eint_mask;
	u32 eint_pend;
	u32 eint_num_wakeup_reg;
	u32 eint_wake_mask_reg;
	void (*set_eint_wakeup_mask)(struct samsung_pinctrl_drv_data *drvdata,
				     struct exynos_irq_chip *irq_chip);
};

static u32 eint_wake_mask_values[MAX_WAKEUP_REG] = { EXYNOS_EINT_WAKEUP_MASK_DISABLED,
						     EXYNOS_EINT_WAKEUP_MASK_DISABLED,
						     EXYNOS_EINT_WAKEUP_MASK_DISABLED};

static inline struct exynos_irq_chip *to_exynos_irq_chip(struct irq_chip *chip)
{
	return container_of(chip, struct exynos_irq_chip, chip);
}

static void exynos_irq_mask(struct irq_data *irqd)
{
	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
	unsigned long reg_mask;
	unsigned int mask;
	unsigned long flags;

	if (bank->eint_mask_offset)
		reg_mask = bank->pctl_offset + bank->eint_mask_offset;
	else
		reg_mask = our_chip->eint_mask + bank->eint_offset;

	if (clk_enable(bank->drvdata->pclk)) {
		dev_err(bank->gpio_chip.parent,
			"unable to enable clock for masking IRQ\n");
		return;
	}

	raw_spin_lock_irqsave(&bank->slock, flags);

	mask = readl(bank->eint_base + reg_mask);
	mask |= 1 << irqd->hwirq;
	writel(mask, bank->eint_base + reg_mask);

	raw_spin_unlock_irqrestore(&bank->slock, flags);

	clk_disable(bank->drvdata->pclk);
}

static void exynos_irq_ack(struct irq_data *irqd)
{
	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
	unsigned long reg_pend;

	if (bank->eint_pend_offset)
		reg_pend = bank->pctl_offset + bank->eint_pend_offset;
	else
		reg_pend = our_chip->eint_pend + bank->eint_offset;

	if (clk_enable(bank->drvdata->pclk)) {
		dev_err(bank->gpio_chip.parent,
			"unable to enable clock to ack IRQ\n");
		return;
	}

	writel(1 << irqd->hwirq, bank->eint_base + reg_pend);

	clk_disable(bank->drvdata->pclk);
}

static void exynos_irq_unmask(struct irq_data *irqd)
{
	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
	unsigned long reg_mask;
	unsigned int mask;
	unsigned long flags;

	/*
	 * Ack level interrupts right before unmask
	 *
	 * If we don't do this we'll get a double-interrupt.  Level triggered
	 * interrupts must not fire an interrupt if the level is not
	 * _currently_ active, even if it was active while the interrupt was

Annotation

Implementation Notes