summaryrefslogtreecommitdiffstats
path: root/carl9170fw/carlfw/src/main.c
blob: 17cbaf922d06500a76234b4e78988edfc19cc76f (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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/*
 * carl9170 firmware - used by the ar9170 wireless device
 *
 * initialization and main() loop
 *
 * Copyright (c) 2000-2005 ZyDAS Technology Corporation
 * Copyright (c) 2007-2009 Atheros Communications, Inc.
 * Copyright	2009	Johannes Berg <johannes@sipsolutions.net>
 * Copyright 2009-2011	Christian Lamparter <chunkeey@googlemail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "carl9170.h"
#include "timer.h"
#include "hostif.h"
#include "printf.h"
#include "gpio.h"
#include "wl.h"
#include "rf.h"
#include "usb.h"

#define AR9170_WATCH_DOG_TIMER		   0x100

static void timer_init(const unsigned int timer, const unsigned int interval)
{
	/* Set timer to periodic mode */
	orl(AR9170_TIMER_REG_CONTROL, BIT(timer));

	/* Set time interval */
	set(AR9170_TIMER_REG_TIMER0 + (timer << 2), interval - 1);

	/* Clear timer interrupt flag */
	orl(AR9170_TIMER_REG_INTERRUPT, BIT(timer));
}

void clock_set(enum cpu_clock_t clock_, bool on)
{
	/*
	 * Word of Warning!
	 * This setting does more than just mess with the CPU Clock.
	 * So watch out, if you need _stable_ timer interrupts.
	 */
#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
	if (fw.phy.frequency < 3000000)
		set(AR9170_PWR_REG_PLL_ADDAC, 0x5163);
	else
		set(AR9170_PWR_REG_PLL_ADDAC, 0x5143);
#else
	set(AR9170_PWR_REG_PLL_ADDAC, 0x5163);
#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */

	fw.ticks_per_usec = GET_VAL(AR9170_PWR_PLL_ADDAC_DIV,
		get(AR9170_PWR_REG_PLL_ADDAC));

	set(AR9170_PWR_REG_CLOCK_SEL, (uint32_t) ((on ? 0x70 : 0x600) | clock_));

	switch (clock_) {
	case AHB_20_22MHZ:
		fw.ticks_per_usec >>= 1;
	case AHB_40MHZ_OSC:
	case AHB_40_44MHZ:
		fw.ticks_per_usec >>= 1;
	case AHB_80_88MHZ:
		break;
	}
}

static void init(void)
{
	led_init();

#ifdef CONFIG_CARL9170FW_DEBUG_UART
	uart_init();
#endif /* CONFIG_CARL9170FW_DEBUG_UART */

	/* 25/50/100ms timer (depends on cpu clock) */
	timer_init(0, 50000);

	/* USB init */
	usb_init();

	/* initialize DMA memory */
	memset(&dma_mem, 0, sizeof(dma_mem));

	/* fill DMA rings */
	dma_init_descriptors();

	/* clear all interrupt */
	set(AR9170_MAC_REG_INT_CTRL, 0xffff);

	orl(AR9170_MAC_REG_AFTER_PNP, 1);

	/* Init watch dog control flag */
	fw.watchdog_enable = 1;

	set(AR9170_TIMER_REG_WATCH_DOG, AR9170_WATCH_DOG_TIMER);

#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT
	fw.cached_gpio_state.gpio = get(AR9170_GPIO_REG_PORT_DATA) &
				    CARL9170_GPIO_MASK;
#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */

	/* this will get the downqueue moving. */
	down_trigger();
}

static void handle_fw(void)
{
	if (fw.watchdog_enable == 1)
		set(AR9170_TIMER_REG_WATCH_DOG, AR9170_WATCH_DOG_TIMER);

	if (fw.reboot)
		reboot();
}

static void timer0_isr(void)
{
	wlan_timer();

#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT
	gpio_timer();
#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */

#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT
	set(AR9170_GPIO_REG_PORT_DATA, get(AR9170_GPIO_REG_PORT_DATA) ^ 1);
#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */
}

static void handle_timer(void)
{
	uint32_t intr;

	intr = get(AR9170_TIMER_REG_INTERRUPT);

	/* ACK timer interrupt */
	set(AR9170_TIMER_REG_INTERRUPT, intr);

#define HANDLER(intr, flag, func)			\
	do {						\
		if ((intr & flag) != 0) {		\
			intr &= ~flag;			\
			func();				\
		}					\
	} while (0)

	HANDLER(intr, BIT(0), timer0_isr);

	if (intr)
		DBG("Unhandled Timer Event %x", (unsigned int) intr);

#undef HANDLER
}

static void tally_update(void)
{
	unsigned int boff, time, delta;

	time = get_clock_counter();
	if (fw.phy.state == CARL9170_PHY_ON) {
		delta = (time - fw.tally_clock);

		fw.tally.active += delta;

		boff = get(AR9170_MAC_REG_BACKOFF_STATUS);
		if (boff & AR9170_MAC_BACKOFF_TX_PE)
			fw.tally.tx_time += delta;
		if (boff & AR9170_MAC_BACKOFF_CCA)
			fw.tally.cca += delta;
	}

	fw.tally_clock = time;
	fw.counter++;
}

static void __noreturn main_loop(void)
{
	/* main loop */
	while (1) {
		handle_fw();

		/*
		 * Due to frame order persevation, the wlan subroutines
		 * must be executed before handle_host_interface.
		 */
		handle_wlan();

		handle_host_interface();

		handle_usb();

		handle_timer();

		tally_update();
	}
}

/*
 * The bootcode will work with the device driver to load the firmware
 * onto the device's Program SRAM. The Program SRAM has a size of 16 KB
 * and also contains the stack, which grows down from 0x204000.
 *
 * The Program SRAM starts at address 0x200000 on the device.
 * The firmware entry point (0x200004) is located in boot.S.
 * we put _start() there with the linker script carl9170.lds.
 */

void __section(boot) start(void)
{
	clock_set(AHB_40MHZ_OSC, true);

	/* watchdog magic pattern check */
	if ((get(AR9170_PWR_REG_WATCH_DOG_MAGIC) & 0xffff0000) == 0x12340000) {
		/* watch dog warm start */
		incl(AR9170_PWR_REG_WATCH_DOG_MAGIC);
		usb_trigger_out();
	} else if ((get(AR9170_PWR_REG_WATCH_DOG_MAGIC) & 0xffff0000) == 0x98760000) {
		/* suspend/resume */
	}

	/* write the magic pattern for watch dog */
	andl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0xFFFF);
	orl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0x12340000);

	init();

#ifdef CONFIG_CARL9170FW_DEBUG

	BUG("TEST BUG");
	BUG_ON(0x2b || !0x2b);
	INFO("INFO MESSAGE");

	/* a set of unique characters to detect transfer data corruptions */
	DBG("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
	    " ~`!1@2#3$4%%5^6&7*8(9)0_-+={[}]|\\:;\"'<,>.?/");
#endif /* CONFIG_CARL9170FW_DEBUG */

	/*
	 * Tell the host, that the firmware has booted and is
	 * now ready to process requests.
	 */
	send_cmd_to_host(0, CARL9170_RSP_BOOT, 0x00, NULL);
	main_loop();
}