blob: 130c4e005ca097848d93dc7091265f3224560ad2 [file] [log] [blame]
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */
#include <asm/io.h>
#include <linux/dma-mapping.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <atheros.h>
#define ADH_MAX_WRITE_SIZE 32
#define ADH_PROC_ENTRY "ath-ddr-hog"
typedef struct {
struct proc_dir_entry *proc;
uint8_t buf[ADH_MAX_WRITE_SIZE];
} ath_ddr_hog_t;
static ath_ddr_hog_t adh_softc, *adh = &adh_softc;
#ifdef CONFIG_ATH_TURN_ON_DDR_HOG
#define DDR_HOG_WRITE_REGION ((252 << 20) | KSEG0)
#define DDR_HOG_WRITE_END (DDR_HOG_WRITE_REGION + 0x40)
#define DDR_START_ADDRESS __stringify(DDR_HOG_WRITE_REGION)
#define DDR_END_ADDRESS __stringify(DDR_HOG_WRITE_END)
#define DDR_INCR_COUNT __stringify(32)
#define UNCACHED_SRAM_BASE (ATH_SRAM_BASE | KSEG1)
noinline void
ath_ddr_hog_infinite(void)
{
asm(
"start_ddr_txns_infinite:\n"
" li $t3," __stringify(RST_GENERAL_BASE) "\n"
" li $t4, 0xffffffff\n"
" sw $t4, 0xa0($t3)\n"
" li $t3," DDR_START_ADDRESS "\n"
" li $t4," DDR_END_ADDRESS "\n"
"_outer_ddr_rw_loop_infinite:\n"
" ori $t0, $t3, 0\n"
" addiu $t8, $t0, 0x8000\n"
" li $t1, 0x10000\n"
"_inner_ddr_rw_loop_infinite:\n"
" sw $t2, 0x0($t0)\n"
" sw $t5, 0x2000($t0)\n"
" sw $t6, 0x4000($t0)\n"
" sw $t7, 0x6000($t0)\n"
" sw $t2, 0x0($t8)\n"
" sw $t5, 0x2000($t8)\n"
" sw $t6, 0x4000($t8)\n"
" sw $t7, 0x6000($t8)\n"
" b _inner_ddr_rw_loop_infinite\n"
" addiu $t1, $t1, -1\n"
" nop\n"
" bnez $t1, _inner_ddr_rw_loop_infinite\n"
" nop\n"
"_changes_before_outer_loop_infinite:\n"
" addiu $t3, $t3," DDR_INCR_COUNT "\n"
" li $t2," __stringify(UNCACHED_SRAM_BASE) "\n"
" li $t3," __stringify(RST_GENERAL_BASE) "\n"
" lw $t4, 0x9c($t3)\n"
" sw $t4, 0x700($t2)\n"
" nop\n"
" nop\n");
}
noinline void
ath_ddr_hog_all_banks(void)
{
asm(
"start_ddr_txns_inf:\n"
" li $t3," __stringify(RST_GENERAL_BASE) "\n"
" li $t4, 0xffffffff\n"
" sw $t4, 0xa0($t3)\n"
" li $t3," DDR_START_ADDRESS "\n"
" li $t4," DDR_END_ADDRESS "\n"
"_outer_ddr_rw_loop_inf:\n"
" ori $t0, $t3, 0\n"
" addiu $t8, $t0, 0x8000\n"
" li $t1, 0x10000\n"
"_inner_ddr_rw_loop_inf:\n"
" sw $t2, 0x0($t0)\n"
" sw $t2, 0x200($t0)\n"
" sw $t2, 0x400($t0)\n"
" sw $t2, 0x600($t0)\n"
" sw $t5, 0x2000($t0)\n"
" sw $t5, 0x2200($t0)\n"
" sw $t5, 0x2400($t0)\n"
" sw $t5, 0x2600($t0)\n"
" sw $t6, 0x4000($t0)\n"
" sw $t6, 0x4200($t0)\n"
" sw $t6, 0x4400($t0)\n"
" sw $t6, 0x4600($t0)\n"
" sw $t7, 0x6000($t0)\n"
" sw $t7, 0x6200($t0)\n"
" sw $t7, 0x6400($t0)\n"
" sw $t7, 0x6600($t0)\n"
" sw $t2, 0x0($t8)\n"
" sw $t2, 0x200($t8)\n"
" sw $t2, 0x400($t8)\n"
" sw $t2, 0x600($t8)\n"
" sw $t5, 0x2000($t8)\n"
" sw $t5, 0x2200($t8)\n"
" sw $t5, 0x2400($t8)\n"
" sw $t5, 0x2600($t8)\n"
" sw $t6, 0x4000($t8)\n"
" sw $t6, 0x4200($t8)\n"
" sw $t6, 0x4400($t8)\n"
" sw $t6, 0x4600($t8)\n"
" sw $t7, 0x6000($t8)\n"
" sw $t7, 0x6200($t8)\n"
" sw $t7, 0x6400($t8)\n"
" sw $t7, 0x6600($t8)\n"
//" b _inner_ddr_rw_loop_inf\n"
" addiu $t1, $t1, -1\n"
" nop\n"
" bnez $t1, _inner_ddr_rw_loop_inf\n"
" nop\n"
"_changes_before_outer_loop_inf:\n"
" addiu $t3, $t3," DDR_INCR_COUNT "\n"
" li $t2," __stringify(UNCACHED_SRAM_BASE) "\n"
" li $t3," __stringify(RST_GENERAL_BASE) "\n"
" lw $t4, 0x9c($t3)\n"
" sw $t4, 0x700($t2)\n"
" nop\n"
" nop\n");
}
noinline void
ath_ddr_hog(void)
{
asm(
"start_ddr_txns:\n"
" li $t3," __stringify(RST_GENERAL_BASE) "\n"
" li $t4, 0xffffffff\n"
" sw $t4, 0xa0($t3)\n"
" li $t3," DDR_START_ADDRESS "\n"
" li $t4," DDR_END_ADDRESS "\n"
"_outer_ddr_rw_loop:\n"
" ori $t0, $t3, 0\n"
" addiu $t8, $t0, 0x8000\n"
" li $t1, 0x10000\n"
"_inner_ddr_rw_loop:\n"
" sw $t2, 0x0($t0)\n"
" sw $t5, 0x2000($t0)\n"
" sw $t6, 0x4000($t0)\n"
" sw $t7, 0x6000($t0)\n"
" sw $t2, 0x0($t8)\n"
" sw $t5, 0x2000($t8)\n"
" sw $t6, 0x4000($t8)\n"
" sw $t7, 0x6000($t8)\n"
//" b _inner_ddr_rw_loop\n"
" addiu $t1, $t1, -1\n"
" nop\n"
" bnez $t1, _inner_ddr_rw_loop\n"
" nop\n"
"_changes_before_outer_loop:\n"
" addiu $t3, $t3," DDR_INCR_COUNT "\n"
" li $t2," __stringify(UNCACHED_SRAM_BASE) "\n"
" li $t3," __stringify(RST_GENERAL_BASE) "\n"
" lw $t4, 0x9c($t3)\n"
" sw $t4, 0x700($t2)\n"
" nop\n"
" nop\n");
}
uint16_t upcase_table[131072] __attribute__ ((aligned (4096))),
lowcase_table[131072] __attribute__ ((aligned (4096)));
#define SSVAL(a, b, c) do { (*(((uint16_t *)(a)) + b)) = (uint16_t)(c); } while (0)
#define UCS2_CHAR(x) ((x) << 8)
void adh_samba_hog(void)
{
int cntr = 0;
ath_reg_wr(RST_GENERAL_TIMER3_RELOAD_ADDRESS, 0xffffffff);
for (cntr = 0; cntr < 0x10000; cntr++) {
uint16_t v;
SSVAL(&v, 0, cntr);
upcase_table[v] = cntr;
}
for (cntr = 0; cntr < 256; cntr++) {
uint16_t v;
SSVAL(&v, 0, UCS2_CHAR(cntr));
upcase_table[v] = UCS2_CHAR(islower(cntr) ? toupper(cntr) : cntr);
}
for (cntr = 0; cntr < 0x10000; cntr++) {
uint16_t v;
SSVAL(&v, 0, cntr);
lowcase_table[v] = cntr;
}
for (cntr = 0; cntr < 256; cntr++) {
uint16_t v;
SSVAL(&v, 0, UCS2_CHAR(cntr));
lowcase_table[v] = UCS2_CHAR(isupper(cntr) ? tolower(cntr) : cntr);
}
cntr = ath_reg_rd(RST_GENERAL_TIMER3_ADDRESS);
printk("Samba 0x%08x...\n", cntr);
}
int adh_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data)
{
return sprintf(buf, "%u", adh->buf[0]);
}
int adh_write_proc(struct file *file, const char *buf, unsigned long count, void *data)
{
if (count > ADH_MAX_WRITE_SIZE)
count = ADH_MAX_WRITE_SIZE;
if (copy_from_user(adh->buf, buf, count))
return -EFAULT;
switch (adh->buf[0]) {
case 'i': ath_ddr_hog_infinite(); break;
case 'd': ath_ddr_hog(); break;
case 'a': ath_ddr_hog_all_banks(); break;
case 's': adh_samba_hog(); break;
default: printk("invalid input\n");
}
return count;
}
void adh_create_proc_entry(void)
{
adh->proc = create_proc_entry(ADH_PROC_ENTRY, 0, NULL);
if (!adh->proc) {
printk("create_proc_entry failed: " ADH_PROC_ENTRY "\n");
return;
}
adh->proc->read_proc = adh_read_proc;
adh->proc->write_proc = adh_write_proc;
adh->proc->size = ADH_MAX_WRITE_SIZE;
printk("/proc/" ADH_PROC_ENTRY " created\n");
}
#endif /* CONFIG_ATH_TURN_ON_DDR_HOG */
int ath_timer_init(void)
{
#if CONFIG_ATH_DDR_RELEASE_TIMER
/*
* An ISR is not registered to handle interrupts generated by this
* timer. arch/mips/kernel/genex.S:handle_int handles this directly
*/
ath_reg_wr(RST_GENERAL_TIMER2_RELOAD_ADDRESS,
CONFIG_ATH_DDR_RELEASE_TIMER * (ath_ref_freq / 1000000));
ath_reg_rmw_set(RST_MISC_INTERRUPT_MASK_ADDRESS,
RST_MISC_INTERRUPT_MASK_TIMER2_MASK_MASK);
#endif /* CONFIG_ATH_DDR_RELEASE_TIMER */
#ifdef CONFIG_ATH_TURN_ON_DDR_HOG
adh_create_proc_entry();
#endif /* CONFIG_ATH_TURN_ON_DDR_HOG */
return 0;
}
late_initcall(ath_timer_init);