drivers/ufs/core/ufshcd.c

Source file repositories/reference/linux-study-clean/drivers/ufs/core/ufshcd.c

File Facts

System
Linux kernel
Corpus path
drivers/ufs/core/ufshcd.c
Extension
.c
Size
316438 bytes
Lines
11553
Domain
Driver Families
Bucket
drivers/ufs
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 blk_mq_ops ufshcd_tmf_ops = {
	.queue_rq = ufshcd_queue_tmf,
};

static int ufshcd_add_scsi_host(struct ufs_hba *hba)
{
	int err;

	WARN_ON_ONCE(!hba->host->can_queue);
	WARN_ON_ONCE(!hba->host->cmd_per_lun);

	if (is_mcq_supported(hba)) {
		ufshcd_mcq_enable(hba);
		err = ufshcd_alloc_mcq(hba);
		if (err) {
			/* Continue with SDB mode */
			ufshcd_mcq_disable(hba);
			use_mcq_mode = false;
			dev_err(hba->dev, "MCQ mode is disabled, err=%d\n",
				err);
		}
	}
	if (!is_mcq_supported(hba) && !hba->lsdb_sup) {
		dev_err(hba->dev,
			"%s: failed to initialize (legacy doorbell mode not supported)\n",
			__func__);
		return -EINVAL;
	}

	err = scsi_add_host(hba->host, hba->dev);
	if (err) {
		dev_err(hba->dev, "scsi_add_host failed\n");
		return err;
	}
	hba->scsi_host_added = true;

	hba->tmf_tag_set = (struct blk_mq_tag_set) {
		.nr_hw_queues	= 1,
		.queue_depth	= hba->nutmrs,
		.ops		= &ufshcd_tmf_ops,
	};
	err = blk_mq_alloc_tag_set(&hba->tmf_tag_set);
	if (err < 0)
		goto remove_scsi_host;
	hba->tmf_queue = blk_mq_alloc_queue(&hba->tmf_tag_set, NULL, NULL);
	if (IS_ERR(hba->tmf_queue)) {
		err = PTR_ERR(hba->tmf_queue);
		goto free_tmf_tag_set;
	}
	hba->tmf_rqs = devm_kcalloc(hba->dev, hba->nutmrs,
				    sizeof(*hba->tmf_rqs), GFP_KERNEL);
	if (!hba->tmf_rqs) {
		err = -ENOMEM;
		goto free_tmf_queue;
	}

	return 0;

free_tmf_queue:
	blk_mq_destroy_queue(hba->tmf_queue);
	blk_put_queue(hba->tmf_queue);

free_tmf_tag_set:
	blk_mq_free_tag_set(&hba->tmf_tag_set);

remove_scsi_host:
	if (hba->scsi_host_added)
		scsi_remove_host(hba->host);

	return err;
}

/**
 * ufshcd_init - Driver initialization routine
 * @hba: per-adapter instance
 * @mmio_base: base register address
 * @irq: Interrupt line of device
 *
 * Return: 0 on success; < 0 on failure.
 */
int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
{
	int err;
	struct Scsi_Host *host = hba->host;
	struct device *dev = hba->dev;

	/*
	 * dev_set_drvdata() must be called before any callbacks are registered
	 * that use dev_get_drvdata() (frequency scaling, clock scaling, hwmon,
	 * sysfs).

Annotation

Implementation Notes