1e2ad626fSUlf Hansson // SPDX-License-Identifier: GPL-2.0+ 2e2ad626fSUlf Hansson 3e2ad626fSUlf Hansson /* 4e2ad626fSUlf Hansson * Copyright 2022 Pengutronix, Lucas Stach <kernel@pengutronix.de> 5e2ad626fSUlf Hansson */ 6e2ad626fSUlf Hansson 7e2ad626fSUlf Hansson #include <linux/bitfield.h> 8e2ad626fSUlf Hansson #include <linux/clk.h> 9e2ad626fSUlf Hansson #include <linux/clk-provider.h> 10e2ad626fSUlf Hansson #include <linux/device.h> 11e2ad626fSUlf Hansson #include <linux/interconnect.h> 12e2ad626fSUlf Hansson #include <linux/module.h> 13e2ad626fSUlf Hansson #include <linux/of.h> 14e2ad626fSUlf Hansson #include <linux/platform_device.h> 15e2ad626fSUlf Hansson #include <linux/pm_domain.h> 16e2ad626fSUlf Hansson #include <linux/pm_runtime.h> 17e2ad626fSUlf Hansson #include <linux/regmap.h> 18e2ad626fSUlf Hansson 19e2ad626fSUlf Hansson #include <dt-bindings/power/imx8mp-power.h> 20e2ad626fSUlf Hansson 21e2ad626fSUlf Hansson #define GPR_REG0 0x0 22e2ad626fSUlf Hansson #define PCIE_CLOCK_MODULE_EN BIT(0) 23e2ad626fSUlf Hansson #define USB_CLOCK_MODULE_EN BIT(1) 24e2ad626fSUlf Hansson #define PCIE_PHY_APB_RST BIT(4) 25e2ad626fSUlf Hansson #define PCIE_PHY_INIT_RST BIT(5) 26e2ad626fSUlf Hansson #define GPR_REG1 0x4 27e2ad626fSUlf Hansson #define PLL_LOCK BIT(13) 28e2ad626fSUlf Hansson #define GPR_REG2 0x8 29e2ad626fSUlf Hansson #define P_PLL_MASK GENMASK(5, 0) 30e2ad626fSUlf Hansson #define M_PLL_MASK GENMASK(15, 6) 31e2ad626fSUlf Hansson #define S_PLL_MASK GENMASK(18, 16) 32e2ad626fSUlf Hansson #define GPR_REG3 0xc 33e2ad626fSUlf Hansson #define PLL_CKE BIT(17) 34e2ad626fSUlf Hansson #define PLL_RST BIT(31) 35e2ad626fSUlf Hansson 36e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain; 37e2ad626fSUlf Hansson 38e2ad626fSUlf Hansson struct imx8mp_blk_ctrl { 39e2ad626fSUlf Hansson struct device *dev; 40e2ad626fSUlf Hansson struct notifier_block power_nb; 41e2ad626fSUlf Hansson struct device *bus_power_dev; 42e2ad626fSUlf Hansson struct regmap *regmap; 43e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain *domains; 44e2ad626fSUlf Hansson struct genpd_onecell_data onecell_data; 45e2ad626fSUlf Hansson void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); 46e2ad626fSUlf Hansson void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); 47e2ad626fSUlf Hansson }; 48e2ad626fSUlf Hansson 49e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain_data { 50e2ad626fSUlf Hansson const char *name; 51e2ad626fSUlf Hansson const char * const *clk_names; 52e2ad626fSUlf Hansson int num_clks; 53e2ad626fSUlf Hansson const char * const *path_names; 54e2ad626fSUlf Hansson int num_paths; 55e2ad626fSUlf Hansson const char *gpc_name; 56e2ad626fSUlf Hansson }; 57e2ad626fSUlf Hansson 58*9d3f959bSAdam Ford #define DOMAIN_MAX_CLKS 3 59e2ad626fSUlf Hansson #define DOMAIN_MAX_PATHS 3 60e2ad626fSUlf Hansson 61e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain { 62e2ad626fSUlf Hansson struct generic_pm_domain genpd; 63e2ad626fSUlf Hansson const struct imx8mp_blk_ctrl_domain_data *data; 64e2ad626fSUlf Hansson struct clk_bulk_data clks[DOMAIN_MAX_CLKS]; 65e2ad626fSUlf Hansson struct icc_bulk_data paths[DOMAIN_MAX_PATHS]; 66e2ad626fSUlf Hansson struct device *power_dev; 67e2ad626fSUlf Hansson struct imx8mp_blk_ctrl *bc; 68e2ad626fSUlf Hansson int num_paths; 69e2ad626fSUlf Hansson int id; 70e2ad626fSUlf Hansson }; 71e2ad626fSUlf Hansson 72e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_data { 73e2ad626fSUlf Hansson int max_reg; 74e2ad626fSUlf Hansson int (*probe) (struct imx8mp_blk_ctrl *bc); 75e2ad626fSUlf Hansson notifier_fn_t power_notifier_fn; 76e2ad626fSUlf Hansson void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); 77e2ad626fSUlf Hansson void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); 78e2ad626fSUlf Hansson const struct imx8mp_blk_ctrl_domain_data *domains; 79e2ad626fSUlf Hansson int num_domains; 80e2ad626fSUlf Hansson }; 81e2ad626fSUlf Hansson 82e2ad626fSUlf Hansson static inline struct imx8mp_blk_ctrl_domain * 83e2ad626fSUlf Hansson to_imx8mp_blk_ctrl_domain(struct generic_pm_domain *genpd) 84e2ad626fSUlf Hansson { 85e2ad626fSUlf Hansson return container_of(genpd, struct imx8mp_blk_ctrl_domain, genpd); 86e2ad626fSUlf Hansson } 87e2ad626fSUlf Hansson 88e2ad626fSUlf Hansson struct clk_hsio_pll { 89e2ad626fSUlf Hansson struct clk_hw hw; 90e2ad626fSUlf Hansson struct regmap *regmap; 91e2ad626fSUlf Hansson }; 92e2ad626fSUlf Hansson 93e2ad626fSUlf Hansson static inline struct clk_hsio_pll *to_clk_hsio_pll(struct clk_hw *hw) 94e2ad626fSUlf Hansson { 95e2ad626fSUlf Hansson return container_of(hw, struct clk_hsio_pll, hw); 96e2ad626fSUlf Hansson } 97e2ad626fSUlf Hansson 98e2ad626fSUlf Hansson static int clk_hsio_pll_prepare(struct clk_hw *hw) 99e2ad626fSUlf Hansson { 100e2ad626fSUlf Hansson struct clk_hsio_pll *clk = to_clk_hsio_pll(hw); 101e2ad626fSUlf Hansson u32 val; 102e2ad626fSUlf Hansson 103e2ad626fSUlf Hansson /* set the PLL configuration */ 104e2ad626fSUlf Hansson regmap_update_bits(clk->regmap, GPR_REG2, 105e2ad626fSUlf Hansson P_PLL_MASK | M_PLL_MASK | S_PLL_MASK, 106e2ad626fSUlf Hansson FIELD_PREP(P_PLL_MASK, 12) | 107e2ad626fSUlf Hansson FIELD_PREP(M_PLL_MASK, 800) | 108e2ad626fSUlf Hansson FIELD_PREP(S_PLL_MASK, 4)); 109e2ad626fSUlf Hansson 110e2ad626fSUlf Hansson /* de-assert PLL reset */ 111e2ad626fSUlf Hansson regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST, PLL_RST); 112e2ad626fSUlf Hansson 113e2ad626fSUlf Hansson /* enable PLL */ 114e2ad626fSUlf Hansson regmap_update_bits(clk->regmap, GPR_REG3, PLL_CKE, PLL_CKE); 115e2ad626fSUlf Hansson 116e2ad626fSUlf Hansson return regmap_read_poll_timeout(clk->regmap, GPR_REG1, val, 117e2ad626fSUlf Hansson val & PLL_LOCK, 10, 100); 118e2ad626fSUlf Hansson } 119e2ad626fSUlf Hansson 120e2ad626fSUlf Hansson static void clk_hsio_pll_unprepare(struct clk_hw *hw) 121e2ad626fSUlf Hansson { 122e2ad626fSUlf Hansson struct clk_hsio_pll *clk = to_clk_hsio_pll(hw); 123e2ad626fSUlf Hansson 124e2ad626fSUlf Hansson regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST | PLL_CKE, 0); 125e2ad626fSUlf Hansson } 126e2ad626fSUlf Hansson 127e2ad626fSUlf Hansson static int clk_hsio_pll_is_prepared(struct clk_hw *hw) 128e2ad626fSUlf Hansson { 129e2ad626fSUlf Hansson struct clk_hsio_pll *clk = to_clk_hsio_pll(hw); 130e2ad626fSUlf Hansson 131e2ad626fSUlf Hansson return regmap_test_bits(clk->regmap, GPR_REG1, PLL_LOCK); 132e2ad626fSUlf Hansson } 133e2ad626fSUlf Hansson 134e2ad626fSUlf Hansson static unsigned long clk_hsio_pll_recalc_rate(struct clk_hw *hw, 135e2ad626fSUlf Hansson unsigned long parent_rate) 136e2ad626fSUlf Hansson { 137e2ad626fSUlf Hansson return 100000000; 138e2ad626fSUlf Hansson } 139e2ad626fSUlf Hansson 140e2ad626fSUlf Hansson static const struct clk_ops clk_hsio_pll_ops = { 141e2ad626fSUlf Hansson .prepare = clk_hsio_pll_prepare, 142e2ad626fSUlf Hansson .unprepare = clk_hsio_pll_unprepare, 143e2ad626fSUlf Hansson .is_prepared = clk_hsio_pll_is_prepared, 144e2ad626fSUlf Hansson .recalc_rate = clk_hsio_pll_recalc_rate, 145e2ad626fSUlf Hansson }; 146e2ad626fSUlf Hansson 147e2ad626fSUlf Hansson static int imx8mp_hsio_blk_ctrl_probe(struct imx8mp_blk_ctrl *bc) 148e2ad626fSUlf Hansson { 149e2ad626fSUlf Hansson struct clk_hsio_pll *clk_hsio_pll; 150e2ad626fSUlf Hansson struct clk_hw *hw; 151e2ad626fSUlf Hansson struct clk_init_data init = {}; 152e2ad626fSUlf Hansson int ret; 153e2ad626fSUlf Hansson 154e2ad626fSUlf Hansson clk_hsio_pll = devm_kzalloc(bc->dev, sizeof(*clk_hsio_pll), GFP_KERNEL); 155e2ad626fSUlf Hansson if (!clk_hsio_pll) 156e2ad626fSUlf Hansson return -ENOMEM; 157e2ad626fSUlf Hansson 158e2ad626fSUlf Hansson init.name = "hsio_pll"; 159e2ad626fSUlf Hansson init.ops = &clk_hsio_pll_ops; 160e2ad626fSUlf Hansson init.parent_names = (const char *[]){"osc_24m"}; 161e2ad626fSUlf Hansson init.num_parents = 1; 162e2ad626fSUlf Hansson 163e2ad626fSUlf Hansson clk_hsio_pll->regmap = bc->regmap; 164e2ad626fSUlf Hansson clk_hsio_pll->hw.init = &init; 165e2ad626fSUlf Hansson 166e2ad626fSUlf Hansson hw = &clk_hsio_pll->hw; 167e2ad626fSUlf Hansson ret = devm_clk_hw_register(bc->bus_power_dev, hw); 168e2ad626fSUlf Hansson if (ret) 169e2ad626fSUlf Hansson return ret; 170e2ad626fSUlf Hansson 171e2ad626fSUlf Hansson return devm_of_clk_add_hw_provider(bc->dev, of_clk_hw_simple_get, hw); 172e2ad626fSUlf Hansson } 173e2ad626fSUlf Hansson 174e2ad626fSUlf Hansson static void imx8mp_hsio_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc, 175e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain *domain) 176e2ad626fSUlf Hansson { 177e2ad626fSUlf Hansson switch (domain->id) { 178e2ad626fSUlf Hansson case IMX8MP_HSIOBLK_PD_USB: 179e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); 180e2ad626fSUlf Hansson break; 181e2ad626fSUlf Hansson case IMX8MP_HSIOBLK_PD_PCIE: 182e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN); 183e2ad626fSUlf Hansson break; 184e2ad626fSUlf Hansson case IMX8MP_HSIOBLK_PD_PCIE_PHY: 185e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, GPR_REG0, 186e2ad626fSUlf Hansson PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST); 187e2ad626fSUlf Hansson break; 188e2ad626fSUlf Hansson default: 189e2ad626fSUlf Hansson break; 190e2ad626fSUlf Hansson } 191e2ad626fSUlf Hansson } 192e2ad626fSUlf Hansson 193e2ad626fSUlf Hansson static void imx8mp_hsio_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc, 194e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain *domain) 195e2ad626fSUlf Hansson { 196e2ad626fSUlf Hansson switch (domain->id) { 197e2ad626fSUlf Hansson case IMX8MP_HSIOBLK_PD_USB: 198e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); 199e2ad626fSUlf Hansson break; 200e2ad626fSUlf Hansson case IMX8MP_HSIOBLK_PD_PCIE: 201e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN); 202e2ad626fSUlf Hansson break; 203e2ad626fSUlf Hansson case IMX8MP_HSIOBLK_PD_PCIE_PHY: 204e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, GPR_REG0, 205e2ad626fSUlf Hansson PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST); 206e2ad626fSUlf Hansson break; 207e2ad626fSUlf Hansson default: 208e2ad626fSUlf Hansson break; 209e2ad626fSUlf Hansson } 210e2ad626fSUlf Hansson } 211e2ad626fSUlf Hansson 212e2ad626fSUlf Hansson static int imx8mp_hsio_power_notifier(struct notifier_block *nb, 213e2ad626fSUlf Hansson unsigned long action, void *data) 214e2ad626fSUlf Hansson { 215e2ad626fSUlf Hansson struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl, 216e2ad626fSUlf Hansson power_nb); 217e2ad626fSUlf Hansson struct clk_bulk_data *usb_clk = bc->domains[IMX8MP_HSIOBLK_PD_USB].clks; 218e2ad626fSUlf Hansson int num_clks = bc->domains[IMX8MP_HSIOBLK_PD_USB].data->num_clks; 219e2ad626fSUlf Hansson int ret; 220e2ad626fSUlf Hansson 221e2ad626fSUlf Hansson switch (action) { 222e2ad626fSUlf Hansson case GENPD_NOTIFY_ON: 223e2ad626fSUlf Hansson /* 224e2ad626fSUlf Hansson * enable USB clock for a moment for the power-on ADB handshake 225e2ad626fSUlf Hansson * to proceed 226e2ad626fSUlf Hansson */ 227e2ad626fSUlf Hansson ret = clk_bulk_prepare_enable(num_clks, usb_clk); 228e2ad626fSUlf Hansson if (ret) 229e2ad626fSUlf Hansson return NOTIFY_BAD; 230e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); 231e2ad626fSUlf Hansson 232e2ad626fSUlf Hansson udelay(5); 233e2ad626fSUlf Hansson 234e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); 235e2ad626fSUlf Hansson clk_bulk_disable_unprepare(num_clks, usb_clk); 236e2ad626fSUlf Hansson break; 237e2ad626fSUlf Hansson case GENPD_NOTIFY_PRE_OFF: 238e2ad626fSUlf Hansson /* enable USB clock for the power-down ADB handshake to work */ 239e2ad626fSUlf Hansson ret = clk_bulk_prepare_enable(num_clks, usb_clk); 240e2ad626fSUlf Hansson if (ret) 241e2ad626fSUlf Hansson return NOTIFY_BAD; 242e2ad626fSUlf Hansson 243e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); 244e2ad626fSUlf Hansson break; 245e2ad626fSUlf Hansson case GENPD_NOTIFY_OFF: 246e2ad626fSUlf Hansson clk_bulk_disable_unprepare(num_clks, usb_clk); 247e2ad626fSUlf Hansson break; 248e2ad626fSUlf Hansson default: 249e2ad626fSUlf Hansson break; 250e2ad626fSUlf Hansson } 251e2ad626fSUlf Hansson 252e2ad626fSUlf Hansson return NOTIFY_OK; 253e2ad626fSUlf Hansson } 254e2ad626fSUlf Hansson 255e2ad626fSUlf Hansson static const struct imx8mp_blk_ctrl_domain_data imx8mp_hsio_domain_data[] = { 256e2ad626fSUlf Hansson [IMX8MP_HSIOBLK_PD_USB] = { 257e2ad626fSUlf Hansson .name = "hsioblk-usb", 258e2ad626fSUlf Hansson .clk_names = (const char *[]){ "usb" }, 259e2ad626fSUlf Hansson .num_clks = 1, 260e2ad626fSUlf Hansson .gpc_name = "usb", 261e2ad626fSUlf Hansson .path_names = (const char *[]){"usb1", "usb2"}, 262e2ad626fSUlf Hansson .num_paths = 2, 263e2ad626fSUlf Hansson }, 264e2ad626fSUlf Hansson [IMX8MP_HSIOBLK_PD_USB_PHY1] = { 265e2ad626fSUlf Hansson .name = "hsioblk-usb-phy1", 266e2ad626fSUlf Hansson .gpc_name = "usb-phy1", 267e2ad626fSUlf Hansson }, 268e2ad626fSUlf Hansson [IMX8MP_HSIOBLK_PD_USB_PHY2] = { 269e2ad626fSUlf Hansson .name = "hsioblk-usb-phy2", 270e2ad626fSUlf Hansson .gpc_name = "usb-phy2", 271e2ad626fSUlf Hansson }, 272e2ad626fSUlf Hansson [IMX8MP_HSIOBLK_PD_PCIE] = { 273e2ad626fSUlf Hansson .name = "hsioblk-pcie", 274e2ad626fSUlf Hansson .clk_names = (const char *[]){ "pcie" }, 275e2ad626fSUlf Hansson .num_clks = 1, 276e2ad626fSUlf Hansson .gpc_name = "pcie", 277e2ad626fSUlf Hansson .path_names = (const char *[]){"noc-pcie", "pcie"}, 278e2ad626fSUlf Hansson .num_paths = 2, 279e2ad626fSUlf Hansson }, 280e2ad626fSUlf Hansson [IMX8MP_HSIOBLK_PD_PCIE_PHY] = { 281e2ad626fSUlf Hansson .name = "hsioblk-pcie-phy", 282e2ad626fSUlf Hansson .gpc_name = "pcie-phy", 283e2ad626fSUlf Hansson }, 284e2ad626fSUlf Hansson }; 285e2ad626fSUlf Hansson 286e2ad626fSUlf Hansson static const struct imx8mp_blk_ctrl_data imx8mp_hsio_blk_ctl_dev_data = { 287e2ad626fSUlf Hansson .max_reg = 0x24, 288e2ad626fSUlf Hansson .probe = imx8mp_hsio_blk_ctrl_probe, 289e2ad626fSUlf Hansson .power_on = imx8mp_hsio_blk_ctrl_power_on, 290e2ad626fSUlf Hansson .power_off = imx8mp_hsio_blk_ctrl_power_off, 291e2ad626fSUlf Hansson .power_notifier_fn = imx8mp_hsio_power_notifier, 292e2ad626fSUlf Hansson .domains = imx8mp_hsio_domain_data, 293e2ad626fSUlf Hansson .num_domains = ARRAY_SIZE(imx8mp_hsio_domain_data), 294e2ad626fSUlf Hansson }; 295e2ad626fSUlf Hansson 296e2ad626fSUlf Hansson #define HDMI_RTX_RESET_CTL0 0x20 297e2ad626fSUlf Hansson #define HDMI_RTX_CLK_CTL0 0x40 298e2ad626fSUlf Hansson #define HDMI_RTX_CLK_CTL1 0x50 299e2ad626fSUlf Hansson #define HDMI_RTX_CLK_CTL2 0x60 300e2ad626fSUlf Hansson #define HDMI_RTX_CLK_CTL3 0x70 301e2ad626fSUlf Hansson #define HDMI_RTX_CLK_CTL4 0x80 302e2ad626fSUlf Hansson #define HDMI_TX_CONTROL0 0x200 303e2ad626fSUlf Hansson #define HDMI_LCDIF_NOC_HURRY_MASK GENMASK(14, 12) 304e2ad626fSUlf Hansson 305e2ad626fSUlf Hansson static void imx8mp_hdmi_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc, 306e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain *domain) 307e2ad626fSUlf Hansson { 308e2ad626fSUlf Hansson switch (domain->id) { 309e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_IRQSTEER: 310e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9)); 311e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16)); 312e2ad626fSUlf Hansson break; 313e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_LCDIF: 314e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, 315e2ad626fSUlf Hansson BIT(16) | BIT(17) | BIT(18) | 316e2ad626fSUlf Hansson BIT(19) | BIT(20)); 317e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11)); 318e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, 319e2ad626fSUlf Hansson BIT(4) | BIT(5) | BIT(6)); 320e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, 321e2ad626fSUlf Hansson FIELD_PREP(HDMI_LCDIF_NOC_HURRY_MASK, 7)); 322e2ad626fSUlf Hansson break; 323e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_PAI: 324e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17)); 325e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18)); 326e2ad626fSUlf Hansson break; 327e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_PVI: 328e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28)); 329e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22)); 330e2ad626fSUlf Hansson break; 331e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_TRNG: 332e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30)); 333e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20)); 334e2ad626fSUlf Hansson break; 335e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_HDMI_TX: 336e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, 337e2ad626fSUlf Hansson BIT(2) | BIT(4) | BIT(5)); 338e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, 339e2ad626fSUlf Hansson BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) | 340e2ad626fSUlf Hansson BIT(18) | BIT(19) | BIT(20) | BIT(21)); 341e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, 342e2ad626fSUlf Hansson BIT(7) | BIT(10) | BIT(11)); 343e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1)); 344e2ad626fSUlf Hansson break; 345e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY: 346e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7)); 347e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24)); 348e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12)); 349e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3)); 350e2ad626fSUlf Hansson break; 351e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_HDCP: 352e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11)); 353e2ad626fSUlf Hansson break; 354e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_HRV: 355e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5)); 356e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15)); 357e2ad626fSUlf Hansson break; 358e2ad626fSUlf Hansson default: 359e2ad626fSUlf Hansson break; 360e2ad626fSUlf Hansson } 361e2ad626fSUlf Hansson } 362e2ad626fSUlf Hansson 363e2ad626fSUlf Hansson static void imx8mp_hdmi_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc, 364e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain *domain) 365e2ad626fSUlf Hansson { 366e2ad626fSUlf Hansson switch (domain->id) { 367e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_IRQSTEER: 368e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9)); 369e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16)); 370e2ad626fSUlf Hansson break; 371e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_LCDIF: 372e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, 373e2ad626fSUlf Hansson BIT(4) | BIT(5) | BIT(6)); 374e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11)); 375e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, 376e2ad626fSUlf Hansson BIT(16) | BIT(17) | BIT(18) | 377e2ad626fSUlf Hansson BIT(19) | BIT(20)); 378e2ad626fSUlf Hansson break; 379e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_PAI: 380e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18)); 381e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17)); 382e2ad626fSUlf Hansson break; 383e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_PVI: 384e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22)); 385e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28)); 386e2ad626fSUlf Hansson break; 387e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_TRNG: 388e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20)); 389e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30)); 390e2ad626fSUlf Hansson break; 391e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_HDMI_TX: 392e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1)); 393e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, 394e2ad626fSUlf Hansson BIT(7) | BIT(10) | BIT(11)); 395e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, 396e2ad626fSUlf Hansson BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) | 397e2ad626fSUlf Hansson BIT(18) | BIT(19) | BIT(20) | BIT(21)); 398e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, 399e2ad626fSUlf Hansson BIT(2) | BIT(4) | BIT(5)); 400e2ad626fSUlf Hansson break; 401e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY: 402e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3)); 403e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12)); 404e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7)); 405e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24)); 406e2ad626fSUlf Hansson break; 407e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_HDCP: 408e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11)); 409e2ad626fSUlf Hansson break; 410e2ad626fSUlf Hansson case IMX8MP_HDMIBLK_PD_HRV: 411e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15)); 412e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5)); 413e2ad626fSUlf Hansson break; 414e2ad626fSUlf Hansson default: 415e2ad626fSUlf Hansson break; 416e2ad626fSUlf Hansson } 417e2ad626fSUlf Hansson } 418e2ad626fSUlf Hansson 419e2ad626fSUlf Hansson static int imx8mp_hdmi_power_notifier(struct notifier_block *nb, 420e2ad626fSUlf Hansson unsigned long action, void *data) 421e2ad626fSUlf Hansson { 422e2ad626fSUlf Hansson struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl, 423e2ad626fSUlf Hansson power_nb); 424e2ad626fSUlf Hansson 425e2ad626fSUlf Hansson if (action != GENPD_NOTIFY_ON) 426e2ad626fSUlf Hansson return NOTIFY_OK; 427e2ad626fSUlf Hansson 428e2ad626fSUlf Hansson /* 429e2ad626fSUlf Hansson * Contrary to other blk-ctrls the reset and clock don't clear when the 430e2ad626fSUlf Hansson * power domain is powered down. To ensure the proper reset pulsing, 431e2ad626fSUlf Hansson * first clear them all to asserted state, then enable the bus clocks 432e2ad626fSUlf Hansson * and then release the ADB reset. 433e2ad626fSUlf Hansson */ 434e2ad626fSUlf Hansson regmap_write(bc->regmap, HDMI_RTX_RESET_CTL0, 0x0); 435e2ad626fSUlf Hansson regmap_write(bc->regmap, HDMI_RTX_CLK_CTL0, 0x0); 436e2ad626fSUlf Hansson regmap_write(bc->regmap, HDMI_RTX_CLK_CTL1, 0x0); 437e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, 438e2ad626fSUlf Hansson BIT(0) | BIT(1) | BIT(10)); 439e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(0)); 440e2ad626fSUlf Hansson 441e2ad626fSUlf Hansson /* 442e2ad626fSUlf Hansson * On power up we have no software backchannel to the GPC to 443e2ad626fSUlf Hansson * wait for the ADB handshake to happen, so we just delay for a 444e2ad626fSUlf Hansson * bit. On power down the GPC driver waits for the handshake. 445e2ad626fSUlf Hansson */ 446e2ad626fSUlf Hansson udelay(5); 447e2ad626fSUlf Hansson 448e2ad626fSUlf Hansson return NOTIFY_OK; 449e2ad626fSUlf Hansson } 450e2ad626fSUlf Hansson 451e2ad626fSUlf Hansson static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = { 452e2ad626fSUlf Hansson [IMX8MP_HDMIBLK_PD_IRQSTEER] = { 453e2ad626fSUlf Hansson .name = "hdmiblk-irqsteer", 454e2ad626fSUlf Hansson .clk_names = (const char *[]){ "apb" }, 455e2ad626fSUlf Hansson .num_clks = 1, 456e2ad626fSUlf Hansson .gpc_name = "irqsteer", 457e2ad626fSUlf Hansson }, 458e2ad626fSUlf Hansson [IMX8MP_HDMIBLK_PD_LCDIF] = { 459e2ad626fSUlf Hansson .name = "hdmiblk-lcdif", 460*9d3f959bSAdam Ford .clk_names = (const char *[]){ "axi", "apb", "fdcc" }, 461*9d3f959bSAdam Ford .num_clks = 3, 462e2ad626fSUlf Hansson .gpc_name = "lcdif", 463e2ad626fSUlf Hansson .path_names = (const char *[]){"lcdif-hdmi"}, 464e2ad626fSUlf Hansson .num_paths = 1, 465e2ad626fSUlf Hansson }, 466e2ad626fSUlf Hansson [IMX8MP_HDMIBLK_PD_PAI] = { 467e2ad626fSUlf Hansson .name = "hdmiblk-pai", 468e2ad626fSUlf Hansson .clk_names = (const char *[]){ "apb" }, 469e2ad626fSUlf Hansson .num_clks = 1, 470e2ad626fSUlf Hansson .gpc_name = "pai", 471e2ad626fSUlf Hansson }, 472e2ad626fSUlf Hansson [IMX8MP_HDMIBLK_PD_PVI] = { 473e2ad626fSUlf Hansson .name = "hdmiblk-pvi", 474e2ad626fSUlf Hansson .clk_names = (const char *[]){ "apb" }, 475e2ad626fSUlf Hansson .num_clks = 1, 476e2ad626fSUlf Hansson .gpc_name = "pvi", 477e2ad626fSUlf Hansson }, 478e2ad626fSUlf Hansson [IMX8MP_HDMIBLK_PD_TRNG] = { 479e2ad626fSUlf Hansson .name = "hdmiblk-trng", 480e2ad626fSUlf Hansson .clk_names = (const char *[]){ "apb" }, 481e2ad626fSUlf Hansson .num_clks = 1, 482e2ad626fSUlf Hansson .gpc_name = "trng", 483e2ad626fSUlf Hansson }, 484e2ad626fSUlf Hansson [IMX8MP_HDMIBLK_PD_HDMI_TX] = { 485e2ad626fSUlf Hansson .name = "hdmiblk-hdmi-tx", 486*9d3f959bSAdam Ford .clk_names = (const char *[]){ "apb", "ref_266m", "fdcc" }, 487*9d3f959bSAdam Ford .num_clks = 3, 488e2ad626fSUlf Hansson .gpc_name = "hdmi-tx", 489e2ad626fSUlf Hansson }, 490e2ad626fSUlf Hansson [IMX8MP_HDMIBLK_PD_HDMI_TX_PHY] = { 491e2ad626fSUlf Hansson .name = "hdmiblk-hdmi-tx-phy", 492e2ad626fSUlf Hansson .clk_names = (const char *[]){ "apb", "ref_24m" }, 493e2ad626fSUlf Hansson .num_clks = 2, 494e2ad626fSUlf Hansson .gpc_name = "hdmi-tx-phy", 495e2ad626fSUlf Hansson }, 496e2ad626fSUlf Hansson [IMX8MP_HDMIBLK_PD_HRV] = { 497e2ad626fSUlf Hansson .name = "hdmiblk-hrv", 498e2ad626fSUlf Hansson .clk_names = (const char *[]){ "axi", "apb" }, 499e2ad626fSUlf Hansson .num_clks = 2, 500e2ad626fSUlf Hansson .gpc_name = "hrv", 501e2ad626fSUlf Hansson .path_names = (const char *[]){"hrv"}, 502e2ad626fSUlf Hansson .num_paths = 1, 503e2ad626fSUlf Hansson }, 504e2ad626fSUlf Hansson [IMX8MP_HDMIBLK_PD_HDCP] = { 505e2ad626fSUlf Hansson .name = "hdmiblk-hdcp", 506e2ad626fSUlf Hansson .clk_names = (const char *[]){ "axi", "apb" }, 507e2ad626fSUlf Hansson .num_clks = 2, 508e2ad626fSUlf Hansson .gpc_name = "hdcp", 509e2ad626fSUlf Hansson .path_names = (const char *[]){"hdcp"}, 510e2ad626fSUlf Hansson .num_paths = 1, 511e2ad626fSUlf Hansson }, 512e2ad626fSUlf Hansson }; 513e2ad626fSUlf Hansson 514e2ad626fSUlf Hansson static const struct imx8mp_blk_ctrl_data imx8mp_hdmi_blk_ctl_dev_data = { 515e2ad626fSUlf Hansson .max_reg = 0x23c, 516e2ad626fSUlf Hansson .power_on = imx8mp_hdmi_blk_ctrl_power_on, 517e2ad626fSUlf Hansson .power_off = imx8mp_hdmi_blk_ctrl_power_off, 518e2ad626fSUlf Hansson .power_notifier_fn = imx8mp_hdmi_power_notifier, 519e2ad626fSUlf Hansson .domains = imx8mp_hdmi_domain_data, 520e2ad626fSUlf Hansson .num_domains = ARRAY_SIZE(imx8mp_hdmi_domain_data), 521e2ad626fSUlf Hansson }; 522e2ad626fSUlf Hansson 523e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd) 524e2ad626fSUlf Hansson { 525e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd); 526e2ad626fSUlf Hansson const struct imx8mp_blk_ctrl_domain_data *data = domain->data; 527e2ad626fSUlf Hansson struct imx8mp_blk_ctrl *bc = domain->bc; 528e2ad626fSUlf Hansson int ret; 529e2ad626fSUlf Hansson 530e2ad626fSUlf Hansson /* make sure bus domain is awake */ 531e2ad626fSUlf Hansson ret = pm_runtime_resume_and_get(bc->bus_power_dev); 532e2ad626fSUlf Hansson if (ret < 0) { 533e2ad626fSUlf Hansson dev_err(bc->dev, "failed to power up bus domain\n"); 534e2ad626fSUlf Hansson return ret; 535e2ad626fSUlf Hansson } 536e2ad626fSUlf Hansson 537e2ad626fSUlf Hansson /* enable upstream clocks */ 538e2ad626fSUlf Hansson ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); 539e2ad626fSUlf Hansson if (ret) { 540e2ad626fSUlf Hansson dev_err(bc->dev, "failed to enable clocks\n"); 541e2ad626fSUlf Hansson goto bus_put; 542e2ad626fSUlf Hansson } 543e2ad626fSUlf Hansson 544e2ad626fSUlf Hansson /* domain specific blk-ctrl manipulation */ 545e2ad626fSUlf Hansson bc->power_on(bc, domain); 546e2ad626fSUlf Hansson 547e2ad626fSUlf Hansson /* power up upstream GPC domain */ 548e2ad626fSUlf Hansson ret = pm_runtime_resume_and_get(domain->power_dev); 549e2ad626fSUlf Hansson if (ret < 0) { 550e2ad626fSUlf Hansson dev_err(bc->dev, "failed to power up peripheral domain\n"); 551e2ad626fSUlf Hansson goto clk_disable; 552e2ad626fSUlf Hansson } 553e2ad626fSUlf Hansson 554e2ad626fSUlf Hansson ret = icc_bulk_set_bw(domain->num_paths, domain->paths); 555e2ad626fSUlf Hansson if (ret) 556e2ad626fSUlf Hansson dev_err(bc->dev, "failed to set icc bw\n"); 557e2ad626fSUlf Hansson 558e2ad626fSUlf Hansson clk_bulk_disable_unprepare(data->num_clks, domain->clks); 559e2ad626fSUlf Hansson 560e2ad626fSUlf Hansson return 0; 561e2ad626fSUlf Hansson 562e2ad626fSUlf Hansson clk_disable: 563e2ad626fSUlf Hansson clk_bulk_disable_unprepare(data->num_clks, domain->clks); 564e2ad626fSUlf Hansson bus_put: 565e2ad626fSUlf Hansson pm_runtime_put(bc->bus_power_dev); 566e2ad626fSUlf Hansson 567e2ad626fSUlf Hansson return ret; 568e2ad626fSUlf Hansson } 569e2ad626fSUlf Hansson 570e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_power_off(struct generic_pm_domain *genpd) 571e2ad626fSUlf Hansson { 572e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd); 573e2ad626fSUlf Hansson const struct imx8mp_blk_ctrl_domain_data *data = domain->data; 574e2ad626fSUlf Hansson struct imx8mp_blk_ctrl *bc = domain->bc; 575e2ad626fSUlf Hansson int ret; 576e2ad626fSUlf Hansson 577e2ad626fSUlf Hansson ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); 578e2ad626fSUlf Hansson if (ret) { 579e2ad626fSUlf Hansson dev_err(bc->dev, "failed to enable clocks\n"); 580e2ad626fSUlf Hansson return ret; 581e2ad626fSUlf Hansson } 582e2ad626fSUlf Hansson 583e2ad626fSUlf Hansson /* domain specific blk-ctrl manipulation */ 584e2ad626fSUlf Hansson bc->power_off(bc, domain); 585e2ad626fSUlf Hansson 586e2ad626fSUlf Hansson clk_bulk_disable_unprepare(data->num_clks, domain->clks); 587e2ad626fSUlf Hansson 588e2ad626fSUlf Hansson /* power down upstream GPC domain */ 589e2ad626fSUlf Hansson pm_runtime_put(domain->power_dev); 590e2ad626fSUlf Hansson 591e2ad626fSUlf Hansson /* allow bus domain to suspend */ 592e2ad626fSUlf Hansson pm_runtime_put(bc->bus_power_dev); 593e2ad626fSUlf Hansson 594e2ad626fSUlf Hansson return 0; 595e2ad626fSUlf Hansson } 596e2ad626fSUlf Hansson 597e2ad626fSUlf Hansson static struct lock_class_key blk_ctrl_genpd_lock_class; 598e2ad626fSUlf Hansson 599e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_probe(struct platform_device *pdev) 600e2ad626fSUlf Hansson { 601e2ad626fSUlf Hansson const struct imx8mp_blk_ctrl_data *bc_data; 602e2ad626fSUlf Hansson struct device *dev = &pdev->dev; 603e2ad626fSUlf Hansson struct imx8mp_blk_ctrl *bc; 604e2ad626fSUlf Hansson void __iomem *base; 605e2ad626fSUlf Hansson int num_domains, i, ret; 606e2ad626fSUlf Hansson 607e2ad626fSUlf Hansson struct regmap_config regmap_config = { 608e2ad626fSUlf Hansson .reg_bits = 32, 609e2ad626fSUlf Hansson .val_bits = 32, 610e2ad626fSUlf Hansson .reg_stride = 4, 611e2ad626fSUlf Hansson }; 612e2ad626fSUlf Hansson 613e2ad626fSUlf Hansson bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL); 614e2ad626fSUlf Hansson if (!bc) 615e2ad626fSUlf Hansson return -ENOMEM; 616e2ad626fSUlf Hansson 617e2ad626fSUlf Hansson bc->dev = dev; 618e2ad626fSUlf Hansson 619e2ad626fSUlf Hansson bc_data = of_device_get_match_data(dev); 620e2ad626fSUlf Hansson num_domains = bc_data->num_domains; 621e2ad626fSUlf Hansson 622e2ad626fSUlf Hansson base = devm_platform_ioremap_resource(pdev, 0); 623e2ad626fSUlf Hansson if (IS_ERR(base)) 624e2ad626fSUlf Hansson return PTR_ERR(base); 625e2ad626fSUlf Hansson 626e2ad626fSUlf Hansson regmap_config.max_register = bc_data->max_reg; 627e2ad626fSUlf Hansson bc->regmap = devm_regmap_init_mmio(dev, base, ®map_config); 628e2ad626fSUlf Hansson if (IS_ERR(bc->regmap)) 629e2ad626fSUlf Hansson return dev_err_probe(dev, PTR_ERR(bc->regmap), 630e2ad626fSUlf Hansson "failed to init regmap\n"); 631e2ad626fSUlf Hansson 632e2ad626fSUlf Hansson bc->domains = devm_kcalloc(dev, num_domains, 633e2ad626fSUlf Hansson sizeof(struct imx8mp_blk_ctrl_domain), 634e2ad626fSUlf Hansson GFP_KERNEL); 635e2ad626fSUlf Hansson if (!bc->domains) 636e2ad626fSUlf Hansson return -ENOMEM; 637e2ad626fSUlf Hansson 638e2ad626fSUlf Hansson bc->onecell_data.num_domains = num_domains; 639e2ad626fSUlf Hansson bc->onecell_data.domains = 640e2ad626fSUlf Hansson devm_kcalloc(dev, num_domains, 641e2ad626fSUlf Hansson sizeof(struct generic_pm_domain *), GFP_KERNEL); 642e2ad626fSUlf Hansson if (!bc->onecell_data.domains) 643e2ad626fSUlf Hansson return -ENOMEM; 644e2ad626fSUlf Hansson 645e2ad626fSUlf Hansson bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus"); 646e2ad626fSUlf Hansson if (IS_ERR(bc->bus_power_dev)) 647e2ad626fSUlf Hansson return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev), 648e2ad626fSUlf Hansson "failed to attach bus power domain\n"); 649e2ad626fSUlf Hansson 650e2ad626fSUlf Hansson bc->power_off = bc_data->power_off; 651e2ad626fSUlf Hansson bc->power_on = bc_data->power_on; 652e2ad626fSUlf Hansson 653e2ad626fSUlf Hansson for (i = 0; i < num_domains; i++) { 654e2ad626fSUlf Hansson const struct imx8mp_blk_ctrl_domain_data *data = &bc_data->domains[i]; 655e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; 656e2ad626fSUlf Hansson int j; 657e2ad626fSUlf Hansson 658e2ad626fSUlf Hansson domain->data = data; 659e2ad626fSUlf Hansson domain->num_paths = data->num_paths; 660e2ad626fSUlf Hansson 661e2ad626fSUlf Hansson for (j = 0; j < data->num_clks; j++) 662e2ad626fSUlf Hansson domain->clks[j].id = data->clk_names[j]; 663e2ad626fSUlf Hansson 664e2ad626fSUlf Hansson for (j = 0; j < data->num_paths; j++) { 665e2ad626fSUlf Hansson domain->paths[j].name = data->path_names[j]; 666e2ad626fSUlf Hansson /* Fake value for now, just let ICC could configure NoC mode/priority */ 667e2ad626fSUlf Hansson domain->paths[j].avg_bw = 1; 668e2ad626fSUlf Hansson domain->paths[j].peak_bw = 1; 669e2ad626fSUlf Hansson } 670e2ad626fSUlf Hansson 671e2ad626fSUlf Hansson ret = devm_of_icc_bulk_get(dev, data->num_paths, domain->paths); 672e2ad626fSUlf Hansson if (ret) { 673e2ad626fSUlf Hansson if (ret != -EPROBE_DEFER) { 674e2ad626fSUlf Hansson dev_warn_once(dev, "Could not get interconnect paths, NoC will stay unconfigured!\n"); 675e2ad626fSUlf Hansson domain->num_paths = 0; 676e2ad626fSUlf Hansson } else { 677e2ad626fSUlf Hansson dev_err_probe(dev, ret, "failed to get noc entries\n"); 678e2ad626fSUlf Hansson goto cleanup_pds; 679e2ad626fSUlf Hansson } 680e2ad626fSUlf Hansson } 681e2ad626fSUlf Hansson 682e2ad626fSUlf Hansson ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks); 683e2ad626fSUlf Hansson if (ret) { 684e2ad626fSUlf Hansson dev_err_probe(dev, ret, "failed to get clock\n"); 685e2ad626fSUlf Hansson goto cleanup_pds; 686e2ad626fSUlf Hansson } 687e2ad626fSUlf Hansson 688e2ad626fSUlf Hansson domain->power_dev = 689e2ad626fSUlf Hansson dev_pm_domain_attach_by_name(dev, data->gpc_name); 690e2ad626fSUlf Hansson if (IS_ERR(domain->power_dev)) { 691e2ad626fSUlf Hansson dev_err_probe(dev, PTR_ERR(domain->power_dev), 692e2ad626fSUlf Hansson "failed to attach power domain %s\n", 693e2ad626fSUlf Hansson data->gpc_name); 694e2ad626fSUlf Hansson ret = PTR_ERR(domain->power_dev); 695e2ad626fSUlf Hansson goto cleanup_pds; 696e2ad626fSUlf Hansson } 697e2ad626fSUlf Hansson 698e2ad626fSUlf Hansson domain->genpd.name = data->name; 699e2ad626fSUlf Hansson domain->genpd.power_on = imx8mp_blk_ctrl_power_on; 700e2ad626fSUlf Hansson domain->genpd.power_off = imx8mp_blk_ctrl_power_off; 701e2ad626fSUlf Hansson domain->bc = bc; 702e2ad626fSUlf Hansson domain->id = i; 703e2ad626fSUlf Hansson 704e2ad626fSUlf Hansson ret = pm_genpd_init(&domain->genpd, NULL, true); 705e2ad626fSUlf Hansson if (ret) { 706e2ad626fSUlf Hansson dev_err_probe(dev, ret, "failed to init power domain\n"); 707e2ad626fSUlf Hansson dev_pm_domain_detach(domain->power_dev, true); 708e2ad626fSUlf Hansson goto cleanup_pds; 709e2ad626fSUlf Hansson } 710e2ad626fSUlf Hansson 711e2ad626fSUlf Hansson /* 712e2ad626fSUlf Hansson * We use runtime PM to trigger power on/off of the upstream GPC 713e2ad626fSUlf Hansson * domain, as a strict hierarchical parent/child power domain 714e2ad626fSUlf Hansson * setup doesn't allow us to meet the sequencing requirements. 715e2ad626fSUlf Hansson * This means we have nested locking of genpd locks, without the 716e2ad626fSUlf Hansson * nesting being visible at the genpd level, so we need a 717e2ad626fSUlf Hansson * separate lock class to make lockdep aware of the fact that 718e2ad626fSUlf Hansson * this are separate domain locks that can be nested without a 719e2ad626fSUlf Hansson * self-deadlock. 720e2ad626fSUlf Hansson */ 721e2ad626fSUlf Hansson lockdep_set_class(&domain->genpd.mlock, 722e2ad626fSUlf Hansson &blk_ctrl_genpd_lock_class); 723e2ad626fSUlf Hansson 724e2ad626fSUlf Hansson bc->onecell_data.domains[i] = &domain->genpd; 725e2ad626fSUlf Hansson } 726e2ad626fSUlf Hansson 727e2ad626fSUlf Hansson ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data); 728e2ad626fSUlf Hansson if (ret) { 729e2ad626fSUlf Hansson dev_err_probe(dev, ret, "failed to add power domain provider\n"); 730e2ad626fSUlf Hansson goto cleanup_pds; 731e2ad626fSUlf Hansson } 732e2ad626fSUlf Hansson 733e2ad626fSUlf Hansson bc->power_nb.notifier_call = bc_data->power_notifier_fn; 734e2ad626fSUlf Hansson ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb); 735e2ad626fSUlf Hansson if (ret) { 736e2ad626fSUlf Hansson dev_err_probe(dev, ret, "failed to add power notifier\n"); 737e2ad626fSUlf Hansson goto cleanup_provider; 738e2ad626fSUlf Hansson } 739e2ad626fSUlf Hansson 740e2ad626fSUlf Hansson if (bc_data->probe) { 741e2ad626fSUlf Hansson ret = bc_data->probe(bc); 742e2ad626fSUlf Hansson if (ret) 743e2ad626fSUlf Hansson goto cleanup_provider; 744e2ad626fSUlf Hansson } 745e2ad626fSUlf Hansson 746e2ad626fSUlf Hansson dev_set_drvdata(dev, bc); 747e2ad626fSUlf Hansson 748e2ad626fSUlf Hansson return 0; 749e2ad626fSUlf Hansson 750e2ad626fSUlf Hansson cleanup_provider: 751e2ad626fSUlf Hansson of_genpd_del_provider(dev->of_node); 752e2ad626fSUlf Hansson cleanup_pds: 753e2ad626fSUlf Hansson for (i--; i >= 0; i--) { 754e2ad626fSUlf Hansson pm_genpd_remove(&bc->domains[i].genpd); 755e2ad626fSUlf Hansson dev_pm_domain_detach(bc->domains[i].power_dev, true); 756e2ad626fSUlf Hansson } 757e2ad626fSUlf Hansson 758e2ad626fSUlf Hansson dev_pm_domain_detach(bc->bus_power_dev, true); 759e2ad626fSUlf Hansson 760e2ad626fSUlf Hansson return ret; 761e2ad626fSUlf Hansson } 762e2ad626fSUlf Hansson 763e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_remove(struct platform_device *pdev) 764e2ad626fSUlf Hansson { 765e2ad626fSUlf Hansson struct imx8mp_blk_ctrl *bc = dev_get_drvdata(&pdev->dev); 766e2ad626fSUlf Hansson int i; 767e2ad626fSUlf Hansson 768e2ad626fSUlf Hansson of_genpd_del_provider(pdev->dev.of_node); 769e2ad626fSUlf Hansson 770e2ad626fSUlf Hansson for (i = 0; bc->onecell_data.num_domains; i++) { 771e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; 772e2ad626fSUlf Hansson 773e2ad626fSUlf Hansson pm_genpd_remove(&domain->genpd); 774e2ad626fSUlf Hansson dev_pm_domain_detach(domain->power_dev, true); 775e2ad626fSUlf Hansson } 776e2ad626fSUlf Hansson 777e2ad626fSUlf Hansson dev_pm_genpd_remove_notifier(bc->bus_power_dev); 778e2ad626fSUlf Hansson 779e2ad626fSUlf Hansson dev_pm_domain_detach(bc->bus_power_dev, true); 780e2ad626fSUlf Hansson 781e2ad626fSUlf Hansson return 0; 782e2ad626fSUlf Hansson } 783e2ad626fSUlf Hansson 784e2ad626fSUlf Hansson #ifdef CONFIG_PM_SLEEP 785e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_suspend(struct device *dev) 786e2ad626fSUlf Hansson { 787e2ad626fSUlf Hansson struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev); 788e2ad626fSUlf Hansson int ret, i; 789e2ad626fSUlf Hansson 790e2ad626fSUlf Hansson /* 791e2ad626fSUlf Hansson * This may look strange, but is done so the generic PM_SLEEP code 792e2ad626fSUlf Hansson * can power down our domains and more importantly power them up again 793e2ad626fSUlf Hansson * after resume, without tripping over our usage of runtime PM to 794e2ad626fSUlf Hansson * control the upstream GPC domains. Things happen in the right order 795e2ad626fSUlf Hansson * in the system suspend/resume paths due to the device parent/child 796e2ad626fSUlf Hansson * hierarchy. 797e2ad626fSUlf Hansson */ 798e2ad626fSUlf Hansson ret = pm_runtime_get_sync(bc->bus_power_dev); 799e2ad626fSUlf Hansson if (ret < 0) { 800e2ad626fSUlf Hansson pm_runtime_put_noidle(bc->bus_power_dev); 801e2ad626fSUlf Hansson return ret; 802e2ad626fSUlf Hansson } 803e2ad626fSUlf Hansson 804e2ad626fSUlf Hansson for (i = 0; i < bc->onecell_data.num_domains; i++) { 805e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; 806e2ad626fSUlf Hansson 807e2ad626fSUlf Hansson ret = pm_runtime_get_sync(domain->power_dev); 808e2ad626fSUlf Hansson if (ret < 0) { 809e2ad626fSUlf Hansson pm_runtime_put_noidle(domain->power_dev); 810e2ad626fSUlf Hansson goto out_fail; 811e2ad626fSUlf Hansson } 812e2ad626fSUlf Hansson } 813e2ad626fSUlf Hansson 814e2ad626fSUlf Hansson return 0; 815e2ad626fSUlf Hansson 816e2ad626fSUlf Hansson out_fail: 817e2ad626fSUlf Hansson for (i--; i >= 0; i--) 818e2ad626fSUlf Hansson pm_runtime_put(bc->domains[i].power_dev); 819e2ad626fSUlf Hansson 820e2ad626fSUlf Hansson pm_runtime_put(bc->bus_power_dev); 821e2ad626fSUlf Hansson 822e2ad626fSUlf Hansson return ret; 823e2ad626fSUlf Hansson } 824e2ad626fSUlf Hansson 825e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_resume(struct device *dev) 826e2ad626fSUlf Hansson { 827e2ad626fSUlf Hansson struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev); 828e2ad626fSUlf Hansson int i; 829e2ad626fSUlf Hansson 830e2ad626fSUlf Hansson for (i = 0; i < bc->onecell_data.num_domains; i++) 831e2ad626fSUlf Hansson pm_runtime_put(bc->domains[i].power_dev); 832e2ad626fSUlf Hansson 833e2ad626fSUlf Hansson pm_runtime_put(bc->bus_power_dev); 834e2ad626fSUlf Hansson 835e2ad626fSUlf Hansson return 0; 836e2ad626fSUlf Hansson } 837e2ad626fSUlf Hansson #endif 838e2ad626fSUlf Hansson 839e2ad626fSUlf Hansson static const struct dev_pm_ops imx8mp_blk_ctrl_pm_ops = { 840e2ad626fSUlf Hansson SET_SYSTEM_SLEEP_PM_OPS(imx8mp_blk_ctrl_suspend, 841e2ad626fSUlf Hansson imx8mp_blk_ctrl_resume) 842e2ad626fSUlf Hansson }; 843e2ad626fSUlf Hansson 844e2ad626fSUlf Hansson static const struct of_device_id imx8mp_blk_ctrl_of_match[] = { 845e2ad626fSUlf Hansson { 846e2ad626fSUlf Hansson .compatible = "fsl,imx8mp-hsio-blk-ctrl", 847e2ad626fSUlf Hansson .data = &imx8mp_hsio_blk_ctl_dev_data, 848e2ad626fSUlf Hansson }, { 849e2ad626fSUlf Hansson .compatible = "fsl,imx8mp-hdmi-blk-ctrl", 850e2ad626fSUlf Hansson .data = &imx8mp_hdmi_blk_ctl_dev_data, 851e2ad626fSUlf Hansson }, { 852e2ad626fSUlf Hansson /* Sentinel */ 853e2ad626fSUlf Hansson } 854e2ad626fSUlf Hansson }; 855e2ad626fSUlf Hansson MODULE_DEVICE_TABLE(of, imx8mp_blk_ctrl_of_match); 856e2ad626fSUlf Hansson 857e2ad626fSUlf Hansson static struct platform_driver imx8mp_blk_ctrl_driver = { 858e2ad626fSUlf Hansson .probe = imx8mp_blk_ctrl_probe, 859e2ad626fSUlf Hansson .remove = imx8mp_blk_ctrl_remove, 860e2ad626fSUlf Hansson .driver = { 861e2ad626fSUlf Hansson .name = "imx8mp-blk-ctrl", 862e2ad626fSUlf Hansson .pm = &imx8mp_blk_ctrl_pm_ops, 863e2ad626fSUlf Hansson .of_match_table = imx8mp_blk_ctrl_of_match, 864e2ad626fSUlf Hansson }, 865e2ad626fSUlf Hansson }; 866e2ad626fSUlf Hansson module_platform_driver(imx8mp_blk_ctrl_driver); 867e2ad626fSUlf Hansson MODULE_LICENSE("GPL"); 868