summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/lima/lima_pmu.c
blob: e397e1146e96314808b4964576fc419988b11053 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */

#include <linux/iopoll.h>
#include <linux/device.h>

#include "lima_device.h"
#include "lima_pmu.h"
#include "lima_regs.h"

#define pmu_write(reg, data) writel(data, ip->iomem + reg)
#define pmu_read(reg) readl(ip->iomem + reg)

static int lima_pmu_wait_cmd(struct lima_ip *ip)
{
	struct lima_device *dev = ip->dev;
	int err;
	u32 v;

	err = readl_poll_timeout(ip->iomem + LIMA_PMU_INT_RAWSTAT,
				 v, v & LIMA_PMU_INT_CMD_MASK,
				 100, 100000);
	if (err) {
		dev_err(dev->dev, "timeout wait pmu cmd\n");
		return err;
	}

	pmu_write(LIMA_PMU_INT_CLEAR, LIMA_PMU_INT_CMD_MASK);
	return 0;
}

static u32 lima_pmu_get_ip_mask(struct lima_ip *ip)
{
	struct lima_device *dev = ip->dev;
	u32 ret = 0;
	int i;

	ret |= LIMA_PMU_POWER_GP0_MASK;

	if (dev->id == lima_gpu_mali400) {
		ret |= LIMA_PMU_POWER_L2_MASK;
		for (i = 0; i < 4; i++) {
			if (dev->ip[lima_ip_pp0 + i].present)
				ret |= LIMA_PMU_POWER_PP_MASK(i);
		}
	} else {
		if (dev->ip[lima_ip_pp0].present)
			ret |= LIMA450_PMU_POWER_PP0_MASK;
		for (i = lima_ip_pp1; i <= lima_ip_pp3; i++) {
			if (dev->ip[i].present) {
				ret |= LIMA450_PMU_POWER_PP13_MASK;
				break;
			}
		}
		for (i = lima_ip_pp4; i <= lima_ip_pp7; i++) {
			if (dev->ip[i].present) {
				ret |= LIMA450_PMU_POWER_PP47_MASK;
				break;
			}
		}
	}

	return ret;
}

static int lima_pmu_hw_init(struct lima_ip *ip)
{
	int err;
	u32 stat;

	pmu_write(LIMA_PMU_INT_MASK, 0);

	/* If this value is too low, when in high GPU clk freq,
	 * GPU will be in unstable state.
	 */
	pmu_write(LIMA_PMU_SW_DELAY, 0xffff);

	/* status reg 1=off 0=on */
	stat = pmu_read(LIMA_PMU_STATUS);

	/* power up all ip */
	if (stat) {
		pmu_write(LIMA_PMU_POWER_UP, stat);
		err = lima_pmu_wait_cmd(ip);
		if (err)
			return err;
	}
	return 0;
}

static void lima_pmu_hw_fini(struct lima_ip *ip)
{
	u32 stat;

	if (!ip->data.mask)
		ip->data.mask = lima_pmu_get_ip_mask(ip);

	stat = ~pmu_read(LIMA_PMU_STATUS) & ip->data.mask;
	if (stat) {
		pmu_write(LIMA_PMU_POWER_DOWN, stat);

		/* Don't wait for interrupt on Mali400 if all domains are
		 * powered off because the HW won't generate an interrupt
		 * in this case.
		 */
		if (ip->dev->id == lima_gpu_mali400)
			pmu_write(LIMA_PMU_INT_CLEAR, LIMA_PMU_INT_CMD_MASK);
		else
			lima_pmu_wait_cmd(ip);
	}
}

int lima_pmu_resume(struct lima_ip *ip)
{
	return lima_pmu_hw_init(ip);
}

void lima_pmu_suspend(struct lima_ip *ip)
{
	lima_pmu_hw_fini(ip);
}

int lima_pmu_init(struct lima_ip *ip)
{
	return lima_pmu_hw_init(ip);
}

void lima_pmu_fini(struct lima_ip *ip)
{
	lima_pmu_hw_fini(ip);
}