// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2013 NVIDIA Corporation * Copyright (c) 2023 Svyatoslav Ryhel */ #include #include #include #include #include #include /* MIPI control registers 0x00 ~ 0x60 */ struct mipi_ctlr { uint mipi_cal_ctrl; uint mipi_cal_autocal_ctrl; uint mipi_cal_status; uint unused1[2]; uint mipi_cal_config_csia; uint mipi_cal_config_csib; uint mipi_cal_config_csic; uint mipi_cal_config_csid; uint mipi_cal_config_csie; uint unused2[4]; uint mipi_cal_config_dsia; uint mipi_cal_config_dsib; uint mipi_cal_config_dsic; uint mipi_cal_config_dsid; uint unused3[4]; uint mipi_cal_bias_pad_cfg0; uint mipi_cal_bias_pad_cfg1; uint mipi_cal_bias_pad_cfg2; }; #define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26) #define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24) #define MIPI_CAL_CTRL_CLKEN_OVR BIT(4) #define MIPI_CAL_CTRL_START BIT(0) #define MIPI_CAL_STATUS_DONE BIT(16) #define MIPI_CAL_STATUS_ACTIVE BIT(0) #define MIPI_CAL_OVERIDE(x) (((x) & 0x1) << 30) #define MIPI_CAL_SEL(x) (((x) & 0x1) << 21) #define MIPI_CAL_HSPDOS(x) (((x) & 0x1f) << 16) #define MIPI_CAL_HSPUOS(x) (((x) & 0x1f) << 8) #define MIPI_CAL_TERMOS(x) (((x) & 0x1f) << 0) #define MIPI_CAL_BIAS_PAD_PDVCLAMP BIT(1) #define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF BIT(0) #define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16) #define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8) #define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16) #define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4) #define MIPI_CAL_BIAS_PAD_PDVREG BIT(1) struct tegra_mipi_priv { struct mipi_ctlr *mipi; struct clk *mipi_cal; }; static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf, int size) { struct tegra_mipi_priv *priv = dev_get_priv(dev); u32 value; value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) | MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0); writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1); value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7); writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x4) | MIPI_CAL_TERMOS(0x5); writel(value, &priv->mipi->mipi_cal_config_dsia); writel(value, &priv->mipi->mipi_cal_config_dsib); /* Deselect PAD C */ value = readl(&priv->mipi->mipi_cal_config_dsic); value &= ~(MIPI_CAL_SEL(0x1)); writel(value, &priv->mipi->mipi_cal_config_dsic); /* Deselect PAD D */ value = readl(&priv->mipi->mipi_cal_config_dsid); value &= ~(MIPI_CAL_SEL(0x1)); writel(value, &priv->mipi->mipi_cal_config_dsid); value = readl(&priv->mipi->mipi_cal_ctrl); value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf); value &= ~MIPI_CAL_CTRL_PRESCALE(0x3); value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa) | MIPI_CAL_CTRL_PRESCALE(0x2) | MIPI_CAL_CTRL_CLKEN_OVR; writel(value, &priv->mipi->mipi_cal_ctrl); /* clear any pending status bits */ value = readl(&priv->mipi->mipi_cal_status); writel(value, &priv->mipi->mipi_cal_status); value = readl(&priv->mipi->mipi_cal_ctrl); value |= MIPI_CAL_CTRL_START; writel(value, &priv->mipi->mipi_cal_ctrl); /* * Wait for min 72uS to let calibration logic finish calibration * sequence codes before waiting for pads idle state to apply the * results. */ udelay(80); return readl_poll_sleep_timeout(&priv->mipi->mipi_cal_status, value, !(value & MIPI_CAL_STATUS_ACTIVE) && (value & MIPI_CAL_STATUS_DONE), 100, 250000); } static int tegra_mipi_enable(struct udevice *dev, bool val) { struct tegra_mipi_priv *priv = dev_get_priv(dev); u32 value; clk_enable(priv->mipi_cal); value = readl(&priv->mipi->mipi_cal_bias_pad_cfg0); value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP; value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; writel(value, &priv->mipi->mipi_cal_bias_pad_cfg0); value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); value &= ~MIPI_CAL_BIAS_PAD_PDVREG; writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); return 0; } static const struct misc_ops tegra_mipi_ops = { .write = tegra_mipi_calibrate, .set_enabled = tegra_mipi_enable, }; static int tegra_mipi_probe(struct udevice *dev) { struct tegra_mipi_priv *priv = dev_get_priv(dev); priv->mipi = (struct mipi_ctlr *)dev_read_addr_ptr(dev); if (!priv->mipi) { log_debug("%s: no MIPI controller address\n", __func__); return -EINVAL; } priv->mipi_cal = devm_clk_get(dev, NULL); if (IS_ERR(priv->mipi_cal)) { log_debug("%s: Could not get MIPI clock: %ld\n", __func__, PTR_ERR(priv->mipi_cal)); return PTR_ERR(priv->mipi_cal); } return 0; } static const struct udevice_id tegra_mipi_ids[] = { { .compatible = "nvidia,tegra114-mipi" }, { } }; U_BOOT_DRIVER(tegra_mipi) = { .name = "tegra_mipi", .id = UCLASS_MISC, .ops = &tegra_mipi_ops, .of_match = tegra_mipi_ids, .probe = tegra_mipi_probe, .priv_auto = sizeof(struct tegra_mipi_priv), };