drivers/soc/aspeed/aspeed-lpc-snoop.c

Source file repositories/reference/linux-study-clean/drivers/soc/aspeed/aspeed-lpc-snoop.c

File Facts

System
Linux kernel
Corpus path
drivers/soc/aspeed/aspeed-lpc-snoop.c
Extension
.c
Size
10143 bytes
Lines
385
Domain
Driver Families
Bucket
drivers/soc
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern 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

static const struct file_operations snoop_fops = {
	.owner  = THIS_MODULE,
	.read   = snoop_file_read,
	.poll   = snoop_file_poll,
	.llseek = noop_llseek,
};

/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */
static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
{
	if (!kfifo_initialized(&chan->fifo))
		return;
	if (kfifo_is_full(&chan->fifo))
		kfifo_skip(&chan->fifo);
	kfifo_put(&chan->fifo, val);
	wake_up_interruptible(&chan->wq);
}

static irqreturn_t aspeed_lpc_snoop_irq(int irq, void *arg)
{
	struct aspeed_lpc_snoop *lpc_snoop = arg;
	u32 reg, data;

	if (regmap_read(lpc_snoop->regmap, HICR6, &reg))
		return IRQ_NONE;

	/* Check if one of the snoop channels is interrupting */
	reg &= (HICR6_STR_SNP0W | HICR6_STR_SNP1W);
	if (!reg)
		return IRQ_NONE;

	/* Ack pending IRQs */
	regmap_write(lpc_snoop->regmap, HICR6, reg);

	/* Read and save most recent snoop'ed data byte to FIFO */
	regmap_read(lpc_snoop->regmap, SNPWDR, &data);

	if (reg & HICR6_STR_SNP0W) {
		u8 val = (data & SNPWDR_CH0_MASK) >> SNPWDR_CH0_SHIFT;

		put_fifo_with_discard(&lpc_snoop->chan[0], val);
	}
	if (reg & HICR6_STR_SNP1W) {
		u8 val = (data & SNPWDR_CH1_MASK) >> SNPWDR_CH1_SHIFT;

		put_fifo_with_discard(&lpc_snoop->chan[1], val);
	}

	return IRQ_HANDLED;
}

static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop,
				       struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	int rc;

	lpc_snoop->irq = platform_get_irq(pdev, 0);
	if (lpc_snoop->irq < 0)
		return -ENODEV;

	rc = devm_request_irq(dev, lpc_snoop->irq,
			      aspeed_lpc_snoop_irq, IRQF_SHARED,
			      DEVICE_NAME, lpc_snoop);
	if (rc < 0) {
		dev_warn(dev, "Unable to request IRQ %d\n", lpc_snoop->irq);
		lpc_snoop->irq = 0;
		return rc;
	}

	return 0;
}

__attribute__((nonnull))
static int aspeed_lpc_enable_snoop(struct device *dev,
				    struct aspeed_lpc_snoop *lpc_snoop,
				    struct aspeed_lpc_snoop_channel *channel,
				    const struct aspeed_lpc_snoop_channel_cfg *cfg,
				    u16 lpc_port)
{
	const struct aspeed_lpc_snoop_model_data *model_data;
	int rc = 0;

	if (WARN_ON(channel->enabled))
		return -EBUSY;

	init_waitqueue_head(&channel->wq);

	channel->cfg = cfg;
	channel->miscdev.minor = MISC_DYNAMIC_MINOR;

Annotation

Implementation Notes