1
0
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:
blogic
2007-07-23 22:10:11 +00:00
parent a39d500fb4
commit e2133d3ce6
47 changed files with 26109 additions and 0 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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);

File diff suppressed because it is too large Load Diff

View 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");

View 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");

View 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");