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