drivers/spmi/hisi-spmi-controller.c

Source file repositories/reference/linux-study-clean/drivers/spmi/hisi-spmi-controller.c

File Facts

System
Linux kernel
Corpus path
drivers/spmi/hisi-spmi-controller.c
Extension
.c
Size
9484 bytes
Lines
348
Domain
Driver Families
Bucket
drivers/spmi
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 spmi_controller_dev {
	struct spmi_controller	*controller;
	struct device		*dev;
	void __iomem		*base;
	spinlock_t		lock;
	u32			channel;
};

static int spmi_controller_wait_for_done(struct device *dev,
					 struct spmi_controller_dev *ctrl_dev,
					 void __iomem *base, u8 sid, u16 addr)
{
	u32 timeout = SPMI_CONTROLLER_TIMEOUT_US;
	u32 status, offset;

	offset  = SPMI_APB_SPMI_STATUS_BASE_ADDR;
	offset += SPMI_CHANNEL_OFFSET * ctrl_dev->channel + SPMI_SLAVE_OFFSET * sid;

	do {
		status = readl(base + offset);

		if (status & SPMI_APB_TRANS_DONE) {
			if (status & SPMI_APB_TRANS_FAIL) {
				dev_err(dev, "%s: transaction failed (0x%x)\n",
					__func__, status);
				return -EIO;
			}
			dev_dbg(dev, "%s: status 0x%x\n", __func__, status);
			return 0;
		}
		udelay(1);
	} while (timeout--);

	dev_err(dev, "%s: timeout, status 0x%x\n", __func__, status);
	return -ETIMEDOUT;
}

static int spmi_read_cmd(struct spmi_controller *ctrl,
			 u8 opc, u8 slave_id, u16 slave_addr, u8 *__buf, size_t bc)
{
	struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);
	u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel;
	unsigned long flags;
	u8 *buf = __buf;
	u32 cmd, data;
	int rc;
	u8 op_code, i;

	if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
		dev_err(&ctrl->dev,
			"spmi_controller supports 1..%d bytes per trans, but:%zu requested\n",
			SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
		return  -EINVAL;
	}

	switch (opc) {
	case SPMI_CMD_READ:
		op_code = SPMI_CMD_REG_READ;
		break;
	case SPMI_CMD_EXT_READ:
		op_code = SPMI_CMD_EXT_REG_READ;
		break;
	case SPMI_CMD_EXT_READL:
		op_code = SPMI_CMD_EXT_REG_READ_L;
		break;
	default:
		dev_err(&ctrl->dev, "invalid read cmd 0x%x\n", opc);
		return -EINVAL;
	}

	cmd = SPMI_APB_SPMI_CMD_EN |
	     (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |
	     ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |
	     ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |  /* slvid */
	     ((slave_addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */

	spin_lock_irqsave(&spmi_controller->lock, flags);

	writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);

	rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller,
					   spmi_controller->base, slave_id, slave_addr);
	if (rc)
		goto done;

	for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) {
		data = readl(spmi_controller->base + chnl_ofst +
			     SPMI_SLAVE_OFFSET * slave_id +
			     SPMI_APB_SPMI_RDATA0_BASE_ADDR +
			     i * SPMI_PER_DATAREG_BYTE);

Annotation

Implementation Notes