From: Thomas Haemmerle <[email protected]> The i.MX8MP LCDIF2 controller lives inside the MEDIAMIX power domain, which has its own blk-ctrl to gate clocks and control resets. Without enabling the MEDIAMIX bus clock and de-asserting the block-level resets before the first register access, any read or write to LCDIF2 registers causes an AXI bus hang and barebox locks up on startup.
Extend the existing blk-ctrl driver (currently HSIO-only) to support the media blk-ctrl (fsl,imx8mp-media-blk-ctrl) by: - Separating the HSIO ADB handshake into the HSIO-specific power_on hook so the generic path is clean for other blk-ctrls without such handshake. - Adding null checks on bc->power_on/off to allow instances without custom callbacks. - Increasing DOMAIN_MAX_CLKS from 2 to 3 (LCDIF2 needs disp2, axi, apb). - Adding media blk-ctrl probe that enables the MEDIAMIX bus clock and de-asserts LCDIF2 clocks/resets, mirroring what the Linux kernel does in imx8mp_media_power_notifier. The media_blk_ctrl DTS node retains its syscon compatible so that syscon_node_to_regmap() continues to work for the LDB bridge sub-device. Assisted-by: Claude:claude-sonnet-4-6 Signed-of-by: Thomas Haemmerle <[email protected]> --- drivers/pmdomain/imx/imx8mp-blk-ctrl.c | 117 +++++++++++++++++++++++-- 1 file changed, 108 insertions(+), 9 deletions(-) diff --git a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c index 3d302cbde7..87e0114c22 100644 --- a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c @@ -17,6 +17,7 @@ #include <dt-bindings/power/imx8mp-power.h> +/* HSIO blk-ctrl registers */ #define GPR_REG0 0x0 #define PCIE_CLOCK_MODULE_EN BIT(0) #define USB_CLOCK_MODULE_EN BIT(1) @@ -32,6 +33,11 @@ #define PLL_CKE BIT(17) #define PLL_RST BIT(31) +/* Media blk-ctrl registers */ +#define LCDIF_ARCACHE_CTRL 0x40 +#define LCDIF_1_RD_HURRY GENMASK(6, 4) +#define LCDIF_0_RD_HURRY GENMASK(2, 0) + struct imx8mp_blk_ctrl_domain; struct imx8mp_blk_ctrl { @@ -51,7 +57,7 @@ struct imx8mp_blk_ctrl_domain_data { const char *gpc_name; }; -#define DOMAIN_MAX_CLKS 2 +#define DOMAIN_MAX_CLKS 3 struct imx8mp_blk_ctrl_domain { struct generic_pm_domain genpd; @@ -155,9 +161,14 @@ static int imx8mp_hsio_blk_ctrl_probe(struct imx8mp_blk_ctrl *bc) return of_clk_add_hw_provider(dev_of_node(bc->dev), of_clk_hw_simple_get, hw); } +static int imx8mp_hsio_propagate_adb_handshake(struct imx8mp_blk_ctrl *bc); + static void imx8mp_hsio_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain) { + /* propagate ADB handshake once before any HSIO sub-domain is enabled */ + imx8mp_hsio_propagate_adb_handshake(bc); + switch (domain->id) { case IMX8MP_HSIOBLK_PD_USB: regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); @@ -259,6 +270,66 @@ static const struct imx8mp_blk_ctrl_data imx8mp_hsio_blk_ctl_dev_data = { .num_domains = ARRAY_SIZE(imx8mp_hsio_domain_data), }; +/* Media blk-ctrl */ + +/* + * MEDIAMIX BLK_CTRL register offsets (from i.MX 8M Plus RM, section 13). + * Bit SET = reset de-asserted / clock enabled. + */ +#define BLK_SFT_RSTN 0x00 +#define BLK_CLK_EN 0x04 + +/* BIT(8): bus/APB clock and reset for the MEDIAMIX interconnect */ +#define MEDIAMIX_BUS_CLK_RST BIT(8) + +/* + * LCDIF2 (mediablk-lcdif-2) bits in BLK_SFT_RSTN and BLK_CLK_EN: + * BIT(11): lcdif2-axi, BIT(12): lcdif2-apb, BIT(24): disp2 pixel clock + * (from Linux kernel imx8m-blk-ctrl.c IMX8MP_MEDIABLK_PD_LCDIF_2) + */ +#define LCDIF2_CLK_RST_MASK (BIT(11) | BIT(12) | BIT(24)) + +static int imx8mp_media_blk_ctrl_probe(struct imx8mp_blk_ctrl *bc) +{ + /* Set panic read hurry level for LCDIF interfaces to 7 */ + regmap_update_bits(bc->regmap, LCDIF_ARCACHE_CTRL, + FIELD_PREP(LCDIF_1_RD_HURRY, 7) | + FIELD_PREP(LCDIF_0_RD_HURRY, 7), + FIELD_PREP(LCDIF_1_RD_HURRY, 7) | + FIELD_PREP(LCDIF_0_RD_HURRY, 7)); + + /* + * Enable the MEDIAMIX bus clock and de-assert its reset so the + * internal AHB/APB fabric is up before sub-module access. + * (Mirrors Linux kernel imx8mp_media_power_notifier BIT(8) writes.) + */ + regmap_set_bits(bc->regmap, BLK_CLK_EN, MEDIAMIX_BUS_CLK_RST); + regmap_set_bits(bc->regmap, BLK_SFT_RSTN, MEDIAMIX_BUS_CLK_RST); + udelay(5); /* wait for ADB handshake, as Linux kernel does */ + + /* + * Enable LCDIF2 clocks and de-assert its reset within MEDIAMIX. + * Without this, LCDIF2 registers are inaccessible (AXI bus hangs). + * (Mirrors Linux kernel imx8mp_blk_ctrl_power_on for LCDIF_2 domain.) + */ + regmap_set_bits(bc->regmap, BLK_CLK_EN, LCDIF2_CLK_RST_MASK); + regmap_set_bits(bc->regmap, BLK_SFT_RSTN, LCDIF2_CLK_RST_MASK); + + return 0; +} + +static const struct imx8mp_blk_ctrl_data imx8mp_media_blk_ctl_dev_data = { + .max_reg = 0x138, + .probe = imx8mp_media_blk_ctrl_probe, + /* + * num_domains intentionally omitted (= 0): skip GPC power domain + * management for the media blk-ctrl. MEDIAMIX is already powered + * on by the boot ROM/SPL, so no GPC sequencing is needed in the + * bootloader. With barebox deep-probe enabled, lcdif2 + * silently ignores the missing genpd provider and probes directly. + */ +}; + static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd) { struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd); @@ -273,12 +344,6 @@ static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd) return ret; } - ret = imx8mp_hsio_propagate_adb_handshake(bc); - if (ret) { - dev_err(bc->dev, "failed to propagate adb handshake\n"); - goto bus_put; - } - /* enable upstream clocks */ ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); if (ret) { @@ -287,7 +352,8 @@ static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd) } /* domain specific blk-ctrl manipulation */ - bc->power_on(bc, domain); + if (bc->power_on) + bc->power_on(bc, domain); /* power up upstream GPC domain */ ret = pm_runtime_resume_and_get_genpd(domain->power_dev); @@ -322,7 +388,8 @@ static int imx8mp_blk_ctrl_power_off(struct generic_pm_domain *genpd) } /* domain specific blk-ctrl manipulation */ - bc->power_off(bc, domain); + if (bc->power_off) + bc->power_off(bc, domain); clk_bulk_disable_unprepare(data->num_clks, domain->clks); @@ -380,6 +447,35 @@ static int imx8mp_blk_ctrl_probe(struct device *dev) if (!bc->onecell_data.domains) return -ENOMEM; + /* + * Skip GPC power domain management when num_domains == 0. + * This is used for blk-ctrl instances (e.g. media) where we only + * want the regmap/probe side-effects and not genpd provider + * registration, which could trigger GPC power-on sequences at + * unexpected times during boot. + */ + if (num_domains == 0) { + /* + * For the media blk-ctrl we skip genpd provider registration to + * avoid triggering full GPC power sequencing for every consumer. + * However we still need the MEDIAMIX domain to be powered before + * accessing any of its registers (including blk-ctrl itself). + * Power it on via the "bus" domain and keep it on. + */ + bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus"); + if (!IS_ERR_OR_NULL(bc->bus_power_dev)) { + ret = pm_runtime_resume_and_get_genpd(bc->bus_power_dev); + if (ret < 0) + dev_warn(dev, "failed to power on MEDIAMIX (ignoring): %d\n", ret); + } + if (bc_data->probe) { + ret = bc_data->probe(bc); + if (ret) + return ret; + } + return 0; + } + bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus"); if (IS_ERR(bc->bus_power_dev)) return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev), @@ -461,6 +557,9 @@ static const struct of_device_id imx8mp_blk_ctrl_of_match[] = { { .compatible = "fsl,imx8mp-hsio-blk-ctrl", .data = &imx8mp_hsio_blk_ctl_dev_data, + }, { + .compatible = "fsl,imx8mp-media-blk-ctrl", + .data = &imx8mp_media_blk_ctl_dev_data, }, { /* Sentinel */ } -- 2.43.0
