414 lines · c
1// SPDX-License-Identifier: (GPL-2.0+ OR MIT)2/*3 * Copyright (c) 2019 Amlogic, Inc.4 * Author: Jianxin Pan <jianxin.pan@amlogic.com>5 */6 7#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt8 9#include <linux/io.h>10#include <linux/of.h>11#include <linux/platform_device.h>12#include <linux/pm_domain.h>13#include <dt-bindings/power/meson-a1-power.h>14#include <dt-bindings/power/amlogic,c3-pwrc.h>15#include <dt-bindings/power/meson-s4-power.h>16#include <dt-bindings/power/amlogic,t7-pwrc.h>17#include <dt-bindings/power/amlogic,a4-pwrc.h>18#include <dt-bindings/power/amlogic,a5-pwrc.h>19#include <linux/arm-smccc.h>20#include <linux/firmware/meson/meson_sm.h>21#include <linux/module.h>22 23#define PWRC_ON 124#define PWRC_OFF 025#define PWRC_NO_PARENT UINT_MAX26 27struct meson_secure_pwrc_domain {28 struct generic_pm_domain base;29 unsigned int index;30 unsigned int parent;31 struct meson_secure_pwrc *pwrc;32};33 34struct meson_secure_pwrc {35 struct meson_secure_pwrc_domain *domains;36 struct genpd_onecell_data xlate;37 struct meson_sm_firmware *fw;38};39 40struct meson_secure_pwrc_domain_desc {41 unsigned int index;42 unsigned int parent;43 unsigned int flags;44 char *name;45 bool (*is_off)(struct meson_secure_pwrc_domain *pwrc_domain);46};47 48struct meson_secure_pwrc_domain_data {49 unsigned int count;50 const struct meson_secure_pwrc_domain_desc *domains;51};52 53static bool pwrc_secure_is_off(struct meson_secure_pwrc_domain *pwrc_domain)54{55 int is_off = 1;56 57 if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_GET, &is_off,58 pwrc_domain->index, 0, 0, 0, 0) < 0)59 pr_err("failed to get power domain status\n");60 61 return is_off;62}63 64static int meson_secure_pwrc_off(struct generic_pm_domain *domain)65{66 int ret = 0;67 struct meson_secure_pwrc_domain *pwrc_domain =68 container_of(domain, struct meson_secure_pwrc_domain, base);69 70 if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL,71 pwrc_domain->index, PWRC_OFF, 0, 0, 0) < 0) {72 pr_err("failed to set power domain off\n");73 ret = -EINVAL;74 }75 76 return ret;77}78 79static int meson_secure_pwrc_on(struct generic_pm_domain *domain)80{81 int ret = 0;82 struct meson_secure_pwrc_domain *pwrc_domain =83 container_of(domain, struct meson_secure_pwrc_domain, base);84 85 if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL,86 pwrc_domain->index, PWRC_ON, 0, 0, 0) < 0) {87 pr_err("failed to set power domain on\n");88 ret = -EINVAL;89 }90 91 return ret;92}93 94#define SEC_PD(__name, __flag) \95[PWRC_##__name##_ID] = \96{ \97 .name = #__name, \98 .index = PWRC_##__name##_ID, \99 .is_off = pwrc_secure_is_off, \100 .flags = __flag, \101 .parent = PWRC_NO_PARENT, \102}103 104#define TOP_PD(__name, __flag, __parent) \105[PWRC_##__name##_ID] = \106{ \107 .name = #__name, \108 .index = PWRC_##__name##_ID, \109 .is_off = pwrc_secure_is_off, \110 .flags = __flag, \111 .parent = __parent, \112}113 114static const struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = {115 SEC_PD(DSPA, 0),116 SEC_PD(DSPB, 0),117 /* UART should keep working in ATF after suspend and before resume */118 SEC_PD(UART, GENPD_FLAG_ALWAYS_ON),119 /* DMC is for DDR PHY ana/dig and DMC, and should be always on */120 SEC_PD(DMC, GENPD_FLAG_ALWAYS_ON),121 SEC_PD(I2C, 0),122 SEC_PD(PSRAM, 0),123 SEC_PD(ACODEC, 0),124 SEC_PD(AUDIO, 0),125 SEC_PD(OTP, 0),126 SEC_PD(DMA, GENPD_FLAG_ALWAYS_ON | GENPD_FLAG_IRQ_SAFE),127 SEC_PD(SD_EMMC, 0),128 SEC_PD(RAMA, 0),129 /* SRAMB is used as ATF runtime memory, and should be always on */130 SEC_PD(RAMB, GENPD_FLAG_ALWAYS_ON),131 SEC_PD(IR, 0),132 SEC_PD(SPICC, 0),133 SEC_PD(SPIFC, 0),134 SEC_PD(USB, 0),135 /* NIC is for the Arm NIC-400 interconnect, and should be always on */136 SEC_PD(NIC, GENPD_FLAG_ALWAYS_ON),137 SEC_PD(PDMIN, 0),138 SEC_PD(RSA, 0),139};140 141static const struct meson_secure_pwrc_domain_desc a4_pwrc_domains[] = {142 SEC_PD(A4_AUDIO, 0),143 SEC_PD(A4_SDIOA, 0),144 SEC_PD(A4_EMMC, 0),145 SEC_PD(A4_USB_COMB, 0),146 SEC_PD(A4_ETH, 0),147 SEC_PD(A4_VOUT, 0),148 SEC_PD(A4_AUDIO_PDM, 0),149 /* DMC is for DDR PHY ana/dig and DMC, and should be always on */150 SEC_PD(A4_DMC, GENPD_FLAG_ALWAYS_ON),151 /* WRAP is secure_top, a lot of modules are included, and should be always on */152 SEC_PD(A4_SYS_WRAP, GENPD_FLAG_ALWAYS_ON),153 SEC_PD(A4_AO_I2C_S, 0),154 SEC_PD(A4_AO_UART, 0),155 /* IR is wake up trigger source, and should be always on */156 SEC_PD(A4_AO_IR, GENPD_FLAG_ALWAYS_ON),157};158 159static const struct meson_secure_pwrc_domain_desc a5_pwrc_domains[] = {160 SEC_PD(A5_NNA, 0),161 SEC_PD(A5_AUDIO, 0),162 SEC_PD(A5_SDIOA, 0),163 SEC_PD(A5_EMMC, 0),164 SEC_PD(A5_USB_COMB, 0),165 SEC_PD(A5_ETH, 0),166 SEC_PD(A5_RSA, 0),167 SEC_PD(A5_AUDIO_PDM, 0),168 /* DMC is for DDR PHY ana/dig and DMC, and should be always on */169 SEC_PD(A5_DMC, GENPD_FLAG_ALWAYS_ON),170 /* WRAP is secure_top, a lot of modules are included, and should be always on */171 SEC_PD(A5_SYS_WRAP, GENPD_FLAG_ALWAYS_ON),172 SEC_PD(A5_DSPA, 0),173};174 175static const struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = {176 SEC_PD(C3_NNA, 0),177 SEC_PD(C3_AUDIO, 0),178 SEC_PD(C3_SDIOA, 0),179 SEC_PD(C3_EMMC, 0),180 SEC_PD(C3_USB_COMB, 0),181 SEC_PD(C3_SDCARD, 0),182 /* ETH is for ethernet online wakeup, and should be always on */183 SEC_PD(C3_ETH, GENPD_FLAG_ALWAYS_ON),184 SEC_PD(C3_GE2D, 0),185 SEC_PD(C3_CVE, 0),186 SEC_PD(C3_GDC_WRAP, 0),187 SEC_PD(C3_ISP_TOP, 0),188 SEC_PD(C3_MIPI_ISP_WRAP, 0),189 SEC_PD(C3_VCODEC, 0),190};191 192static const struct meson_secure_pwrc_domain_desc s4_pwrc_domains[] = {193 SEC_PD(S4_DOS_HEVC, 0),194 SEC_PD(S4_DOS_VDEC, 0),195 SEC_PD(S4_VPU_HDMI, 0),196 SEC_PD(S4_USB_COMB, 0),197 SEC_PD(S4_GE2D, 0),198 /* ETH is for ethernet online wakeup, and should be always on */199 SEC_PD(S4_ETH, GENPD_FLAG_ALWAYS_ON),200 SEC_PD(S4_DEMOD, 0),201 SEC_PD(S4_AUDIO, 0),202};203 204static const struct meson_secure_pwrc_domain_desc t7_pwrc_domains[] = {205 SEC_PD(T7_DSPA, 0),206 SEC_PD(T7_DSPB, 0),207 TOP_PD(T7_DOS_HCODEC, 0, PWRC_T7_NIC3_ID),208 TOP_PD(T7_DOS_HEVC, 0, PWRC_T7_NIC3_ID),209 TOP_PD(T7_DOS_VDEC, 0, PWRC_T7_NIC3_ID),210 TOP_PD(T7_DOS_WAVE, 0, PWRC_T7_NIC3_ID),211 SEC_PD(T7_VPU_HDMI, 0),212 SEC_PD(T7_USB_COMB, 0),213 SEC_PD(T7_PCIE, 0),214 TOP_PD(T7_GE2D, 0, PWRC_T7_NIC3_ID),215 /* SRAMA is used as ATF runtime memory, and should be always on */216 SEC_PD(T7_SRAMA, GENPD_FLAG_ALWAYS_ON),217 /* SRAMB is used as ATF runtime memory, and should be always on */218 SEC_PD(T7_SRAMB, GENPD_FLAG_ALWAYS_ON),219 SEC_PD(T7_HDMIRX, 0),220 SEC_PD(T7_VI_CLK1, 0),221 SEC_PD(T7_VI_CLK2, 0),222 /* ETH is for ethernet online wakeup, and should be always on */223 SEC_PD(T7_ETH, GENPD_FLAG_ALWAYS_ON),224 SEC_PD(T7_ISP, 0),225 SEC_PD(T7_MIPI_ISP, 0),226 TOP_PD(T7_GDC, 0, PWRC_T7_NIC3_ID),227 TOP_PD(T7_DEWARP, 0, PWRC_T7_NIC3_ID),228 SEC_PD(T7_SDIO_A, 0),229 SEC_PD(T7_SDIO_B, 0),230 SEC_PD(T7_EMMC, 0),231 TOP_PD(T7_MALI_SC0, 0, PWRC_T7_NNA_TOP_ID),232 TOP_PD(T7_MALI_SC1, 0, PWRC_T7_NNA_TOP_ID),233 TOP_PD(T7_MALI_SC2, 0, PWRC_T7_NNA_TOP_ID),234 TOP_PD(T7_MALI_SC3, 0, PWRC_T7_NNA_TOP_ID),235 SEC_PD(T7_MALI_TOP, 0),236 TOP_PD(T7_NNA_CORE0, 0, PWRC_T7_NNA_TOP_ID),237 TOP_PD(T7_NNA_CORE1, 0, PWRC_T7_NNA_TOP_ID),238 TOP_PD(T7_NNA_CORE2, 0, PWRC_T7_NNA_TOP_ID),239 TOP_PD(T7_NNA_CORE3, 0, PWRC_T7_NNA_TOP_ID),240 SEC_PD(T7_NNA_TOP, 0),241 SEC_PD(T7_DDR0, GENPD_FLAG_ALWAYS_ON),242 SEC_PD(T7_DDR1, GENPD_FLAG_ALWAYS_ON),243 /* DMC0 is for DDR PHY ana/dig and DMC, and should be always on */244 SEC_PD(T7_DMC0, GENPD_FLAG_ALWAYS_ON),245 /* DMC1 is for DDR PHY ana/dig and DMC, and should be always on */246 SEC_PD(T7_DMC1, GENPD_FLAG_ALWAYS_ON),247 /* NOC is related to clk bus, and should be always on */248 SEC_PD(T7_NOC, GENPD_FLAG_ALWAYS_ON),249 /* NIC is for the Arm NIC-400 interconnect, and should be always on */250 SEC_PD(T7_NIC2, GENPD_FLAG_ALWAYS_ON),251 SEC_PD(T7_NIC3, 0),252 /* CPU accesses the interleave data to the ddr need cci, and should be always on */253 SEC_PD(T7_CCI, GENPD_FLAG_ALWAYS_ON),254 SEC_PD(T7_MIPI_DSI0, 0),255 SEC_PD(T7_SPICC0, 0),256 SEC_PD(T7_SPICC1, 0),257 SEC_PD(T7_SPICC2, 0),258 SEC_PD(T7_SPICC3, 0),259 SEC_PD(T7_SPICC4, 0),260 SEC_PD(T7_SPICC5, 0),261 SEC_PD(T7_EDP0, 0),262 SEC_PD(T7_EDP1, 0),263 SEC_PD(T7_MIPI_DSI1, 0),264 SEC_PD(T7_AUDIO, 0),265};266 267static int meson_secure_pwrc_probe(struct platform_device *pdev)268{269 int i;270 struct device_node *sm_np;271 struct meson_secure_pwrc *pwrc;272 const struct meson_secure_pwrc_domain_data *match;273 274 match = of_device_get_match_data(&pdev->dev);275 if (!match) {276 dev_err(&pdev->dev, "failed to get match data\n");277 return -ENODEV;278 }279 280 sm_np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gxbb-sm");281 if (!sm_np) {282 dev_err(&pdev->dev, "no secure-monitor node\n");283 return -ENODEV;284 }285 286 pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL);287 if (!pwrc) {288 of_node_put(sm_np);289 return -ENOMEM;290 }291 292 pwrc->fw = meson_sm_get(sm_np);293 of_node_put(sm_np);294 if (!pwrc->fw)295 return -EPROBE_DEFER;296 297 pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count,298 sizeof(*pwrc->xlate.domains),299 GFP_KERNEL);300 if (!pwrc->xlate.domains)301 return -ENOMEM;302 303 pwrc->domains = devm_kcalloc(&pdev->dev, match->count,304 sizeof(*pwrc->domains), GFP_KERNEL);305 if (!pwrc->domains)306 return -ENOMEM;307 308 pwrc->xlate.num_domains = match->count;309 platform_set_drvdata(pdev, pwrc);310 311 for (i = 0 ; i < match->count ; ++i) {312 struct meson_secure_pwrc_domain *dom = &pwrc->domains[i];313 314 if (!match->domains[i].name)315 continue;316 317 dom->pwrc = pwrc;318 dom->index = match->domains[i].index;319 dom->parent = match->domains[i].parent;320 dom->base.name = match->domains[i].name;321 dom->base.flags = match->domains[i].flags;322 dom->base.power_on = meson_secure_pwrc_on;323 dom->base.power_off = meson_secure_pwrc_off;324 325 if (match->domains[i].is_off(dom) && (dom->base.flags & GENPD_FLAG_ALWAYS_ON))326 meson_secure_pwrc_on(&dom->base);327 328 pm_genpd_init(&dom->base, NULL, match->domains[i].is_off(dom));329 330 pwrc->xlate.domains[i] = &dom->base;331 }332 333 for (i = 0; i < match->count; i++) {334 struct meson_secure_pwrc_domain *dom = pwrc->domains;335 336 if (!match->domains[i].name || match->domains[i].parent == PWRC_NO_PARENT)337 continue;338 339 pm_genpd_add_subdomain(&dom[dom[i].parent].base, &dom[i].base);340 }341 342 return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate);343}344 345static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = {346 .domains = a1_pwrc_domains,347 .count = ARRAY_SIZE(a1_pwrc_domains),348};349 350static struct meson_secure_pwrc_domain_data amlogic_secure_a4_pwrc_data = {351 .domains = a4_pwrc_domains,352 .count = ARRAY_SIZE(a4_pwrc_domains),353};354 355static struct meson_secure_pwrc_domain_data amlogic_secure_a5_pwrc_data = {356 .domains = a5_pwrc_domains,357 .count = ARRAY_SIZE(a5_pwrc_domains),358};359 360static struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = {361 .domains = c3_pwrc_domains,362 .count = ARRAY_SIZE(c3_pwrc_domains),363};364 365static struct meson_secure_pwrc_domain_data meson_secure_s4_pwrc_data = {366 .domains = s4_pwrc_domains,367 .count = ARRAY_SIZE(s4_pwrc_domains),368};369 370static struct meson_secure_pwrc_domain_data amlogic_secure_t7_pwrc_data = {371 .domains = t7_pwrc_domains,372 .count = ARRAY_SIZE(t7_pwrc_domains),373};374 375static const struct of_device_id meson_secure_pwrc_match_table[] = {376 {377 .compatible = "amlogic,meson-a1-pwrc",378 .data = &meson_secure_a1_pwrc_data,379 },380 {381 .compatible = "amlogic,a4-pwrc",382 .data = &amlogic_secure_a4_pwrc_data,383 },384 {385 .compatible = "amlogic,a5-pwrc",386 .data = &amlogic_secure_a5_pwrc_data,387 },388 {389 .compatible = "amlogic,c3-pwrc",390 .data = &amlogic_secure_c3_pwrc_data,391 },392 {393 .compatible = "amlogic,meson-s4-pwrc",394 .data = &meson_secure_s4_pwrc_data,395 },396 {397 .compatible = "amlogic,t7-pwrc",398 .data = &amlogic_secure_t7_pwrc_data,399 },400 { /* sentinel */ }401};402MODULE_DEVICE_TABLE(of, meson_secure_pwrc_match_table);403 404static struct platform_driver meson_secure_pwrc_driver = {405 .probe = meson_secure_pwrc_probe,406 .driver = {407 .name = "meson_secure_pwrc",408 .of_match_table = meson_secure_pwrc_match_table,409 },410};411module_platform_driver(meson_secure_pwrc_driver);412MODULE_DESCRIPTION("Amlogic Meson Secure Power Domains driver");413MODULE_LICENSE("Dual MIT/GPL");414