1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright(c) 2007 - 2011 Realtek Corporation. */
3 
4 #include "../include/HalPwrSeqCmd.h"
5 
6 #define PWR_CMD_WRITE			0x01
7      /*  offset: the read register offset */
8      /*  msk: the mask of the write bits */
9      /*  value: write value */
10      /*  note: driver shall implement this cmd by read & msk after write */
11 
12 #define PWR_CMD_POLLING			0x02
13      /*  offset: the read register offset */
14      /*  msk: the mask of the polled value */
15      /*  value: the value to be polled, masked by the msd field. */
16      /*  note: driver shall implement this cmd by */
17      /*  do{ */
18      /*  if ( (Read(offset) & msk) == (value & msk) ) */
19      /*  break; */
20      /*  } while (not timeout); */
21 
22 #define PWR_CMD_DELAY			0x03
23      /*  offset: the value to delay (in us) */
24      /*  msk: N/A */
25      /*  value: N/A */
26 
27 struct wl_pwr_cfg {
28 	u16 offset;
29 	u8 cmd:4;
30 	u8 msk;
31 	u8 value;
32 };
33 
34 #define GET_PWR_CFG_OFFSET(__PWR_CMD)		__PWR_CMD.offset
35 #define GET_PWR_CFG_CMD(__PWR_CMD)		__PWR_CMD.cmd
36 #define GET_PWR_CFG_MASK(__PWR_CMD)		__PWR_CMD.msk
37 #define GET_PWR_CFG_VALUE(__PWR_CMD)		__PWR_CMD.value
38 
39 static struct wl_pwr_cfg rtl8188E_power_on_flow[] = {
40 	{ 0x0006, PWR_CMD_POLLING, BIT(1), BIT(1) },
41 	{ 0x0002, PWR_CMD_WRITE, BIT(0) | BIT(1), 0 }, /* reset BB */
42 	{ 0x0026, PWR_CMD_WRITE, BIT(7), BIT(7) }, /* schmitt trigger */
43 	{ 0x0005, PWR_CMD_WRITE, BIT(7), 0 }, /* disable HWPDN (control by DRV)*/
44 	{ 0x0005, PWR_CMD_WRITE, BIT(4) | BIT(3), 0 }, /* disable WL suspend*/
45 	{ 0x0005, PWR_CMD_WRITE, BIT(0), BIT(0) },
46 	{ 0x0005, PWR_CMD_POLLING, BIT(0), 0 },
47 	{ 0x0023, PWR_CMD_WRITE, BIT(4), 0 },
48 };
49 
50 static struct wl_pwr_cfg rtl8188E_card_disable_flow[] = {
51 	{ 0x001F, PWR_CMD_WRITE, 0xFF, 0 }, /* turn off RF */
52 	{ 0x0023, PWR_CMD_WRITE, BIT(4), BIT(4) }, /* LDO Sleep mode */
53 	{ 0x0005, PWR_CMD_WRITE, BIT(1), BIT(1) }, /* turn off MAC by HW state machine */
54 	{ 0x0005, PWR_CMD_POLLING, BIT(1), 0 },
55 	{ 0x0026, PWR_CMD_WRITE, BIT(7), BIT(7) }, /* schmitt trigger */
56 	{ 0x0005, PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3) }, /* enable WL suspend */
57 	{ 0x0007, PWR_CMD_WRITE, 0xFF, 0 }, /* enable bandgap mbias in suspend */
58 	{ 0x0041, PWR_CMD_WRITE, BIT(4), 0 }, /* Clear SIC_EN register */
59 	{ 0xfe10, PWR_CMD_WRITE, BIT(4), BIT(4) }, /* Set USB suspend enable local register */
60 };
61 
62 /* This is used by driver for LPSRadioOff Procedure, not for FW LPS Step */
63 static struct wl_pwr_cfg rtl8188E_enter_lps_flow[] = {
64 	{ 0x0522, PWR_CMD_WRITE, 0xFF, 0x7F },/* Tx Pause */
65 	{ 0x05F8, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */
66 	{ 0x05F9, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */
67 	{ 0x05FA, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */
68 	{ 0x05FB, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */
69 	{ 0x0002, PWR_CMD_WRITE, BIT(0), 0 }, /* CCK and OFDM are disabled, clocks are gated */
70 	{ 0x0002, PWR_CMD_DELAY, 0, 0 },
71 	{ 0x0100, PWR_CMD_WRITE, 0xFF, 0x3F }, /* Reset MAC TRX */
72 	{ 0x0101, PWR_CMD_WRITE, BIT(1), 0 }, /* check if removed later */
73 	{ 0x0553, PWR_CMD_WRITE, BIT(5), BIT(5) }, /* Respond TxOK to scheduler */
74 };
75 
HalPwrSeqCmdParsing(struct adapter * padapter,enum r8188eu_pwr_seq seq)76 u8 HalPwrSeqCmdParsing(struct adapter *padapter, enum r8188eu_pwr_seq seq)
77 {
78 	struct wl_pwr_cfg pwrcfgcmd = {0};
79 	struct wl_pwr_cfg *pwrseqcmd;
80 	u8 poll_bit = false;
81 	u8 idx, num_steps;
82 	u8 value = 0;
83 	u32 offset = 0;
84 	u32 poll_count = 0; /*  polling autoload done. */
85 	u32 max_poll_count = 5000;
86 	int res;
87 
88 	switch (seq) {
89 	case PWR_ON_FLOW:
90 		pwrseqcmd = rtl8188E_power_on_flow;
91 		num_steps = ARRAY_SIZE(rtl8188E_power_on_flow);
92 		break;
93 	case DISABLE_FLOW:
94 		pwrseqcmd = rtl8188E_card_disable_flow;
95 		num_steps = ARRAY_SIZE(rtl8188E_card_disable_flow);
96 		break;
97 	case LPS_ENTER_FLOW:
98 		pwrseqcmd = rtl8188E_enter_lps_flow;
99 		num_steps = ARRAY_SIZE(rtl8188E_enter_lps_flow);
100 		break;
101 	default:
102 		return false;
103 	}
104 
105 	for (idx = 0; idx < num_steps; idx++) {
106 		pwrcfgcmd = pwrseqcmd[idx];
107 
108 		switch (GET_PWR_CFG_CMD(pwrcfgcmd)) {
109 		case PWR_CMD_WRITE:
110 			offset = GET_PWR_CFG_OFFSET(pwrcfgcmd);
111 
112 			/*  Read the value from system register */
113 			res = rtw_read8(padapter, offset, &value);
114 			if (res)
115 				return false;
116 
117 			value &= ~(GET_PWR_CFG_MASK(pwrcfgcmd));
118 			value |= (GET_PWR_CFG_VALUE(pwrcfgcmd) & GET_PWR_CFG_MASK(pwrcfgcmd));
119 
120 			/*  Write the value back to system register */
121 			rtw_write8(padapter, offset, value);
122 			break;
123 		case PWR_CMD_POLLING:
124 			poll_bit = false;
125 			offset = GET_PWR_CFG_OFFSET(pwrcfgcmd);
126 			do {
127 				res = rtw_read8(padapter, offset, &value);
128 				if (res)
129 					return false;
130 
131 				value &= GET_PWR_CFG_MASK(pwrcfgcmd);
132 				if (value == (GET_PWR_CFG_VALUE(pwrcfgcmd) & GET_PWR_CFG_MASK(pwrcfgcmd)))
133 					poll_bit = true;
134 				else
135 					udelay(10);
136 
137 				if (poll_count++ > max_poll_count)
138 					return false;
139 			} while (!poll_bit);
140 			break;
141 		case PWR_CMD_DELAY:
142 			udelay(GET_PWR_CFG_OFFSET(pwrcfgcmd));
143 			break;
144 		default:
145 			break;
146 		}
147 	}
148 	return true;
149 }
150