mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2025-04-21 12:27:27 +03:00
initial merge of infineon code for amazon, pci is still broken a bit. a big thank you goes to infineon for providing info and reference code
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@8137 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
3074
target/linux/amazon-2.6/files/drivers/atm/amazon_tpe.c
Normal file
3074
target/linux/amazon-2.6/files/drivers/atm/amazon_tpe.c
Normal file
File diff suppressed because it is too large
Load Diff
1486
target/linux/amazon-2.6/files/drivers/char/admmod.c
Normal file
1486
target/linux/amazon-2.6/files/drivers/char/admmod.c
Normal file
File diff suppressed because it is too large
Load Diff
7918
target/linux/amazon-2.6/files/drivers/char/amazon_mei.c
Normal file
7918
target/linux/amazon-2.6/files/drivers/char/amazon_mei.c
Normal file
File diff suppressed because it is too large
Load Diff
261
target/linux/amazon-2.6/files/drivers/char/amazon_wdt.c
Normal file
261
target/linux/amazon-2.6/files/drivers/char/amazon_wdt.c
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Infineon AP DC COM Amazon WDT driver
|
||||
* Copyright 2004 Wu Qi Ming <gokimi@msn.com>
|
||||
* All rights reserved
|
||||
*/
|
||||
#if defined(MODVERSIONS)
|
||||
#include <linux/modversions.h>
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/selection.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/amazon/amazon.h>
|
||||
#include <asm/amazon/amazon_wdt.h>
|
||||
|
||||
#define AMAZON_WDT_EMSG(fmt, args...) printk( "%s: " fmt, __FUNCTION__ , ##args)
|
||||
|
||||
extern unsigned int amazon_get_fpi_hz(void);
|
||||
|
||||
/* forward declarations for _fops */
|
||||
static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *offset);
|
||||
static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_t *offset);
|
||||
static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
|
||||
static int wdt_open(struct inode *inode, struct file *file);
|
||||
static int wdt_release(struct inode *inode, struct file *file);
|
||||
static int wdt_proc_read(char *buf, char **start, off_t offset,int count, int *eof, void *data);
|
||||
|
||||
|
||||
static struct file_operations wdt_fops = {
|
||||
read:wdt_read,
|
||||
write:wdt_write,
|
||||
ioctl:wdt_ioctl,
|
||||
open:wdt_open,
|
||||
release:wdt_release,
|
||||
};
|
||||
|
||||
/* data */
|
||||
static struct wdt_dev *amazon_wdt_dev;
|
||||
static struct proc_dir_entry* amazon_wdt_dir;
|
||||
static int occupied=0;
|
||||
|
||||
|
||||
/* Brief: enable WDT
|
||||
* Parameter:
|
||||
timeout: time interval for WDT
|
||||
* Return:
|
||||
0 OK
|
||||
EINVAL
|
||||
* Describes:
|
||||
1. Password Access
|
||||
2. Modify Access (change ENDINIT => 0)
|
||||
3. Change WDT_CON1 (enable WDT)
|
||||
4. Password Access again
|
||||
5. Modify Access (change ENDINIT => 1)
|
||||
*/
|
||||
int wdt_enable(int timeout)
|
||||
{
|
||||
u32 hard_psw,ffpi;
|
||||
int reload_value, divider=0;
|
||||
|
||||
ffpi = amazon_get_fpi_hz();
|
||||
|
||||
divider = 1;
|
||||
if((reload_value=65536-timeout*ffpi/256)<0){
|
||||
divider = 0;
|
||||
reload_value=65536-timeout*ffpi/16384;
|
||||
}
|
||||
if (reload_value < 0){
|
||||
AMAZON_WDT_EMSG("timeout too large %d\n", timeout);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
AMAZON_WDT_EMSG("timeout:%d reload_value: %8x\n", timeout, reload_value);
|
||||
|
||||
hard_psw=(AMAZON_WDT_REG32(AMAZON_WDT_CON0)&0xffffff01)+(AMAZON_WDT_REG32(AMAZON_WDT_CON1)&0xc)+ 0xf0;
|
||||
AMAZON_WDT_REG32(AMAZON_WDT_CON0)=hard_psw;
|
||||
wmb();
|
||||
|
||||
AMAZON_WDT_REG32(AMAZON_WDT_CON0)=(hard_psw&0xff00)+(reload_value<<16)+0xf2;
|
||||
wmb();
|
||||
|
||||
AMAZON_WDT_REG32(AMAZON_WDT_CON1)=divider<<2;
|
||||
wmb();
|
||||
|
||||
hard_psw=(AMAZON_WDT_REG32(AMAZON_WDT_CON0)&0xffffff01)+(AMAZON_WDT_REG32(AMAZON_WDT_CON1)&0xc)+ 0xf0;
|
||||
AMAZON_WDT_REG32(AMAZON_WDT_CON0)=hard_psw;
|
||||
|
||||
wmb();
|
||||
AMAZON_WDT_REG32(AMAZON_WDT_CON0)=(AMAZON_WDT_REG32(AMAZON_WDT_CON0)&0xffffff00)+0xf3;
|
||||
wmb();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Brief: Disable/stop WDT
|
||||
*/
|
||||
void wdt_disable(void)
|
||||
{
|
||||
u32 hard_psw=0;
|
||||
|
||||
hard_psw=(AMAZON_WDT_REG32(AMAZON_WDT_CON0)&0xffffff01)+(AMAZON_WDT_REG32(AMAZON_WDT_CON1)&0xc)+ 0xf0;
|
||||
AMAZON_WDT_REG32(AMAZON_WDT_CON0)=hard_psw;
|
||||
wmb();
|
||||
|
||||
AMAZON_WDT_REG32(AMAZON_WDT_CON0)=(AMAZON_WDT_REG32(AMAZON_WDT_CON0)&0xffffff00)+0xf2;
|
||||
wmb();
|
||||
|
||||
AMAZON_WDT_REG32(AMAZON_WDT_CON1)|=8;
|
||||
wmb();
|
||||
|
||||
hard_psw=(AMAZON_WDT_REG32(AMAZON_WDT_CON0)&0xffffff01)+(AMAZON_WDT_REG32(AMAZON_WDT_CON1)&0xc)+ 0xf0;
|
||||
AMAZON_WDT_REG32(AMAZON_WDT_CON0)=hard_psw;
|
||||
wmb();
|
||||
|
||||
AMAZON_WDT_REG32(AMAZON_WDT_CON0)=(AMAZON_WDT_REG32(AMAZON_WDT_CON0)&0xffffff00)+0xf3;
|
||||
wmb();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int result=0;
|
||||
static int timeout=-1;
|
||||
|
||||
switch(cmd){
|
||||
case AMAZON_WDT_IOC_START:
|
||||
AMAZON_WDT_DMSG("enable watch dog timer!\n");
|
||||
if ( copy_from_user((void*)&timeout, (void*)arg, sizeof (int)) ){
|
||||
AMAZON_WDT_EMSG("invalid argument\n");
|
||||
result=-EINVAL;
|
||||
}else{
|
||||
if ((result = wdt_enable(timeout)) < 0){
|
||||
timeout = -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AMAZON_WDT_IOC_STOP:
|
||||
AMAZON_WDT_DMSG("disable watch dog timer\n");
|
||||
timeout = -1;
|
||||
wdt_disable();
|
||||
|
||||
break;
|
||||
case AMAZON_WDT_IOC_PING:
|
||||
if (timeout <0 ){
|
||||
result = -EIO;
|
||||
}else{
|
||||
result = wdt_enable(timeout);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *offset)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_t *offset)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
static int wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
AMAZON_WDT_DMSG("wdt_open\n");
|
||||
|
||||
if (occupied == 1) return -EBUSY;
|
||||
occupied = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
AMAZON_WDT_DMSG("wdt_release\n");
|
||||
|
||||
occupied = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wdt_register_proc_read(char *buf, char **start, off_t offset,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
int len=0;
|
||||
printk("wdt_registers:\n");
|
||||
len+=sprintf(buf+len,"NMISR: 0x%08x\n",AMAZON_WDT_REG32(AMAZON_WDT_NMISR));
|
||||
len+=sprintf(buf+len,"RST_REQ: 0x%08x\n",AMAZON_WDT_REG32(AMAZON_RST_REQ));
|
||||
len+=sprintf(buf+len,"RST_SR: 0x%08x\n",AMAZON_WDT_REG32(AMAZON_RST_SR));
|
||||
len+=sprintf(buf+len,"WDT_CON0: 0x%08x\n",AMAZON_WDT_REG32(AMAZON_WDT_CON0));
|
||||
len+=sprintf(buf+len,"WDT_CON1: 0x%08x\n",AMAZON_WDT_REG32(AMAZON_WDT_CON1));
|
||||
len+=sprintf(buf+len,"WDT_SR: 0x%08x\n",AMAZON_WDT_REG32(AMAZON_WDT_SR));
|
||||
*eof = 1;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int __init amazon_wdt_init_module(void)
|
||||
{
|
||||
int result=0;
|
||||
|
||||
amazon_wdt_dev = (wdt_dev*)kmalloc(sizeof(wdt_dev),GFP_KERNEL);
|
||||
if (amazon_wdt_dev == NULL){
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(amazon_wdt_dev,0,sizeof(wdt_dev));
|
||||
|
||||
amazon_wdt_dev->major=result;
|
||||
strcpy(amazon_wdt_dev->name,"wdt");
|
||||
|
||||
result = register_chrdev(0,amazon_wdt_dev->name,&wdt_fops);
|
||||
if (result < 0) {
|
||||
AMAZON_WDT_EMSG("cannot register device\n");
|
||||
kfree(amazon_wdt_dev);
|
||||
return result;
|
||||
}
|
||||
|
||||
amazon_wdt_dir=proc_mkdir("amazon_wdt",NULL);
|
||||
create_proc_read_entry("wdt_register",
|
||||
0,
|
||||
amazon_wdt_dir,
|
||||
wdt_register_proc_read,
|
||||
NULL);
|
||||
|
||||
occupied=0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void amazon_wdt_cleanup_module(void)
|
||||
{
|
||||
unregister_chrdev(amazon_wdt_dev->major,amazon_wdt_dev->name);
|
||||
kfree(amazon_wdt_dev);
|
||||
remove_proc_entry("wdt_register",amazon_wdt_dir);
|
||||
remove_proc_entry("amazon_wdt",NULL);
|
||||
AMAZON_WDT_DMSG("unloaded\n");
|
||||
return;
|
||||
}
|
||||
|
||||
MODULE_LICENSE ("GPL");
|
||||
MODULE_AUTHOR("Infineon IFAP DC COM");
|
||||
MODULE_DESCRIPTION("AMAZON WDT driver");
|
||||
|
||||
module_init(amazon_wdt_init_module);
|
||||
module_exit(amazon_wdt_cleanup_module);
|
||||
|
||||
2121
target/linux/amazon-2.6/files/drivers/char/ifx_ssc.c
Normal file
2121
target/linux/amazon-2.6/files/drivers/char/ifx_ssc.c
Normal file
File diff suppressed because it is too large
Load Diff
166
target/linux/amazon-2.6/files/drivers/mtd/maps/amazon.c
Normal file
166
target/linux/amazon-2.6/files/drivers/mtd/maps/amazon.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Handle mapping of the flash memory access routines
|
||||
* on Amazon based devices.
|
||||
*
|
||||
* Copyright(C) 2004 peng.liu@infineon.com
|
||||
*
|
||||
* This code is GPLed
|
||||
*
|
||||
*/
|
||||
// 000005:fchang 2005/6/2 Modified by Bingtao to double check if the EBU is enabled/disabled
|
||||
// 506231:tc.chen 2005/06/23 increase firmware partition size form 192KB to 256KB
|
||||
// 050701:linmars 2005/07/01 fix flash size wrong alignment after increase firmware partition
|
||||
// 165001:henryhsu 2005/8/18 Remove the support for Intel flash because of 2.1 not enough rootfs partition size
|
||||
// 165001:henryhsu 2005/9/7 Rolback to support INtel flash
|
||||
// 509071:tc.chen 2005/09/07 Reduced flash writing time
|
||||
// 511046:linmars 2005/11/04 change bootloader size from 128 into 64
|
||||
// 511241:linmars 2005/11/24 merge TaiChen's IRM patch
|
||||
|
||||
// copyright 2005 infineon
|
||||
|
||||
// copyright 2007 john crispin <blogic@openwrt.org>
|
||||
// copyright 2007 felix fietkau <nbd@openwrt.org>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/cfi.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/amazon/amazon.h>
|
||||
|
||||
#define AMAZON_PCI_ARB_CTL_ALT 0xb100205c
|
||||
#define AMAZON_MTD_REG32( addr ) (*(volatile u32 *)(addr))
|
||||
|
||||
|
||||
static struct map_info amazon_map = {
|
||||
.name = "AMAZON_FLASH",
|
||||
.bankwidth = 2,
|
||||
.size = 0x400000,
|
||||
};
|
||||
|
||||
static map_word amazon_read16(struct map_info * map, unsigned long ofs)
|
||||
{
|
||||
map_word temp;
|
||||
ofs ^= 2;
|
||||
temp.x[0] = *((__u16 *) (map->virt + ofs));
|
||||
return temp;
|
||||
}
|
||||
|
||||
static void amazon_write16(struct map_info *map, map_word d, unsigned long adr)
|
||||
{
|
||||
adr ^= 2;
|
||||
*((__u16 *) (map->virt + adr)) = d.x[0];
|
||||
}
|
||||
|
||||
void amazon_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
|
||||
{
|
||||
u8 *p;
|
||||
u8 *to_8;
|
||||
ssize_t l = len;
|
||||
from = (unsigned long) (from + map->virt);
|
||||
p = (u8 *) from;
|
||||
to_8 = (u8 *) to;
|
||||
while(len--){
|
||||
*to_8++ = *p++;
|
||||
}
|
||||
}
|
||||
|
||||
void amazon_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
|
||||
{
|
||||
u8 *p = (u8*) from;
|
||||
u8 *to_8;
|
||||
to += (unsigned long) map->virt;
|
||||
to_8 = (u8*)to;
|
||||
while(len--){
|
||||
*p++ = *to_8++;
|
||||
}
|
||||
}
|
||||
|
||||
#define UBOOT_SIZE 0x40000
|
||||
|
||||
static struct mtd_partition amazon_partitions[3] = {
|
||||
{
|
||||
name:"U-Boot", /* U-Boot firmware */
|
||||
offset:0x00000000,
|
||||
size:UBOOT_SIZE , /* 128k */
|
||||
},
|
||||
{
|
||||
name:"kernel", /* firmware */
|
||||
offset:UBOOT_SIZE,
|
||||
size:0x00100000, /* 192K */
|
||||
},
|
||||
{
|
||||
name:"rootfs", /* default partition */
|
||||
offset:0x00200000,
|
||||
size:0x00200000,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
unsigned long flash_start = 0x13000000;
|
||||
unsigned long flash_size = 0x800000;
|
||||
unsigned long uImage_size = 0x10000d;
|
||||
|
||||
int find_uImage_size(unsigned long start_offset){
|
||||
unsigned long temp;
|
||||
|
||||
printk("trying to find uImage and its size\n");
|
||||
amazon_copy_from(&amazon_map, &temp, start_offset + 12, 4);
|
||||
printk("kernel size is %d \n", temp + 0x40);
|
||||
return temp + 0x40;
|
||||
}
|
||||
|
||||
int __init init_amazon_mtd(void)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mtd_info *mymtd = NULL;
|
||||
struct mtd_partition *parts = NULL;
|
||||
|
||||
*AMAZON_EBU_BUSCON0 = 0x1d7ff;
|
||||
|
||||
amazon_map.read = amazon_read16;
|
||||
amazon_map.write = amazon_write16;
|
||||
amazon_map.copy_from = amazon_copy_from;
|
||||
amazon_map.copy_to = amazon_copy_to;
|
||||
|
||||
amazon_map.phys = flash_start;
|
||||
amazon_map.virt = ioremap_nocache(flash_start, flash_size);
|
||||
|
||||
if (!amazon_map.virt) {
|
||||
printk(KERN_WARNING "Failed to ioremap!\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
mymtd = (struct mtd_info *) do_map_probe("cfi_probe", &amazon_map);
|
||||
if (!mymtd) {
|
||||
iounmap(amazon_map.virt);
|
||||
printk("probing failed\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
mymtd->owner = THIS_MODULE;
|
||||
parts = &amazon_partitions[0];
|
||||
amazon_partitions[2].offset = UBOOT_SIZE + find_uImage_size(amazon_partitions[1].offset);
|
||||
amazon_partitions[1].size = mymtd->size - amazon_partitions[1].offset - (2 * mymtd->erasesize);
|
||||
amazon_partitions[2].size = mymtd->size - amazon_partitions[2].offset - (2 * mymtd->erasesize);
|
||||
add_mtd_partitions(mymtd, parts, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_amazon_mtd(void)
|
||||
{
|
||||
/* FIXME! */
|
||||
}
|
||||
|
||||
module_init(init_amazon_mtd);
|
||||
module_exit(cleanup_amazon_mtd);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("john crispin blogic@openwrt.org");
|
||||
MODULE_DESCRIPTION("MTD map driver for AMAZON boards");
|
||||
876
target/linux/amazon-2.6/files/drivers/net/amazon_sw.c
Normal file
876
target/linux/amazon-2.6/files/drivers/net/amazon_sw.c
Normal file
@@ -0,0 +1,876 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
//-----------------------------------------------------------------------
|
||||
/*
|
||||
* Description:
|
||||
* Driver for Infineon Amazon 3 port switch
|
||||
*/
|
||||
//-----------------------------------------------------------------------
|
||||
/* Author: Wu Qi Ming[Qi-Ming.Wu@infineon.com]
|
||||
* Created: 7-April-2004
|
||||
*/
|
||||
//-----------------------------------------------------------------------
|
||||
/* History
|
||||
* Changed on: Jun 28, 2004
|
||||
* Changed by: peng.liu@infineon.com
|
||||
* Reason: add hardware flow control (HFC) (CONFIG_NET_HW_FLOWCONTROL)
|
||||
*
|
||||
* Changed on: Apr 6, 2005
|
||||
* Changed by: mars.lin@infineon.com
|
||||
* Reason : supoort port identification
|
||||
*/
|
||||
|
||||
|
||||
// copyright 2004-2005 infineon.com
|
||||
|
||||
// copyright 2007 john crispin <blogic@openwrt.org>
|
||||
// copyright 2007 felix fietkau <nbd@openwrt.org>
|
||||
|
||||
|
||||
// TODO
|
||||
// port vlan code from bcrm target... the tawainese code was scrapped due to crappyness
|
||||
// check all the mmi reg settings and possibly document them better
|
||||
// verify the ethtool code
|
||||
// remove the while(1) stuff
|
||||
// further clean up and rework ... but it works for now
|
||||
// check the mode[]=bridge stuff
|
||||
// verify that the ethaddr can be set from u-boot
|
||||
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#define __KERNEL__
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
|
||||
#define MODVERSIONS
|
||||
#endif
|
||||
|
||||
#if defined(MODVERSIONS) && !defined(__GENKSYMS__)
|
||||
#include <linux/modversions.h>
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mii.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <asm/checksum.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/amazon/amazon.h>
|
||||
#include <asm/amazon/amazon_dma.h>
|
||||
#include <asm/amazon/amazon_sw.h>
|
||||
|
||||
// how many mii ports are there ?
|
||||
#define AMAZON_SW_INT_NO 2
|
||||
|
||||
#define ETHERNET_PACKET_DMA_BUFFER_SIZE 1536
|
||||
|
||||
/***************************************** Module Parameters *************************************/
|
||||
char mode[] = "bridge";
|
||||
module_param_array(mode, charp, NULL, 0);
|
||||
|
||||
static int timeout = 1 * HZ;
|
||||
module_param(timeout, int, 0);
|
||||
|
||||
int switch_init(struct net_device *dev);
|
||||
void switch_tx_timeout(struct net_device *dev);
|
||||
|
||||
struct net_device switch_devs[2] = {
|
||||
{init:switch_init,},
|
||||
{init:switch_init,}
|
||||
};
|
||||
|
||||
int add_mac_table_entry(u64 entry_value)
|
||||
{
|
||||
int i;
|
||||
u32 data1, data2;
|
||||
|
||||
AMAZON_SW_REG32(AMAZON_SW_ARL_CTL) = ~7;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) = 0x80000000 | 0x20 | i;
|
||||
while (AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) & (0x80000000)) {};
|
||||
data1 = AMAZON_SW_REG32(AMAZON_SW_DATA1);
|
||||
data2 = AMAZON_SW_REG32(AMAZON_SW_DATA2);
|
||||
if ((data1 & (0x00700000)) != 0x00700000)
|
||||
continue;
|
||||
AMAZON_SW_REG32(AMAZON_SW_DATA1) = (u32) (entry_value >> 32);
|
||||
AMAZON_SW_REG32(AMAZON_SW_DATA2) = (u32) entry_value & 0xffffffff;
|
||||
AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) = 0xc0000020 | i;
|
||||
while (AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) & (0x80000000)) {};
|
||||
break;
|
||||
}
|
||||
AMAZON_SW_REG32(AMAZON_SW_ARL_CTL) |= 7;
|
||||
if (i >= 32)
|
||||
return -1;
|
||||
return OK;
|
||||
}
|
||||
|
||||
u64 read_mac_table_entry(int index)
|
||||
{
|
||||
u32 data1, data2;
|
||||
u64 value;
|
||||
AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) = 0x80000000 | 0x20 | index;
|
||||
while (AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) & (0x80000000)) {};
|
||||
data1 = AMAZON_SW_REG32(AMAZON_SW_DATA1) & 0xffffff;
|
||||
data2 = AMAZON_SW_REG32(AMAZON_SW_DATA2);
|
||||
value = (u64) data1 << 32 | (u64) data2;
|
||||
return value;
|
||||
}
|
||||
|
||||
int write_mac_table_entry(int index, u64 value)
|
||||
{
|
||||
u32 data1, data2;
|
||||
data1 = (u32) (value >> 32);
|
||||
data2 = (u32) value & 0xffffffff;
|
||||
AMAZON_SW_REG32(AMAZON_SW_DATA1) = data1;
|
||||
AMAZON_SW_REG32(AMAZON_SW_DATA2) = data2;
|
||||
AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) = 0xc0000020 | index;
|
||||
while (AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) & (0x80000000)) {};
|
||||
return OK;
|
||||
}
|
||||
|
||||
u32 get_mdio_reg(int phy_addr, int reg_num)
|
||||
{
|
||||
u32 value;
|
||||
AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) = (3 << 30) | ((phy_addr & 0x1f) << 21) | ((reg_num & 0x1f) << 16);
|
||||
while (AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) & (1 << 31)) {};
|
||||
value = AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) & 0xffff;
|
||||
return value;
|
||||
}
|
||||
|
||||
int set_mdio_reg(int phy_addr, int reg_num, u32 value)
|
||||
{
|
||||
AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) = (2 << 30) | ((phy_addr & 0x1f) << 21) | ((reg_num & 0x1f) << 16) | (value & 0xffff);
|
||||
while (AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) & (1 << 31)) {};
|
||||
return OK;
|
||||
}
|
||||
|
||||
int auto_negotiate(int phy_addr)
|
||||
{
|
||||
u32 value = 0;
|
||||
value = get_mdio_reg(phy_addr, MDIO_BASE_CONTROL_REG);
|
||||
set_mdio_reg(phy_addr, MDIO_BASE_CONTROL_REG, (value | RESTART_AUTO_NEGOTIATION | AUTO_NEGOTIATION_ENABLE | PHY_RESET));
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
In this version of switch driver, we split the dma channels for the switch.
|
||||
2 for port0 and 2 for port1. So that we can do internal bridging if necessary.
|
||||
In switch mode, packets coming in from port0 or port1 is able to do Destination
|
||||
address lookup. Packets coming from port0 with destination address of port1 should
|
||||
not go to pmac again. The switch hardware should be able to do the switch in the hard
|
||||
ware level. Packets coming from the pmac should not do the DA look up in that the
|
||||
desination is already known for the kernel. It only needs to go to the correct NIC to
|
||||
find its way out.
|
||||
*/
|
||||
int amazon_sw_chip_init(void)
|
||||
{
|
||||
u32 tmp1;
|
||||
int i = 0;
|
||||
|
||||
/* Aging tick select: 5mins */
|
||||
tmp1 = 0xa0;
|
||||
if (strcmp(mode, "bridge") == 0) {
|
||||
// bridge mode, set militarised mode to 1, no learning!
|
||||
tmp1 |= 0xC00;
|
||||
} else {
|
||||
// enable learning for P0 and P1,
|
||||
tmp1 |= 3;
|
||||
}
|
||||
|
||||
/* unknown broadcast/multicast/unicast to all ports */
|
||||
AMAZON_SW_REG32(AMAZON_SW_UN_DEST) = 0x1ff;
|
||||
|
||||
AMAZON_SW_REG32(AMAZON_SW_ARL_CTL) = tmp1;
|
||||
|
||||
/* OCS:1 set OCS bit, split the two NIC in rx direction EDL:1 (enable DA lookup) */
|
||||
#if defined(CONFIG_IFX_NFEXT_AMAZON_SWITCH_PHYPORT) || defined(CONFIG_IFX_NFEXT_AMAZON_SWITCH_PHYPORT_MODULE)
|
||||
AMAZON_SW_REG32(AMAZON_SW_P2_PCTL) = 0x700;
|
||||
#else
|
||||
AMAZON_SW_REG32(AMAZON_SW_P2_PCTL) = 0x401;
|
||||
#endif
|
||||
|
||||
/* EPC: 1 split the two NIC in tx direction CRC is generated */
|
||||
AMAZON_SW_REG32(AMAZON_SW_P2_CTL) = 0x6;
|
||||
|
||||
// for bi-directional
|
||||
AMAZON_SW_REG32(AMAZON_SW_P0_WM) = 0x14141412;
|
||||
AMAZON_SW_REG32(AMAZON_SW_P1_WM) = 0x14141412;
|
||||
AMAZON_SW_REG32(AMAZON_SW_P2_WM) = 0x28282826;
|
||||
AMAZON_SW_REG32(AMAZON_SW_GBL_WM) = 0x0;
|
||||
|
||||
AMAZON_SW_REG32(AMAZON_CGU_PLL0SR) = (AMAZON_SW_REG32(AMAZON_CGU_PLL0SR)) | 0x58000000;
|
||||
// clock for PHY
|
||||
AMAZON_SW_REG32(AMAZON_CGU_IFCCR) = (AMAZON_SW_REG32(AMAZON_CGU_IFCCR)) | 0x80000004;
|
||||
// enable power for PHY
|
||||
AMAZON_SW_REG32(AMAZON_PMU_PWDCR) = (AMAZON_SW_REG32(AMAZON_PMU_PWDCR)) | AMAZON_PMU_PWDCR_EPHY;
|
||||
// set reverse MII, enable MDIO statemachine
|
||||
AMAZON_SW_REG32(AMAZON_SW_MDIO_CFG) = 0x800027bf;
|
||||
while (1)
|
||||
if (((AMAZON_SW_REG32(AMAZON_SW_MDIO_CFG)) & 0x80000000) == 0)
|
||||
break;
|
||||
AMAZON_SW_REG32(AMAZON_SW_EPHY) = 0xff;
|
||||
|
||||
// auto negotiation
|
||||
AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) = 0x83e08000;
|
||||
auto_negotiate(0x1f);
|
||||
|
||||
/* enable all ports */
|
||||
AMAZON_SW_REG32(AMAZON_SW_PS_CTL) = 0x7;
|
||||
for (i = 0; i < 32; i++)
|
||||
write_mac_table_entry(i, 1 << 50);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned char my_ethaddr[MAX_ADDR_LEN];
|
||||
/* need to get the ether addr from u-boot */
|
||||
static int __init ethaddr_setup(char *line)
|
||||
{
|
||||
char *ep;
|
||||
int i;
|
||||
|
||||
memset(my_ethaddr, 0, MAX_ADDR_LEN);
|
||||
for (i = 0; i < 6; i++) {
|
||||
my_ethaddr[i] = line ? simple_strtoul(line, &ep, 16) : 0;
|
||||
if (line)
|
||||
line = (*ep) ? ep + 1 : ep;
|
||||
}
|
||||
printk("mac address %2x-%2x-%2x-%2x-%2x-%2x \n", my_ethaddr[0], my_ethaddr[1], my_ethaddr[2], my_ethaddr[3], my_ethaddr[4], my_ethaddr[5]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__setup("ethaddr=", ethaddr_setup);
|
||||
|
||||
static void open_rx_dma(struct net_device *dev)
|
||||
{
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
struct dma_device_info *dma_dev = priv->dma_device;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dma_dev->num_rx_chan; i++)
|
||||
dma_dev->rx_chan[i].control = 1;
|
||||
dma_device_update_rx(dma_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_HW_FLOWCONTROL
|
||||
static void close_rx_dma(struct net_device *dev)
|
||||
{
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
struct dma_device_info *dma_dev = priv->dma_device;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dma_dev->num_rx_chan; i++)
|
||||
dma_dev->rx_chan[i].control = 0;
|
||||
dma_device_update_rx(dma_dev);
|
||||
}
|
||||
|
||||
void amazon_xon(struct net_device *dev)
|
||||
{
|
||||
unsigned long flag;
|
||||
local_irq_save(flag);
|
||||
open_rx_dma(dev);
|
||||
local_irq_restore(flag);
|
||||
}
|
||||
#endif
|
||||
|
||||
int switch_open(struct net_device *dev)
|
||||
{
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
if (!strcmp(dev->name, "eth1")) {
|
||||
priv->mdio_phy_addr = PHY0_ADDR;
|
||||
}
|
||||
open_rx_dma(dev);
|
||||
|
||||
#ifdef CONFIG_NET_HW_FLOWCONTROL
|
||||
if ((priv->fc_bit = netdev_register_fc(dev, amazon_xon)) == 0) {
|
||||
printk("Hardware Flow Control register fails\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
netif_start_queue(dev);
|
||||
return OK;
|
||||
}
|
||||
|
||||
int switch_release(struct net_device *dev)
|
||||
{
|
||||
int i;
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
struct dma_device_info *dma_dev = priv->dma_device;
|
||||
|
||||
for (i = 0; i < dma_dev->num_tx_chan; i++)
|
||||
dma_dev->tx_chan[i].control = 0;
|
||||
for (i = 0; i < dma_dev->num_rx_chan; i++)
|
||||
dma_dev->rx_chan[i].control = 0;
|
||||
|
||||
dma_device_update(dma_dev);
|
||||
|
||||
#ifdef CONFIG_NET_HW_FLOWCONTROL
|
||||
if (priv->fc_bit) {
|
||||
netdev_unregister_fc(priv->fc_bit);
|
||||
}
|
||||
#endif
|
||||
netif_stop_queue(dev);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
void switch_rx(struct net_device *dev, int len, struct sk_buff *skb)
|
||||
{
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
#ifdef CONFIG_NET_HW_FLOWCONTROL
|
||||
int mit_sel = 0;
|
||||
#endif
|
||||
skb->dev = dev;
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
|
||||
#ifdef CONFIG_NET_HW_FLOWCONTROL
|
||||
mit_sel = netif_rx(skb);
|
||||
switch (mit_sel) {
|
||||
case NET_RX_SUCCESS:
|
||||
case NET_RX_CN_LOW:
|
||||
case NET_RX_CN_MOD:
|
||||
break;
|
||||
case NET_RX_CN_HIGH:
|
||||
break;
|
||||
case NET_RX_DROP:
|
||||
if ((priv->fc_bit)
|
||||
&& (!test_and_set_bit(priv->fc_bit, &netdev_fc_xoff))) {
|
||||
close_rx_dma(dev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#else
|
||||
netif_rx(skb);
|
||||
#endif
|
||||
priv->stats.rx_packets++;
|
||||
priv->stats.rx_bytes += len;
|
||||
return;
|
||||
}
|
||||
|
||||
int asmlinkage switch_hw_tx(char *buf, int len, struct net_device *dev)
|
||||
{
|
||||
struct switch_priv *priv = dev->priv;
|
||||
struct dma_device_info *dma_dev = priv->dma_device;
|
||||
|
||||
dma_dev->current_tx_chan = 0;
|
||||
return dma_device_write(dma_dev, buf, len, priv->skb);
|
||||
}
|
||||
|
||||
int asmlinkage switch_tx(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
int len;
|
||||
char *data;
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
|
||||
len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
|
||||
data = skb->data;
|
||||
priv->skb = skb;
|
||||
dev->trans_start = jiffies;
|
||||
|
||||
if (switch_hw_tx(data, len, dev) != len) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return OK;
|
||||
}
|
||||
|
||||
priv->stats.tx_packets++;
|
||||
priv->stats.tx_bytes += len;
|
||||
return OK;
|
||||
}
|
||||
|
||||
void switch_tx_timeout(struct net_device *dev)
|
||||
{
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
priv->stats.tx_errors++;
|
||||
netif_wake_queue(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
void negotiate(struct net_device *dev)
|
||||
{
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
unsigned short data = get_mdio_reg(priv->mdio_phy_addr, MDIO_ADVERTISMENT_REG);
|
||||
|
||||
data &= ~(MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD);
|
||||
|
||||
switch (priv->current_speed_selection) {
|
||||
case 10:
|
||||
if (priv->current_duplex == full)
|
||||
data |= MDIO_ADVERT_10_FD;
|
||||
else if (priv->current_duplex == half)
|
||||
data |= MDIO_ADVERT_10_HD;
|
||||
else
|
||||
data |= MDIO_ADVERT_10_HD | MDIO_ADVERT_10_FD;
|
||||
break;
|
||||
|
||||
case 100:
|
||||
if (priv->current_duplex == full)
|
||||
data |= MDIO_ADVERT_100_FD;
|
||||
else if (priv->current_duplex == half)
|
||||
data |= MDIO_ADVERT_100_HD;
|
||||
else
|
||||
data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD;
|
||||
break;
|
||||
|
||||
case 0: /* Auto */
|
||||
if (priv->current_duplex == full)
|
||||
data |= MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD;
|
||||
else if (priv->current_duplex == half)
|
||||
data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_10_HD;
|
||||
else
|
||||
data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD;
|
||||
break;
|
||||
|
||||
default: /* assume autoneg speed and duplex */
|
||||
data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD;
|
||||
}
|
||||
|
||||
set_mdio_reg(priv->mdio_phy_addr, MDIO_ADVERTISMENT_REG, data);
|
||||
|
||||
/* Renegotiate with link partner */
|
||||
|
||||
data = get_mdio_reg(priv->mdio_phy_addr, MDIO_BASE_CONTROL_REG);
|
||||
data |= MDIO_BC_NEGOTIATE;
|
||||
|
||||
set_mdio_reg(priv->mdio_phy_addr, MDIO_BASE_CONTROL_REG, data);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void set_duplex(struct net_device *dev, enum duplex new_duplex)
|
||||
{
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
if (new_duplex != priv->current_duplex) {
|
||||
priv->current_duplex = new_duplex;
|
||||
negotiate(dev);
|
||||
}
|
||||
}
|
||||
|
||||
void set_speed(struct net_device *dev, unsigned long speed)
|
||||
{
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
priv->current_speed_selection = speed;
|
||||
negotiate(dev);
|
||||
}
|
||||
|
||||
static int switch_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr)
|
||||
{
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
struct ethtool_cmd ecmd;
|
||||
|
||||
if (copy_from_user(&ecmd, ifr->ifr_data, sizeof(ecmd)))
|
||||
return -EFAULT;
|
||||
|
||||
switch (ecmd.cmd) {
|
||||
case ETHTOOL_GSET:
|
||||
memset((void *) &ecmd, 0, sizeof(ecmd));
|
||||
ecmd.supported = SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
|
||||
SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full;
|
||||
ecmd.port = PORT_TP;
|
||||
ecmd.transceiver = XCVR_EXTERNAL;
|
||||
ecmd.phy_address = priv->mdio_phy_addr;
|
||||
|
||||
ecmd.speed = priv->current_speed;
|
||||
|
||||
ecmd.duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
|
||||
|
||||
ecmd.advertising = ADVERTISED_TP;
|
||||
if (priv->current_duplex == autoneg && priv->current_speed_selection == 0)
|
||||
ecmd.advertising |= ADVERTISED_Autoneg;
|
||||
else {
|
||||
ecmd.advertising |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
|
||||
ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
|
||||
if (priv->current_speed_selection == 10)
|
||||
ecmd.advertising &= ~(ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full);
|
||||
else if (priv->current_speed_selection == 100)
|
||||
ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full);
|
||||
if (priv->current_duplex == half)
|
||||
ecmd.advertising &= ~(ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Full);
|
||||
else if (priv->current_duplex == full)
|
||||
ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_100baseT_Half);
|
||||
}
|
||||
ecmd.autoneg = AUTONEG_ENABLE;
|
||||
if (copy_to_user(ifr->ifr_data, &ecmd, sizeof(ecmd)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
|
||||
case ETHTOOL_SSET:
|
||||
if (!capable(CAP_NET_ADMIN)) {
|
||||
return -EPERM;
|
||||
}
|
||||
if (ecmd.autoneg == AUTONEG_ENABLE) {
|
||||
set_duplex(dev, autoneg);
|
||||
set_speed(dev, 0);
|
||||
} else {
|
||||
set_duplex(dev, ecmd.duplex == DUPLEX_HALF ? half : full);
|
||||
set_speed(dev, ecmd.speed == SPEED_10 ? 10 : 100);
|
||||
}
|
||||
break;
|
||||
|
||||
case ETHTOOL_GDRVINFO:
|
||||
{
|
||||
struct ethtool_drvinfo info;
|
||||
memset((void *) &info, 0, sizeof(info));
|
||||
strncpy(info.driver, "AMAZONE", sizeof(info.driver) - 1);
|
||||
strncpy(info.fw_version, "N/A", sizeof(info.fw_version) - 1);
|
||||
strncpy(info.bus_info, "N/A", sizeof(info.bus_info) - 1);
|
||||
info.regdump_len = 0;
|
||||
info.eedump_len = 0;
|
||||
info.testinfo_len = 0;
|
||||
if (copy_to_user(ifr->ifr_data, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
}
|
||||
break;
|
||||
case ETHTOOL_NWAY_RST:
|
||||
if (priv->current_duplex == autoneg && priv->current_speed_selection == 0)
|
||||
negotiate(dev);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int mac_table_tools_ioctl(struct net_device *dev, struct mac_table_req *req)
|
||||
{
|
||||
int cmd;
|
||||
int i;
|
||||
cmd = req->cmd;
|
||||
switch (cmd) {
|
||||
case RESET_MAC_TABLE:
|
||||
for (i = 0; i < 32; i++) {
|
||||
write_mac_table_entry(i, 0);
|
||||
}
|
||||
break;
|
||||
case READ_MAC_ENTRY:
|
||||
req->entry_value = read_mac_table_entry(req->index);
|
||||
break;
|
||||
case WRITE_MAC_ENTRY:
|
||||
write_mac_table_entry(req->index, req->entry_value);
|
||||
break;
|
||||
case ADD_MAC_ENTRY:
|
||||
add_mac_table_entry(req->entry_value);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
the ioctl for the switch driver is developed in the conventional way
|
||||
the control type falls into some basic categories, among them, the
|
||||
SIOCETHTOOL is the traditional eth interface. VLAN_TOOLS and
|
||||
MAC_TABLE_TOOLS are designed specifically for amazon chip. User
|
||||
should be aware of the data structures used in these interfaces.
|
||||
*/
|
||||
int switch_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
struct data_req *switch_data_req = (struct data_req *) ifr->ifr_data;
|
||||
struct mac_table_req *switch_mac_table_req;
|
||||
switch (cmd) {
|
||||
case SIOCETHTOOL:
|
||||
switch_ethtool_ioctl(dev, ifr);
|
||||
break;
|
||||
case SIOCGMIIPHY: /* Get PHY address */
|
||||
break;
|
||||
case SIOCGMIIREG: /* Read MII register */
|
||||
break;
|
||||
case SIOCSMIIREG: /* Write MII register */
|
||||
break;
|
||||
case SET_ETH_SPEED_10: /* 10 Mbps */
|
||||
break;
|
||||
case SET_ETH_SPEED_100: /* 100 Mbps */
|
||||
break;
|
||||
case SET_ETH_SPEED_AUTO: /* Auto negotiate speed */
|
||||
break;
|
||||
case SET_ETH_DUPLEX_HALF: /* Half duplex. */
|
||||
break;
|
||||
case SET_ETH_DUPLEX_FULL: /* Full duplex. */
|
||||
break;
|
||||
case SET_ETH_DUPLEX_AUTO: /* Autonegotiate duplex */
|
||||
break;
|
||||
case SET_ETH_REG:
|
||||
AMAZON_SW_REG32(switch_data_req->index) = switch_data_req->value;
|
||||
break;
|
||||
case MAC_TABLE_TOOLS:
|
||||
switch_mac_table_req = (struct mac_table_req *) ifr->ifr_data;
|
||||
mac_table_tools_ioctl(dev, switch_mac_table_req);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct net_device_stats *switch_stats(struct net_device *dev)
|
||||
{
|
||||
struct switch_priv *priv = (struct switch_priv *) dev->priv;
|
||||
return &priv->stats;
|
||||
}
|
||||
|
||||
int switch_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
if (new_mtu >= 1516)
|
||||
new_mtu = 1516;
|
||||
dev->mtu = new_mtu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int switch_hw_receive(struct net_device *dev, struct dma_device_info *dma_dev)
|
||||
{
|
||||
u8 *buf = NULL;
|
||||
int len = 0;
|
||||
struct sk_buff *skb = NULL;
|
||||
|
||||
len = dma_device_read(dma_dev, &buf, (void **) &skb);
|
||||
|
||||
if (len >= 0x600) {
|
||||
printk("packet too large %d\n", len);
|
||||
goto switch_hw_receive_err_exit;
|
||||
}
|
||||
|
||||
/* remove CRC */
|
||||
len -= 4;
|
||||
if (skb == NULL) {
|
||||
printk("cannot restore pointer\n");
|
||||
goto switch_hw_receive_err_exit;
|
||||
}
|
||||
if (len > (skb->end - skb->tail)) {
|
||||
printk("BUG, len:%d end:%p tail:%p\n", (len + 4), skb->end, skb->tail);
|
||||
goto switch_hw_receive_err_exit;
|
||||
}
|
||||
skb_put(skb, len);
|
||||
skb->dev = dev;
|
||||
switch_rx(dev, len, skb);
|
||||
return OK;
|
||||
|
||||
switch_hw_receive_err_exit:
|
||||
if (skb)
|
||||
dev_kfree_skb_any(skb);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int dma_intr_handler(struct dma_device_info *dma_dev, int status)
|
||||
{
|
||||
struct net_device *dev;
|
||||
|
||||
dev = switch_devs + (u32) dma_dev->priv;
|
||||
switch (status) {
|
||||
case RCV_INT:
|
||||
switch_hw_receive(dev, dma_dev);
|
||||
break;
|
||||
case TX_BUF_FULL_INT:
|
||||
netif_stop_queue(dev);
|
||||
break;
|
||||
case TRANSMIT_CPT_INT:
|
||||
netif_wake_queue(dev);
|
||||
break;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* reserve 2 bytes in front of data pointer*/
|
||||
u8 *dma_buffer_alloc(int len, int *byte_offset, void **opt)
|
||||
{
|
||||
u8 *buffer = NULL;
|
||||
struct sk_buff *skb = NULL;
|
||||
skb = dev_alloc_skb(ETHERNET_PACKET_DMA_BUFFER_SIZE);
|
||||
if (skb == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
buffer = (u8 *) (skb->data);
|
||||
skb_reserve(skb, 2);
|
||||
*(int *) opt = (int) skb;
|
||||
*byte_offset = 2;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int dma_buffer_free(u8 * dataptr, void *opt)
|
||||
{
|
||||
struct sk_buff *skb = NULL;
|
||||
if (opt == NULL) {
|
||||
kfree(dataptr);
|
||||
} else {
|
||||
skb = (struct sk_buff *) opt;
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int init_dma_device(_dma_device_info * dma_dev)
|
||||
{
|
||||
int i;
|
||||
int num_tx_chan, num_rx_chan;
|
||||
if (strcmp(dma_dev->device_name, "switch1") == 0) {
|
||||
num_tx_chan = 1;
|
||||
num_rx_chan = 2;
|
||||
dma_dev->priv = (void *) 0;
|
||||
} else {
|
||||
num_tx_chan = 1;
|
||||
num_rx_chan = 2;
|
||||
dma_dev->priv = (void *) 1;
|
||||
}
|
||||
|
||||
dma_dev->weight = 1;
|
||||
dma_dev->num_tx_chan = num_tx_chan;
|
||||
dma_dev->num_rx_chan = num_rx_chan;
|
||||
dma_dev->ack = 1;
|
||||
dma_dev->tx_burst_len = 4;
|
||||
dma_dev->rx_burst_len = 4;
|
||||
for (i = 0; i < dma_dev->num_tx_chan; i++) {
|
||||
dma_dev->tx_chan[i].weight = QOS_DEFAULT_WGT;
|
||||
dma_dev->tx_chan[i].desc_num = 10;
|
||||
dma_dev->tx_chan[i].packet_size = 0;
|
||||
dma_dev->tx_chan[i].control = 0;
|
||||
}
|
||||
for (i = 0; i < num_rx_chan; i++) {
|
||||
dma_dev->rx_chan[i].weight = QOS_DEFAULT_WGT;
|
||||
dma_dev->rx_chan[i].desc_num = 10;
|
||||
dma_dev->rx_chan[i].packet_size = ETHERNET_PACKET_DMA_BUFFER_SIZE;
|
||||
dma_dev->rx_chan[i].control = 0;
|
||||
}
|
||||
dma_dev->intr_handler = dma_intr_handler;
|
||||
dma_dev->buffer_alloc = dma_buffer_alloc;
|
||||
dma_dev->buffer_free = dma_buffer_free;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int switch_set_mac_address(struct net_device *dev, void *p)
|
||||
{
|
||||
struct sockaddr *addr = p;
|
||||
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
int switch_init(struct net_device *dev)
|
||||
{
|
||||
u64 retval = 0;
|
||||
int i;
|
||||
int result;
|
||||
struct switch_priv *priv;
|
||||
ether_setup(dev); /* assign some of the fields */
|
||||
printk("%s up using ", dev->name);
|
||||
dev->open = switch_open;
|
||||
dev->stop = switch_release;
|
||||
dev->hard_start_xmit = switch_tx;
|
||||
dev->do_ioctl = switch_ioctl;
|
||||
dev->get_stats = switch_stats;
|
||||
dev->change_mtu = switch_change_mtu;
|
||||
dev->set_mac_address = switch_set_mac_address;
|
||||
dev->tx_timeout = switch_tx_timeout;
|
||||
dev->watchdog_timeo = timeout;
|
||||
|
||||
SET_MODULE_OWNER(dev);
|
||||
|
||||
dev->priv = kmalloc(sizeof(struct switch_priv), GFP_KERNEL);
|
||||
if (dev->priv == NULL)
|
||||
return -ENOMEM;
|
||||
memset(dev->priv, 0, sizeof(struct switch_priv));
|
||||
priv = dev->priv;
|
||||
priv->dma_device = (struct dma_device_info *) kmalloc(sizeof(struct dma_device_info), GFP_KERNEL);
|
||||
if ((dev - switch_devs) == 0) {
|
||||
sprintf(priv->dma_device->device_name, "switch1");
|
||||
} else if ((dev - switch_devs) == 1) {
|
||||
sprintf(priv->dma_device->device_name, "switch2");
|
||||
}
|
||||
printk("\"%s\"\n", priv->dma_device->device_name);
|
||||
init_dma_device(priv->dma_device);
|
||||
result = dma_device_register(priv->dma_device);
|
||||
|
||||
/* read the mac address from the mac table and put them into the mac table. */
|
||||
for (i = 0; i < 6; i++) {
|
||||
retval += my_ethaddr[i];
|
||||
}
|
||||
/* ethaddr not set in u-boot ? */
|
||||
if (retval == 0) {
|
||||
dev->dev_addr[0] = 0x00;
|
||||
dev->dev_addr[1] = 0x20;
|
||||
dev->dev_addr[2] = 0xda;
|
||||
dev->dev_addr[3] = 0x86;
|
||||
dev->dev_addr[4] = 0x23;
|
||||
dev->dev_addr[5] = 0x74 + (unsigned char) (dev - switch_devs);
|
||||
} else {
|
||||
for (i = 0; i < 6; i++) {
|
||||
dev->dev_addr[i] = my_ethaddr[i];
|
||||
}
|
||||
dev->dev_addr[5] += +(unsigned char) (dev - switch_devs);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int switch_init_module(void)
|
||||
{
|
||||
int i = 0, result, device_present = 0;
|
||||
|
||||
for (i = 0; i < AMAZON_SW_INT_NO; i++) {
|
||||
sprintf(switch_devs[i].name, "eth%d", i);
|
||||
|
||||
if ((result = register_netdev(switch_devs + i)))
|
||||
printk("error %i registering device \"%s\"\n", result, switch_devs[i].name);
|
||||
else
|
||||
device_present++;
|
||||
}
|
||||
amazon_sw_chip_init();
|
||||
return device_present ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
void switch_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
struct switch_priv *priv;
|
||||
for (i = 0; i < AMAZON_SW_INT_NO; i++) {
|
||||
priv = switch_devs[i].priv;
|
||||
if (priv->dma_device) {
|
||||
dma_device_unregister(priv->dma_device);
|
||||
kfree(priv->dma_device);
|
||||
}
|
||||
kfree(switch_devs[i].priv);
|
||||
unregister_netdev(switch_devs + i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(switch_init_module);
|
||||
module_exit(switch_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Wu Qi Ming");
|
||||
755
target/linux/amazon-2.6/files/drivers/serial/amazon_asc.c
Normal file
755
target/linux/amazon-2.6/files/drivers/serial/amazon_asc.c
Normal file
@@ -0,0 +1,755 @@
|
||||
/*
|
||||
* linux/drivers/char/amazon_asc.c
|
||||
*
|
||||
* Driver for AMAZONASC serial ports
|
||||
*
|
||||
* Copyright (C) 2004 Infineon IFAP DC COM CPE
|
||||
* Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2007 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
* Based on drivers/serial/serial_s3c2400.c
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* $Id: amazon_asc.c,v 1.2 2005/04/01 02:40:48 pliu Exp $
|
||||
*
|
||||
* This is a generic driver for AMAZONASC-type serial ports.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/circ_buf.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/bitops.h>
|
||||
#include <asm/amazon/amazon.h>
|
||||
#include <asm/amazon/irq.h>
|
||||
#include <asm/amazon/serial.h>
|
||||
|
||||
#define PORT_AMAZONASC 111
|
||||
|
||||
#if defined(CONFIG_SERIAL_AMAZONASC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/serial_core.h>
|
||||
|
||||
#define UART_NR 1
|
||||
|
||||
#define SERIAL_AMAZONASC_MAJOR TTY_MAJOR
|
||||
#define CALLOUT_AMAZONASC_MAJOR TTYAUX_MAJOR
|
||||
#define SERIAL_AMAZONASC_MINOR 64
|
||||
#define SERIAL_AMAZONASC_NR UART_NR
|
||||
|
||||
extern void prom_printf(const char * fmt, ...);
|
||||
static struct uart_port amazonasc_ports[UART_NR];
|
||||
static struct uart_driver amazonasc_reg;
|
||||
#ifdef CONFIG_SERIAL_AMAZONASC_CONSOLE /*SUPPORT_SYSRQ*/
|
||||
static struct console amazonasc_console;
|
||||
#endif
|
||||
static unsigned int uartclk = 0;
|
||||
|
||||
#define SET_BIT(reg, mask) *reg |= (mask)
|
||||
#define CLEAR_BIT(reg, mask) *reg &= (~mask)
|
||||
#define CLEAR_BITS(reg, mask) CLEAR_BIT(reg, mask)
|
||||
#define SET_BITS(reg, mask) SET_BIT(reg, mask)
|
||||
#define SET_BITFIELD(reg, mask, off, val) \
|
||||
{*reg &= (~mask); *reg |= (val << off);}
|
||||
|
||||
static void amazonasc_tx_chars(struct uart_port *port);
|
||||
|
||||
/* fake flag to indicate CREAD was not set -> throw away all bytes */
|
||||
#define UART_DUMMY_UER_RX 1
|
||||
|
||||
/* macro to set the bit corresponding to an interrupt number */
|
||||
#define BIT_NO(irq) (1 << (irq - 64))
|
||||
|
||||
#define SERIAL_DEBUG
|
||||
|
||||
extern unsigned int amazon_get_fpi_hz(void);
|
||||
static int tx_enabled = 0;
|
||||
|
||||
static void amazonasc_stop_tx(struct uart_port *port)
|
||||
{
|
||||
/* fifo underrun shuts up after firing once */
|
||||
return;
|
||||
}
|
||||
|
||||
static void amazonasc_start_tx(struct uart_port *port)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
amazonasc_tx_chars(port);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void amazonasc_stop_rx(struct uart_port *port)
|
||||
{
|
||||
/* clear the RX enable bit */
|
||||
*AMAZON_ASC_WHBCON = ASCWHBCON_CLRREN;
|
||||
}
|
||||
|
||||
static void amazonasc_enable_ms(struct uart_port *port)
|
||||
{
|
||||
/* no modem signals */
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
amazonasc_rx_chars(struct uart_port *port)
|
||||
{
|
||||
struct tty_struct *tty = port->info->tty;
|
||||
unsigned int ch = 0, rsr = 0, fifocnt;
|
||||
unsigned long flags;
|
||||
|
||||
fifocnt = *AMAZON_ASC_FSTAT & ASCFSTAT_RXFFLMASK;
|
||||
while (fifocnt--)
|
||||
{
|
||||
u8 flag = TTY_NORMAL;
|
||||
ch = *AMAZON_ASC_RBUF;
|
||||
rsr = (*AMAZON_ASC_CON & ASCCON_ANY) | UART_DUMMY_UER_RX;
|
||||
tty_flip_buffer_push(tty);
|
||||
port->icount.rx++;
|
||||
|
||||
/*
|
||||
* Note that the error handling code is
|
||||
* out of the main execution path
|
||||
*/
|
||||
if (rsr & ASCCON_ANY) {
|
||||
if (rsr & ASCCON_PE) {
|
||||
port->icount.parity++;
|
||||
SET_BIT(AMAZON_ASC_WHBCON, ASCWHBCON_CLRPE);
|
||||
} else if (rsr & ASCCON_FE) {
|
||||
port->icount.frame++;
|
||||
SET_BIT(AMAZON_ASC_WHBCON, ASCWHBCON_CLRFE);
|
||||
}
|
||||
if (rsr & ASCCON_OE) {
|
||||
port->icount.overrun++;
|
||||
SET_BIT(AMAZON_ASC_WHBCON, ASCWHBCON_CLROE);
|
||||
}
|
||||
|
||||
rsr &= port->read_status_mask;
|
||||
|
||||
if (rsr & ASCCON_PE)
|
||||
flag = TTY_PARITY;
|
||||
else if (rsr & ASCCON_FE)
|
||||
flag = TTY_FRAME;
|
||||
}
|
||||
|
||||
if ((rsr & port->ignore_status_mask) == 0)
|
||||
tty_insert_flip_char(tty, ch, flag);
|
||||
|
||||
if (rsr & ASCCON_OE)
|
||||
/*
|
||||
* Overrun is special, since it's reported
|
||||
* immediately, and doesn't affect the current
|
||||
* character
|
||||
*/
|
||||
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
|
||||
}
|
||||
if (ch != 0)
|
||||
tty_flip_buffer_push(tty);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void amazonasc_tx_chars(struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
|
||||
if (uart_tx_stopped(port)) {
|
||||
amazonasc_stop_tx(port);
|
||||
return;
|
||||
}
|
||||
|
||||
while (((*AMAZON_ASC_FSTAT & ASCFSTAT_TXFFLMASK)
|
||||
>> ASCFSTAT_TXFFLOFF) != AMAZONASC_TXFIFO_FULL)
|
||||
{
|
||||
if (port->x_char) {
|
||||
*AMAZON_ASC_TBUF = port->x_char;
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
|
||||
*AMAZON_ASC_TBUF = xmit->buf[xmit->tail];
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
}
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
}
|
||||
|
||||
static irqreturn_t amazonasc_tx_int(int irq, void *port)
|
||||
{
|
||||
*(AMAZON_ASC_IRNCR1) = ASC_IRNCR_TIR;
|
||||
amazonasc_start_tx(port);
|
||||
|
||||
/* clear any pending interrupts */
|
||||
SET_BIT(AMAZON_ASC_WHBCON, ASCWHBCON_CLRPE);
|
||||
SET_BIT(AMAZON_ASC_WHBCON, ASCWHBCON_CLRFE);
|
||||
SET_BIT(AMAZON_ASC_WHBCON, ASCWHBCON_CLROE);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t amazonasc_er_int(int irq, void *port)
|
||||
{
|
||||
/* clear any pending interrupts */
|
||||
SET_BIT(AMAZON_ASC_WHBCON, ASCWHBCON_CLRPE);
|
||||
SET_BIT(AMAZON_ASC_WHBCON, ASCWHBCON_CLRFE);
|
||||
SET_BIT(AMAZON_ASC_WHBCON, ASCWHBCON_CLROE);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t amazonasc_rx_int(int irq, void *port)
|
||||
{
|
||||
*(AMAZON_ASC_IRNCR1) = ASC_IRNCR_RIR;
|
||||
amazonasc_rx_chars((struct uart_port *) port);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static u_int amazonasc_tx_empty(struct uart_port *port)
|
||||
{
|
||||
int status;
|
||||
|
||||
/*
|
||||
* FSTAT tells exactly how many bytes are in the FIFO.
|
||||
* The question is whether we really need to wait for all
|
||||
* 16 bytes to be transmitted before reporting that the
|
||||
* transmitter is empty.
|
||||
*/
|
||||
status = *AMAZON_ASC_FSTAT & ASCFSTAT_TXFFLMASK;
|
||||
return status ? 0 : TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
static u_int amazonasc_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
/* no modem control signals - the readme says to pretend all are set */
|
||||
return TIOCM_CTS|TIOCM_CAR|TIOCM_DSR;
|
||||
}
|
||||
|
||||
static void amazonasc_set_mctrl(struct uart_port *port, u_int mctrl)
|
||||
{
|
||||
/* no modem control - just return */
|
||||
return;
|
||||
}
|
||||
|
||||
static void amazonasc_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
/* no way to send a break */
|
||||
return;
|
||||
}
|
||||
|
||||
static int amazonasc_startup(struct uart_port *port)
|
||||
{
|
||||
unsigned int con = 0;
|
||||
unsigned long flags;
|
||||
int retval;
|
||||
|
||||
/* this assumes: CON.BRS = CON.FDE = 0 */
|
||||
if (uartclk == 0)
|
||||
uartclk = amazon_get_fpi_hz();
|
||||
|
||||
amazonasc_ports[0].uartclk = uartclk;
|
||||
|
||||
/* block the IRQs */
|
||||
local_irq_save(flags);
|
||||
|
||||
/* this setup was probably already done in u-boot */
|
||||
/* ASC and GPIO Port 1 bits 3 and 4 share the same pins
|
||||
* P1.3 (RX) in, Alternate 10
|
||||
* P1.4 (TX) in, Alternate 10
|
||||
*/
|
||||
SET_BITFIELD((AMAZON_GPIO_P1_DIR), 0x8, 4, 1); //P1.4 output, P1.3 input
|
||||
SET_BIT((AMAZON_GPIO_P1_ALTSEL0), 0x18); //ALTSETL0 11
|
||||
CLEAR_BIT((AMAZON_GPIO_P1_ALTSEL1), 0x18); //ALTSETL1 00
|
||||
SET_BITFIELD((AMAZON_GPIO_P1_OD), 0x8, 4, 1);
|
||||
|
||||
/* set up the CLC */
|
||||
CLEAR_BIT(AMAZON_ASC_CLC, AMAZON_ASC_CLC_DISS);
|
||||
SET_BITFIELD(AMAZON_ASC_CLC, ASCCLC_RMCMASK, ASCCLC_RMCOFFSET, 1);
|
||||
/* asynchronous mode */
|
||||
con = ASCCON_M_8ASYNC;
|
||||
/* set error signals - framing and overrun */
|
||||
con |= ASCCON_FEN;
|
||||
con |= ASCCON_OEN;
|
||||
con |= ASCCON_PEN;
|
||||
/* choose the line - there's only one */
|
||||
*AMAZON_ASC_PISEL = 0;
|
||||
#if 1
|
||||
*AMAZON_ASC_TXFCON = (((AMAZONASC_TXFIFO_FL<<ASCTXFCON_TXFITLOFF)&ASCTXFCON_TXFITLMASK) | ASCTXFCON_TXFEN |ASCTXFCON_TXFFLU);
|
||||
*AMAZON_ASC_RXFCON = (((AMAZONASC_RXFIFO_FL<<ASCRXFCON_RXFITLOFF)&ASCRXFCON_RXFITLMASK) | ASCRXFCON_RXFEN |ASCRXFCON_RXFFLU);
|
||||
wmb();
|
||||
#else
|
||||
/* TXFIFO's fill level */
|
||||
SET_BITFIELD(AMAZON_ASC_TXFCON, ASCTXFCON_TXFITLMASK,
|
||||
ASCTXFCON_TXFITLOFF, AMAZONASC_TXFIFO_FL);
|
||||
/* enable TXFIFO */
|
||||
SET_BIT(AMAZON_ASC_TXFCON, ASCTXFCON_TXFEN);
|
||||
/* RXFIFO's fill level */
|
||||
SET_BITFIELD(AMAZON_ASC_RXFCON, ASCRXFCON_RXFITLMASK,
|
||||
ASCRXFCON_RXFITLOFF, AMAZONASC_RXFIFO_FL);
|
||||
/* enable RXFIFO */
|
||||
SET_BIT(AMAZON_ASC_RXFCON, ASCRXFCON_RXFEN);
|
||||
/* now really set CON */
|
||||
#endif
|
||||
SET_BIT(AMAZON_ASC_CON,con);
|
||||
|
||||
/*
|
||||
* Allocate the IRQs
|
||||
*/
|
||||
retval = request_irq(AMAZONASC_RIR, amazonasc_rx_int, 0, "asc_rx", port);
|
||||
if (retval){
|
||||
printk("-------req1 failed\n");
|
||||
return retval;
|
||||
}
|
||||
retval = request_irq(AMAZONASC_TIR, amazonasc_tx_int, 0, "asc_tx", port);
|
||||
if (retval){
|
||||
printk("----------req2 failed\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
retval = request_irq(AMAZONASC_EIR, amazonasc_er_int, 0, "asc_er", port);
|
||||
if (retval){
|
||||
printk("---------req3 failed\n");
|
||||
goto err2;
|
||||
}
|
||||
/* unblock the IRQs */
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
free_irq(AMAZONASC_TIR, port);
|
||||
|
||||
err1:
|
||||
free_irq(AMAZONASC_RIR, port);
|
||||
local_irq_restore(flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void amazonasc_shutdown(struct uart_port *port)
|
||||
{
|
||||
/*
|
||||
* Free the interrupts
|
||||
*/
|
||||
free_irq(AMAZONASC_RIR, port);
|
||||
free_irq(AMAZONASC_TIR, port);
|
||||
free_irq(AMAZONASC_EIR, port);
|
||||
/*
|
||||
* disable the baudrate generator to disable the ASC
|
||||
*/
|
||||
*AMAZON_ASC_CON = 0;
|
||||
|
||||
/* flush and then disable the fifos */
|
||||
SET_BIT(AMAZON_ASC_RXFCON, ASCRXFCON_RXFFLU);
|
||||
CLEAR_BIT(AMAZON_ASC_RXFCON, ASCRXFCON_RXFEN);
|
||||
SET_BIT(AMAZON_ASC_TXFCON, ASCTXFCON_TXFFLU);
|
||||
CLEAR_BIT(AMAZON_ASC_TXFCON, ASCTXFCON_TXFEN);
|
||||
}
|
||||
|
||||
static void amazonasc_set_termios(struct uart_port *port, struct ktermios *new, struct ktermios *old)
|
||||
{
|
||||
unsigned int cflag;
|
||||
unsigned int iflag;
|
||||
unsigned int baud, quot;
|
||||
unsigned int con = 0;
|
||||
unsigned long flags;
|
||||
|
||||
cflag = new->c_cflag;
|
||||
iflag = new->c_iflag;
|
||||
|
||||
/* byte size and parity */
|
||||
switch (cflag & CSIZE) {
|
||||
/* 7 bits are always with parity */
|
||||
case CS7: con = ASCCON_M_7ASYNCPAR; break;
|
||||
/* the ASC only suports 7 and 8 bits */
|
||||
case CS5:
|
||||
case CS6:
|
||||
default:
|
||||
if (cflag & PARENB)
|
||||
con = ASCCON_M_8ASYNCPAR;
|
||||
else
|
||||
con = ASCCON_M_8ASYNC;
|
||||
break;
|
||||
}
|
||||
if (cflag & CSTOPB)
|
||||
con |= ASCCON_STP;
|
||||
if (cflag & PARENB) {
|
||||
if (!(cflag & PARODD))
|
||||
con &= ~ASCCON_ODD;
|
||||
else
|
||||
con |= ASCCON_ODD;
|
||||
}
|
||||
|
||||
port->read_status_mask = ASCCON_OE;
|
||||
if (iflag & INPCK)
|
||||
port->read_status_mask |= ASCCON_FE | ASCCON_PE;
|
||||
/* the ASC can't really detect or generate a BREAK */
|
||||
#if 0
|
||||
if (iflag & (BRKINT | PARMRK))
|
||||
port->read_status_mask |= UERSTAT_BREAK;
|
||||
#endif
|
||||
/*
|
||||
* Characters to ignore
|
||||
*/
|
||||
port->ignore_status_mask = 0;
|
||||
if (iflag & IGNPAR)
|
||||
port->ignore_status_mask |= ASCCON_FE | ASCCON_PE;
|
||||
#if 0
|
||||
/* always ignore breaks - the ASC can't handle them XXXX */
|
||||
port->ignore_status_mask |= UERSTAT_BREAK;
|
||||
#endif
|
||||
if (iflag & IGNBRK) {
|
||||
/*port->ignore_status_mask |= UERSTAT_BREAK;*/
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (iflag & IGNPAR)
|
||||
port->ignore_status_mask |= ASCCON_OE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore all characters if CREAD is not set.
|
||||
*/
|
||||
if ((cflag & CREAD) == 0)
|
||||
port->ignore_status_mask |= UART_DUMMY_UER_RX;
|
||||
|
||||
/* set error signals - framing, parity and overrun */
|
||||
con |= ASCCON_FEN;
|
||||
con |= ASCCON_OEN;
|
||||
con |= ASCCON_PEN;
|
||||
/* enable the receiver */
|
||||
con |= ASCCON_REN;
|
||||
|
||||
/* block the IRQs */
|
||||
local_irq_save(flags);
|
||||
|
||||
/* set up CON */
|
||||
*AMAZON_ASC_CON = con;
|
||||
|
||||
/* Set baud rate - take a divider of 2 into account */
|
||||
baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
quot = quot/2 - 1;
|
||||
|
||||
/* the next 3 probably already happened when we set CON above */
|
||||
/* disable the baudrate generator */
|
||||
CLEAR_BIT(AMAZON_ASC_CON, ASCCON_R);
|
||||
/* make sure the fractional divider is off */
|
||||
CLEAR_BIT(AMAZON_ASC_CON, ASCCON_FDE);
|
||||
/* set up to use divisor of 2 */
|
||||
CLEAR_BIT(AMAZON_ASC_CON, ASCCON_BRS);
|
||||
/* now we can write the new baudrate into the register */
|
||||
*AMAZON_ASC_BTR = quot;
|
||||
/* turn the baudrate generator back on */
|
||||
SET_BIT(AMAZON_ASC_CON, ASCCON_R);
|
||||
|
||||
/* unblock the IRQs */
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static const char *amazonasc_type(struct uart_port *port)
|
||||
{
|
||||
return port->type == PORT_AMAZONASC ? "AMAZONASC" : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the memory region(s) being used by 'port'
|
||||
*/
|
||||
static void amazonasc_release_port(struct uart_port *port)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Request the memory region(s) being used by 'port'
|
||||
*/
|
||||
static int amazonasc_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure/autoconfigure the port.
|
||||
*/
|
||||
static void amazonasc_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE) {
|
||||
port->type = PORT_AMAZONASC;
|
||||
amazonasc_request_port(port);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* verify the new serial_struct (for TIOCSSERIAL).
|
||||
*/
|
||||
static int amazonasc_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
int ret = 0;
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMAZONASC)
|
||||
ret = -EINVAL;
|
||||
if (ser->irq < 0 || ser->irq >= NR_IRQS)
|
||||
ret = -EINVAL;
|
||||
if (ser->baud_base < 9600)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops amazonasc_pops = {
|
||||
.tx_empty = amazonasc_tx_empty,
|
||||
.set_mctrl = amazonasc_set_mctrl,
|
||||
.get_mctrl = amazonasc_get_mctrl,
|
||||
.stop_tx = amazonasc_stop_tx,
|
||||
.start_tx = amazonasc_start_tx,
|
||||
.stop_rx = amazonasc_stop_rx,
|
||||
.enable_ms = amazonasc_enable_ms,
|
||||
.break_ctl = amazonasc_break_ctl,
|
||||
.startup = amazonasc_startup,
|
||||
.shutdown = amazonasc_shutdown,
|
||||
.set_termios = amazonasc_set_termios,
|
||||
.type = amazonasc_type,
|
||||
.release_port = amazonasc_release_port,
|
||||
.request_port = amazonasc_request_port,
|
||||
.config_port = amazonasc_config_port,
|
||||
.verify_port = amazonasc_verify_port,
|
||||
};
|
||||
|
||||
static struct uart_port amazonasc_ports[UART_NR] = {
|
||||
{
|
||||
membase: (void *)AMAZON_ASC,
|
||||
mapbase: AMAZON_ASC,
|
||||
iotype: SERIAL_IO_MEM,
|
||||
irq: AMAZONASC_RIR, /* RIR */
|
||||
uartclk: 0, /* filled in dynamically */
|
||||
fifosize: 16,
|
||||
unused: { AMAZONASC_TIR, AMAZONASC_EIR}, /* xmit/error/xmit-buffer-empty IRQ */
|
||||
type: PORT_AMAZONASC,
|
||||
ops: &amazonasc_pops,
|
||||
flags: ASYNC_BOOT_AUTOCONF,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void amazonasc_console_write(struct console *co, const char *s, u_int count)
|
||||
{
|
||||
int i, fifocnt;
|
||||
unsigned long flags;
|
||||
/* block the IRQ */
|
||||
local_irq_save(flags);
|
||||
/*
|
||||
* Now, do each character
|
||||
*/
|
||||
for (i = 0; i < count;)
|
||||
{
|
||||
/* wait until the FIFO is not full */
|
||||
do
|
||||
{
|
||||
fifocnt = (*AMAZON_ASC_FSTAT & ASCFSTAT_TXFFLMASK)
|
||||
>> ASCFSTAT_TXFFLOFF;
|
||||
} while (fifocnt == AMAZONASC_TXFIFO_FULL);
|
||||
#if 1
|
||||
if (s[i] == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (s[i] == '\n')
|
||||
{
|
||||
*AMAZON_ASC_TBUF = '\r';
|
||||
do
|
||||
{
|
||||
fifocnt = (*AMAZON_ASC_FSTAT &
|
||||
ASCFSTAT_TXFFLMASK) >> ASCFSTAT_TXFFLOFF;
|
||||
} while (fifocnt == AMAZONASC_TXFIFO_FULL);
|
||||
}
|
||||
*AMAZON_ASC_TBUF = s[i];
|
||||
i++;
|
||||
} /* for */
|
||||
|
||||
/* restore the IRQ */
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void __init
|
||||
amazonasc_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
|
||||
{
|
||||
u_int lcr_h;
|
||||
|
||||
lcr_h = *AMAZON_ASC_CON;
|
||||
/* do this only if the ASC is turned on */
|
||||
if (lcr_h & ASCCON_R) {
|
||||
u_int quot, div, fdiv, frac;
|
||||
|
||||
*parity = 'n';
|
||||
if ((lcr_h & ASCCON_MODEMASK) == ASCCON_M_7ASYNCPAR ||
|
||||
(lcr_h & ASCCON_MODEMASK) == ASCCON_M_8ASYNCPAR) {
|
||||
if (lcr_h & ASCCON_ODD)
|
||||
*parity = 'o';
|
||||
else
|
||||
*parity = 'e';
|
||||
}
|
||||
|
||||
if ((lcr_h & ASCCON_MODEMASK) == ASCCON_M_7ASYNCPAR)
|
||||
*bits = 7;
|
||||
else
|
||||
*bits = 8;
|
||||
|
||||
quot = *AMAZON_ASC_BTR + 1;
|
||||
|
||||
/* this gets hairy if the fractional divider is used */
|
||||
if (lcr_h & ASCCON_FDE)
|
||||
{
|
||||
div = 1;
|
||||
fdiv = *AMAZON_ASC_FDV;
|
||||
if (fdiv == 0)
|
||||
fdiv = 512;
|
||||
frac = 512;
|
||||
}
|
||||
else
|
||||
{
|
||||
div = lcr_h & ASCCON_BRS ? 3 : 2;
|
||||
fdiv = frac = 1;
|
||||
}
|
||||
/*
|
||||
* This doesn't work exactly because we use integer
|
||||
* math to calculate baud which results in rounding
|
||||
* errors when we try to go from quot -> baud !!
|
||||
* Try to make this work for both the fractional divider
|
||||
* and the simple divider. Also try to avoid rounding
|
||||
* errors using integer math.
|
||||
*/
|
||||
|
||||
*baud = frac * (port->uartclk / (div * 512 * 16 * quot));
|
||||
if (*baud > 1100 && *baud < 2400)
|
||||
*baud = 1200;
|
||||
if (*baud > 2300 && *baud < 4800)
|
||||
*baud = 2400;
|
||||
if (*baud > 4700 && *baud < 9600)
|
||||
*baud = 4800;
|
||||
if (*baud > 9500 && *baud < 19200)
|
||||
*baud = 9600;
|
||||
if (*baud > 19000 && *baud < 38400)
|
||||
*baud = 19200;
|
||||
if (*baud > 38400 && *baud < 57600)
|
||||
*baud = 38400;
|
||||
if (*baud > 57600 && *baud < 115200)
|
||||
*baud = 57600;
|
||||
if (*baud > 115200 && *baud < 230400)
|
||||
*baud = 115200;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init amazonasc_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port;
|
||||
int baud = 115200;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
/* this assumes: CON.BRS = CON.FDE = 0 */
|
||||
if (uartclk == 0)
|
||||
uartclk = amazon_get_fpi_hz();
|
||||
co->index = 0;
|
||||
port = &amazonasc_ports[0];
|
||||
amazonasc_ports[0].uartclk = uartclk;
|
||||
amazonasc_ports[0].type = PORT_AMAZONASC;
|
||||
|
||||
if (options){
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
}
|
||||
|
||||
return uart_set_options(port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver amazonasc_reg;
|
||||
static struct console amazonasc_console = {
|
||||
name: "ttyS",
|
||||
write: amazonasc_console_write,
|
||||
device: uart_console_device,
|
||||
setup: amazonasc_console_setup,
|
||||
flags: CON_PRINTBUFFER,
|
||||
index: -1,
|
||||
data: &amazonasc_reg,
|
||||
};
|
||||
|
||||
static int __init amazonasc_console_init(void)
|
||||
{
|
||||
register_console(&amazonasc_console);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(amazonasc_console_init);
|
||||
|
||||
static struct uart_driver amazonasc_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "serial",
|
||||
.dev_name = "ttyS",
|
||||
.major = TTY_MAJOR,
|
||||
.minor = 64,
|
||||
.nr = UART_NR,
|
||||
.cons = &amazonasc_console,
|
||||
};
|
||||
|
||||
static int __init amazonasc_init(void)
|
||||
{
|
||||
unsigned char res;
|
||||
uart_register_driver(&amazonasc_reg);
|
||||
res = uart_add_one_port(&amazonasc_reg, &amazonasc_ports[0]);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void __exit amazonasc_exit(void)
|
||||
{
|
||||
uart_unregister_driver(&amazonasc_reg);
|
||||
}
|
||||
|
||||
module_init(amazonasc_init);
|
||||
module_exit(amazonasc_exit);
|
||||
|
||||
MODULE_AUTHOR("Gary Jennejohn, Felix Fietkau, John Crispin");
|
||||
MODULE_DESCRIPTION("MIPS AMAZONASC serial port driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user