2020-02-19 12:29:41

by Manish Narani

[permalink] [raw]
Subject: [PATCH 2/2] sdhci: arasan: Add support for Versal Tap Delays

Add support to set tap delays for Xilinx Versal SD controller. The tap
delay registers have moved to SD controller space in Versal. Make the
changes accordingly.

Signed-off-by: Manish Narani <[email protected]>
---
drivers/mmc/host/sdhci-of-arasan.c | 176 ++++++++++++++++++++++++++++-
1 file changed, 174 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index 0146d7dd315b..d18280793e5b 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -28,15 +28,26 @@
#include "sdhci-pltfm.h"

#define SDHCI_ARASAN_VENDOR_REGISTER 0x78
+
+#define SDHCI_ARASAN_ITAPDLY_REGISTER 0xF0F8
+#define SDHCI_ARASAN_OTAPDLY_REGISTER 0xF0FC
+
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
#define VENDOR_ENHANCED_STROBE BIT(0)

#define PHY_CLK_TOO_SLOW_HZ 400000

+#define SDHCI_ITAPDLY_CHGWIN 0x200
+#define SDHCI_ITAPDLY_ENABLE 0x100
+#define SDHCI_OTAPDLY_ENABLE 0x40
+
/* Default settings for ZynqMP Clock Phases */
#define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0}
#define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0}

+#define VERSAL_ICLK_PHASE {0, 132, 132, 0, 132, 0, 0, 162, 90, 0, 0}
+#define VERSAL_OCLK_PHASE {0, 60, 48, 0, 48, 72, 90, 36, 60, 90, 0}
+
/*
* On some SoCs the syscon area has a feature where the upper 16-bits of
* each 32-bit register act as a write mask for the lower 16-bits. This allows
@@ -566,6 +577,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
.compatible = "xlnx,zynqmp-8.9a",
.data = &sdhci_arasan_zynqmp_data,
},
+ {
+ .compatible = "xlnx,versal-8.9a",
+ .data = &sdhci_arasan_zynqmp_data,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
@@ -634,7 +649,6 @@ static const struct clk_ops arasan_sampleclk_ops = {
* Return: 0 on success and error value on error
*/
static int sdhci_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
-
{
struct sdhci_arasan_clk_data *clk_data =
container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
@@ -706,7 +720,6 @@ static const struct clk_ops zynqmp_sdcardclk_ops = {
* Return: 0 on success and error value on error
*/
static int sdhci_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
-
{
struct sdhci_arasan_clk_data *clk_data =
container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
@@ -768,6 +781,151 @@ static const struct clk_ops zynqmp_sampleclk_ops = {
.set_phase = sdhci_zynqmp_sampleclk_set_phase,
};

+/**
+ * sdhci_versal_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
+ *
+ * Set the SD Output Clock Tap Delays for Output path
+ *
+ * @hw: Pointer to the hardware clock structure.
+ * @degrees The clock phase shift between 0 - 359.
+ * Return: 0 on success and error value on error
+ */
+static int sdhci_versal_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+ struct sdhci_arasan_data *sdhci_arasan =
+ container_of(clk_data, struct sdhci_arasan_data, clk_data);
+ struct sdhci_host *host = sdhci_arasan->host;
+ u8 tap_delay, tap_max = 0;
+ int ret;
+
+ /*
+ * This is applicable for SDHCI_SPEC_300 and above
+ * Versal does not set phase for <=25MHz clock.
+ * If degrees is zero, no need to do anything.
+ */
+ if (host->version < SDHCI_SPEC_300 ||
+ host->timing == MMC_TIMING_LEGACY ||
+ host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
+ return 0;
+
+ switch (host->timing) {
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_UHS_SDR25:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ /* For 50MHz clock, 30 Taps are available */
+ tap_max = 30;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ /* For 100MHz clock, 15 Taps are available */
+ tap_max = 15;
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ /* For 200MHz clock, 8 Taps are available */
+ tap_max = 8;
+ default:
+ break;
+ }
+
+ tap_delay = (degrees * tap_max) / 360;
+
+ /* Set the Clock Phase */
+ if (tap_delay) {
+ u32 regval;
+
+ regval = sdhci_readl(host, SDHCI_ARASAN_OTAPDLY_REGISTER);
+ regval |= SDHCI_OTAPDLY_ENABLE;
+ sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
+ regval |= tap_delay;
+ sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
+ }
+
+ return ret;
+}
+
+static const struct clk_ops versal_sdcardclk_ops = {
+ .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate,
+ .set_phase = sdhci_versal_sdcardclk_set_phase,
+};
+
+/**
+ * sdhci_versal_sampleclk_set_phase - Set the SD Input Clock Tap Delays
+ *
+ * Set the SD Input Clock Tap Delays for Input path
+ *
+ * @hw: Pointer to the hardware clock structure.
+ * @degrees The clock phase shift between 0 - 359.
+ * Return: 0 on success and error value on error
+ */
+static int sdhci_versal_sampleclk_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
+ struct sdhci_arasan_data *sdhci_arasan =
+ container_of(clk_data, struct sdhci_arasan_data, clk_data);
+ struct sdhci_host *host = sdhci_arasan->host;
+ u8 tap_delay, tap_max = 0;
+ int ret;
+
+ /*
+ * This is applicable for SDHCI_SPEC_300 and above
+ * Versal does not set phase for <=25MHz clock.
+ * If degrees is zero, no need to do anything.
+ */
+ if (host->version < SDHCI_SPEC_300 ||
+ host->timing == MMC_TIMING_LEGACY ||
+ host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
+ return 0;
+
+ switch (host->timing) {
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_UHS_SDR25:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ /* For 50MHz clock, 120 Taps are available */
+ tap_max = 120;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ /* For 100MHz clock, 60 Taps are available */
+ tap_max = 60;
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ /* For 200MHz clock, 30 Taps are available */
+ tap_max = 30;
+ default:
+ break;
+ }
+
+ tap_delay = (degrees * tap_max) / 360;
+
+ /* Set the Clock Phase */
+ if (tap_delay) {
+ u32 regval;
+
+ regval = sdhci_readl(host, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval |= SDHCI_ITAPDLY_CHGWIN;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval |= SDHCI_ITAPDLY_ENABLE;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval |= tap_delay;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval &= ~SDHCI_ITAPDLY_CHGWIN;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ }
+
+ return ret;
+}
+
+static const struct clk_ops versal_sampleclk_ops = {
+ .recalc_rate = sdhci_arasan_sampleclk_recalc_rate,
+ .set_phase = sdhci_versal_sampleclk_set_phase,
+};
static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u32 deviceid)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -965,6 +1123,16 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
}
}

+ if (of_device_is_compatible(dev->of_node, "xlnx,versal-8.9a")) {
+ iclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_ICLK_PHASE;
+ oclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_OCLK_PHASE;
+
+ for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
+ clk_data->clk_phase_in[i] = iclk_phase[i];
+ clk_data->clk_phase_out[i] = oclk_phase[i];
+ }
+ }
+
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
"clk-phase-legacy");
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
@@ -1025,6 +1193,8 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
sdcardclk_init.ops = &zynqmp_sdcardclk_ops;
+ else if (of_device_is_compatible(np, "xlnx,versal-8.9a"))
+ sdcardclk_init.ops = &versal_sdcardclk_ops;
else
sdcardclk_init.ops = &arasan_sdcardclk_ops;

@@ -1077,6 +1247,8 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
sampleclk_init.ops = &zynqmp_sampleclk_ops;
+ else if (of_device_is_compatible(np, "xlnx,versal-8.9a"))
+ sampleclk_init.ops = &versal_sampleclk_ops;
else
sampleclk_init.ops = &arasan_sampleclk_ops;

--
2.17.1


2020-02-24 10:31:19

by Michal Simek

[permalink] [raw]
Subject: Re: [PATCH 2/2] sdhci: arasan: Add support for Versal Tap Delays

On 19. 02. 20 13:28, Manish Narani wrote:
> Add support to set tap delays for Xilinx Versal SD controller. The tap
> delay registers have moved to SD controller space in Versal. Make the
> changes accordingly.
>
> Signed-off-by: Manish Narani <[email protected]>
> ---
> drivers/mmc/host/sdhci-of-arasan.c | 176 ++++++++++++++++++++++++++++-
> 1 file changed, 174 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
> index 0146d7dd315b..d18280793e5b 100644
> --- a/drivers/mmc/host/sdhci-of-arasan.c
> +++ b/drivers/mmc/host/sdhci-of-arasan.c
> @@ -28,15 +28,26 @@
> #include "sdhci-pltfm.h"
>
> #define SDHCI_ARASAN_VENDOR_REGISTER 0x78
> +
> +#define SDHCI_ARASAN_ITAPDLY_REGISTER 0xF0F8
> +#define SDHCI_ARASAN_OTAPDLY_REGISTER 0xF0FC
> +
> #define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
> #define VENDOR_ENHANCED_STROBE BIT(0)
>
> #define PHY_CLK_TOO_SLOW_HZ 400000
>
> +#define SDHCI_ITAPDLY_CHGWIN 0x200
> +#define SDHCI_ITAPDLY_ENABLE 0x100
> +#define SDHCI_OTAPDLY_ENABLE 0x40
> +
> /* Default settings for ZynqMP Clock Phases */
> #define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0}
> #define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0}
>
> +#define VERSAL_ICLK_PHASE {0, 132, 132, 0, 132, 0, 0, 162, 90, 0, 0}
> +#define VERSAL_OCLK_PHASE {0, 60, 48, 0, 48, 72, 90, 36, 60, 90, 0}
> +
> /*
> * On some SoCs the syscon area has a feature where the upper 16-bits of
> * each 32-bit register act as a write mask for the lower 16-bits. This allows
> @@ -566,6 +577,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
> .compatible = "xlnx,zynqmp-8.9a",
> .data = &sdhci_arasan_zynqmp_data,
> },
> + {
> + .compatible = "xlnx,versal-8.9a",
> + .data = &sdhci_arasan_zynqmp_data,
> + },
> { /* sentinel */ }
> };
> MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
> @@ -634,7 +649,6 @@ static const struct clk_ops arasan_sampleclk_ops = {
> * Return: 0 on success and error value on error
> */
> static int sdhci_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
> -

nit: unrelated.


> {
> struct sdhci_arasan_clk_data *clk_data =
> container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
> @@ -706,7 +720,6 @@ static const struct clk_ops zynqmp_sdcardclk_ops = {
> * Return: 0 on success and error value on error
> */
> static int sdhci_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
> -

nit: unrelated.

> {
> struct sdhci_arasan_clk_data *clk_data =
> container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
> @@ -768,6 +781,151 @@ static const struct clk_ops zynqmp_sampleclk_ops = {
> .set_phase = sdhci_zynqmp_sampleclk_set_phase,
> };
>
> +/**
> + * sdhci_versal_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
> + *
> + * Set the SD Output Clock Tap Delays for Output path
> + *
> + * @hw: Pointer to the hardware clock structure.
> + * @degrees The clock phase shift between 0 - 359.
> + * Return: 0 on success and error value on error
> + */


drivers/mmc/host/sdhci-of-arasan.c:785: info: Scanning doc for
sdhci_versal_sdcardclk_set_phase
drivers/mmc/host/sdhci-of-arasan.c:789: warning: contents before sections
drivers/mmc/host/sdhci-of-arasan.c:794: warning: Function parameter or
member 'degrees' not described in 'sdhci_versal_sdcardclk_set_phase'

Also I see a lot of other kernel-doc warnings.


> +static int sdhci_versal_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
> +{
> + struct sdhci_arasan_clk_data *clk_data =
> + container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
> + struct sdhci_arasan_data *sdhci_arasan =
> + container_of(clk_data, struct sdhci_arasan_data, clk_data);
> + struct sdhci_host *host = sdhci_arasan->host;
> + u8 tap_delay, tap_max = 0;
> + int ret;
> +
> + /*
> + * This is applicable for SDHCI_SPEC_300 and above
> + * Versal does not set phase for <=25MHz clock.
> + * If degrees is zero, no need to do anything.
> + */
> + if (host->version < SDHCI_SPEC_300 ||
> + host->timing == MMC_TIMING_LEGACY ||
> + host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
> + return 0;
> +
> + switch (host->timing) {
> + case MMC_TIMING_MMC_HS:
> + case MMC_TIMING_SD_HS:
> + case MMC_TIMING_UHS_SDR25:
> + case MMC_TIMING_UHS_DDR50:
> + case MMC_TIMING_MMC_DDR52:
> + /* For 50MHz clock, 30 Taps are available */
> + tap_max = 30;
> + break;
> + case MMC_TIMING_UHS_SDR50:
> + /* For 100MHz clock, 15 Taps are available */
> + tap_max = 15;
> + break;
> + case MMC_TIMING_UHS_SDR104:
> + case MMC_TIMING_MMC_HS200:
> + /* For 200MHz clock, 8 Taps are available */
> + tap_max = 8;
> + default:
> + break;
> + }
> +
> + tap_delay = (degrees * tap_max) / 360;
> +
> + /* Set the Clock Phase */
> + if (tap_delay) {
> + u32 regval;
> +
> + regval = sdhci_readl(host, SDHCI_ARASAN_OTAPDLY_REGISTER);
> + regval |= SDHCI_OTAPDLY_ENABLE;
> + sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
> + regval |= tap_delay;
> + sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
> + }
> +
> + return ret;
> +}
> +
> +static const struct clk_ops versal_sdcardclk_ops = {
> + .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate,
> + .set_phase = sdhci_versal_sdcardclk_set_phase,
> +};
> +
> +/**
> + * sdhci_versal_sampleclk_set_phase - Set the SD Input Clock Tap Delays
> + *
> + * Set the SD Input Clock Tap Delays for Input path
> + *
> + * @hw: Pointer to the hardware clock structure.
> + * @degrees The clock phase shift between 0 - 359.
> + * Return: 0 on success and error value on error
> + */

ditto.

> +static int sdhci_versal_sampleclk_set_phase(struct clk_hw *hw, int degrees)
> +{
> + struct sdhci_arasan_clk_data *clk_data =
> + container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
> + struct sdhci_arasan_data *sdhci_arasan =
> + container_of(clk_data, struct sdhci_arasan_data, clk_data);
> + struct sdhci_host *host = sdhci_arasan->host;
> + u8 tap_delay, tap_max = 0;
> + int ret;
> +
> + /*
> + * This is applicable for SDHCI_SPEC_300 and above
> + * Versal does not set phase for <=25MHz clock.
> + * If degrees is zero, no need to do anything.
> + */
> + if (host->version < SDHCI_SPEC_300 ||
> + host->timing == MMC_TIMING_LEGACY ||
> + host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
> + return 0;
> +
> + switch (host->timing) {
> + case MMC_TIMING_MMC_HS:
> + case MMC_TIMING_SD_HS:
> + case MMC_TIMING_UHS_SDR25:
> + case MMC_TIMING_UHS_DDR50:
> + case MMC_TIMING_MMC_DDR52:
> + /* For 50MHz clock, 120 Taps are available */
> + tap_max = 120;
> + break;
> + case MMC_TIMING_UHS_SDR50:
> + /* For 100MHz clock, 60 Taps are available */
> + tap_max = 60;
> + break;
> + case MMC_TIMING_UHS_SDR104:
> + case MMC_TIMING_MMC_HS200:
> + /* For 200MHz clock, 30 Taps are available */
> + tap_max = 30;
> + default:
> + break;
> + }
> +
> + tap_delay = (degrees * tap_max) / 360;
> +
> + /* Set the Clock Phase */
> + if (tap_delay) {
> + u32 regval;
> +
> + regval = sdhci_readl(host, SDHCI_ARASAN_ITAPDLY_REGISTER);
> + regval |= SDHCI_ITAPDLY_CHGWIN;
> + sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
> + regval |= SDHCI_ITAPDLY_ENABLE;
> + sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
> + regval |= tap_delay;
> + sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
> + regval &= ~SDHCI_ITAPDLY_CHGWIN;
> + sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
> + }
> +
> + return ret;
> +}
> +
> +static const struct clk_ops versal_sampleclk_ops = {
> + .recalc_rate = sdhci_arasan_sampleclk_recalc_rate,
> + .set_phase = sdhci_versal_sampleclk_set_phase,
> +};
> static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u32 deviceid)
> {
> struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> @@ -965,6 +1123,16 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
> }
> }
>
> + if (of_device_is_compatible(dev->of_node, "xlnx,versal-8.9a")) {
> + iclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_ICLK_PHASE;
> + oclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_OCLK_PHASE;
> +
> + for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
> + clk_data->clk_phase_in[i] = iclk_phase[i];
> + clk_data->clk_phase_out[i] = oclk_phase[i];
> + }
> + }
> +
> arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
> "clk-phase-legacy");
> arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
> @@ -1025,6 +1193,8 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
> sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
> if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
> sdcardclk_init.ops = &zynqmp_sdcardclk_ops;
> + else if (of_device_is_compatible(np, "xlnx,versal-8.9a"))
> + sdcardclk_init.ops = &versal_sdcardclk_ops;
> else
> sdcardclk_init.ops = &arasan_sdcardclk_ops;
>
> @@ -1077,6 +1247,8 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
> sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
> if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
> sampleclk_init.ops = &zynqmp_sampleclk_ops;
> + else if (of_device_is_compatible(np, "xlnx,versal-8.9a"))
> + sampleclk_init.ops = &versal_sampleclk_ops;
> else
> sampleclk_init.ops = &arasan_sampleclk_ops;


I would consider to start to think how to remove these
of_device_is_compatible() calls because at best it should be setup
checked only once.

Thanks,
Michal