static int cci_probe()

in busses/i2c-qcom-cci.c [523:668]


static int cci_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	unsigned long cci_clk_rate = 0;
	struct device_node *child;
	struct resource *r;
	struct cci *cci;
	int ret, i;
	u32 val;

	cci = devm_kzalloc(dev, sizeof(*cci), GFP_KERNEL);
	if (!cci)
		return -ENOMEM;

	cci->dev = dev;
	platform_set_drvdata(pdev, cci);
	cci->data = device_get_match_data(dev);
	if (!cci->data)
		return -ENOENT;

	for_each_available_child_of_node(dev->of_node, child) {
		u32 idx;

		ret = of_property_read_u32(child, "reg", &idx);
		if (ret) {
			dev_err(dev, "%pOF invalid 'reg' property", child);
			continue;
		}

		if (idx >= cci->data->num_masters) {
			dev_err(dev, "%pOF invalid 'reg' value: %u (max is %u)",
				child, idx, cci->data->num_masters - 1);
			continue;
		}

		cci->master[idx].adap.quirks = &cci->data->quirks;
		cci->master[idx].adap.algo = &cci_algo;
		cci->master[idx].adap.dev.parent = dev;
		cci->master[idx].adap.dev.of_node = child;
		cci->master[idx].master = idx;
		cci->master[idx].cci = cci;

		i2c_set_adapdata(&cci->master[idx].adap, &cci->master[idx]);
		snprintf(cci->master[idx].adap.name,
			 sizeof(cci->master[idx].adap.name), "Qualcomm-CCI");

		cci->master[idx].mode = I2C_MODE_STANDARD;
		ret = of_property_read_u32(child, "clock-frequency", &val);
		if (!ret) {
			if (val == I2C_MAX_FAST_MODE_FREQ)
				cci->master[idx].mode = I2C_MODE_FAST;
			else if (val == I2C_MAX_FAST_MODE_PLUS_FREQ)
				cci->master[idx].mode = I2C_MODE_FAST_PLUS;
		}

		init_completion(&cci->master[idx].irq_complete);
	}

	/* Memory */

	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	cci->base = devm_ioremap_resource(dev, r);
	if (IS_ERR(cci->base))
		return PTR_ERR(cci->base);

	/* Clocks */

	ret = devm_clk_bulk_get_all(dev, &cci->clocks);
	if (ret < 1) {
		dev_err(dev, "failed to get clocks %d\n", ret);
		return ret;
	}
	cci->nclocks = ret;

	/* Retrieve CCI clock rate */
	for (i = 0; i < cci->nclocks; i++) {
		if (!strcmp(cci->clocks[i].id, "cci")) {
			cci_clk_rate = clk_get_rate(cci->clocks[i].clk);
			break;
		}
	}

	if (cci_clk_rate != cci->data->cci_clk_rate) {
		/* cci clock set by the bootloader or via assigned clock rate
		 * in DT.
		 */
		dev_warn(dev, "Found %lu cci clk rate while %lu was expected\n",
			 cci_clk_rate, cci->data->cci_clk_rate);
	}

	ret = cci_enable_clocks(cci);
	if (ret < 0)
		return ret;

	/* Interrupt */

	ret = platform_get_irq(pdev, 0);
	if (ret < 0)
		goto disable_clocks;
	cci->irq = ret;

	ret = devm_request_irq(dev, cci->irq, cci_isr, 0, dev_name(dev), cci);
	if (ret < 0) {
		dev_err(dev, "request_irq failed, ret: %d\n", ret);
		goto disable_clocks;
	}

	val = readl(cci->base + CCI_HW_VERSION);
	dev_dbg(dev, "CCI HW version = 0x%08x", val);

	ret = cci_reset(cci);
	if (ret < 0)
		goto error;

	ret = cci_init(cci);
	if (ret < 0)
		goto error;

	for (i = 0; i < cci->data->num_masters; i++) {
		if (!cci->master[i].cci)
			continue;

		ret = i2c_add_adapter(&cci->master[i].adap);
		if (ret < 0)
			goto error_i2c;
	}

	pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
	pm_runtime_use_autosuspend(dev);
	pm_runtime_set_active(dev);
	pm_runtime_enable(dev);

	return 0;

error_i2c:
	for (; i >= 0; i--) {
		if (cci->master[i].cci)
			i2c_del_adapter(&cci->master[i].adap);
	}
error:
	disable_irq(cci->irq);
disable_clocks:
	cci_disable_clocks(cci);

	return ret;
}