| /* |
| * 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(); |
| } |