static int ocmem_dev_probe()

in qcom/ocmem.c [298:404]


static int ocmem_dev_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	unsigned long reg, region_size;
	int i, j, ret, num_banks;
	struct ocmem *ocmem;

	if (!qcom_scm_is_available())
		return -EPROBE_DEFER;

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

	ocmem->dev = dev;
	ocmem->config = device_get_match_data(dev);

	ret = devm_clk_bulk_get(dev, ARRAY_SIZE(ocmem_clks), ocmem_clks);
	if (ret) {
		if (ret != -EPROBE_DEFER)
			dev_err(dev, "Unable to get clocks\n");

		return ret;
	}

	ocmem->mmio = devm_platform_ioremap_resource_byname(pdev, "ctrl");
	if (IS_ERR(ocmem->mmio)) {
		dev_err(&pdev->dev, "Failed to ioremap ocmem_ctrl resource\n");
		return PTR_ERR(ocmem->mmio);
	}

	ocmem->memory = platform_get_resource_byname(pdev, IORESOURCE_MEM,
						     "mem");
	if (!ocmem->memory) {
		dev_err(dev, "Could not get mem region\n");
		return -ENXIO;
	}

	/* The core clock is synchronous with graphics */
	WARN_ON(clk_set_rate(ocmem_clks[OCMEM_CLK_CORE_IDX].clk, 1000) < 0);

	ret = clk_bulk_prepare_enable(ARRAY_SIZE(ocmem_clks), ocmem_clks);
	if (ret) {
		dev_info(ocmem->dev, "Failed to enable clocks\n");
		return ret;
	}

	if (qcom_scm_restore_sec_cfg_available()) {
		dev_dbg(dev, "configuring scm\n");
		ret = qcom_scm_restore_sec_cfg(QCOM_SCM_OCMEM_DEV_ID, 0);
		if (ret) {
			dev_err(dev, "Could not enable secure configuration\n");
			goto err_clk_disable;
		}
	}

	reg = ocmem_read(ocmem, OCMEM_REG_HW_PROFILE);
	ocmem->num_ports = OCMEM_HW_PROFILE_NUM_PORTS(reg);
	ocmem->num_macros = OCMEM_HW_PROFILE_NUM_MACROS(reg);
	ocmem->interleaved = !!(reg & OCMEM_HW_PROFILE_INTERLEAVING);

	num_banks = ocmem->num_ports / 2;
	region_size = ocmem->config->macro_size * num_banks;

	dev_info(dev, "%u ports, %u regions, %u macros, %sinterleaved\n",
		 ocmem->num_ports, ocmem->config->num_regions,
		 ocmem->num_macros, ocmem->interleaved ? "" : "not ");

	ocmem->regions = devm_kcalloc(dev, ocmem->config->num_regions,
				      sizeof(struct ocmem_region), GFP_KERNEL);
	if (!ocmem->regions) {
		ret = -ENOMEM;
		goto err_clk_disable;
	}

	for (i = 0; i < ocmem->config->num_regions; i++) {
		struct ocmem_region *region = &ocmem->regions[i];

		if (WARN_ON(num_banks > ARRAY_SIZE(region->macro_state))) {
			ret = -EINVAL;
			goto err_clk_disable;
		}

		region->mode = MODE_DEFAULT;
		region->num_macros = num_banks;

		if (i == (ocmem->config->num_regions - 1) &&
		    reg & OCMEM_HW_PROFILE_LAST_REGN_HALFSIZE) {
			region->macro_size = ocmem->config->macro_size / 2;
			region->region_size = region_size / 2;
		} else {
			region->macro_size = ocmem->config->macro_size;
			region->region_size = region_size;
		}

		for (j = 0; j < ARRAY_SIZE(region->macro_state); j++)
			region->macro_state[j] = CLK_OFF;
	}

	platform_set_drvdata(pdev, ocmem);

	return 0;

err_clk_disable:
	clk_bulk_disable_unprepare(ARRAY_SIZE(ocmem_clks), ocmem_clks);
	return ret;
}