Commit 32eb7c73 authored by Xose Vazquez Perez's avatar Xose Vazquez Perez Committed by Ben Hutchings
Browse files

linux-firmware: add carl9170 firmware



GPLv2 firmware for carl9170, Atheros AR9170 802.11 draft-n USB driver.

Cc: Christian Lamparter <chunkeey@googlemail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: default avatarXose Vazquez Perez <xose.vazquez@gmail.com>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 8cf14f0a
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* printf and his friends...
*
* 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.
*/
#ifndef __CARL9170FW_PRINTF_H
#define __CARL9170FW_PRINTF_H
#include <stdarg.h>
#include <string.h>
#include "config.h"
#include "carl9170.h"
#include "uart.h"
#include "fwcmd.h"
#ifdef CONFIG_CARL9170FW_PRINTF
void __attribute__((format (printf, 1, 2))) tfp_printf(const char *fmt, ...);
#define printf tfp_printf
#else
void __attribute__((format (printf, 1, 2))) min_printf(const char *fmt, ...);
#define printf min_printf
#endif /* CONFIG_CARL9170FW_PRINTF */
#define PRINT(fmt, args...) \
do { \
printf(fmt, ## args); \
} while (0)
#define INFO(fmt, args...) PRINT(fmt, ## args)
#define ERR(fmt, args...) PRINT(CARL9170_ERR_MAGIC fmt, ## args)
#ifdef CONFIG_CARL9170FW_DEBUG
#define DBG(fmt, args...) PRINT(fmt, ## args)
#else
#define DBG(...) do { } while (0);
#endif
/*
* NB: even though the MACRO is called "stall". It isn't supposed
* to stall since this will render the device unresponsive, until
* someone pulls the plug.
*/
#define STALL()
#define BUG(fmt, args...) \
do { \
PRINT(CARL9170_BUG_MAGIC" %s()@%d \"" fmt "\"" , \
__func__, __LINE__, ## args); \
STALL() \
} while (0);
#define BUG_ON(condition) \
({ \
int __ret = !!(condition); \
if (unlikely(!!(__ret))) \
BUG(#condition); \
(__ret); \
})
static inline __inline void putcharacter(const char c __unused)
{
#ifdef CONFIG_CARL9170FW_DEBUG_USB
usb_putc(c);
#endif /* CONFIG_CARL9170FW_DEBUG_USB */
#ifdef CONFIG_CARL9170FW_DEBUG_UART
uart_putc(c);
#endif /* CONFIG_CARL9170FW_DEBUG_UART */
}
static inline __inline void print_hex_dump(const void *buf __unused, int len __unused)
{
#ifdef CONFIG_CARL9170FW_DEBUG_USB
usb_print_hex_dump(buf, len);
#endif /* CONFIG_CARL9170FW_DEBUG_USB */
#ifdef CONFIG_CARL9170FW_DEBUG_UART
uart_print_hex_dump(buf, len);
#endif /* CONFIG_CARL9170FW_DEBUG_UART */
}
#endif /* __CARL9170FW_PRINTF_H */
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* RF routine definitions
*
* 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.
*/
#ifndef __CARL9170FW_RF_H
#define __CARL9170FW_RF_H
#include "config.h"
#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
void rf_notify_set_channel(void);
void rf_cmd(const struct carl9170_cmd *cmd, struct carl9170_rsp *resp);
void rf_psm(void);
#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
#endif /* __CARL9170FW_RF_H */
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* ROM layout
*
* 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.
*/
#ifndef __CARL9170FW_ROM_H
#define __CARL9170FW_ROM_H
#include "types.h"
#include "config.h"
#include "compiler.h"
#include "usb.h"
#include "eeprom.h"
struct ar9170_hwtype {
/* 0x00001370 */
uint8_t data[4];
/* 0x00001374 */
struct ar9170_led_mode led_mode[AR9170_NUM_LEDS];
/* 0x00001378 */
uint8_t nulldata[2];
struct {
/* 0x0000137a */
struct usb_device_descriptor device_desc;
/* 0x0000138c */
uint8_t string0_desc[4];
/* 0x00001390 */
uint8_t string1_desc[32];
/* 0x000013b0 */
uint8_t string2_desc[48];
/* 0x000013e0 */
uint8_t string3_desc[32];
} usb;
} __packed;
struct ar9170_rom {
/* 0x00000000 */
uint32_t *irq_table[2];
/* 0x00000008 */
uint8_t bootcode[4968];
/* 0x00001370 */
struct ar9170_hwtype hw;
/* 0x00001400 */
uint8_t data[512];
/* eeprom */
struct ar9170_eeprom sys;
} __packed;
static const struct ar9170_rom rom __section(eeprom);
#endif /* __CARL9170FW_ROM_H */
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* Clock, Timer & Timing
*
* 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.
*/
#ifndef __CARL9170FW_TIMER_H
#define __CARL9170FW_TIMER_H
#include "config.h"
enum cpu_clock_t {
AHB_40MHZ_OSC = 0,
AHB_20_22MHZ = 1,
AHB_40_44MHZ = 2,
AHB_80_88MHZ = 3
};
static inline __inline uint32_t get_clock_counter(void)
{
return (get(AR9170_TIMER_REG_CLOCK_HIGH) << 16) | get(AR9170_TIMER_REG_CLOCK_LOW);
}
/*
* works only up to 97 secs [44 MHz] or 107 secs for 40 MHz
* Also, the delay wait will be affected by 2.4GHz<->5GHz
* band changes.
*/
static inline __inline bool is_after_msecs(const uint32_t t0, const uint32_t msecs)
{
return ((get_clock_counter() - t0) / 1000) > (msecs * fw.ticks_per_usec);
}
/*
* Note: Be careful with [u]delay. They won't service the
* hardware watchdog timer. It might trigger if you
* wait long enough. Also they don't terminate if sec is
* above 97 sec [44MHz] or more than 107 sec [40MHz].
*/
static inline __inline void delay(const uint32_t msec)
{
uint32_t t1, t2, dt, wt;
wt = msec * fw.ticks_per_usec;
t1 = get_clock_counter();
while (1) {
t2 = get_clock_counter();
dt = (t2 - t1) / 1000;
if (dt >= wt)
break;
}
}
static inline __inline void udelay(const uint32_t usec)
{
uint32_t t1, t2, dt;
t1 = get_clock_counter();
while (1) {
t2 = get_clock_counter();
dt = (t2 - t1);
if (dt >= (usec * fw.ticks_per_usec))
break;
}
}
void clock_set(enum cpu_clock_t _clock, bool on);
#endif /* __CARL9170FW_TIMER_H */
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* UART functions definition
*
* 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.
*/
#ifndef __CARL9170FW_UART_H
#define __CARL9170FW_UART_H
#include "config.h"
#ifdef CONFIG_CARL9170FW_DEBUG_UART
void uart_putc(const char c);
void uart_print_hex_dump(const void *buf, const int len);
void uart_init(void);
#endif /* CONFIG_CARL9170FW_DEBUG_UART */
#endif /* __CARL9170FW_UART_H */
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* USB definitions
*
* 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.
*/
#ifndef __CARL9170FW_USB_H
#define __CARL9170FW_USB_H
#include "config.h"
#include "types.h"
#include "io.h"
#include "hw.h"
#include "ch9.h"
struct ar9170_usb_config {
struct usb_config_descriptor cfg;
struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor ep[AR9170_USB_NUM_EXTRA_EP];
} __packed;
static inline __inline bool usb_detect_highspeed(void)
{
return !!(getb(AR9170_USB_REG_MAIN_CTRL) &
AR9170_USB_MAIN_CTRL_HIGHSPEED);
}
static inline __inline bool usb_configured(void)
{
return !!(getb(AR9170_USB_REG_DEVICE_ADDRESS) &
AR9170_USB_DEVICE_ADDRESS_CONFIGURE);
}
static inline __inline void usb_enable_remote_wakeup(void)
{
orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_REMOTE_WAKEUP);
}
static inline __inline void usb_disable_remote_wakeup(void)
{
andb(AR9170_USB_REG_MAIN_CTRL, ~AR9170_USB_MAIN_CTRL_REMOTE_WAKEUP);
}
static inline __inline void usb_enable_global_int(void)
{
orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_ENABLE_GLOBAL_INT);
}
static inline __inline void usb_trigger_out(void)
{
andb(AR9170_USB_REG_INTR_MASK_BYTE_4,
(uint8_t) ~AR9170_USB_INTR_DISABLE_OUT_INT);
}
static inline __inline void usb_reset_out(void)
{
orb(AR9170_USB_REG_INTR_MASK_BYTE_4, AR9170_USB_INTR_DISABLE_OUT_INT);
}
static inline __inline void usb_trigger_in(void)
{
andb(AR9170_USB_REG_INTR_MASK_BYTE_6, ~AR9170_USB_INTR_DISABLE_IN_INT);
}
static inline __inline void usb_reset_in(void)
{
orb(AR9170_USB_REG_INTR_MASK_BYTE_6, AR9170_USB_INTR_DISABLE_IN_INT);
}
static inline __inline void usb_ep3_xfer_done(void)
{
orb(AR9170_USB_REG_EP3_BYTE_COUNT_HIGH, 0x08);
}
static inline __inline void usb_suspend_ack(void)
{
/*
* uP must do-over everything it should handle
* and do before into the suspend mode
*/
andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(2));
}
static inline __inline void usb_resume_ack(void)
{
/*
* uP must do-over everything it should handle
* and do before into the suspend mode
*/
andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(3));
}
static inline __inline void usb_reset_ack(void)
{
andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(1));
}
static inline __inline void usb_data_out0Byte(void)
{
andb(AR9170_USB_REG_INTR_SOURCE_7, (uint8_t) ~BIT(7));
}
static inline __inline void usb_data_in0Byte(void)
{
andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(6));
}
static inline __inline void usb_stop_down_queue(void)
{
andl(AR9170_USB_REG_DMA_CTL, ~AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE);
}
static inline __inline void usb_start_down_queue(void)
{
orl(AR9170_USB_REG_DMA_CTL, AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE);
}
static inline __inline void usb_clear_input_ep_toggle(unsigned int ep)
{
andl(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1),
~AR9170_USB_EP_IN_TOGGLE);
}
static inline __inline void usb_set_input_ep_toggle(unsigned int ep)
{
orl(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1),
AR9170_USB_EP_IN_TOGGLE);
}
static inline __inline void usb_clear_output_ep_toggle(unsigned int ep)
{
andl(AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH + (ep << 1),
~AR9170_USB_EP_OUT_TOGGLE);
}
static inline __inline void usb_set_output_ep_toggle(unsigned int ep)
{
orl(AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH + (ep << 1),
AR9170_USB_EP_OUT_TOGGLE);
}
static inline void usb_structure_check(void)
{
BUILD_BUG_ON(sizeof(struct usb_config_descriptor) != USB_DT_CONFIG_SIZE);
BUILD_BUG_ON(sizeof(struct usb_device_descriptor) != USB_DT_DEVICE_SIZE);
BUILD_BUG_ON(sizeof(struct usb_endpoint_descriptor) != USB_DT_ENDPOINT_SIZE);
BUILD_BUG_ON(sizeof(struct usb_interface_descriptor) != USB_DT_INTERFACE_SIZE);
}
void __noreturn jump_to_bootcode(void);
void send_cmd_to_host(const uint8_t len, const uint8_t type,
const uint8_t ext, const uint8_t *body);
void usb_init(void);
void usb_ep0rx(void);
void usb_ep0tx(void);
void usb_ep0setup(void);
void handle_usb(void);
void usb_timer(void);
void usb_putc(const char c);
void usb_print_hex_dump(const void *buf, int len);
void usb_init_highspeed_fifo_cfg(void);
void usb_init_fullspeed_fifo_cfg(void);
void __noreturn start(void);
void __noreturn reboot(void);
#endif /* __CARL9170FW_USB_H */
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* USB definitions
*
* 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.
*/
#ifndef __CARL9170FW_USB_FIFO_H
#define __CARL9170FW_USB_FIFO_H
#include "config.h"
#define MASK_F0 0xf0
/* Block Size define */
#define BLK512BYTE 1
#define BLK1024BYTE 2
#define BLK64BYTE 1
#define BLK128BYTE 2
/* Block toggle number define */
#define SINGLE_BLK 1
#define DOUBLE_BLK 2
#define TRIBLE_BLK 3
/* Endpoint transfer type */
#define TF_TYPE_ISOCHRONOUS 1
#define TF_TYPE_BULK 2
#define TF_TYPE_INTERRUPT 3
/* Endpoint or FIFO direction define */
#define DIRECTION_IN 0
#define DIRECTION_OUT 1
#define HS_C1_I0_A0_EP1_MAX_PACKET 512
#define HS_C1_I0_A0_EP1_bInterval 0
#define HS_C1_I0_A0_EP_NUMBER 0x04
#define HS_C1_I0_A0_EP_LENGTH (EP_LENGTH * HS_C1_I0_A0_EP_NUMBER)
#define HS_C1_I0_ALT_LENGTH (HS_C1_I0_A0_EP_LENGTH)
#define HS_C1_INTERFACE_LENGTH (HS_C1_I0_ALT_LENGTH)
#define HS_C1_CONFIG_TOTAL_LENGTH (CONFIG_LENGTH + INTERFACE_LENGTH + HS_C1_INTERFACE_LENGTH)
#define FS_C1_CONFIG_TOTAL_LENGTH (CONFIG_LENGTH + INTERFACE_LENGTH + FS_C1_INTERFACE_LENGTH)
#define FS_C1_I0_A0_EP1_MAX_PACKET 64
/* #define FS_C1_I0_A0_EP1_bInterval HS_C1_I0_A0_EP1_bInterval */
#define HS_CONFIGURATION_NUMBER 1
#define FS_CONFIGURATION_NUMBER 1
#define fDOUBLE_BUF 1
#define fDOUBLE_BUF_IN 0
#define fFLASH_DISK 0
#define fENABLE_ISO 0
#define HS_C1_INTERFACE_NUMBER 0x01
#define HS_C1 0x01
#define HS_C1_iConfiguration 0x00
#define HS_C1_bmAttribute 0x80
#define HS_C1_iMaxPower 0xFA
/* Interface 0 */
#define HS_C1_I0_ALT_NUMBER 0X01
/* AlternateSetting 0 */
#define HS_C1_I0_A0_bInterfaceNumber 0x00
#define HS_C1_I0_A0_bAlternateSetting 0x00
/* JWEI 2003/07/14 */
#define HS_C1_I0_A0_EP_NUMBER 0x04
#define HS_C1_I0_A0_bInterfaceClass 0xff
#define HS_C1_I0_A0_bInterfaceSubClass 0x00
#define HS_C1_I0_A0_bInterfaceProtocol 0x00
#define HS_C1_I0_A0_iInterface 0x00
/* EP 1 */
#define HS_C1_I0_A0_EP1_BLKSIZE 512
#define HS_C1_I0_A0_EP1_BLKNO DOUBLE_BLK
#define HS_C1_I0_A0_EP1_DIRECTION DIRECTION_OUT
#define HS_C1_I0_A0_EP1_TYPE TF_TYPE_BULK
#define HS_C1_I0_A0_EP1_MAX_PACKET 512
#define HS_C1_I0_A0_EP1_bInterval 0
/* EP 2 */
#define HS_C1_I0_A0_EP2_BLKSIZE 512
/* JWEI 2003/08/20 */
#define HS_C1_I0_A0_EP2_BLKNO SINGLE_BLK
#define HS_C1_I0_A0_EP2_DIRECTION DIRECTION_IN
#define HS_C1_I0_A0_EP2_TYPE TF_TYPE_BULK
#define HS_C1_I0_A0_EP2_MAX_PACKET 512
#define HS_C1_I0_A0_EP2_bInterval 0
/* EP 3 */
#define HS_C1_I0_A0_EP3_BLKSIZE 64
#define HS_C1_I0_A0_EP3_BLKNO SINGLE_BLK
#define HS_C1_I0_A0_EP3_DIRECTION DIRECTION_IN
#define HS_C1_I0_A0_EP3_TYPE TF_TYPE_INTERRUPT
#define HS_C1_I0_A0_EP3_MAX_PACKET 0x0040
#define HS_C1_I0_A0_EP3_bInterval 01
/*
* Note: HS Bulk type require max pkt size = 512
* ==> must use Interrupt type for max pkt size = 64
*/
/* EP 4 */
#define HS_C1_I0_A0_EP4_BLKSIZE 64
#define HS_C1_I0_A0_EP4_BLKNO SINGLE_BLK
#define HS_C1_I0_A0_EP4_DIRECTION DIRECTION_OUT
#define HS_C1_I0_A0_EP4_TYPE TF_TYPE_INTERRUPT
#define HS_C1_I0_A0_EP4_MAX_PACKET 0x0040
#define HS_C1_I0_A0_EP4_bInterval 01
#define HS_C1_I0_A0_EP_LENGTH (EP_LENGTH * HS_C1_I0_A0_EP_NUMBER)
/* EP 1 */
#define HS_C1_I0_A0_EP1_FIFO_START 0
#define HS_C1_I0_A0_EP1_FIFO_NO (HS_C1_I0_A0_EP1_BLKNO * HS_C1_I0_A0_EP1_BLKSIZE)
#define HS_C1_I0_A0_EP1_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP1_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP1_BLKNO - 1) << 2) | HS_C1_I0_A0_EP1_TYPE)
#define HS_C1_I0_A0_EP1_FIFO_MAP (((1 - HS_C1_I0_A0_EP1_DIRECTION) << 4) | 1)
#define HS_C1_I0_A0_EP1_MAP (HS_C1_I0_A0_EP1_FIFO_START | (HS_C1_I0_A0_EP1_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP1_DIRECTION)))
/* EP 2 */
#define HS_C1_I0_A0_EP2_FIFO_START (uint8_t)(HS_C1_I0_A0_EP1_FIFO_START + HS_C1_I0_A0_EP1_FIFO_NO)
#define HS_C1_I0_A0_EP2_FIFO_NO (uint8_t)(HS_C1_I0_A0_EP2_BLKNO * HS_C1_I0_A0_EP2_BLKSIZE)
#define HS_C1_I0_A0_EP2_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP2_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP2_BLKNO - 1) << 2) | HS_C1_I0_A0_EP2_TYPE)
#define HS_C1_I0_A0_EP2_FIFO_MAP (uint8_t)(((1 - HS_C1_I0_A0_EP2_DIRECTION) << 4) | 2)
#define HS_C1_I0_A0_EP2_MAP (uint8_t)(HS_C1_I0_A0_EP2_FIFO_START | (HS_C1_I0_A0_EP2_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP2_DIRECTION)))
/* EP 3 */
#define HS_C1_I0_A0_EP3_FIFO_START 14
#define HS_C1_I0_A0_EP3_FIFO_NO (HS_C1_I0_A0_EP3_BLKNO * HS_C1_I0_A0_EP3_BLKSIZE)
#define HS_C1_I0_A0_EP3_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP3_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP3_BLKNO - 1) << 2) | HS_C1_I0_A0_EP3_TYPE)
#define HS_C1_I0_A0_EP3_FIFO_MAP (uint8_t)(((1 - HS_C1_I0_A0_EP3_DIRECTION) << 4) | 3)
#define HS_C1_I0_A0_EP3_MAP (uint8_t)(HS_C1_I0_A0_EP3_FIFO_START | (HS_C1_I0_A0_EP3_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP3_DIRECTION)))
/* EP 4 */
#define HS_C1_I0_A0_EP4_FIFO_START (HS_C1_I0_A0_EP3_FIFO_START + HS_C1_I0_A0_EP3_FIFO_NO)
#define HS_C1_I0_A0_EP4_FIFO_NO (HS_C1_I0_A0_EP4_BLKNO * HS_C1_I0_A0_EP4_BLKSIZE)
#define HS_C1_I0_A0_EP4_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP4_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP4_BLKNO - 1) << 2) | HS_C1_I0_A0_EP4_TYPE)
#define HS_C1_I0_A0_EP4_FIFO_MAP (((1 - HS_C1_I0_A0_EP4_DIRECTION) << 4) | 4)
#define HS_C1_I0_A0_EP4_MAP (uint8_t)(HS_C1_I0_A0_EP4_FIFO_START | (HS_C1_I0_A0_EP4_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP4_DIRECTION)))
/* Configuration 1 */
#define FS_C1_INTERFACE_NUMBER 0x01
#define FS_C1 0x01
#define FS_C1_iConfiguration 0x00
#define FS_C1_bmAttribute 0x80
#define FS_C1_iMaxPower 0xfa
/* Interface 0 */
#define FS_C1_I0_ALT_NUMBER 0x01
/* AlternateSetting 0x00 */
#define FS_C1_I0_A0_bInterfaceNumber 0x00
#define FS_C1_I0_A0_bAlternateSetting 0x00
#define FS_C1_I0_A0_EP_NUMBER 0x04
#define FS_C1_I0_A0_bInterfaceClass 0xff
#define FS_C1_I0_A0_bInterfaceSubClass 0x00
#define FS_C1_I0_A0_bInterfaceProtocol 0x00
/* EP 1 */
#define FS_C1_I0_A0_EP1_BLKSIZE 512
/* JWEI 2003/05/19 */
#define FS_C1_I0_A0_EP1_BLKNO DOUBLE_BLK
#define FS_C1_I0_A0_EP1_DIRECTION DIRECTION_OUT
#define FS_C1_I0_A0_EP1_TYPE TF_TYPE_BULK
#define FS_C1_I0_A0_EP1_MAX_PACKET 64
#define FS_C1_I0_A0_EP1_bInterval 0
/* EP 2 */
#define FS_C1_I0_A0_EP2_BLKSIZE 512
/* JWEI 2003/08/20 */
#define FS_C1_I0_A0_EP2_BLKNO SINGLE_BLK
#define FS_C1_I0_A0_EP2_DIRECTION DIRECTION_IN
#define FS_C1_I0_A0_EP2_TYPE TF_TYPE_BULK
#define FS_C1_I0_A0_EP2_MAX_PACKET 64
#define FS_C1_I0_A0_EP2_bInterval 0
/* EP 3 */
#define FS_C1_I0_A0_EP3_BLKSIZE 64
#define FS_C1_I0_A0_EP3_BLKNO SINGLE_BLK
#define FS_C1_I0_A0_EP3_DIRECTION DIRECTION_IN
#define FS_C1_I0_A0_EP3_TYPE TF_TYPE_INTERRUPT
#define FS_C1_I0_A0_EP3_MAX_PACKET 0x0040
#define FS_C1_I0_A0_EP3_bInterval 1
/* EP 4 */
#define FS_C1_I0_A0_EP4_BLKSIZE 64
#define FS_C1_I0_A0_EP4_BLKNO SINGLE_BLK
#define FS_C1_I0_A0_EP4_DIRECTION DIRECTION_OUT
#define FS_C1_I0_A0_EP4_TYPE TF_TYPE_BULK
#define FS_C1_I0_A0_EP4_MAX_PACKET 0x0040
#define FS_C1_I0_A0_EP4_bInterval 0
#define FS_C1_I0_A0_EP_LENGTH (EP_LENGTH * FS_C1_I0_A0_EP_NUMBER)
/* EP 1 */
#define FS_C1_I0_A0_EP1_FIFO_START 0
#define FS_C1_I0_A0_EP1_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP1_BLKNO * FS_C1_I0_A0_EP1_BLKSIZE)
#define FS_C1_I0_A0_EP1_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP1_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP1_BLKNO - 1) << 2) | FS_C1_I0_A0_EP1_TYPE)
#define FS_C1_I0_A0_EP1_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP1_DIRECTION) << 4) | 1)
#define FS_C1_I0_A0_EP1_MAP (uint8_t)(FS_C1_I0_A0_EP1_FIFO_START | (FS_C1_I0_A0_EP1_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP1_DIRECTION)))
/* EP 2 */
#define FS_C1_I0_A0_EP2_FIFO_START (uint8_t)(FS_C1_I0_A0_EP1_FIFO_START + FS_C1_I0_A0_EP1_FIFO_NO)
#define FS_C1_I0_A0_EP2_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP2_BLKNO * FS_C1_I0_A0_EP2_BLKSIZE)
#define FS_C1_I0_A0_EP2_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP2_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP2_BLKNO - 1) << 2) | FS_C1_I0_A0_EP2_TYPE)
#define FS_C1_I0_A0_EP2_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP2_DIRECTION) << 4) | 2)
#define FS_C1_I0_A0_EP2_MAP (uint8_t)(FS_C1_I0_A0_EP2_FIFO_START | (FS_C1_I0_A0_EP2_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP2_DIRECTION)))
/* EP 3 */
#define FS_C1_I0_A0_EP3_FIFO_START 14
#define FS_C1_I0_A0_EP3_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP3_BLKNO * FS_C1_I0_A0_EP3_BLKSIZE)
#define FS_C1_I0_A0_EP3_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP3_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP3_BLKNO - 1) << 2) | FS_C1_I0_A0_EP3_TYPE)
#define FS_C1_I0_A0_EP3_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP3_DIRECTION) << 4) | 3)
#define FS_C1_I0_A0_EP3_MAP (uint8_t)(FS_C1_I0_A0_EP3_FIFO_START | (FS_C1_I0_A0_EP3_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP3_DIRECTION)))
/* EP 4 */
#define FS_C1_I0_A0_EP4_FIFO_START (uint8_t)(FS_C1_I0_A0_EP3_FIFO_START + FS_C1_I0_A0_EP3_FIFO_NO)
#define FS_C1_I0_A0_EP4_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP4_BLKNO * FS_C1_I0_A0_EP4_BLKSIZE)
#define FS_C1_I0_A0_EP4_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP4_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP4_BLKNO - 1) << 2) | FS_C1_I0_A0_EP4_TYPE)
#define FS_C1_I0_A0_EP4_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP4_DIRECTION) << 4) | 4)
#define FS_C1_I0_A0_EP4_MAP (uint8_t)(FS_C1_I0_A0_EP4_FIFO_START | (FS_C1_I0_A0_EP4_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP4_DIRECTION)))
#endif /* __CARL9170FW_USB_FIFO_H */
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* WLAN
*
* 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.
*/
#ifndef __CARL9170FW_WLAN_H
#define __CARL9170FW_WLAN_H
#include "config.h"
#include "carl9170.h"
#include "io.h"
struct ieee80211_hdr;
static inline __inline void set_wlan_txq_dma_addr(const unsigned int q, const uint32_t v)
{
set(AR9170_MAC_REG_DMA_TXQ_ADDR + (q << 3), v);
}
static inline __inline void set_wlan_txq_dma_curr_addr(const unsigned int q, const uint32_t v)
{
set(AR9170_MAC_REG_DMA_TXQ_CURR_ADDR + (q << 3), v);
}
static inline __inline volatile struct dma_desc *get_wlan_txq_dma_addr(const unsigned int q)
{
return getp(AR9170_MAC_REG_DMA_TXQ_ADDR + (q << 3));
}
static inline __inline volatile struct dma_desc *get_wlan_txq_addr(const unsigned int q)
{
return getp(AR9170_MAC_REG_DMA_TXQ_CURR_ADDR + (q << 3));
}
static inline __inline volatile struct dma_desc *get_wlan_txq_last_addr(const unsigned int q)
{
return getp(AR9170_MAC_REG_DMA_TXQ_LAST_ADDR + (q << 2));
}
static inline __inline void wlan_trigger(const uint32_t queue_bit)
{
set(AR9170_MAC_REG_DMA_TRIGGER, queue_bit);
}
static inline __inline uint8_t ar9170_get_rx_macstatus_status(struct dma_desc *desc)
{
return *((uint8_t *) DESC_PAYLOAD_OFF(desc->lastAddr,
(unsigned int) desc->lastAddr->dataSize - 1));
}
static inline __inline uint8_t ar9170_get_rx_macstatus_error(struct dma_desc *desc)
{
unsigned int offset;
if (desc->lastAddr->dataSize == 1) {
while (desc->lastAddr != desc->nextAddr)
desc = desc->nextAddr;
offset = (unsigned int) (desc->dataSize - 1);
} else {
desc = desc->lastAddr;
offset = desc->dataSize -
(sizeof(struct ar9170_rx_macstatus) -
offsetof(struct ar9170_rx_macstatus, error));
}
return *((uint8_t *) DESC_PAYLOAD_OFF(desc, offset));
}
static inline __inline struct ieee80211_hdr *ar9170_get_rx_i3e(struct dma_desc *desc)
{
if (!((ar9170_get_rx_macstatus_status(desc) &
AR9170_RX_STATUS_MPDU) & AR9170_RX_STATUS_MPDU_LAST)) {
return (void *)(DESC_PAYLOAD_OFF(desc,
offsetof(struct ar9170_rx_frame_head, i3e)));
} else {
return (void *)(DESC_PAYLOAD_OFF(desc,
offsetof(struct ar9170_rx_frame_tail, i3e)));
}
}
static inline __inline struct ar9170_rx_head *ar9170_get_rx_head(struct dma_desc *desc)
{
if (!((ar9170_get_rx_macstatus_status(desc) &
AR9170_RX_STATUS_MPDU) & AR9170_RX_STATUS_MPDU_LAST)) {
return (void *)((uint8_t *)DESC_PAYLOAD(desc) +
offsetof(struct ar9170_rx_frame_head, phy_head));
} else {
return (void *) NULL;
}
}
static inline __inline uint32_t ar9170_rx_to_phy(struct dma_desc *rx)
{
struct ar9170_tx_hw_phy_control phy;
struct ar9170_rx_head *head;
uint8_t mac_status;
phy.set = 0;
head = ar9170_get_rx_head(rx);
if (!head)
return le32_to_cpu(phy.set);
mac_status = ar9170_get_rx_macstatus_status(rx);
phy.modulation = mac_status & AR9170_RX_STATUS_MODULATION;
phy.chains = AR9170_TX_PHY_TXCHAIN_1;
switch (phy.modulation) {
case AR9170_RX_STATUS_MODULATION_CCK:
if (mac_status & AR9170_RX_STATUS_SHORT_PREAMBLE)
phy.preamble = 1;
switch (head->plcp[0]) {
case AR9170_RX_PHY_RATE_CCK_2M:
phy.mcs = AR9170_TX_PHY_RATE_CCK_2M;
break;
case AR9170_RX_PHY_RATE_CCK_5M:
phy.mcs = AR9170_TX_PHY_RATE_CCK_5M;
break;
case AR9170_RX_PHY_RATE_CCK_11M:
phy.mcs = AR9170_TX_PHY_RATE_CCK_11M;
break;
case AR9170_RX_PHY_RATE_CCK_1M:
default:
phy.mcs = AR9170_TX_PHY_RATE_CCK_1M;
break;
}
break;
case AR9170_RX_STATUS_MODULATION_DUPOFDM:
case AR9170_RX_STATUS_MODULATION_OFDM:
phy.mcs = head->plcp[0] & 0xf;
break;
case AR9170_RX_STATUS_MODULATION_HT:
if (head->plcp[3] & 0x80)
phy.bandwidth = 2;
if (head->plcp[6] & 0x80)
phy.short_gi = 1;
/* TODO: Enable both chains for MCS > 7 */
phy.mcs = head->plcp[6] & 0x7;
break;
}
return le32_to_cpu(phy.set);
}
static inline __inline unsigned int ar9170_get_rx_mpdu_len(struct dma_desc *desc)
{
/*
* WARNING: you have to check the error bits in macstatus first!
*/
unsigned int mpdu_len = desc->totalLen;
mpdu_len -= sizeof(struct ar9170_rx_macstatus);
switch (ar9170_get_rx_macstatus_status(desc) & AR9170_RX_STATUS_MPDU) {
case AR9170_RX_STATUS_MPDU_LAST:
mpdu_len -= sizeof(struct ar9170_rx_phystatus);
break;
case AR9170_RX_STATUS_MPDU_SINGLE:
mpdu_len -= sizeof(struct ar9170_rx_phystatus);
case AR9170_RX_STATUS_MPDU_FIRST:
mpdu_len -= sizeof(struct ar9170_rx_head);
break;
case AR9170_RX_STATUS_MPDU_MIDDLE:
default:
break;
}
return mpdu_len;
}
static inline __inline bool ar9170_tx_length_check(const uint16_t len)
{
return len > (sizeof(struct carl9170_tx_superframe) + 24 +
FCS_LEN);
}
static inline __inline struct carl9170_tx_superframe *get_super(struct dma_desc *desc)
{
return container_of(DESC_PAYLOAD(desc), struct carl9170_tx_superframe,
f);
}
static inline __inline struct carl9170_tx_superframe *__get_super(struct dma_desc *desc)
{
return DESC_PAYLOAD(desc);
}
static inline __inline void hide_super(struct dma_desc *desc)
{
desc->dataAddr = (uint8_t *)
(((unsigned long)(DESC_PAYLOAD(desc)) +
offsetof(struct carl9170_tx_superframe, f)));
desc->dataSize -= sizeof(struct carl9170_tx_superdesc);
desc->totalLen -= sizeof(struct carl9170_tx_superdesc);
}
static inline __inline void unhide_super(struct dma_desc *desc)
{
desc->dataAddr = (uint8_t *) get_super(desc);
desc->dataSize += sizeof(struct carl9170_tx_superdesc);
desc->totalLen += sizeof(struct carl9170_tx_superdesc);
}
static inline __inline __hot void read_tsf(uint32_t *tsf)
{
/*
* "According to the [hardware] documentation:
* > when TSF_LOW is read, TSF_HI is automatically concurrently
* > copied into a temporary register so that an immediate read
* > of TSF_HI will get the value that was present when TSF_LOW
* > was read. "
*
* (David H. Lynch Jr. - mail from 2010-05-22)
* http://permalink.gmane.org/gmane.linux.kernel.wireless.general/51249
*/
tsf[0] = get(AR9170_MAC_REG_TSF_L);
tsf[1] = get(AR9170_MAC_REG_TSF_H);
}
/* This function will only work on uint32_t-aligned pointers! */
static inline bool compare_ether_address(const void *_d0, const void *_d1)
{
const uint32_t *d0 = _d0;
const uint32_t *d1 = _d1;
/* BUG_ON((unsigned long)d0 & 3 || (unsigned long)d1 & 3)) */
return !((d0[0] ^ d1[0]) | (unsigned short)(d0[1] ^ d1[1]));
}
void wlan_tx(struct dma_desc *desc);
void wlan_tx_fw(struct carl9170_tx_superdesc *super, fw_desc_callback_t cb);
void wlan_timer(void);
void handle_wlan(void);
void wlan_cab_flush_queue(const unsigned int vif);
void wlan_modify_beacon(const unsigned int vif,
const unsigned int bcn_addr,
const unsigned int bcn_len);
void wlan_tx_complete(struct carl9170_tx_superframe *super, bool txs);
void wlan_prepare_wol(void);
static inline void __check_wlantx(void)
{
BUILD_BUG_ON(CARL9170_TX_SUPERDESC_LEN & 3);
BUILD_BUG_ON(sizeof(struct carl9170_tx_superdesc) != CARL9170_TX_SUPERDESC_LEN);
BUILD_BUG_ON(sizeof(struct _carl9170_tx_superdesc) != CARL9170_TX_SUPERDESC_LEN);
BUILD_BUG_ON(sizeof(struct _carl9170_tx_superframe) != CARL9170_TX_SUPERFRAME_LEN);
BUILD_BUG_ON((offsetof(struct carl9170_tx_superframe, f) & 3) != 0);
BUILD_BUG_ON(offsetof(struct _carl9170_tx_superframe, f) !=
(offsetof(struct _carl9170_tx_superframe, f)));
BUILD_BUG_ON(sizeof(struct ar9170_tx_hwdesc) != AR9170_TX_HWDESC_LEN);
BUILD_BUG_ON(sizeof(struct _ar9170_tx_hwdesc) != AR9170_TX_HWDESC_LEN);
BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != AR9170_RX_HEAD_LEN);
BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != AR9170_RX_PHYSTATUS_LEN);
BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != AR9170_RX_MACSTATUS_LEN);
}
#endif /* __CARL9170FW_WLAN_H */
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* WakeUp on WLAN definitions
*
* 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.
*/
#ifndef __CARL9170FW_WOL_H
#define __CARL9170FW_WOL_H
#include "config.h"
#include "compiler.h"
#include "types.h"
#include "fwcmd.h"
#ifdef CONFIG_CARL9170FW_WOL
struct ieee80211_hdr;
void wol_prepare(void);
void wol_janitor(void);
void wol_rx(const unsigned int rx_filter __unused,
const struct ieee80211_hdr *hdr __unused,
const unsigned int len __unused);
void wol_cmd(const struct carl9170_wol_cmd *cmd);
#else
static inline void wol_cmd(const struct carl9170_wol_cmd *cmd __unused)
{
}
static inline void wol_prepare(void)
{
}
static inline void wol_janitor(void)
{
}
static inline void wol_rx(const unsigned int rx_filter __unused,
const struct ieee80211_hdr *hdr __unused,
const unsigned int len __unused)
{
}
#endif /* CONFIG_CARL9170FW_WOL */
#endif /* __CARL9170FW_CMD_H */
/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
2004, 2005, 2006
Free Software Foundation, Inc.
This file 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, or (at your option) any
later version.
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
This file 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; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
!! libgcc routines for the Renesas / SuperH SH CPUs.
!! Contributed by Steve Chamberlain.
!! sac@cygnus.com
!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
!! recoded in assembly by Toshiyasu Morita
!! tm@netcom.com
/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
ELF local label prefixes by J"orn Rennecke
amylaar@cygnus.com */
!
! __ashlsi3
!
! Entry:
!
! r4: Value to shift
! r5: Shifts
!
! Exit:
!
! r0: Result
!
! Destroys:
!
! (none)
!
.global ___ashlsi3
.align 2
___ashlsi3:
mov #31,r0
and r0,r5
mova ashlsi3_table,r0
mov.b @(r0,r5),r5
#ifdef __sh1__
add r5,r0
jmp @r0
#else
braf r5
#endif
mov r4,r0
.align 2
ashlsi3_table:
.byte ashlsi3_0-ashlsi3_table
.byte ashlsi3_1-ashlsi3_table
.byte ashlsi3_2-ashlsi3_table
.byte ashlsi3_3-ashlsi3_table
.byte ashlsi3_4-ashlsi3_table
.byte ashlsi3_5-ashlsi3_table
.byte ashlsi3_6-ashlsi3_table
.byte ashlsi3_7-ashlsi3_table
.byte ashlsi3_8-ashlsi3_table
.byte ashlsi3_9-ashlsi3_table
.byte ashlsi3_10-ashlsi3_table
.byte ashlsi3_11-ashlsi3_table
.byte ashlsi3_12-ashlsi3_table
.byte ashlsi3_13-ashlsi3_table
.byte ashlsi3_14-ashlsi3_table
.byte ashlsi3_15-ashlsi3_table
.byte ashlsi3_16-ashlsi3_table
.byte ashlsi3_17-ashlsi3_table
.byte ashlsi3_18-ashlsi3_table
.byte ashlsi3_19-ashlsi3_table
.byte ashlsi3_20-ashlsi3_table
.byte ashlsi3_21-ashlsi3_table
.byte ashlsi3_22-ashlsi3_table
.byte ashlsi3_23-ashlsi3_table
.byte ashlsi3_24-ashlsi3_table
.byte ashlsi3_25-ashlsi3_table
.byte ashlsi3_26-ashlsi3_table
.byte ashlsi3_27-ashlsi3_table
.byte ashlsi3_28-ashlsi3_table
.byte ashlsi3_29-ashlsi3_table
.byte ashlsi3_30-ashlsi3_table
.byte ashlsi3_31-ashlsi3_table
ashlsi3_6:
shll2 r0
ashlsi3_4:
shll2 r0
ashlsi3_2:
rts
shll2 r0
ashlsi3_7:
shll2 r0
ashlsi3_5:
shll2 r0
ashlsi3_3:
shll2 r0
ashlsi3_1:
rts
shll r0
ashlsi3_14:
shll2 r0
ashlsi3_12:
shll2 r0
ashlsi3_10:
shll2 r0
ashlsi3_8:
rts
shll8 r0
ashlsi3_15:
shll2 r0
ashlsi3_13:
shll2 r0
ashlsi3_11:
shll2 r0
ashlsi3_9:
shll8 r0
rts
shll r0
ashlsi3_22:
shll2 r0
ashlsi3_20:
shll2 r0
ashlsi3_18:
shll2 r0
ashlsi3_16:
rts
shll16 r0
ashlsi3_23:
shll2 r0
ashlsi3_21:
shll2 r0
ashlsi3_19:
shll2 r0
ashlsi3_17:
shll16 r0
rts
shll r0
ashlsi3_30:
shll2 r0
ashlsi3_28:
shll2 r0
ashlsi3_26:
shll2 r0
ashlsi3_24:
shll16 r0
rts
shll8 r0
ashlsi3_31:
shll2 r0
ashlsi3_29:
shll2 r0
ashlsi3_27:
shll2 r0
ashlsi3_25:
shll16 r0
shll8 r0
rts
shll r0
ashlsi3_0:
rts
nop
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* Security Engine
*
* 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 "cam.h"
#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE
static void disable_cam_user(const uint16_t userId)
{
if (userId <= 31)
andl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L, (~((uint32_t) 1 << userId)));
else if (userId <= 63)
andl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, (~((uint32_t) 1 << (userId - 32))));
}
static void enable_cam_user(const uint16_t userId)
{
if (userId <= 31)
orl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L, (((uint32_t) 1) << userId));
else if (userId <= 63)
orl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, (((uint32_t) 1) << (userId - 32)));
}
static void wait_for_cam_read_ready(void)
{
while ((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_READ_PENDING) == 0) {
/*
* wait
*/
}
}
static void wait_for_cam_write_ready(void)
{
while ((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_WRITE_PENDING) == 0) {
/*
* wait some more
*/
}
}
static void HW_CAM_Avail(void)
{
uint32_t tmpValue;
do {
tmpValue = get(AR9170_MAC_REG_CAM_MODE);
} while (tmpValue & AR9170_MAC_CAM_HOST_PENDING);
}
static void HW_CAM_Write128(const uint32_t address, const uint32_t *data)
{
HW_CAM_Avail();
set(AR9170_MAC_REG_CAM_DATA0, data[0]);
set(AR9170_MAC_REG_CAM_DATA1, data[1]);
set(AR9170_MAC_REG_CAM_DATA2, data[2]);
set(AR9170_MAC_REG_CAM_DATA3, data[3]);
set(AR9170_MAC_REG_CAM_ADDR, address | AR9170_MAC_CAM_ADDR_WRITE);
wait_for_cam_write_ready();
}
static void HW_CAM_Read128(const uint32_t address, uint32_t *data)
{
HW_CAM_Avail();
set(AR9170_MAC_REG_CAM_ADDR, address);
wait_for_cam_read_ready();
HW_CAM_Avail();
data[0] = get(AR9170_MAC_REG_CAM_DATA0);
data[1] = get(AR9170_MAC_REG_CAM_DATA1);
data[2] = get(AR9170_MAC_REG_CAM_DATA2);
data[3] = get(AR9170_MAC_REG_CAM_DATA3);
}
void set_key(const struct carl9170_set_key_cmd *key)
{
uint32_t data[4];
uint16_t row, wordId, nibbleId, i;
if (key->user > (AR9170_CAM_MAX_USER + 3))
return ;
if (key->keyId > 1)
return ;
/* Disable Key */
disable_cam_user(key->user);
/* Set encrypt type */
if (key->user >= AR9170_CAM_MAX_USER) {
/* default */
row = DEFAULT_ENCRY_TYPE;
wordId = 0;
nibbleId = (key->user - AR9170_CAM_MAX_USER) & 0x7;
} else {
row = ENCRY_TYPE_START_ADDR + (key->user >> 5);
wordId = (key->user >> 3) & 0x3;
nibbleId = key->user & 0x7;
}
HW_CAM_Read128(row, data);
data[wordId] &= (~(0xf << ((uint32_t) nibbleId * 4)));
data[wordId] |= (key->type << ((uint32_t) nibbleId * 4));
HW_CAM_Write128(row, data);
/* Set MAC address */
if (key->user < AR9170_CAM_MAX_USER) {
uint16_t byteId;
wordId = (key->user >> 2) & 0x3;
byteId = key->user & 0x3;
row = (key->user >> 4) * 6;
for (i = 0; i < 6; i++) {
HW_CAM_Read128(row + i, data);
data[wordId] &= (~(0xff << ((uint32_t) byteId * 8)));
data[wordId] |= (key->macAddr[i] << ((uint32_t) byteId * 8));
HW_CAM_Write128(row + i, data);
}
}
/* Set key */
row = KEY_START_ADDR + (key->user * 2) + key->keyId;
HW_CAM_Write128(row, key->key);
/* Enable Key */
enable_cam_user(key->user);
}
void disable_key(const struct carl9170_disable_key_cmd *key)
{
disable_cam_user(key->user);
}
#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* Code to handle commands from the host driver.
*
* 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 "io.h"
#include "cam.h"
#include "rf.h"
#include "printf.h"
#include "timer.h"
#include "wl.h"
#include "wol.h"
void handle_cmd(struct carl9170_rsp *resp)
{
struct carl9170_cmd *cmd = &dma_mem.reserved.cmd.cmd;
unsigned int i;
/* copies cmd, len and extra fields */
resp->hdr.len = cmd->hdr.len;
resp->hdr.cmd = cmd->hdr.cmd;
resp->hdr.ext = cmd->hdr.ext;
resp->hdr.seq |= cmd->hdr.seq;
switch (cmd->hdr.cmd & ~CARL9170_CMD_ASYNC_FLAG) {
case CARL9170_CMD_RREG:
for (i = 0; i < (cmd->hdr.len / 4); i++)
resp->rreg_res.vals[i] = get(cmd->rreg.regs[i]);
break;
case CARL9170_CMD_WREG:
resp->hdr.len = 0;
for (i = 0; i < (cmd->hdr.len / 8); i++)
set(cmd->wreg.regs[i].addr, cmd->wreg.regs[i].val);
break;
case CARL9170_CMD_ECHO:
memcpy(resp->echo.vals, cmd->echo.vals, cmd->hdr.len);
break;
case CARL9170_CMD_SWRST:
#ifdef CONFIG_CARL9170FW_FW_MAC_RESET
/*
* Command has no payload, so the response
* has no payload either.
* resp->hdr.len = 0;
*/
fw.wlan.mac_reset = CARL9170_MAC_RESET_FORCE;
#endif /* CONFIG_CARL9170FW_FW_MAC_RESET */
break;
case CARL9170_CMD_REBOOT:
/*
* resp->len = 0;
*/
fw.reboot = 1;
break;
case CARL9170_CMD_READ_TSF:
resp->hdr.len = 8;
read_tsf((uint32_t *)resp->tsf.tsf);
break;
case CARL9170_CMD_RX_FILTER:
resp->hdr.len = 0;
fw.wlan.rx_filter = cmd->rx_filter.rx_filter;
break;
case CARL9170_CMD_WOL:
wol_cmd(&cmd->wol);
break;
case CARL9170_CMD_TALLY:
resp->hdr.len = sizeof(struct carl9170_tally_rsp);
memcpy(&resp->tally, &fw.tally, sizeof(struct carl9170_tally_rsp));
resp->tally.tick = fw.ticks_per_usec;
memset(&fw.tally, 0, sizeof(struct carl9170_tally_rsp));
break;
#ifdef CONFIG_CARL9170FW_CAB_QUEUE
case CARL9170_CMD_BCN_CTRL:
resp->hdr.len = 0;
if (cmd->bcn_ctrl.mode & CARL9170_BCN_CTRL_CAB_TRIGGER) {
wlan_modify_beacon(cmd->bcn_ctrl.vif_id,
cmd->bcn_ctrl.bcn_addr, cmd->bcn_ctrl.bcn_len);
set(AR9170_MAC_REG_BCN_ADDR, cmd->bcn_ctrl.bcn_addr);
set(AR9170_MAC_REG_BCN_LENGTH, cmd->bcn_ctrl.bcn_len);
set(AR9170_MAC_REG_BCN_CTRL, AR9170_BCN_CTRL_READY);
} else {
wlan_cab_flush_queue(cmd->bcn_ctrl.vif_id);
fw.wlan.cab_flush_trigger[cmd->bcn_ctrl.vif_id] = CARL9170_CAB_TRIGGER_EMPTY;
}
break;
#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE
case CARL9170_CMD_EKEY:
resp->hdr.len = 0;
set_key(&cmd->setkey);
break;
case CARL9170_CMD_DKEY:
resp->hdr.len = 0;
disable_key(&cmd->disablekey);
break;
#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */
#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
case CARL9170_CMD_FREQUENCY:
case CARL9170_CMD_RF_INIT:
rf_cmd(cmd, resp);
break;
case CARL9170_CMD_FREQ_START:
/*
* resp->hdr.len = 0;
*/
rf_notify_set_channel();
break;
case CARL9170_CMD_PSM:
resp->hdr.len = 0;
fw.phy.psm.state = le32_to_cpu(cmd->psm.state);
rf_psm();
break;
#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
default:
BUG("Unknown command %x\n", cmd->hdr.cmd);
break;
}
}
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* DMA descriptor handling functions
*
* 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 "wl.h"
#include "printf.h"
struct ar9170_dma_memory dma_mem __section(sram);
static void copy_dma_desc(struct dma_desc *dst,
struct dma_desc *src)
{
memcpy(dst, src, sizeof(struct dma_desc));
}
static void clear_descriptor(struct dma_desc *d)
{
d->status = AR9170_OWN_BITS_SW;
d->ctrl = 0;
d->dataSize = 0;
d->totalLen = 0;
d->lastAddr = d;
d->dataAddr = NULL;
d->nextAddr = d;
}
static void fill_descriptor(struct dma_desc *d, uint16_t size, uint8_t *data)
{
d->status = AR9170_OWN_BITS_SW;
d->ctrl = 0;
d->dataSize = size;
d->totalLen = 0;
d->lastAddr = d;
d->dataAddr = data;
d->nextAddr = NULL;
}
static void init_queue(struct dma_queue *q, struct dma_desc *d)
{
q->head = q->terminator = d;
}
/*
* - Init up_queue, down_queue, tx_queue[5], rx_queue.
* - Setup descriptors and data buffer address.
* - Ring descriptors rx_queue and down_queue by dma_reclaim().
*
* NOTE: LastAddr tempary point (same) to nextAddr after initialize.
* Because LastAddr is don't care in function dma_reclaim().
*/
void dma_init_descriptors(void)
{
unsigned int i, j;
for (i = 0; i < ARRAY_SIZE(dma_mem.terminator); i++)
clear_descriptor(&dma_mem.terminator[i]);
/* Assign terminators to DMA queues */
i = 0;
init_queue(&fw.pta.up_queue, &dma_mem.terminator[i++]);
init_queue(&fw.pta.down_queue, &dma_mem.terminator[i++]);
for (j = 0; j < __AR9170_NUM_TX_QUEUES; j++)
init_queue(&fw.wlan.tx_queue[j], &dma_mem.terminator[i++]);
init_queue(&fw.wlan.tx_retry, &dma_mem.terminator[i++]);
init_queue(&fw.wlan.rx_queue, &dma_mem.terminator[i++]);
fw.usb.int_desc = &dma_mem.terminator[i++];
fw.wlan.fw_desc = &dma_mem.terminator[i++];
#ifdef CONFIG_CARL9170FW_CAB_QUEUE
for (j = 0; j < CARL9170_INTF_NUM; j++)
init_queue(&fw.wlan.cab_queue[j], &dma_mem.terminator[i++]);
#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
BUG_ON(AR9170_TERMINATOR_NUMBER != i);
DBG("Blocks:%d [tx:%d, rx:%d] Terminators:%d/%d\n",
AR9170_BLOCK_NUMBER, AR9170_TX_BLOCK_NUMBER,
AR9170_RX_BLOCK_NUMBER, AR9170_TERMINATOR_NUMBER, i);
/* Init descriptors and memory blocks */
for (i = 0; i < AR9170_BLOCK_NUMBER; i++) {
fill_descriptor(&dma_mem.block[i], AR9170_BLOCK_SIZE, dma_mem.data[i].data);
if (i < AR9170_TX_BLOCK_NUMBER)
dma_reclaim(&fw.pta.down_queue, &dma_mem.block[i]);
else
dma_reclaim(&fw.wlan.rx_queue, &dma_mem.block[i]);
}
/* Set DMA address registers */
set(AR9170_PTA_REG_DN_DMA_ADDRH, (uint32_t) fw.pta.down_queue.head >> 16);
set(AR9170_PTA_REG_DN_DMA_ADDRL, (uint32_t) fw.pta.down_queue.head & 0xffff);
set(AR9170_PTA_REG_UP_DMA_ADDRH, (uint32_t) fw.pta.up_queue.head >> 16);
set(AR9170_PTA_REG_UP_DMA_ADDRL, (uint32_t) fw.pta.up_queue.head & 0xffff);
for (i = 0; i < __AR9170_NUM_TX_QUEUES; i++)
set_wlan_txq_dma_addr(i, (uint32_t) fw.wlan.tx_queue[i].head);
set(AR9170_MAC_REG_DMA_RXQ_ADDR, (uint32_t) fw.wlan.rx_queue.head);
fw.usb.int_desc->dataSize = AR9170_BLOCK_SIZE;
fw.usb.int_desc->dataAddr = (void *) &dma_mem.reserved.rsp;
memset(DESC_PAYLOAD(fw.usb.int_desc), 0xff,
AR9170_INT_MAGIC_HEADER_SIZE);
memset(DESC_PAYLOAD_OFF(fw.usb.int_desc, AR9170_INT_MAGIC_HEADER_SIZE),
0, AR9170_BLOCK_SIZE - AR9170_INT_MAGIC_HEADER_SIZE);
/* rsp is now available for use */
fw.usb.int_desc_available = 1;
memset(DESC_PAYLOAD(fw.wlan.fw_desc), 0, 128);
fw.wlan.fw_desc_available = 1;
}
/*
* Free descriptor.
*
* Exchange the terminator and the first descriptor of the packet
* for hardware ascy...
*/
void dma_reclaim(struct dma_queue *q, struct dma_desc *desc)
{
struct dma_desc *tmpDesc, *last;
struct dma_desc tdesc;
/* 1. Set OWN bit to HW for all TDs to be added, clear ctrl and size */
tmpDesc = desc;
last = desc->lastAddr;
while (1) {
tmpDesc->status = AR9170_OWN_BITS_HW;
tmpDesc->ctrl = 0;
tmpDesc->totalLen = 0;
tmpDesc->dataSize = AR9170_BLOCK_SIZE;
/* TODO : Exception handle */
tmpDesc->lastAddr = tmpDesc;
if (tmpDesc == last)
break;
tmpDesc = tmpDesc->nextAddr;
}
/* 2. Next address of Last TD to be added = first TD */
tmpDesc->nextAddr = desc;
/* Link first TD to self */
desc->lastAddr = q->terminator;
/* 3. Copy first TD to be added to TTD */
copy_dma_desc(&tdesc, desc);
/* 4. Initialize new terminator */
clear_descriptor(desc);
/* 5. Copy TTD to last TD */
tdesc.status = 0;
copy_dma_desc((void *)q->terminator, (void *)&tdesc);
q->terminator->status |= AR9170_OWN_BITS_HW;
/* Update terminator pointer */
q->terminator = desc;
}
/*
* Put a complete packet into the tail of the Queue q.
* Exchange the terminator and the first descriptor of the packet
* for hardware ascy...
*/
void dma_put(struct dma_queue *q, struct dma_desc *desc)
{
struct dma_desc *tmpDesc;
struct dma_desc tdesc;
tmpDesc = desc;
while (1) {
/* update totalLen */
tmpDesc->totalLen = desc->totalLen;
/* 1. Set OWN bit to HW for all TDs to be added */
tmpDesc->status = AR9170_OWN_BITS_HW;
/* TODO : Exception handle */
tmpDesc->lastAddr = desc->lastAddr;
if (desc->lastAddr == tmpDesc)
break;
tmpDesc = tmpDesc->nextAddr;
}
/* 2. Next address of Last TD to be added = first TD */
desc->lastAddr->nextAddr = desc;
/* If there is only one descriptor, update pointer of last descriptor */
if (desc->lastAddr == desc)
desc->lastAddr = q->terminator;
/* 3. Copy first TD to be added to TTD */
copy_dma_desc(&tdesc, desc);
/* 4. Initialize new terminator */
clear_descriptor(desc);
/* 5. Copy TTD to last TD */
tdesc.status &= (~AR9170_OWN_BITS);
copy_dma_desc((void *)q->terminator, (void *)&tdesc);
q->terminator->status |= AR9170_OWN_BITS_HW;
/* Update terminator pointer */
q->terminator = desc;
}
struct dma_desc *dma_unlink_head(struct dma_queue *queue)
{
struct dma_desc *desc;
if (queue_empty(queue))
return NULL;
desc = queue->head;
queue->head = desc->lastAddr->nextAddr;
/* poison nextAddr address */
desc->lastAddr->nextAddr = desc->lastAddr;
desc->lastAddr->lastAddr = desc->lastAddr;
return desc;
}
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* Firmware descriptor
*
* 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 "fwdsc.h"
#define FILL(small, big, more...) \
.small = { \
CARL9170FW_FILL_DESC(big##_MAGIC, \
sizeof(struct carl9170fw_## small##_desc), \
CARL9170FW_## big##_DESC_MIN_VER, \
CARL9170FW_## big##_DESC_CUR_VER), \
more \
}
const struct carl9170_firmware_descriptor __section(fwdsc) carl9170fw_desc = {
FILL(otus, OTUS,
.feature_set = cpu_to_le32(BIT(CARL9170FW_DUMMY_FEATURE) |
BIT(CARL9170FW_USB_RESP_EP2) |
BIT(CARL9170FW_HANDLE_BACK_REQ) |
BIT(CARL9170FW_RX_FILTER) |
BIT(CARL9170FW_HW_COUNTERS) |
BIT(CARL9170FW_RX_BA_FILTER) |
BIT(CARL9170FW_USB_INIT_FIRMWARE) |
#ifdef CONFIG_CARL9170FW_USB_UP_STREAM
BIT(CARL9170FW_USB_UP_STREAM) |
#endif /* CONFIG_CARL9170FW_USB_UP_STREAM */
#ifdef CONFIG_CARL9170FW_USB_DOWN_STREAM
BIT(CARL9170FW_USB_DOWN_STREAM) |
#endif /* CONFIG_CARL9170FW_USB_DOWN_STREAM */
#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
BIT(CARL9170FW_COMMAND_PHY) |
BIT(CARL9170FW_PSM) |
BIT(CARL9170FW_FIXED_5GHZ_PSM) |
#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE
BIT(CARL9170FW_COMMAND_CAM) |
#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */
#ifdef CONFIG_CARL9170FW_CAB_QUEUE
BIT(CARL9170FW_WLANTX_CAB) |
#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
#ifdef CONFIG_CARL9170FW_UNUSABLE
BIT(CARL9170FW_UNUSABLE) |
#endif /* CONFIG_CARL9170FW_UNUSABLE */
#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT
BIT(CARL9170FW_GPIO_INTERRUPT) |
#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */
#ifdef CONFIG_CARL9170FW_WOL
BIT(CARL9170FW_WOL) |
#endif /* CONFIG_CARL9170FW_WOL */
(0)),
.miniboot_size = cpu_to_le16(0),
.tx_descs = AR9170_TX_BLOCK_NUMBER,
.cmd_bufs = CARL9170_INT_RQ_CACHES,
.rx_max_frame_len = cpu_to_le16(CONFIG_CARL9170FW_RX_FRAME_LEN),
.tx_frag_len = cpu_to_le16(AR9170_BLOCK_SIZE),
.fw_address = cpu_to_le32(AR9170_PRAM_OFFSET),
.bcn_addr = (__le32) cpu_to_le32(&dma_mem.reserved.bcn),
.bcn_len = (__le16) cpu_to_le16(sizeof(dma_mem.reserved.bcn)),
.vif_num = CARL9170_INTF_NUM,
.api_ver = CONFIG_CARL9170FW_RELEASE_VERSION,
),
FILL(txsq, TXSQ,
.seq_table_addr = cpu_to_le32(&fw.wlan.sequence),
),
#ifdef CONFIG_CARL9170FW_WOL
FILL(wol, WOL,
.supported_triggers = BIT(CARL9170_WOL_DISCONNECT) |
BIT(CARL9170_WOL_MAGIC_PKT),
),
#endif /* CONFIG_CARL9170FW_WOL */
FILL(motd, MOTD,
.fw_year_month_day = cpu_to_le32(
CARL9170FW_SET_DAY(CARL9170FW_VERSION_DAY) +
CARL9170FW_SET_MONTH(CARL9170FW_VERSION_MONTH) +
CARL9170FW_SET_YEAR(CARL9170FW_VERSION_YEAR)),
.desc = "Community AR9170 Linux",
.release = CARL9170FW_VERSION_GIT),
FILL(dbg, DBG,
.bogoclock_addr = cpu_to_le32(0),
.counter_addr = cpu_to_le32(&fw.counter),
.rx_total_addr = cpu_to_le32(0),
.rx_overrun_addr = cpu_to_le32(0),
.rx_filter = cpu_to_le32(&fw.wlan.rx_filter),
),
FILL(last, LAST),
};
#undef FILL
struct firmware_context_struct fw;
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* GPIO interrupt service
*
* 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 "gpio.h"
#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT
void gpio_timer(void)
{
uint32_t cur;
cur = get(AR9170_GPIO_REG_PORT_DATA) & CARL9170_GPIO_MASK;
if (cur != fw.cached_gpio_state.gpio) {
fw.cached_gpio_state.gpio = cur;
send_cmd_to_host(sizeof(struct carl9170_gpio),
CARL9170_RSP_GPIO, 0x00,
(uint8_t *)&fw.cached_gpio_state);
# ifdef CONFIG_CARL9170FW_WATCHDOG_BUTTON
for (;;) {
/*
* Loop forever... Until the watchdog triggers.
*/
}
# endif /* CONFIG_CARL9170FW_WATCHDOG_BUTTON */
}
}
#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */
/*
* carl9170 firmware - used by the ar9170 wireless device
*
* Host interface routines
*
* 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 "hostif.h"
#include "printf.h"
#include "wl.h"
static bool length_check(struct dma_desc *desc)
{
volatile struct carl9170_tx_superframe *super = __get_super(desc);
if (unlikely(desc->totalLen < sizeof(struct carl9170_tx_superdesc)))
return false;
/*
* check if the DMA is complete, or clipped.
*
* NB: The hardware aligns the descriptor length to
* a 4 byte boundary. This makes the direct comparison
* difficult, or unnecessary complex for a hot-path.
*/
if (unlikely(super->s.len > desc->totalLen))
return false;
return true;
}
static void handle_download(void)
{
struct dma_desc *desc;
/*
* Under normal conditions, all completed descs should have
* the AR9170_OWN_BITS_SE status flag set.
* However there seems to be a undocumented case where the flag
* is _SW ( handle_download_exception )
*/
for_each_desc_not_bits(desc, &fw.pta.down_queue, AR9170_OWN_BITS_HW) {
if (unlikely((length_check(desc) == false))) {
/*
* There is no easy way of telling what was lost.
*
* Therefore we just reclaim the data.
* The driver has to have some sort frame
* timeout mechanism.
*/
wlan_tx_complete(__get_super(desc), false);
dma_reclaim(&fw.pta.down_queue, desc);
down_trigger();
} else {
wlan_tx(desc);
}
}
#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT
xorl(AR9170_GPIO_REG_PORT_DATA, 2);
#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */
}
static void handle_upload(void)
{
struct dma_desc *desc;
for_each_desc_not_bits(desc, &fw.pta.up_queue, AR9170_OWN_BITS_HW) {
/*
* BIG FAT NOTE:
*
* DO NOT compare the descriptor addresses.
*/
if (DESC_PAYLOAD(desc) == (void *) &dma_mem.reserved.rsp) {
fw.usb.int_desc = desc;
fw.usb.int_desc_available = 1;
} else {
dma_reclaim(&fw.wlan.rx_queue, desc);
wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
}
}
#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT
xorl(AR9170_GPIO_REG_PORT_DATA, 2);
#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */
}
static void handle_download_exception(void)
{
struct dma_desc *desc, *target;
/* actually, the queue should be stopped by now? */
usb_stop_down_queue();
target = (void *)((get(AR9170_PTA_REG_DN_CURR_ADDRH) << 16) |
get(AR9170_PTA_REG_DN_CURR_ADDRL));
/*
* Put "forgotten" packets from the head of the queue, back
* to the current position
*/
__while_desc_bits(desc, &fw.pta.down_queue, AR9170_OWN_BITS_HW) {
if (desc == target)
break;
dma_reclaim(&fw.pta.down_queue,
dma_unlink_head(&fw.pta.down_queue));
}
__for_each_desc_continue(desc, &fw.pta.down_queue) {
if ((desc->status & AR9170_OWN_BITS) == AR9170_OWN_BITS_SW)
dma_fix_downqueue(desc);
}
usb_start_down_queue();
down_trigger();
}
/* handle interrupts from DMA chip */
void handle_host_interface(void)
{
uint32_t pta_int;
pta_int = get(AR9170_PTA_REG_INT_FLAG);
#define HANDLER(intr, flag, func) \
do { \
if ((intr & flag) != 0) { \
func(); \
} \
} while (0)
HANDLER(pta_int, AR9170_PTA_INT_FLAG_DN, handle_download);
HANDLER(pta_int, AR9170_PTA_INT_FLAG_UP, handle_upload);
/* This is just guesswork and MAGIC */
pta_int = get(AR9170_PTA_REG_DMA_STATUS);
HANDLER(pta_int, 0x1, handle_download_exception);
#undef HANDLER
}
/*
* 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();
}
/* $Id: memcpy.S,v 1.3 2001/07/27 11:50:52 gniibe Exp $
*
* "memcpy" implementation of SuperH
*
* Copyright (C) 1999 Niibe Yutaka
*
*/
/*
* void *memcpy(void *dst, const void *src, size_t n);
* No overlap between the memory of DST and of SRC are assumed.
*/
.globl _memcpy
.align 2
_memcpy:
tst r6,r6
bt/s 9f ! if n=0, do nothing
mov r4,r0
sub r4,r5 ! From here, r5 has the distance to r0
add r6,r0 ! From here, r0 points the end of copying point
mov #12,r1
cmp/gt r6,r1
bt/s 7f ! if it's too small, copy a byte at once
add #-1,r5
add #1,r5
! From here, r6 is free
!
! r4 --> [ ... ] DST [ ... ] SRC
! [ ... ] [ ... ]
! : :
! r0 --> [ ... ] r0+r5 --> [ ... ]
!
!
mov r5,r1
mov #3,r2
and r2,r1
shll2 r1
mov r0,r3 ! Save the value on R0 to R3
mova jmptable,r0
add r1,r0
mov.l @r0,r1
jmp @r1
mov r3,r0 ! and back to R0
.balign 4
jmptable:
.long case0
.long case1
.long case2
.long case3
! copy a byte at once
7: mov r4,r2
add #1,r2
8:
cmp/hi r2,r0
mov.b @(r0,r5),r1
bt/s 8b ! while (r0>r2)
mov.b r1,@-r0
9:
rts
nop
case0:
!
! GHIJ KLMN OPQR --> GHIJ KLMN OPQR
!
! First, align to long word boundary
mov r0,r3
and r2,r3
tst r3,r3
bt/s 2f
add #-4,r5
add #3,r5
1: dt r3
mov.b @(r0,r5),r1
bf/s 1b
mov.b r1,@-r0
!
add #-3,r5
2: ! Second, copy a long word at once
mov r4,r2
add #7,r2
3: mov.l @(r0,r5),r1
cmp/hi r2,r0
bt/s 3b
mov.l r1,@-r0
!
! Third, copy a byte at once, if necessary
cmp/eq r4,r0
bt/s 9b
add #3,r5
bra 8b
add #-6,r2
case1:
!
! GHIJ KLMN OPQR --> ...G HIJK LMNO PQR.
!
! First, align to long word boundary
mov r0,r3
and r2,r3
tst r3,r3
bt/s 2f
add #-1,r5
1: dt r3
mov.b @(r0,r5),r1
bf/s 1b
mov.b r1,@-r0
!
2: ! Second, read a long word and write a long word at once
mov.l @(r0,r5),r1
add #-4,r5
mov r4,r2
add #7,r2
!
#ifdef __LITTLE_ENDIAN__
3: mov r1,r3 ! RQPO
shll16 r3
shll8 r3 ! Oxxx
mov.l @(r0,r5),r1 ! NMLK
mov r1,r6
shlr8 r6 ! xNML
or r6,r3 ! ONML
cmp/hi r2,r0
bt/s 3b
mov.l r3,@-r0
#else
3: mov r1,r3 ! OPQR
shlr16 r3
shlr8 r3 ! xxxO
mov.l @(r0,r5),r1 ! KLMN
mov r1,r6
shll8 r6 ! LMNx
or r6,r3 ! LMNO
cmp/hi r2,r0
bt/s 3b
mov.l r3,@-r0
#endif
!
! Third, copy a byte at once, if necessary
cmp/eq r4,r0
bt/s 9b
add #4,r5
bra 8b
add #-6,r2
case2:
!
! GHIJ KLMN OPQR --> ..GH IJKL MNOP QR..
!
! First, align to word boundary
tst #1,r0
bt/s 2f
add #-1,r5
mov.b @(r0,r5),r1
mov.b r1,@-r0
!
2: ! Second, read a word and write a word at once
add #-1,r5
mov r4,r2
add #3,r2
!
3: mov.w @(r0,r5),r1
cmp/hi r2,r0
bt/s 3b
mov.w r1,@-r0
!
! Third, copy a byte at once, if necessary
cmp/eq r4,r0
bt/s 9b
add #1,r5
mov.b @(r0,r5),r1
rts
mov.b r1,@-r0
case3:
!
! GHIJ KLMN OPQR --> .GHI JKLM NOPQ R...
!
! First, align to long word boundary
mov r0,r3
and r2,r3
tst r3,r3
bt/s 2f
add #-1,r5
1: dt r3
mov.b @(r0,r5),r1
bf/s 1b
mov.b r1,@-r0
!
2: ! Second, read a long word and write a long word at once
add #-2,r5
mov.l @(r0,r5),r1
add #-4,r5
mov r4,r2
add #7,r2
!
#ifdef __LITTLE_ENDIAN__
3: mov r1,r3 ! RQPO
shll8 r3 ! QPOx
mov.l @(r0,r5),r1 ! NMLK
mov r1,r6
shlr16 r6
shlr8 r6 ! xxxN
or r6,r3 ! QPON
cmp/hi r2,r0
bt/s 3b
mov.l r3,@-r0
#else
3: mov r1,r3 ! OPQR
shlr8 r3 ! xOPQ
mov.l @(r0,r5),r1 ! KLMN
mov r1,r6
shll16 r6
shll8 r6 ! Nxxx
or r6,r3 ! NOPQ
cmp/hi r2,r0
bt/s 3b
mov.l r3,@-r0
#endif
!
! Third, copy a byte at once, if necessary
cmp/eq r4,r0
bt/s 9b
add #6,r5
bra 8b
add #-6,r2
/* $Id: memset.S,v 1.1 2000/04/14 16:49:01 mjd Exp $
*
* "memset" implementation of SuperH
*
* Copyright (C) 1999 Niibe Yutaka
*
*/
/*
* void *memset(void *s, int c, size_t n);
*/
.globl _memset
.align 2
_memset:
tst r6,r6
bt/s 5f ! if n=0, do nothing
add r6,r4
mov #12,r0
cmp/gt r6,r0
bt/s 4f ! if it's too small, set a byte at once
mov r4,r0
and #3,r0
cmp/eq #0,r0
bt/s 2f ! It's aligned
sub r0,r6
1:
dt r0
bf/s 1b
mov.b r5,@-r4
2: ! make VVVV
extu.b r5,r5
swap.b r5,r0 ! V0
or r0,r5 ! VV
swap.w r5,r0 ! VV00
or r0,r5 ! VVVV
!
mov r6,r0
shlr2 r0
shlr r0 ! r0 = r6 >> 3
3:
dt r0
mov.l r5,@-r4 ! set 8-byte at once
bf/s 3b
mov.l r5,@-r4
!
mov #7,r0
and r0,r6
tst r6,r6
bt 5f
! fill bytes
4:
dt r6
bf/s 4b
mov.b r5,@-r4
5:
rts
mov r4,r0
/*
* Copyright (C) 2004,2008 Kustaa Nyholm
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "carl9170.h"
#include "printf.h"
#ifdef CONFIG_CARL9170FW_PRINTF
static char *bf;
static char buf[12];
static unsigned int num;
static char uc;
static char zs;
static void out(const char c)
{
*bf++ = c;
}
static void outDgt(const char dgt)
{
out(dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10));
zs = 1;
}
static void divOut(const unsigned int d)
{
unsigned char dgt = 0;
while (num >= d) {
num -= d;
dgt++;
}
if (zs || dgt > 0)
outDgt(dgt);
}
void tfp_printf(const char *fmt, ...)
{
va_list va;
char *p;
unsigned int i;
char ch;
va_start(va, fmt);
while ((ch = *(fmt++))) {
if (ch != '%') {
putcharacter(ch);
} else {
char lz = 0;
char w = 0;
ch = *(fmt++);
if (ch == '0') {
ch = *(fmt++);
lz = 1;
}
if (ch >= '0' && ch <= '9') {
w = 0;
while (ch >= '0' && ch <= '9') {
w = (((w << 2) + w) << 1) + ch - '0';
ch = *fmt++;
}
}
bf = buf;
p = bf;
zs = 0;
switch (ch) {
case 0:
goto abort;
case 'u':
case 'd':
num = va_arg(va, unsigned int);
if (ch == 'd' && (int) num < 0) {
num = -(int)num;
out('-');
}
for (i = 100000000; i != 1; i /= 10)
divOut(i);
outDgt(num);
break;
case 'p':
case 'x':
case 'X':
uc = ch == 'X';
num = va_arg(va, unsigned int);
for (i = 0x10000000; i != 0x1; i >>= 4)
divOut(i);
outDgt(num);
break;
case 'c':
out((char)(va_arg(va, int)));
break;
case 's':
p = va_arg(va, char*);
break;
case '%':
out('%');
break;
default:
break;
}
*bf = 0;
bf = p;
while (*bf++ && w > 0)
w--;
while (w-- > 0)
putcharacter(lz ? '0' : ' ');
while ((ch = *p++))
putcharacter(ch);
}
}
abort:
putcharacter('\0');
va_end(va);
}
#else
void min_printf(const char *fmt, ...)
{
char ch;
do {
ch = *(fmt++);
putcharacter(ch);
} while (ch);
}
#endif /* CONFIG_CARL9170FW_PRINTF */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment