cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

meson-gx-pwrc-vpu.c (8768B)


      1/*
      2 * Copyright (c) 2017 BayLibre, SAS
      3 * Author: Neil Armstrong <narmstrong@baylibre.com>
      4 *
      5 * SPDX-License-Identifier: GPL-2.0+
      6 */
      7
      8#include <linux/of_address.h>
      9#include <linux/platform_device.h>
     10#include <linux/pm_domain.h>
     11#include <linux/bitfield.h>
     12#include <linux/regmap.h>
     13#include <linux/mfd/syscon.h>
     14#include <linux/of_device.h>
     15#include <linux/reset.h>
     16#include <linux/clk.h>
     17#include <linux/module.h>
     18
     19/* AO Offsets */
     20
     21#define AO_RTI_GEN_PWR_SLEEP0		(0x3a << 2)
     22
     23#define GEN_PWR_VPU_HDMI		BIT(8)
     24#define GEN_PWR_VPU_HDMI_ISO		BIT(9)
     25
     26/* HHI Offsets */
     27
     28#define HHI_MEM_PD_REG0			(0x40 << 2)
     29#define HHI_VPU_MEM_PD_REG0		(0x41 << 2)
     30#define HHI_VPU_MEM_PD_REG1		(0x42 << 2)
     31#define HHI_VPU_MEM_PD_REG2		(0x4d << 2)
     32
     33struct meson_gx_pwrc_vpu {
     34	struct generic_pm_domain genpd;
     35	struct regmap *regmap_ao;
     36	struct regmap *regmap_hhi;
     37	struct reset_control *rstc;
     38	struct clk *vpu_clk;
     39	struct clk *vapb_clk;
     40};
     41
     42static inline
     43struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d)
     44{
     45	return container_of(d, struct meson_gx_pwrc_vpu, genpd);
     46}
     47
     48static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd)
     49{
     50	struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
     51	int i;
     52
     53	regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
     54			   GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
     55	udelay(20);
     56
     57	/* Power Down Memories */
     58	for (i = 0; i < 32; i += 2) {
     59		regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
     60				   0x3 << i, 0x3 << i);
     61		udelay(5);
     62	}
     63	for (i = 0; i < 32; i += 2) {
     64		regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
     65				   0x3 << i, 0x3 << i);
     66		udelay(5);
     67	}
     68	for (i = 8; i < 16; i++) {
     69		regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
     70				   BIT(i), BIT(i));
     71		udelay(5);
     72	}
     73	udelay(20);
     74
     75	regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
     76			   GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
     77
     78	msleep(20);
     79
     80	clk_disable_unprepare(pd->vpu_clk);
     81	clk_disable_unprepare(pd->vapb_clk);
     82
     83	return 0;
     84}
     85
     86static int meson_g12a_pwrc_vpu_power_off(struct generic_pm_domain *genpd)
     87{
     88	struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
     89	int i;
     90
     91	regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
     92			   GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
     93	udelay(20);
     94
     95	/* Power Down Memories */
     96	for (i = 0; i < 32; i += 2) {
     97		regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
     98				   0x3 << i, 0x3 << i);
     99		udelay(5);
    100	}
    101	for (i = 0; i < 32; i += 2) {
    102		regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
    103				   0x3 << i, 0x3 << i);
    104		udelay(5);
    105	}
    106	for (i = 0; i < 32; i += 2) {
    107		regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2,
    108				   0x3 << i, 0x3 << i);
    109		udelay(5);
    110	}
    111	for (i = 8; i < 16; i++) {
    112		regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
    113				   BIT(i), BIT(i));
    114		udelay(5);
    115	}
    116	udelay(20);
    117
    118	regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
    119			   GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
    120
    121	msleep(20);
    122
    123	clk_disable_unprepare(pd->vpu_clk);
    124	clk_disable_unprepare(pd->vapb_clk);
    125
    126	return 0;
    127}
    128
    129static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd)
    130{
    131	int ret;
    132
    133	ret = clk_prepare_enable(pd->vpu_clk);
    134	if (ret)
    135		return ret;
    136
    137	ret = clk_prepare_enable(pd->vapb_clk);
    138	if (ret)
    139		clk_disable_unprepare(pd->vpu_clk);
    140
    141	return ret;
    142}
    143
    144static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd)
    145{
    146	struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
    147	int ret;
    148	int i;
    149
    150	regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
    151			   GEN_PWR_VPU_HDMI, 0);
    152	udelay(20);
    153
    154	/* Power Up Memories */
    155	for (i = 0; i < 32; i += 2) {
    156		regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
    157				   0x3 << i, 0);
    158		udelay(5);
    159	}
    160
    161	for (i = 0; i < 32; i += 2) {
    162		regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
    163				   0x3 << i, 0);
    164		udelay(5);
    165	}
    166
    167	for (i = 8; i < 16; i++) {
    168		regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
    169				   BIT(i), 0);
    170		udelay(5);
    171	}
    172	udelay(20);
    173
    174	ret = reset_control_assert(pd->rstc);
    175	if (ret)
    176		return ret;
    177
    178	regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
    179			   GEN_PWR_VPU_HDMI_ISO, 0);
    180
    181	ret = reset_control_deassert(pd->rstc);
    182	if (ret)
    183		return ret;
    184
    185	ret = meson_gx_pwrc_vpu_setup_clk(pd);
    186	if (ret)
    187		return ret;
    188
    189	return 0;
    190}
    191
    192static int meson_g12a_pwrc_vpu_power_on(struct generic_pm_domain *genpd)
    193{
    194	struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
    195	int ret;
    196	int i;
    197
    198	regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
    199			   GEN_PWR_VPU_HDMI, 0);
    200	udelay(20);
    201
    202	/* Power Up Memories */
    203	for (i = 0; i < 32; i += 2) {
    204		regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
    205				   0x3 << i, 0);
    206		udelay(5);
    207	}
    208
    209	for (i = 0; i < 32; i += 2) {
    210		regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
    211				   0x3 << i, 0);
    212		udelay(5);
    213	}
    214
    215	for (i = 0; i < 32; i += 2) {
    216		regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2,
    217				   0x3 << i, 0);
    218		udelay(5);
    219	}
    220
    221	for (i = 8; i < 16; i++) {
    222		regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
    223				   BIT(i), 0);
    224		udelay(5);
    225	}
    226	udelay(20);
    227
    228	ret = reset_control_assert(pd->rstc);
    229	if (ret)
    230		return ret;
    231
    232	regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
    233			   GEN_PWR_VPU_HDMI_ISO, 0);
    234
    235	ret = reset_control_deassert(pd->rstc);
    236	if (ret)
    237		return ret;
    238
    239	ret = meson_gx_pwrc_vpu_setup_clk(pd);
    240	if (ret)
    241		return ret;
    242
    243	return 0;
    244}
    245
    246static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd)
    247{
    248	u32 reg;
    249
    250	regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, &reg);
    251
    252	return (reg & GEN_PWR_VPU_HDMI);
    253}
    254
    255static struct meson_gx_pwrc_vpu vpu_hdmi_pd = {
    256	.genpd = {
    257		.name = "vpu_hdmi",
    258		.power_off = meson_gx_pwrc_vpu_power_off,
    259		.power_on = meson_gx_pwrc_vpu_power_on,
    260	},
    261};
    262
    263static struct meson_gx_pwrc_vpu vpu_hdmi_pd_g12a = {
    264	.genpd = {
    265		.name = "vpu_hdmi",
    266		.power_off = meson_g12a_pwrc_vpu_power_off,
    267		.power_on = meson_g12a_pwrc_vpu_power_on,
    268	},
    269};
    270
    271static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev)
    272{
    273	const struct meson_gx_pwrc_vpu *vpu_pd_match;
    274	struct regmap *regmap_ao, *regmap_hhi;
    275	struct meson_gx_pwrc_vpu *vpu_pd;
    276	struct reset_control *rstc;
    277	struct clk *vpu_clk;
    278	struct clk *vapb_clk;
    279	bool powered_off;
    280	int ret;
    281
    282	vpu_pd_match = of_device_get_match_data(&pdev->dev);
    283	if (!vpu_pd_match) {
    284		dev_err(&pdev->dev, "failed to get match data\n");
    285		return -ENODEV;
    286	}
    287
    288	vpu_pd = devm_kzalloc(&pdev->dev, sizeof(*vpu_pd), GFP_KERNEL);
    289	if (!vpu_pd)
    290		return -ENOMEM;
    291
    292	memcpy(vpu_pd, vpu_pd_match, sizeof(*vpu_pd));
    293
    294	regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node));
    295	if (IS_ERR(regmap_ao)) {
    296		dev_err(&pdev->dev, "failed to get regmap\n");
    297		return PTR_ERR(regmap_ao);
    298	}
    299
    300	regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
    301						     "amlogic,hhi-sysctrl");
    302	if (IS_ERR(regmap_hhi)) {
    303		dev_err(&pdev->dev, "failed to get HHI regmap\n");
    304		return PTR_ERR(regmap_hhi);
    305	}
    306
    307	rstc = devm_reset_control_array_get_exclusive(&pdev->dev);
    308	if (IS_ERR(rstc)) {
    309		if (PTR_ERR(rstc) != -EPROBE_DEFER)
    310			dev_err(&pdev->dev, "failed to get reset lines\n");
    311		return PTR_ERR(rstc);
    312	}
    313
    314	vpu_clk = devm_clk_get(&pdev->dev, "vpu");
    315	if (IS_ERR(vpu_clk)) {
    316		dev_err(&pdev->dev, "vpu clock request failed\n");
    317		return PTR_ERR(vpu_clk);
    318	}
    319
    320	vapb_clk = devm_clk_get(&pdev->dev, "vapb");
    321	if (IS_ERR(vapb_clk)) {
    322		dev_err(&pdev->dev, "vapb clock request failed\n");
    323		return PTR_ERR(vapb_clk);
    324	}
    325
    326	vpu_pd->regmap_ao = regmap_ao;
    327	vpu_pd->regmap_hhi = regmap_hhi;
    328	vpu_pd->rstc = rstc;
    329	vpu_pd->vpu_clk = vpu_clk;
    330	vpu_pd->vapb_clk = vapb_clk;
    331
    332	platform_set_drvdata(pdev, vpu_pd);
    333
    334	powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd);
    335
    336	/* If already powered, sync the clock states */
    337	if (!powered_off) {
    338		ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd);
    339		if (ret)
    340			return ret;
    341	}
    342
    343	vpu_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON;
    344	pm_genpd_init(&vpu_pd->genpd, NULL, powered_off);
    345
    346	return of_genpd_add_provider_simple(pdev->dev.of_node,
    347					    &vpu_pd->genpd);
    348}
    349
    350static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev)
    351{
    352	struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev);
    353	bool powered_off;
    354
    355	powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd);
    356	if (!powered_off)
    357		vpu_pd->genpd.power_off(&vpu_pd->genpd);
    358}
    359
    360static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = {
    361	{ .compatible = "amlogic,meson-gx-pwrc-vpu", .data = &vpu_hdmi_pd },
    362	{
    363	  .compatible = "amlogic,meson-g12a-pwrc-vpu",
    364	  .data = &vpu_hdmi_pd_g12a
    365	},
    366	{ /* sentinel */ }
    367};
    368MODULE_DEVICE_TABLE(of, meson_gx_pwrc_vpu_match_table);
    369
    370static struct platform_driver meson_gx_pwrc_vpu_driver = {
    371	.probe	= meson_gx_pwrc_vpu_probe,
    372	.shutdown = meson_gx_pwrc_vpu_shutdown,
    373	.driver = {
    374		.name		= "meson_gx_pwrc_vpu",
    375		.of_match_table	= meson_gx_pwrc_vpu_match_table,
    376	},
    377};
    378module_platform_driver(meson_gx_pwrc_vpu_driver);
    379MODULE_LICENSE("GPL v2");