static int stm32f7_i2c_probe()

in busses/i2c-stm32f7.c [2098:2304]


static int stm32f7_i2c_probe(struct platform_device *pdev)
{
	struct stm32f7_i2c_dev *i2c_dev;
	const struct stm32f7_i2c_setup *setup;
	struct resource *res;
	struct i2c_adapter *adap;
	struct reset_control *rst;
	dma_addr_t phy_addr;
	int irq_error, irq_event, ret;

	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
	if (!i2c_dev)
		return -ENOMEM;

	i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
	if (IS_ERR(i2c_dev->base))
		return PTR_ERR(i2c_dev->base);
	phy_addr = (dma_addr_t)res->start;

	irq_event = platform_get_irq(pdev, 0);
	if (irq_event <= 0)
		return irq_event ? : -ENOENT;

	irq_error = platform_get_irq(pdev, 1);
	if (irq_error <= 0)
		return irq_error ? : -ENOENT;

	i2c_dev->wakeup_src = of_property_read_bool(pdev->dev.of_node,
						    "wakeup-source");

	i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(i2c_dev->clk))
		return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk),
				     "Failed to get controller clock\n");

	ret = clk_prepare_enable(i2c_dev->clk);
	if (ret) {
		dev_err(&pdev->dev, "Failed to prepare_enable clock\n");
		return ret;
	}

	rst = devm_reset_control_get(&pdev->dev, NULL);
	if (IS_ERR(rst)) {
		ret = dev_err_probe(&pdev->dev, PTR_ERR(rst),
				    "Error: Missing reset ctrl\n");
		goto clk_free;
	}
	reset_control_assert(rst);
	udelay(2);
	reset_control_deassert(rst);

	i2c_dev->dev = &pdev->dev;

	ret = devm_request_threaded_irq(&pdev->dev, irq_event,
					stm32f7_i2c_isr_event,
					stm32f7_i2c_isr_event_thread,
					IRQF_ONESHOT,
					pdev->name, i2c_dev);
	if (ret) {
		dev_err(&pdev->dev, "Failed to request irq event %i\n",
			irq_event);
		goto clk_free;
	}

	ret = devm_request_irq(&pdev->dev, irq_error, stm32f7_i2c_isr_error, 0,
			       pdev->name, i2c_dev);
	if (ret) {
		dev_err(&pdev->dev, "Failed to request irq error %i\n",
			irq_error);
		goto clk_free;
	}

	setup = of_device_get_match_data(&pdev->dev);
	if (!setup) {
		dev_err(&pdev->dev, "Can't get device data\n");
		ret = -ENODEV;
		goto clk_free;
	}
	i2c_dev->setup = *setup;

	ret = stm32f7_i2c_setup_timing(i2c_dev, &i2c_dev->setup);
	if (ret)
		goto clk_free;

	/* Setup Fast mode plus if necessary */
	if (i2c_dev->bus_rate > I2C_MAX_FAST_MODE_FREQ) {
		ret = stm32f7_i2c_setup_fm_plus_bits(pdev, i2c_dev);
		if (ret)
			goto clk_free;
		ret = stm32f7_i2c_write_fm_plus_bits(i2c_dev, true);
		if (ret)
			goto clk_free;
	}

	adap = &i2c_dev->adap;
	i2c_set_adapdata(adap, i2c_dev);
	snprintf(adap->name, sizeof(adap->name), "STM32F7 I2C(%pa)",
		 &res->start);
	adap->owner = THIS_MODULE;
	adap->timeout = 2 * HZ;
	adap->retries = 3;
	adap->algo = &stm32f7_i2c_algo;
	adap->dev.parent = &pdev->dev;
	adap->dev.of_node = pdev->dev.of_node;

	init_completion(&i2c_dev->complete);

	/* Init DMA config if supported */
	i2c_dev->dma = stm32_i2c_dma_request(i2c_dev->dev, phy_addr,
					     STM32F7_I2C_TXDR,
					     STM32F7_I2C_RXDR);
	if (IS_ERR(i2c_dev->dma)) {
		ret = PTR_ERR(i2c_dev->dma);
		/* DMA support is optional, only report other errors */
		if (ret != -ENODEV)
			goto fmp_clear;
		dev_dbg(i2c_dev->dev, "No DMA option: fallback using interrupts\n");
		i2c_dev->dma = NULL;
	}

	if (i2c_dev->wakeup_src) {
		device_set_wakeup_capable(i2c_dev->dev, true);

		ret = dev_pm_set_wake_irq(i2c_dev->dev, irq_event);
		if (ret) {
			dev_err(i2c_dev->dev, "Failed to set wake up irq\n");
			goto clr_wakeup_capable;
		}
	}

	platform_set_drvdata(pdev, i2c_dev);

	pm_runtime_set_autosuspend_delay(i2c_dev->dev,
					 STM32F7_AUTOSUSPEND_DELAY);
	pm_runtime_use_autosuspend(i2c_dev->dev);
	pm_runtime_set_active(i2c_dev->dev);
	pm_runtime_enable(i2c_dev->dev);

	pm_runtime_get_noresume(&pdev->dev);

	stm32f7_i2c_hw_config(i2c_dev);

	i2c_dev->smbus_mode = of_property_read_bool(pdev->dev.of_node, "smbus");

	ret = i2c_add_adapter(adap);
	if (ret)
		goto pm_disable;

	if (i2c_dev->smbus_mode) {
		ret = stm32f7_i2c_enable_smbus_host(i2c_dev);
		if (ret) {
			dev_err(i2c_dev->dev,
				"failed to enable SMBus Host-Notify protocol (%d)\n",
				ret);
			goto i2c_adapter_remove;
		}
	}

	if (of_property_read_bool(pdev->dev.of_node, "smbus-alert")) {
		ret = stm32f7_i2c_enable_smbus_alert(i2c_dev);
		if (ret) {
			dev_err(i2c_dev->dev,
				"failed to enable SMBus alert protocol (%d)\n",
				ret);
			goto i2c_disable_smbus_host;
		}
	}

	dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr);

	pm_runtime_mark_last_busy(i2c_dev->dev);
	pm_runtime_put_autosuspend(i2c_dev->dev);

	return 0;

i2c_disable_smbus_host:
	stm32f7_i2c_disable_smbus_host(i2c_dev);

i2c_adapter_remove:
	i2c_del_adapter(adap);

pm_disable:
	pm_runtime_put_noidle(i2c_dev->dev);
	pm_runtime_disable(i2c_dev->dev);
	pm_runtime_set_suspended(i2c_dev->dev);
	pm_runtime_dont_use_autosuspend(i2c_dev->dev);

	if (i2c_dev->wakeup_src)
		dev_pm_clear_wake_irq(i2c_dev->dev);

clr_wakeup_capable:
	if (i2c_dev->wakeup_src)
		device_set_wakeup_capable(i2c_dev->dev, false);

	if (i2c_dev->dma) {
		stm32_i2c_dma_free(i2c_dev->dma);
		i2c_dev->dma = NULL;
	}

fmp_clear:
	stm32f7_i2c_write_fm_plus_bits(i2c_dev, false);

clk_free:
	clk_disable_unprepare(i2c_dev->clk);

	return ret;
}