summaryrefslogtreecommitdiff
path: root/drivers/staging/r8188eu/hal/HalPwrSeqCmd.c
blob: 6c0b1368383da9c8c8b4c9a23d9cf1d9aa1dc14d (plain)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2007 - 2011 Realtek Corporation. */

#include "../include/HalPwrSeqCmd.h"

#define PWR_CMD_WRITE			0x01
     /*  offset: the read register offset */
     /*  msk: the mask of the write bits */
     /*  value: write value */
     /*  note: driver shall implement this cmd by read & msk after write */

#define PWR_CMD_POLLING			0x02
     /*  offset: the read register offset */
     /*  msk: the mask of the polled value */
     /*  value: the value to be polled, masked by the msd field. */
     /*  note: driver shall implement this cmd by */
     /*  do{ */
     /*  if ( (Read(offset) & msk) == (value & msk) ) */
     /*  break; */
     /*  } while (not timeout); */

#define PWR_CMD_DELAY			0x03
     /*  offset: the value to delay (in us) */
     /*  msk: N/A */
     /*  value: N/A */

struct wl_pwr_cfg {
	u16 offset;
	u8 cmd:4;
	u8 msk;
	u8 value;
};

#define GET_PWR_CFG_OFFSET(__PWR_CMD)		__PWR_CMD.offset
#define GET_PWR_CFG_CMD(__PWR_CMD)		__PWR_CMD.cmd
#define GET_PWR_CFG_MASK(__PWR_CMD)		__PWR_CMD.msk
#define GET_PWR_CFG_VALUE(__PWR_CMD)		__PWR_CMD.value

static struct wl_pwr_cfg rtl8188E_power_on_flow[] = {
	{ 0x0006, PWR_CMD_POLLING, BIT(1), BIT(1) },
	{ 0x0002, PWR_CMD_WRITE, BIT(0) | BIT(1), 0 }, /* reset BB */
	{ 0x0026, PWR_CMD_WRITE, BIT(7), BIT(7) }, /* schmitt trigger */
	{ 0x0005, PWR_CMD_WRITE, BIT(7), 0 }, /* disable HWPDN (control by DRV)*/
	{ 0x0005, PWR_CMD_WRITE, BIT(4) | BIT(3), 0 }, /* disable WL suspend*/
	{ 0x0005, PWR_CMD_WRITE, BIT(0), BIT(0) },
	{ 0x0005, PWR_CMD_POLLING, BIT(0), 0 },
	{ 0x0023, PWR_CMD_WRITE, BIT(4), 0 },
};

static struct wl_pwr_cfg rtl8188E_card_disable_flow[] = {
	{ 0x001F, PWR_CMD_WRITE, 0xFF, 0 }, /* turn off RF */
	{ 0x0023, PWR_CMD_WRITE, BIT(4), BIT(4) }, /* LDO Sleep mode */
	{ 0x0005, PWR_CMD_WRITE, BIT(1), BIT(1) }, /* turn off MAC by HW state machine */
	{ 0x0005, PWR_CMD_POLLING, BIT(1), 0 },
	{ 0x0026, PWR_CMD_WRITE, BIT(7), BIT(7) }, /* schmitt trigger */
	{ 0x0005, PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3) }, /* enable WL suspend */
	{ 0x0007, PWR_CMD_WRITE, 0xFF, 0 }, /* enable bandgap mbias in suspend */
	{ 0x0041, PWR_CMD_WRITE, BIT(4), 0 }, /* Clear SIC_EN register */
	{ 0xfe10, PWR_CMD_WRITE, BIT(4), BIT(4) }, /* Set USB suspend enable local register */
};

/* This is used by driver for LPSRadioOff Procedure, not for FW LPS Step */
static struct wl_pwr_cfg rtl8188E_enter_lps_flow[] = {
	{ 0x0522, PWR_CMD_WRITE, 0xFF, 0x7F },/* Tx Pause */
	{ 0x05F8, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */
	{ 0x05F9, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */
	{ 0x05FA, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */
	{ 0x05FB, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */
	{ 0x0002, PWR_CMD_WRITE, BIT(0), 0 }, /* CCK and OFDM are disabled, clocks are gated */
	{ 0x0002, PWR_CMD_DELAY, 0, 0 },
	{ 0x0100, PWR_CMD_WRITE, 0xFF, 0x3F }, /* Reset MAC TRX */
	{ 0x0101, PWR_CMD_WRITE, BIT(1), 0 }, /* check if removed later */
	{ 0x0553, PWR_CMD_WRITE, BIT(5), BIT(5) }, /* Respond TxOK to scheduler */
};

u8 HalPwrSeqCmdParsing(struct adapter *padapter, enum r8188eu_pwr_seq seq)
{
	struct wl_pwr_cfg pwrcfgcmd = {0};
	struct wl_pwr_cfg *pwrseqcmd;
	u8 poll_bit = false;
	u8 idx, num_steps;
	u8 value = 0;
	u32 offset = 0;
	u32 poll_count = 0; /*  polling autoload done. */
	u32 max_poll_count = 5000;
	int res;

	switch (seq) {
	case PWR_ON_FLOW:
		pwrseqcmd = rtl8188E_power_on_flow;
		num_steps = ARRAY_SIZE(rtl8188E_power_on_flow);
		break;
	case DISABLE_FLOW:
		pwrseqcmd = rtl8188E_card_disable_flow;
		num_steps = ARRAY_SIZE(rtl8188E_card_disable_flow);
		break;
	case LPS_ENTER_FLOW:
		pwrseqcmd = rtl8188E_enter_lps_flow;
		num_steps = ARRAY_SIZE(rtl8188E_enter_lps_flow);
		break;
	default:
		return false;
	}

	for (idx = 0; idx < num_steps; idx++) {
		pwrcfgcmd = pwrseqcmd[idx];

		switch (GET_PWR_CFG_CMD(pwrcfgcmd)) {
		case PWR_CMD_WRITE:
			offset = GET_PWR_CFG_OFFSET(pwrcfgcmd);

			/*  Read the value from system register */
			res = rtw_read8(padapter, offset, &value);
			if (res)
				return false;

			value &= ~(GET_PWR_CFG_MASK(pwrcfgcmd));
			value |= (GET_PWR_CFG_VALUE(pwrcfgcmd) & GET_PWR_CFG_MASK(pwrcfgcmd));

			/*  Write the value back to system register */
			rtw_write8(padapter, offset, value);
			break;
		case PWR_CMD_POLLING:
			poll_bit = false;
			offset = GET_PWR_CFG_OFFSET(pwrcfgcmd);
			do {
				res = rtw_read8(padapter, offset, &value);
				if (res)
					return false;

				value &= GET_PWR_CFG_MASK(pwrcfgcmd);
				if (value == (GET_PWR_CFG_VALUE(pwrcfgcmd) & GET_PWR_CFG_MASK(pwrcfgcmd)))
					poll_bit = true;
				else
					udelay(10);

				if (poll_count++ > max_poll_count)
					return false;
			} while (!poll_bit);
			break;
		case PWR_CMD_DELAY:
			udelay(GET_PWR_CFG_OFFSET(pwrcfgcmd));
			break;
		default:
			break;
		}
	}
	return true;
}