drivers/mfd/loongson-se.c

Source file repositories/reference/linux-study-clean/drivers/mfd/loongson-se.c

File Facts

System
Linux kernel
Corpus path
drivers/mfd/loongson-se.c
Extension
.c
Size
6531 bytes
Lines
254
Domain
Driver Families
Bucket
drivers/mfd
Inferred role
Driver Families: exported/initcall integration point
Status
integration 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 loongson_se {
	void __iomem *base;
	spinlock_t dev_lock;
	struct completion cmd_completion;

	void *dmam_base;
	int dmam_size;

	struct mutex engine_init_lock;
	struct loongson_se_engine engines[SE_ENGINE_MAX];
};

struct loongson_se_controller_cmd {
	u32 command_id;
	u32 info[7];
};

static int loongson_se_poll(struct loongson_se *se, u32 int_bit)
{
	u32 status;
	int err;

	spin_lock_irq(&se->dev_lock);

	/* Notify the controller that the engine needs to be started */
	writel(int_bit, se->base + SE_L2SINT_SET);

	/* Polling until the controller has forwarded the engine command */
	err = readl_relaxed_poll_timeout_atomic(se->base + SE_L2SINT_STAT, status,
						!(status & int_bit),
						1, LOONGSON_ENGINE_CMD_TIMEOUT_US);

	spin_unlock_irq(&se->dev_lock);

	return err;
}

static int loongson_se_send_controller_cmd(struct loongson_se *se,
					   struct loongson_se_controller_cmd *cmd)
{
	u32 *send_cmd = (u32 *)cmd;
	int err, i;

	for (i = 0; i < SE_SEND_CMD_REG_LEN; i++)
		writel(send_cmd[i], se->base + SE_SEND_CMD_REG + i * 4);

	err = loongson_se_poll(se, SE_INT_CONTROLLER);
	if (err)
		return err;

	return wait_for_completion_interruptible(&se->cmd_completion);
}

int loongson_se_send_engine_cmd(struct loongson_se_engine *engine)
{
	/*
	 * After engine initialization, the controller already knows
	 * where to obtain engine commands from. Now all we need to
	 * do is notify the controller that the engine needs to be started.
	 */
	int err = loongson_se_poll(engine->se, BIT(engine->id));

	if (err)
		return err;

	return wait_for_completion_interruptible(&engine->completion);
}
EXPORT_SYMBOL_GPL(loongson_se_send_engine_cmd);

struct loongson_se_engine *loongson_se_init_engine(struct device *dev, int id)
{
	struct loongson_se *se = dev_get_drvdata(dev);
	struct loongson_se_engine *engine = &se->engines[id];
	struct loongson_se_controller_cmd cmd;

	engine->se = se;
	engine->id = id;
	init_completion(&engine->completion);

	/* Divide DMA memory equally among all engines */
	engine->buffer_size = se->dmam_size / SE_ENGINE_MAX;
	engine->buffer_off = (se->dmam_size / SE_ENGINE_MAX) * id;
	engine->data_buffer = se->dmam_base + engine->buffer_off;

	/*
	 * There has no engine0, use its data buffer as command buffer for other
	 * engines. The DMA memory size is obtained from the ACPI table, which
	 * ensures that the data buffer size of engine0 is larger than the
	 * command buffer size of all engines.
	 */

Annotation

Implementation Notes