1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Amlogic Meson VPU Power Domain Controller driver 4 * 5 * Copyright (c) 2018 BayLibre, SAS. 6 * Author: Neil Armstrong <narmstrong@baylibre.com> 7 */ 8 9 #include <common.h> 10 #include <dm.h> 11 #include <power-domain-uclass.h> 12 #include <regmap.h> 13 #include <syscon.h> 14 #include <reset.h> 15 #include <clk.h> 16 17 /* AO Offsets */ 18 19 #define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) 20 21 #define GEN_PWR_VPU_HDMI BIT(8) 22 #define GEN_PWR_VPU_HDMI_ISO BIT(9) 23 24 /* HHI Offsets */ 25 26 #define HHI_MEM_PD_REG0 (0x40 << 2) 27 #define HHI_VPU_MEM_PD_REG0 (0x41 << 2) 28 #define HHI_VPU_MEM_PD_REG1 (0x42 << 2) 29 30 struct meson_gx_pwrc_vpu_priv { 31 struct regmap *regmap_ao; 32 struct regmap *regmap_hhi; 33 struct reset_ctl_bulk resets; 34 struct clk_bulk clks; 35 }; 36 37 static int meson_gx_pwrc_vpu_request(struct power_domain *power_domain) 38 { 39 return 0; 40 } 41 42 static int meson_gx_pwrc_vpu_free(struct power_domain *power_domain) 43 { 44 return 0; 45 } 46 47 static int meson_gx_pwrc_vpu_on(struct power_domain *power_domain) 48 { 49 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev); 50 int i, ret; 51 52 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 53 GEN_PWR_VPU_HDMI, 0); 54 udelay(20); 55 56 /* Power Up Memories */ 57 for (i = 0; i < 32; i += 2) { 58 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0, 59 0x3 << i, 0); 60 udelay(5); 61 } 62 63 for (i = 0; i < 32; i += 2) { 64 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1, 65 0x3 << i, 0); 66 udelay(5); 67 } 68 69 for (i = 8; i < 16; i++) { 70 regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0, 71 BIT(i), 0); 72 udelay(5); 73 } 74 udelay(20); 75 76 ret = reset_assert_bulk(&priv->resets); 77 if (ret) 78 return ret; 79 80 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 81 GEN_PWR_VPU_HDMI_ISO, 0); 82 83 ret = reset_deassert_bulk(&priv->resets); 84 if (ret) 85 return ret; 86 87 ret = clk_enable_bulk(&priv->clks); 88 if (ret) 89 return ret; 90 91 return 0; 92 } 93 94 static int meson_gx_pwrc_vpu_off(struct power_domain *power_domain) 95 { 96 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev); 97 int i; 98 99 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 100 GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); 101 udelay(20); 102 103 /* Power Down Memories */ 104 for (i = 0; i < 32; i += 2) { 105 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0, 106 0x3 << i, 0x3 << i); 107 udelay(5); 108 } 109 for (i = 0; i < 32; i += 2) { 110 regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1, 111 0x3 << i, 0x3 << i); 112 udelay(5); 113 } 114 for (i = 8; i < 16; i++) { 115 regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0, 116 BIT(i), BIT(i)); 117 udelay(5); 118 } 119 udelay(20); 120 121 regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 122 GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); 123 mdelay(20); 124 125 clk_disable_bulk(&priv->clks); 126 127 return 0; 128 } 129 130 static int meson_gx_pwrc_vpu_of_xlate(struct power_domain *power_domain, 131 struct ofnode_phandle_args *args) 132 { 133 /* #power-domain-cells is 0 */ 134 135 if (args->args_count != 0) { 136 debug("Invalid args_count: %d\n", args->args_count); 137 return -EINVAL; 138 } 139 140 return 0; 141 } 142 143 struct power_domain_ops meson_gx_pwrc_vpu_ops = { 144 .free = meson_gx_pwrc_vpu_free, 145 .off = meson_gx_pwrc_vpu_off, 146 .on = meson_gx_pwrc_vpu_on, 147 .request = meson_gx_pwrc_vpu_request, 148 .of_xlate = meson_gx_pwrc_vpu_of_xlate, 149 }; 150 151 static const struct udevice_id meson_gx_pwrc_vpu_ids[] = { 152 { .compatible = "amlogic,meson-gx-pwrc-vpu" }, 153 { } 154 }; 155 156 static int meson_gx_pwrc_vpu_probe(struct udevice *dev) 157 { 158 struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(dev); 159 u32 hhi_phandle; 160 ofnode hhi_node; 161 int ret; 162 163 priv->regmap_ao = syscon_node_to_regmap(dev_get_parent(dev)->node); 164 if (IS_ERR(priv->regmap_ao)) 165 return PTR_ERR(priv->regmap_ao); 166 167 ret = ofnode_read_u32(dev->node, "amlogic,hhi-sysctrl", 168 &hhi_phandle); 169 if (ret) 170 return ret; 171 172 hhi_node = ofnode_get_by_phandle(hhi_phandle); 173 if (!ofnode_valid(hhi_node)) 174 return -EINVAL; 175 176 priv->regmap_hhi = syscon_node_to_regmap(hhi_node); 177 if (IS_ERR(priv->regmap_hhi)) 178 return PTR_ERR(priv->regmap_hhi); 179 180 ret = reset_get_bulk(dev, &priv->resets); 181 if (ret) 182 return ret; 183 184 ret = clk_get_bulk(dev, &priv->clks); 185 if (ret) 186 return ret; 187 188 return 0; 189 } 190 191 U_BOOT_DRIVER(meson_gx_pwrc_vpu) = { 192 .name = "meson_gx_pwrc_vpu", 193 .id = UCLASS_POWER_DOMAIN, 194 .of_match = meson_gx_pwrc_vpu_ids, 195 .probe = meson_gx_pwrc_vpu_probe, 196 .ops = &meson_gx_pwrc_vpu_ops, 197 .priv_auto_alloc_size = sizeof(struct meson_gx_pwrc_vpu_priv), 198 }; 199