diff -urN linux-2.4.19-rmk7-pxa2/arch/arm/config.in linux-2.4.19-rmk7-pxa2-dimm/arch/arm/config.in --- linux-2.4.19-rmk7-pxa2/arch/arm/config.in Wed Nov 5 09:37:57 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/arch/arm/config.in Mon Sep 8 18:42:42 2003 @@ -170,7 +170,7 @@ fi if [ "$CONFIG_ARCH_LUBBOCK" = "y" ]; then - define_bool CONFIG_SA1111 y + dep_bool ' Support for SA1111' CONFIG_SA1111 fi if [ "$CONFIG_ARCH_TRIZEPS2" = "y" ]; then @@ -658,7 +658,7 @@ fi endmenu -if [ "$CONFIG_ARCH_CLPS711X" = "y" ]; then +if [ "$CONFIG_ARCH_CLPS711X" = "y" -o "$CONFIG_ARCH_PXA" = "y" ]; then source drivers/ssi/Config.in fi diff -urN linux-2.4.19-rmk7-pxa2/arch/arm/mach-pxa/lubbock.c linux-2.4.19-rmk7-pxa2-dimm/arch/arm/mach-pxa/lubbock.c --- linux-2.4.19-rmk7-pxa2/arch/arm/mach-pxa/lubbock.c Wed Nov 5 09:37:58 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/arch/arm/mach-pxa/lubbock.c Tue Oct 7 16:15:50 2003 @@ -38,6 +38,11 @@ #include "sa1111.h" #endif +#define PCMCIA_SLOT0_IRQ_GPIO 10 +#define PCMCIA_SLOT1_IRQ_GPIO 11 + +#define ETH0_IRQ_GPIO 20 +#define ETH1_IRQ_GPIO 19 static unsigned long lubbock_irq_enabled; @@ -89,10 +94,43 @@ set_GPIO_IRQ_edge(GPIO_LUBBOCK_IRQ, GPIO_FALLING_EDGE); setup_arm_irq(IRQ_GPIO_LUBBOCK_IRQ, &lubbock_irq); + + // enable eth0 & eth1 interrupt + set_GPIO_IRQ_edge( ETH0_IRQ_GPIO, GPIO_RISING_EDGE); + set_GPIO_IRQ_edge( ETH1_IRQ_GPIO, GPIO_RISING_EDGE); + + // enable pcmcia (ide) interrupt + set_GPIO_IRQ_edge( PCMCIA_SLOT0_IRQ_GPIO, GPIO_BOTH_EDGES); + set_GPIO_IRQ_edge( PCMCIA_SLOT1_IRQ_GPIO, GPIO_BOTH_EDGES); + +#ifdef CONFIG_BLK_DEV_IDEDISK +/* setup PCMCIA socket, used by IDE ctrl */ + GPSR(GPIO48_nPOE) = GPIO_bit(GPIO48_nPOE) | + GPIO_bit(GPIO49_nPWE) | + GPIO_bit(GPIO50_nPIOR) | + GPIO_bit(GPIO51_nPIOW) | + GPIO_bit(GPIO52_nPCE_1) | + GPIO_bit(GPIO53_nPCE_2); + + set_GPIO_mode(GPIO48_nPOE_MD); + set_GPIO_mode(GPIO49_nPWE_MD); + set_GPIO_mode(GPIO50_nPIOR_MD); + set_GPIO_mode(GPIO51_nPIOW_MD); + set_GPIO_mode(GPIO52_nPCE_1_MD); + set_GPIO_mode(GPIO53_nPCE_2_MD); + set_GPIO_mode(GPIO54_pSKTSEL_MD); /* REVISIT: s/b dependent on num sockets */ + set_GPIO_mode(GPIO55_nPREG_MD); + set_GPIO_mode(GPIO56_nPWAIT_MD); + set_GPIO_mode(GPIO57_nIOIS16_MD); + + MECR |= GPIO_bit(0); // configure socket count = 2 + MECR |= GPIO_bit(1); // enable PCMCIA ctrl +#endif } static int __init lubbock_init(void) { +#ifdef CONFIG_SA1111 int ret; ret = sa1111_probe(LUBBOCK_SA1111_BASE); @@ -100,6 +138,7 @@ return ret; sa1111_wake(); sa1111_init_irq(LUBBOCK_SA1111_IRQ); +#endif return 0; } @@ -120,6 +159,7 @@ { 0xf1000000, 0x0c000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* LAN91C96 IO */ { 0xf1100000, 0x0e000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* LAN91C96 Attr */ { 0xf4000000, 0x10000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* SA1111 */ + { 0xf5000000, 0x14000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* PXA_CS5_PHYS */ LAST_DESC }; @@ -148,9 +188,10 @@ PCFR |= PCFR_OPDE; } -MACHINE_START(LUBBOCK, "Intel DBPXA250 Development Platform") +MACHINE_START(LUBBOCK, "Voipac PXA250 Developement board") MAINTAINER("MontaVista Software Inc.") BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + BOOT_PARAMS(0xa0000100) FIXUP(fixup_lubbock) MAPIO(lubbock_map_io) INITIRQ(lubbock_init_irq) diff -urN linux-2.4.19-rmk7-pxa2/drivers/char/Config.in linux-2.4.19-rmk7-pxa2-dimm/drivers/char/Config.in --- linux-2.4.19-rmk7-pxa2/drivers/char/Config.in Wed Nov 5 09:37:58 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/char/Config.in Thu Oct 9 12:13:58 2003 @@ -136,6 +136,14 @@ dep_tristate ' DS1307 RTC' CONFIG_I2C_DS1307 $CONFIG_I2C fi +if [ "$CONFIG_I2C" != "n" ]; then + dep_tristate ' DS1339 RTC' CONFIG_I2C_DS1339 $CONFIG_I2C +fi + +if [ "$CONFIG_I2C" != "n" ]; then + dep_tristate ' DS1672 RTC' CONFIG_I2C_DS1672 $CONFIG_I2C +fi + source drivers/l3/Config.in mainmenu_option next_comment diff -urN linux-2.4.19-rmk7-pxa2/drivers/char/Makefile linux-2.4.19-rmk7-pxa2-dimm/drivers/char/Makefile --- linux-2.4.19-rmk7-pxa2/drivers/char/Makefile Wed Nov 5 09:37:58 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/char/Makefile Thu Oct 9 12:14:27 2003 @@ -295,6 +295,8 @@ # I2C char devices obj-$(CONFIG_I2C_DS1307) += ds1307.o +obj-$(CONFIG_I2C_DS1339) += ds1339.o +obj-$(CONFIG_I2C_DS1672) += ds1672.o # TTL-I/O device (MT6N board) obj-$(CONFIG_TRIZEPS2_TTLIO) += mt6n_ttl.o diff -urN linux-2.4.19-rmk7-pxa2/drivers/char/ds1339.c linux-2.4.19-rmk7-pxa2-dimm/drivers/char/ds1339.c --- linux-2.4.19-rmk7-pxa2/drivers/char/ds1339.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/char/ds1339.c Thu Oct 9 14:29:51 2003 @@ -0,0 +1,345 @@ +/* + * ds1339.c + * + * Device driver for Dallas Semiconductor's Real Time Controller DS1339. + * + * Copyright (C) 2003 Voipac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ds1339.h" + +//#define RTC_DEBUG + +static unsigned short slave_address = DS1339_I2C_SLAVE_ADDR; + +struct i2c_driver ds1339_driver; +struct i2c_client *ds1339_i2c_client = 0; + +static unsigned short ignore[] = { I2C_CLIENT_END }; +static unsigned short normal_addr[] = { DS1339_I2C_SLAVE_ADDR, I2C_CLIENT_END }; + +static struct i2c_client_address_data addr_data = { + normal_i2c: normal_addr, + normal_i2c_range: ignore, + probe: ignore, + probe_range: ignore, + ignore: ignore, + ignore_range: ignore, + force: ignore, +}; + +static int ds1339_rtc_ioctl( struct inode *, struct file *, unsigned int, unsigned long); +static int ds1339_rtc_open(struct inode *inode, struct file *file); +static int ds1339_rtc_release(struct inode *inode, struct file *file); + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + ioctl: ds1339_rtc_ioctl, + open: ds1339_rtc_open, + release: ds1339_rtc_release, +}; + +static struct miscdevice ds1339_rtc_miscdev = { + RTC_MINOR, + "rtc", + &rtc_fops +}; + +static int ds1339_probe(struct i2c_adapter *adap); +static int ds1339_detach(struct i2c_client *client); + +struct i2c_driver ds1339_driver = { + name: "DS1339", + id: I2C_DRIVERID_DS1339, + flags: I2C_DF_NOTIFY, + attach_adapter: ds1339_probe, + detach_client: ds1339_detach +}; + +static spinlock_t ds1339_rtc_lock = SPIN_LOCK_UNLOCKED; + +static void ds1339_osc_enable(void) +{ +#ifdef RTC_DEBUG + printk ("ds1339_osc_enable()\n"); +#endif + + unsigned long flags; + unsigned char buf[2], ad[1] = { DS1339_CONTROL_ADDR }; + struct i2c_msg msgs[2] = { + { ds1339_i2c_client->addr, 0, 1, ad }, + { ds1339_i2c_client->addr, I2C_M_RD, 1, buf } + }; + + // read ds1339 control register + spin_lock_irqsave(&ds1339_rtc_lock, flags); + i2c_transfer(ds1339_i2c_client->adapter, msgs, 2); + spin_unlock_irqrestore(&ds1339_rtc_lock,flags); + + buf[1] = buf[0] & ~DS1339_OSC_ENABLE; // clear oscilator enable bit + buf[0] = DS1339_CONTROL_ADDR; // control register address on DS1339 + + if( i2c_master_send(ds1339_i2c_client, (char *)buf, 2) != 2) + printk ("ds1339_osc_enable(): i2c_master_send failed.\n"); +} + +static int ds1339_convert_to_time( struct rtc_time *dt, char* buf) +{ +#ifdef RTC_DEBUG + printk("ds1339_convert_to_time\n"); +#endif + + memset( dt, 0, sizeof(struct rtc_time)); + + dt->tm_sec = BCD_TO_BIN(buf[0]); + dt->tm_min = BCD_TO_BIN(buf[1]); + + if ( TWELVE_HOUR_MODE(buf[2]) ) + { + dt->tm_hour = HOURS_12(buf[2]); + if (HOURS_AP(buf[2])) // pm + dt->tm_hour += 12; + } + else // 24-hour-mode + dt->tm_hour = HOURS_24(buf[2]); + + dt->tm_mday = BCD_TO_BIN(buf[4]); + // dt->tm_mon is zero-based + dt->tm_mon = BCD_TO_BIN(buf[5]) - 1; + // year is 1900 + dt->tm_year + dt->tm_year = BCD_TO_BIN(buf[6]) + 100; + +#ifdef RTC_DEBUG + printk("ds1339_get_datetime: year = %d\n", dt->tm_year); + printk("ds1339_get_datetime: mon = %d\n", dt->tm_mon); + printk("ds1339_get_datetime: mday = %d\n", dt->tm_mday); + printk("ds1339_get_datetime: hour = %d\n", dt->tm_hour); + printk("ds1339_get_datetime: min = %d\n", dt->tm_min); + printk("ds1339_get_datetime: sec = %d\n", dt->tm_sec); +#endif + return 0; +} + +static int ds1339_get_datetime(struct i2c_client *client, struct rtc_time *dt) +{ + int ret = -EIO; + unsigned long flags; + unsigned char buf[DS1339_BCD_CNT_SIZE], addr[1] = { DS1339_BCD_CNT_ADDR }; + struct i2c_msg msgs[2] = { + { client->addr, 0, 1, addr }, + { client->addr, I2C_M_RD, DS1339_BCD_CNT_SIZE, buf } + }; + + memset(buf, 0, sizeof(buf)); + + spin_lock_irqsave(&ds1339_rtc_lock, flags); + ret = i2c_transfer(client->adapter, msgs, 2); + spin_unlock_irqrestore(&ds1339_rtc_lock,flags); + + if (ret == 2) + return ds1339_convert_to_time( dt, buf); + else + printk("ds1339_get_datetime(), i2c_transfer() returned %d\n",ret); + + return ret; +} + +static int ds1339_attach(struct i2c_adapter *adap, int addr, unsigned short flags,int kind) +{ + struct i2c_client *c; + struct rtc_time dt; + + if( (c = (struct i2c_client *) kmalloc(sizeof(*c), GFP_KERNEL)) == NULL) + return -ENOMEM; + + strcpy(c->name, "DS1339"); + c->id = ds1339_driver.id; + c->flags = 0; + c->addr = addr; + c->adapter = adap; + c->driver = &ds1339_driver; + c->data = NULL; + + if( ds1339_get_datetime( c, &dt) != 0) + printk ("ds1339_attach(): i2c_transfer failed.\n"); + + ds1339_i2c_client = c; + ds1339_osc_enable(); + + return i2c_attach_client(c); +} + +static int ds1339_probe(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, ds1339_attach); +} + +static int ds1339_detach(struct i2c_client *client) +{ + i2c_detach_client(client); + return 0; +} + +static int ds1339_set_datetime(struct i2c_client *client, struct rtc_time *dt) +{ + int ret = 0; + unsigned long flags; + unsigned char buf[DS1339_BCD_CNT_SIZE+1]; + +#ifdef RTC_DEBUG + printk("ds1339_set_datetime: tm_year = %d\n", dt->tm_year); + printk("ds1339_set_datetime: tm_mon = %d\n", dt->tm_mon); + printk("ds1339_set_datetime: tm_mday = %d\n", dt->tm_mday); + printk("ds1339_set_datetime: tm_hour = %d\n", dt->tm_hour); + printk("ds1339_set_datetime: tm_min = %d\n", dt->tm_min); + printk("ds1339_set_datetime: tm_sec = %d\n", dt->tm_sec); +#endif + + buf[0] = DS1339_BCD_CNT_ADDR; // register address on DS1307 + buf[1] = (BIN_TO_BCD(dt->tm_sec)); + buf[2] = (BIN_TO_BCD(dt->tm_min)); + buf[3] = (BIN_TO_BCD(dt->tm_hour)); + + buf[5] = (BIN_TO_BCD(dt->tm_mday)); + buf[6] = (BIN_TO_BCD(dt->tm_mon + 1)); + // The year only ranges from 0-99, we are being passed an offset from 1900 + // and the chip calulates leap years based on 2000, thus we adjust by 100. + buf[7] = (BIN_TO_BCD(dt->tm_year - 100)); + + spin_lock_irqsave(&ds1339_rtc_lock, flags); + ret = i2c_master_send(client, (char *)buf, DS1339_BCD_CNT_SIZE+1); + spin_unlock_irqrestore(&ds1339_rtc_lock,flags); + + if (ret == (DS1339_BCD_CNT_SIZE+1)) + return 0; + else + printk("ds1339_set_datetime(), i2c_master_send() returned %d\n",ret); + + return ret; +} + +static int ds1339_rtc_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int ds1339_rtc_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int ds1339_rtc_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct rtc_time wtime; + + switch (cmd) + { + case RTC_RD_TIME: + ds1339_get_datetime(ds1339_i2c_client, &wtime); + + if( copy_to_user((void *)arg, &wtime, sizeof (struct rtc_time))) + return -EFAULT; + break; + + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&wtime, (struct rtc_time *)arg, sizeof(struct rtc_time)) ) + return -EFAULT; + + if( wtime.tm_year < 100) // ds1339 base is year 2000 + return -EINVAL; + + ds1339_set_datetime(ds1339_i2c_client, &wtime); + + break; + + default: + return -EINVAL; + } + + return 0; +} + +static char* ds1339_mon2str( unsigned int mon) +{ + char *mon2str[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + return mon2str[mon%12]; +} + +static int ds1339_rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + struct rtc_time dt; + + len += sprintf(page + len, "Voipac DS1339 RTC driver (c) 2003\n\n"); + + if( ds1339_get_datetime(ds1339_i2c_client, &dt) == 0) + { + len += sprintf(page + len, "Date/Time : %02d-%s-%04d %02d:%02d:%02d\n", + dt.tm_mday, ds1339_mon2str(dt.tm_mon), dt.tm_year + 1900, dt.tm_hour, dt.tm_min, dt.tm_sec); + } + *eof = 1; + + return len; +} + +static __init int ds1339_init(void) +{ + if( slave_address != 0xffff) + normal_addr[0] = slave_address; + + if( normal_addr[0] > 127) + { + printk(KERN_ERR"I2C: Invalid slave address for DS1339 RTC (%#x)\n", normal_addr[0]); + return -EINVAL; + } + + if( i2c_add_driver(&ds1339_driver) == 0) + { + misc_register (&ds1339_rtc_miscdev); + create_proc_read_entry (PROC_DS1339_NAME, 0, 0, ds1339_rtc_read_proc, NULL); + printk("I2C: DS1339 RTC driver successfully loaded\n"); + } + return 0; +} + +static __exit void ds1339_exit(void) +{ + remove_proc_entry (PROC_DS1339_NAME, NULL); + misc_deregister(&ds1339_rtc_miscdev); + i2c_del_driver(&ds1339_driver); +} + +module_init(ds1339_init); +module_exit(ds1339_exit); + +MODULE_PARM (slave_address, "i"); +MODULE_PARM_DESC (slave_address, "I2C slave address for DS1339 RTC"); + +MODULE_AUTHOR ("Voipac"); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.19-rmk7-pxa2/drivers/char/ds1339.h linux-2.4.19-rmk7-pxa2-dimm/drivers/char/ds1339.h --- linux-2.4.19-rmk7-pxa2/drivers/char/ds1339.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/char/ds1339.h Thu Oct 9 13:07:34 2003 @@ -0,0 +1,43 @@ +/* + * ds1339.h + * + * Copyright (C) 2003 Voipac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#ifndef DS1339_H +#define DS1339_H + +#define DS1339_I2C_SLAVE_ADDR 0x68 + +#define I2C_DRIVERID_DS1339 61 + +#define DS1339_BCD_CNT_ADDR 0x00 // counter start address +#define DS1339_BCD_CNT_SIZE 0x07 // counter length (bytes) + +#define DS1339_ALARM1_ADDR 0x07 +#define DS1339_ALARM1_SIZE 0x04 + +#define DS1339_ALARM2_ADDR 0x0b +#define DS1339_ALARM2_SIZE 0x03 + +#define DS1339_CONTROL_ADDR 0x0e // enable/disable counter +#define DS1339_STATUS_ADDR 0x0f // osf + alarms status +#define DS1339_CHARGER_ADDR 0x10 // charger address + +#define PROC_DS1339_NAME "driver/ds1339" + +#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10) +#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) + +#define TWELVE_HOUR_MODE(n) (((n)>>6)&1) +#define HOURS_AP(n) (((n)>>5)&1) +#define HOURS_12(n) BCD_TO_BIN((n)&0x1F) +#define HOURS_24(n) BCD_TO_BIN((n)&0x3F) + +#define DS1339_OSC_ENABLE 0x80 // osc enable mask + +#endif diff -urN linux-2.4.19-rmk7-pxa2/drivers/char/ds1672.c linux-2.4.19-rmk7-pxa2-dimm/drivers/char/ds1672.c --- linux-2.4.19-rmk7-pxa2/drivers/char/ds1672.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/char/ds1672.c Thu Oct 9 13:14:22 2003 @@ -0,0 +1,363 @@ +/* + * ds1672.c + * + * Device driver for Dallas Semiconductor's Real Time Controller DS1672. + * + * Copyright (C) 2003 Voipac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ds1672.h" + +//#define RTC_DEBUG + +static unsigned short slave_address = DS1672_I2C_SLAVE_ADDR; + +struct i2c_driver ds1672_driver; +struct i2c_client *ds1672_i2c_client = 0; + +static unsigned short ignore[] = { I2C_CLIENT_END }; +static unsigned short normal_addr[] = { DS1672_I2C_SLAVE_ADDR, I2C_CLIENT_END }; + +static struct i2c_client_address_data addr_data = { + normal_i2c: normal_addr, + normal_i2c_range: ignore, + probe: ignore, + probe_range: ignore, + ignore: ignore, + ignore_range: ignore, + force: ignore, +}; + +static int ds1672_rtc_ioctl( struct inode *, struct file *, unsigned int, unsigned long); +static int ds1672_rtc_open(struct inode *inode, struct file *file); +static int ds1672_rtc_release(struct inode *inode, struct file *file); + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + ioctl: ds1672_rtc_ioctl, + open: ds1672_rtc_open, + release: ds1672_rtc_release, +}; + +static struct miscdevice ds1672_rtc_miscdev = { + RTC_MINOR, + "rtc", + &rtc_fops +}; + +static int ds1672_probe(struct i2c_adapter *adap); +static int ds1672_detach(struct i2c_client *client); + +struct i2c_driver ds1672_driver = { + name: "DS1672", + id: I2C_DRIVERID_DS1672, + flags: I2C_DF_NOTIFY, + attach_adapter: ds1672_probe, + detach_client: ds1672_detach +}; + +static spinlock_t ds1672_rtc_lock = SPIN_LOCK_UNLOCKED; + +static void ds1672_enable_clock(void) +{ +#ifdef RTC_DEBUG + printk ("ds1672_enable_clock()\n"); +#endif + + unsigned long flags; + unsigned char buf[2], ad[1] = { DS1672_CONTROL_ADDR }; + struct i2c_msg msgs[2] = { + { ds1672_i2c_client->addr, 0, 1, ad }, + { ds1672_i2c_client->addr, I2C_M_RD, 1, buf } + }; + + // read ds1672 control register + spin_lock_irqsave(&ds1672_rtc_lock, flags); + i2c_transfer(ds1672_i2c_client->adapter, msgs, 2); + spin_unlock_irqrestore(&ds1672_rtc_lock,flags); + + buf[1] = buf[0] & ~DS1672_CLOCK_ENABLE; // clear counter enable bit + buf[0] = DS1672_CONTROL_ADDR; // control register address on DS1672 + + if( i2c_master_send(ds1672_i2c_client, (char *)buf, 2) != 2) + printk ("ds1672_enable_clock(): i2c_master_send failed.\n"); +} + +static const unsigned char days_in_mo[] = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +#define is_leap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) + +#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) + +// converts seconds since 1970-01-01 00:00:00 to Gregorian date + +static int ds1672_convert_to_time( struct rtc_time *tval, unsigned long t) +{ +#ifdef RTC_DEBUG + printk("ds1672_convert_to_time: time = %ld\n", t); +#endif + + memset( tval, 0, sizeof(struct rtc_time)); + + long days, month, year, rem; + + days = t / 86400; + rem = t % 86400; + tval->tm_hour = rem / 3600; + rem %= 3600; + tval->tm_min = rem / 60; + tval->tm_sec = rem % 60; + tval->tm_wday = (4 + days) % 7; + + year = 1970 + days / 365; + days -= ((year - 1970) * 365 + LEAPS_THRU_END_OF (year - 1) - LEAPS_THRU_END_OF (1970 - 1)); + if (days < 0) { + year -= 1; + days += 365 + is_leap(year); + } + tval->tm_year = year - 1900; + tval->tm_yday = days + 1; + + month = 0; + if (days >= 31) { + days -= 31; + month++; + if (days >= (28 + is_leap(year))) { + days -= (28 + is_leap(year)); + month++; + while (days >= days_in_mo[month]) { + days -= days_in_mo[month]; + month++; + } + } + } + tval->tm_mon = month; + tval->tm_mday = days + 1; + + return 0; +} + +static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *dt) +{ + int ret = -EIO; + unsigned long flags; + unsigned char buf[DS1672_COUNTER_SIZE], addr[1] = { DS1672_COUNTER_ADDR }; + struct i2c_msg msgs[2] = { + { client->addr, 0, 1, addr }, + { client->addr, I2C_M_RD, DS1672_COUNTER_SIZE, buf } + }; + + memset(buf, 0, sizeof(buf)); + + spin_lock_irqsave(&ds1672_rtc_lock, flags); + ret = i2c_transfer(client->adapter, msgs, 2); + spin_unlock_irqrestore(&ds1672_rtc_lock,flags); + + if (ret == 2) + return ds1672_convert_to_time( dt, *(unsigned long*)buf); + else + printk("ds1672_get_datetime(), i2c_transfer() returned %d\n",ret); + + return ret; +} + +static int ds1672_attach(struct i2c_adapter *adap, int addr, unsigned short flags,int kind) +{ + struct i2c_client *c; + struct rtc_time dt; + + if( (c = (struct i2c_client *) kmalloc(sizeof(*c), GFP_KERNEL)) == NULL) + return -ENOMEM; + + strcpy(c->name, "DS1672"); + c->id = ds1672_driver.id; + c->flags = 0; + c->addr = addr; + c->adapter = adap; + c->driver = &ds1672_driver; + c->data = NULL; + + if( ds1672_get_datetime( c, &dt) != 0) + printk ("ds1672_attach(): i2c_transfer failed.\n"); + + ds1672_i2c_client = c; + ds1672_enable_clock(); + + return i2c_attach_client(c); +} + +static int ds1672_probe(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, ds1672_attach); +} + +static int ds1672_detach(struct i2c_client *client) +{ + i2c_detach_client(client); + return 0; +} + +static int ds1672_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + int ret = 0; + unsigned long flags; + unsigned char buf[DS1672_COUNTER_SIZE+1]; + +#ifdef RTC_DEBUG + printk("ds1672_set_datetime: tm_year = %d\n", tm->tm_year); + printk("ds1672_set_datetime: tm_mon = %d\n", tm->tm_mon); + printk("ds1672_set_datetime: tm_mday = %d\n", tm->tm_mday); + printk("ds1672_set_datetime: tm_hour = %d\n", tm->tm_hour); + printk("ds1672_set_datetime: tm_min = %d\n", tm->tm_min); + printk("ds1672_set_datetime: tm_sec = %d\n", tm->tm_sec); +#endif + + buf[0] = DS1672_COUNTER_ADDR; // register address on DS1672 + *((unsigned long*)(buf+1)) = mktime ( tm->tm_year, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + spin_lock_irqsave(&ds1672_rtc_lock, flags); + ret = i2c_master_send(client, (char *)buf, DS1672_COUNTER_SIZE+1); + spin_unlock_irqrestore(&ds1672_rtc_lock,flags); + + if (ret == (DS1672_COUNTER_SIZE+1)) + return 0; + else + printk("ds1672_set_datetime(), i2c_master_send() returned %d\n",ret); + + return ret; +} + +static int ds1672_rtc_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int ds1672_rtc_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int ds1672_rtc_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct rtc_time wtime; + + switch (cmd) + { + case RTC_RD_TIME: + ds1672_get_datetime(ds1672_i2c_client, &wtime); + + if( copy_to_user((void *)arg, &wtime, sizeof (struct rtc_time))) + return -EFAULT; + break; + + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&wtime, (struct rtc_time *)arg, sizeof(struct rtc_time)) ) + return -EFAULT; + + wtime.tm_year += 1900; + if (wtime.tm_year < 1970 || (unsigned)wtime.tm_mon >= 12 || + wtime.tm_mday < 1 || wtime.tm_mday > (days_in_mo[wtime.tm_mon] + + (wtime.tm_mon == 1 && is_leap(wtime.tm_year))) || + (unsigned)wtime.tm_hour >= 24 || + (unsigned)wtime.tm_min >= 60 || + (unsigned)wtime.tm_sec >= 60) + return -EINVAL; + + ds1672_set_datetime(ds1672_i2c_client, &wtime); + + break; + + default: + return -EINVAL; + } + + return 0; +} + +static char* ds1672_mon2str( unsigned int mon) +{ + char *mon2str[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + return mon2str[mon%12]; +} + +static int ds1672_rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + struct rtc_time dt; + + len += sprintf(page + len, "Voipac DS1672 RTC driver (c) 2003\n\n"); + + if( ds1672_get_datetime(ds1672_i2c_client, &dt) == 0) + { + len += sprintf(page + len, "Date/Time : %02d-%s-%04d %02d:%02d:%02d\n", + dt.tm_mday, ds1672_mon2str(dt.tm_mon), dt.tm_year + 1900, dt.tm_hour, dt.tm_min, dt.tm_sec); + } + *eof = 1; + + return len; +} + +static __init int ds1672_init(void) +{ + if( slave_address != 0xffff) + normal_addr[0] = slave_address; + + if( normal_addr[0] > 127) + { + printk(KERN_ERR"I2C: Invalid slave address for DS1672 RTC (%#x)\n", normal_addr[0]); + return -EINVAL; + } + + if( i2c_add_driver(&ds1672_driver) == 0) + { + misc_register (&ds1672_rtc_miscdev); + create_proc_read_entry (PROC_DS1672_NAME, 0, 0, ds1672_rtc_read_proc, NULL); + printk("I2C: DS1672 RTC driver successfully loaded\n"); + } + return 0; +} + +static __exit void ds1672_exit(void) +{ + remove_proc_entry (PROC_DS1672_NAME, NULL); + misc_deregister(&ds1672_rtc_miscdev); + i2c_del_driver(&ds1672_driver); +} + +module_init(ds1672_init); +module_exit(ds1672_exit); + +MODULE_PARM (slave_address, "i"); +MODULE_PARM_DESC (slave_address, "I2C slave address for DS1672 RTC"); + +MODULE_AUTHOR ("Voipac"); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.19-rmk7-pxa2/drivers/char/ds1672.h linux-2.4.19-rmk7-pxa2-dimm/drivers/char/ds1672.h --- linux-2.4.19-rmk7-pxa2/drivers/char/ds1672.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/char/ds1672.h Tue Oct 7 14:53:18 2003 @@ -0,0 +1,27 @@ +/* + * ds1672.h + * + * Copyright (C) 2003 Voipac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#ifndef DS1672_H +#define DS1672_H + +#define DS1672_I2C_SLAVE_ADDR 0x68 + +#define I2C_DRIVERID_DS1672 60 + +#define DS1672_COUNTER_ADDR 0x00 // counter start address +#define DS1672_COUNTER_SIZE 0x04 // counter length (bytes) +#define DS1672_CONTROL_ADDR 0x04 // enable/disable counter +#define DS1672_CHARGER_ADDR 0x05 // charger address + +#define PROC_DS1672_NAME "driver/ds1672" + +#define DS1672_CLOCK_ENABLE 0x80 // enable/disable counter bit + +#endif diff -urN linux-2.4.19-rmk7-pxa2/drivers/char/misc.c linux-2.4.19-rmk7-pxa2-dimm/drivers/char/misc.c --- linux-2.4.19-rmk7-pxa2/drivers/char/misc.c Sat Aug 3 02:39:43 2002 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/char/misc.c Tue Oct 7 12:01:27 2003 @@ -77,6 +77,8 @@ extern int tosh_init(void); extern int i8k_init(void); extern int lcd_init(void); +extern int max6818(void); +extern int mcp2510_init(void); static int misc_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *private) @@ -281,6 +283,9 @@ #endif #ifdef CONFIG_COBALT_LCD lcd_init(); +#endif +#ifdef CONFIG_SSI_MCP2510 + mcp2510_init(); #endif #ifdef CONFIG_I8K i8k_init(); diff -urN linux-2.4.19-rmk7-pxa2/drivers/mtd/maps/lubbock.c linux-2.4.19-rmk7-pxa2-dimm/drivers/mtd/maps/lubbock.c --- linux-2.4.19-rmk7-pxa2/drivers/mtd/maps/lubbock.c Wed Nov 5 09:37:58 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/mtd/maps/lubbock.c Wed Sep 10 16:19:16 2003 @@ -85,12 +85,12 @@ mask_flags: MTD_WRITEABLE /* force read-only */ },{ name: "Kernel", - size: 0x00100000, + size: 0x000c0000, offset: 0x00040000, },{ name: "Filesystem", size: MTDPART_SIZ_FULL, - offset: 0x00140000 + offset: 0x00100000 } }; diff -urN linux-2.4.19-rmk7-pxa2/drivers/net/8390.c linux-2.4.19-rmk7-pxa2-dimm/drivers/net/8390.c --- linux-2.4.19-rmk7-pxa2/drivers/net/8390.c Sat Aug 3 02:39:44 2002 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/net/8390.c Tue Sep 23 21:54:03 2003 @@ -8,11 +8,9 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 This is the chip-specific code for many 8390-based ethernet adaptors. This is not a complete driver, it must be combined with board-specific @@ -47,7 +45,7 @@ */ -static const char version[] = +static const char *version = "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; #include @@ -68,7 +66,6 @@ #include #include #include -#include #include #include @@ -103,7 +100,7 @@ /* use 0 for production, 1 for verification, >2 for debug */ #ifndef ei_debug -int ei_debug = 1; +int ei_debug = 4; #endif /* Index to functions. */ @@ -456,8 +453,6 @@ { if (!netif_running(dev)) { printk(KERN_WARNING "%s: interrupt from stopped card\n", dev->name); - /* rmk - acknowledge the interrupts */ - outb_p(interrupts, e8390_base + EN0_ISR); interrupts = 0; break; } @@ -886,6 +881,27 @@ } /* + * Update the given Autodin II CRC value with another data byte. + */ + +static inline u32 update_crc(u8 byte, u32 current_crc) +{ + int bit; + u8 ah = 0; + for (bit=0; bit<8; bit++) + { + u8 carry = (current_crc>>31); + current_crc <<= 1; + ah = ((ah<<1) | carry) ^ byte; + if (ah&1) + current_crc ^= 0x04C11DB7; /* CRC polynomial */ + ah >>= 1; + byte >>= 1; + } + return current_crc; +} + +/* * Form the 64 bit 8390 multicast table from the linked list of addresses * associated with this dev structure. */ @@ -896,13 +912,16 @@ for (dmi=dev->mc_list; dmi; dmi=dmi->next) { + int i; u32 crc; if (dmi->dmi_addrlen != ETH_ALEN) { printk(KERN_INFO "%s: invalid multicast address length given.\n", dev->name); continue; } - crc = ether_crc(ETH_ALEN, dmi->dmi_addr); + crc = 0xffffffff; /* initial CRC value */ + for (i=0; idmi_addr[i], crc); /* * The 8390 uses the 6 most significant bits of the * CRC to index the multicast table. @@ -1121,6 +1140,7 @@ EXPORT_SYMBOL(ethdev_init); EXPORT_SYMBOL(NS8390_init); +/* Masked by Mark #if defined(MODULE) int init_module(void) @@ -1132,9 +1152,7 @@ { } -#endif /* MODULE */ -MODULE_LICENSE("GPL"); - +#endif */ /* MODULE */ /* * Local variables: diff -urN linux-2.4.19-rmk7-pxa2/drivers/net/8390.h linux-2.4.19-rmk7-pxa2-dimm/drivers/net/8390.h --- linux-2.4.19-rmk7-pxa2/drivers/net/8390.h Wed Nov 5 09:22:10 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/net/8390.h Tue Sep 23 21:33:16 2003 @@ -45,11 +45,12 @@ extern unsigned long autoirq_report(int waittime); #endif -extern int ethdev_init(struct net_device *dev); -extern void NS8390_init(struct net_device *dev, int startp); -extern int ei_open(struct net_device *dev); -extern int ei_close(struct net_device *dev); -extern void ei_interrupt(int irq, void *dev_id, struct pt_regs *regs); +/* Modified by Mark */ +int ethdev_init(struct net_device *dev); +void NS8390_init(struct net_device *dev, int startp); +int ei_open(struct net_device *dev); +int ei_close(struct net_device *dev); +void ei_interrupt(int irq, void *dev_id, struct pt_regs *regs); /* Most of these entries should be in 'struct net_device' (or most of the things in there should be here!) */ @@ -94,8 +95,8 @@ /* Some generic ethernet register configurations. */ #define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */ #define E8390_RX_IRQ_MASK 0x5 -#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */ -#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */ +#define E8390_RXCONFIG 0x44 /* EN0_RXCR: broadcasts, no multicast,errors */ +#define E8390_RXOFF 0x60 /* EN0_RXCR: Accept no packets */ #define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */ #define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */ @@ -112,28 +113,15 @@ /* * Only generate indirect loads given a machine that needs them. - * - removed AMIGA_PCMCIA from this list, handled as ISA io now */ -#if defined(CONFIG_MAC) || \ - defined(CONFIG_ARIADNE2) || defined(CONFIG_ARIADNE2_MODULE) || \ - defined(CONFIG_HYDRA) || defined(CONFIG_HYDRA_MODULE) -#define EI_SHIFT(x) (ei_local->reg_offset[x]) -#undef inb -#undef inb_p -#undef outb -#undef outb_p - -#define inb(port) in_8(port) -#define outb(val,port) out_8(port,val) -#define inb_p(port) in_8(port) -#define outb_p(val,port) out_8(port,val) - -#elif defined(CONFIG_ARM_ETHERH) || defined(CONFIG_ARM_ETHERH_MODULE) -#define EI_SHIFT(x) (ei_local->reg_offset[x]) -#else -#define EI_SHIFT(x) (x) -#endif +//#if defined(CONFIG_MAC) || defined(CONFIG_AMIGA_PCMCIA) || \ +// defined(CONFIG_ARIADNE2) || defined(CONFIG_ARIADNE2_MODULE) || \ +// defined(CONFIG_HYDRA) || defined(CONFIG_HYDRA_MODULE) +//#define EI_SHIFT(x) (ei_local->reg_offset[x]) +//#else +#define EI_SHIFT(x) (x<<1) +//#endif #define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages) */ /* Page 0 register offsets. */ diff -urN linux-2.4.19-rmk7-pxa2/drivers/net/Config.in linux-2.4.19-rmk7-pxa2-dimm/drivers/net/Config.in --- linux-2.4.19-rmk7-pxa2/drivers/net/Config.in Wed Nov 5 09:37:58 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/net/Config.in Wed Oct 1 11:50:23 2003 @@ -26,7 +26,9 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then if [ "$CONFIG_ARM" = "y" ]; then dep_bool ' ARM EBSA110 AM79C961A support' CONFIG_ARM_AM79C961A $CONFIG_ARCH_EBSA110 - tristate ' Cirrus Logic CS8900A support' CONFIG_ARM_CIRRUS + dep_tristate ' Cirrus Logic CS8900A support' CONFIG_CS89x0 + dep_tristate ' Asix AX88796 support' CONFIG_NE2000 +# tristate ' Cirrus Logic CS8900A support' CONFIG_ARM_CIRRUS if [ "$CONFIG_ARCH_ACORN" = "y" ]; then source drivers/acorn/net/Config.in fi diff -urN linux-2.4.19-rmk7-pxa2/drivers/net/Space.c linux-2.4.19-rmk7-pxa2-dimm/drivers/net/Space.c --- linux-2.4.19-rmk7-pxa2/drivers/net/Space.c Sat Aug 3 02:39:44 2002 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/net/Space.c Sat Oct 4 15:17:41 2003 @@ -34,6 +34,7 @@ #include #include #include +#include #define NEXT_DEV NULL @@ -512,6 +513,9 @@ # define ETH0_IRQ 0 #endif +#define ETH0_IRQ_GPIO 20 +#define ETH1_IRQ_GPIO 19 + /* "eth0" defaults to autoprobe (== 0), other use a base of 0xffe0 (== -0x20), which means "don't do ISA probes". Distributions don't ship kernels with all ISA drivers compiled in anymore, so its probably no longer an issue. */ @@ -531,10 +535,11 @@ static struct net_device eth2_dev = { "eth%d", 0,0,0,0,ETH_NOPROBE_ADDR /* I/O base*/, 0,0,0,0, ð3_dev, ethif_probe }; static struct net_device eth1_dev = { - "eth%d", 0,0,0,0,ETH_NOPROBE_ADDR /* I/O base*/, 0,0,0,0, ð2_dev, ethif_probe }; +// "eth%d", 0,0,0,0,ETH_NOPROBE_ADDR /* I/O base*/, 0,0,0,0, ð2_dev, ethif_probe }; + "eth%d", 0, 0, 0, 0, 0xf1000400, IRQ_GPIO(ETH1_IRQ_GPIO), 0, 0, 0, ð2_dev, ethif_probe }; static struct net_device eth0_dev = { - "eth%d", 0, 0, 0, 0, ETH0_ADDR, ETH0_IRQ, 0, 0, 0, ð1_dev, ethif_probe }; + "eth%d", 0, 0, 0, 0, 0xf0000400, IRQ_GPIO(ETH0_IRQ_GPIO), 0, 0, 0, ð1_dev, ethif_probe }; # undef NEXT_DEV # define NEXT_DEV (ð0_dev) diff -urN linux-2.4.19-rmk7-pxa2/drivers/net/Space.orig.c linux-2.4.19-rmk7-pxa2-dimm/drivers/net/Space.orig.c --- linux-2.4.19-rmk7-pxa2/drivers/net/Space.orig.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/net/Space.orig.c Sat Aug 3 02:39:44 2002 @@ -0,0 +1,669 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Holds initial configuration information for devices. + * + * Version: @(#)Space.c 1.0.7 08/12/93 + * + * Authors: Ross Biro, + * Fred N. van Kempen, + * Donald J. Becker, + * + * Changelog: + * Arnaldo Carvalho de Melo - 09/1999 + * - fix sbni: s/device/net_device/ + * Paul Gortmaker (06/98): + * - sort probes in a sane way, make sure all (safe) probes + * get run once & failed autoprobes don't autoprobe again. + * + * FIXME: + * Phase out placeholder dev entries put in the linked list + * here in favour of drivers using init_etherdev(NULL, ...) + * combined with a single find_all_devs() function (for 2.3) + * + * 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. + */ +#include +#include +#include +#include +#include +#include + +#define NEXT_DEV NULL + + +/* A unified ethernet device probe. This is the easiest way to have every + ethernet adaptor have the name "eth[0123...]". + */ + +extern int ne2_probe(struct net_device *dev); +extern int hp100_probe(struct net_device *dev); +extern int ultra_probe(struct net_device *dev); +extern int ultra32_probe(struct net_device *dev); +extern int ultramca_probe(struct net_device *dev); +extern int wd_probe(struct net_device *dev); +extern int el2_probe(struct net_device *dev); +extern int ne_probe(struct net_device *dev); +extern int hp_probe(struct net_device *dev); +extern int hp_plus_probe(struct net_device *dev); +extern int znet_probe(struct net_device *); +extern int express_probe(struct net_device *); +extern int eepro_probe(struct net_device *); +extern int el3_probe(struct net_device *); +extern int at1500_probe(struct net_device *); +extern int at1700_probe(struct net_device *); +extern int fmv18x_probe(struct net_device *); +extern int eth16i_probe(struct net_device *); +extern int depca_probe(struct net_device *); +extern int i82596_probe(struct net_device *); +extern int ewrk3_probe(struct net_device *); +extern int de4x5_probe(struct net_device *); +extern int el1_probe(struct net_device *); +extern int wavelan_probe(struct net_device *); +extern int arlan_probe(struct net_device *); +extern int el16_probe(struct net_device *); +extern int elmc_probe(struct net_device *); +extern int skmca_probe(struct net_device *); +extern int elplus_probe(struct net_device *); +extern int ac3200_probe(struct net_device *); +extern int es_probe(struct net_device *); +extern int lne390_probe(struct net_device *); +extern int ne3210_probe(struct net_device *); +extern int e2100_probe(struct net_device *); +extern int ni5010_probe(struct net_device *); +extern int ni52_probe(struct net_device *); +extern int ni65_probe(struct net_device *); +extern int sonic_probe(struct net_device *); +extern int SK_init(struct net_device *); +extern int seeq8005_probe(struct net_device *); +extern int smc_init( struct net_device * ); +extern int sgiseeq_probe(struct net_device *); +extern int atarilance_probe(struct net_device *); +extern int sun3lance_probe(struct net_device *); +extern int sun3_82586_probe(struct net_device *); +extern int apne_probe(struct net_device *); +extern int bionet_probe(struct net_device *); +extern int pamsnet_probe(struct net_device *); +extern int cs89x0_probe(struct net_device *dev); +extern int ethertap_probe(struct net_device *dev); +extern int hplance_probe(struct net_device *dev); +extern int bagetlance_probe(struct net_device *); +extern int mvme147lance_probe(struct net_device *dev); +extern int tc515_probe(struct net_device *dev); +extern int lance_probe(struct net_device *dev); +extern int mace_probe(struct net_device *dev); +extern int macsonic_probe(struct net_device *dev); +extern int mac8390_probe(struct net_device *dev); +extern int mac89x0_probe(struct net_device *dev); +extern int mc32_probe(struct net_device *dev); + +/* Detachable devices ("pocket adaptors") */ +extern int de600_probe(struct net_device *); +extern int de620_probe(struct net_device *); + +/* FDDI adapters */ +extern int skfp_probe(struct net_device *dev); + +/* Fibre Channel adapters */ +extern int iph5526_probe(struct net_device *dev); + +/* SBNI adapters */ +extern int sbni_probe(struct net_device *); + +struct devprobe +{ + int (*probe)(struct net_device *dev); + int status; /* non-zero if autoprobe has failed */ +}; + +/* + * probe_list walks a list of probe functions and calls each so long + * as a non-zero ioaddr is given, or as long as it hasn't already failed + * to find a card in the past (as recorded by "status") when asked to + * autoprobe (i.e. a probe that fails to find a card when autoprobing + * will not be asked to autoprobe again). It exits when a card is found. + */ +static int __init probe_list(struct net_device *dev, struct devprobe *plist) +{ + struct devprobe *p = plist; + unsigned long base_addr = dev->base_addr; +#ifdef CONFIG_NET_DIVERT + int ret; +#endif /* CONFIG_NET_DIVERT */ + + while (p->probe != NULL) { + if (base_addr && p->probe(dev) == 0) { /* probe given addr */ +#ifdef CONFIG_NET_DIVERT + ret = alloc_divert_blk(dev); + if (ret) + return ret; +#endif /* CONFIG_NET_DIVERT */ + return 0; + } else if (p->status == 0) { /* has autoprobe failed yet? */ + p->status = p->probe(dev); /* no, try autoprobe */ + if (p->status == 0) { +#ifdef CONFIG_NET_DIVERT + ret = alloc_divert_blk(dev); + if (ret) + return ret; +#endif /* CONFIG_NET_DIVERT */ + return 0; + } + } + p++; + } + return -ENODEV; +} + +/* + * This is a bit of an artificial separation as there are PCI drivers + * that also probe for EISA cards (in the PCI group) and there are ISA + * drivers that probe for EISA cards (in the ISA group). These are the + * EISA only driver probes, and also the legacy PCI probes + */ +static struct devprobe eisa_probes[] __initdata = { +#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */ + {de4x5_probe, 0}, +#endif +#ifdef CONFIG_ULTRA32 + {ultra32_probe, 0}, +#endif +#ifdef CONFIG_AC3200 + {ac3200_probe, 0}, +#endif +#ifdef CONFIG_ES3210 + {es_probe, 0}, +#endif +#ifdef CONFIG_LNE390 + {lne390_probe, 0}, +#endif +#ifdef CONFIG_NE3210 + {ne3210_probe, 0}, +#endif + {NULL, 0}, +}; + + +static struct devprobe mca_probes[] __initdata = { +#ifdef CONFIG_ULTRAMCA + {ultramca_probe, 0}, +#endif +#ifdef CONFIG_NE2_MCA + {ne2_probe, 0}, +#endif +#ifdef CONFIG_ELMC /* 3c523 */ + {elmc_probe, 0}, +#endif +#ifdef CONFIG_ELMC_II /* 3c527 */ + {mc32_probe, 0}, +#endif +#ifdef CONFIG_SKMC /* SKnet Microchannel */ + {skmca_probe, 0}, +#endif + {NULL, 0}, +}; + +/* + * ISA probes that touch addresses < 0x400 (including those that also + * look for EISA/PCI/MCA cards in addition to ISA cards). + */ +static struct devprobe isa_probes[] __initdata = { +#ifdef CONFIG_EL3 /* ISA, EISA, MCA 3c5x9 */ + {el3_probe, 0}, +#endif +#ifdef CONFIG_HP100 /* ISA, EISA & PCI */ + {hp100_probe, 0}, +#endif +#ifdef CONFIG_3C515 + {tc515_probe, 0}, +#endif +#ifdef CONFIG_ULTRA + {ultra_probe, 0}, +#endif +#ifdef CONFIG_WD80x3 + {wd_probe, 0}, +#endif +#ifdef CONFIG_EL2 /* 3c503 */ + {el2_probe, 0}, +#endif +#ifdef CONFIG_HPLAN + {hp_probe, 0}, +#endif +#ifdef CONFIG_HPLAN_PLUS + {hp_plus_probe, 0}, +#endif +#ifdef CONFIG_E2100 /* Cabletron E21xx series. */ + {e2100_probe, 0}, +#endif +#ifdef CONFIG_NE2000 /* ISA (use ne2k-pci for PCI cards) */ + {ne_probe, 0}, +#endif +#ifdef CONFIG_LANCE /* ISA/VLB (use pcnet32 for PCI cards) */ + {lance_probe, 0}, +#endif +#ifdef CONFIG_SMC9194 + {smc_init, 0}, +#endif +#ifdef CONFIG_SEEQ8005 + {seeq8005_probe, 0}, +#endif +#ifdef CONFIG_AT1500 + {at1500_probe, 0}, +#endif +#ifdef CONFIG_CS89x0 + {cs89x0_probe, 0}, +#endif +#ifdef CONFIG_AT1700 + {at1700_probe, 0}, +#endif +#ifdef CONFIG_FMV18X /* Fujitsu FMV-181/182 */ + {fmv18x_probe, 0}, +#endif +#ifdef CONFIG_ETH16I + {eth16i_probe, 0}, /* ICL EtherTeam 16i/32 */ +#endif +#ifdef CONFIG_ZNET /* Zenith Z-Note and some IBM Thinkpads. */ + {znet_probe, 0}, +#endif +#ifdef CONFIG_EEXPRESS /* Intel EtherExpress */ + {express_probe, 0}, +#endif +#ifdef CONFIG_EEXPRESS_PRO /* Intel EtherExpress Pro/10 */ + {eepro_probe, 0}, +#endif +#ifdef CONFIG_DEPCA /* DEC DEPCA */ + {depca_probe, 0}, +#endif +#ifdef CONFIG_EWRK3 /* DEC EtherWORKS 3 */ + {ewrk3_probe, 0}, +#endif +#if defined(CONFIG_APRICOT) || defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET) /* Intel I82596 */ + {i82596_probe, 0}, +#endif +#ifdef CONFIG_EL1 /* 3c501 */ + {el1_probe, 0}, +#endif +#ifdef CONFIG_WAVELAN /* WaveLAN */ + {wavelan_probe, 0}, +#endif +#ifdef CONFIG_ARLAN /* Aironet */ + {arlan_probe, 0}, +#endif +#ifdef CONFIG_EL16 /* 3c507 */ + {el16_probe, 0}, +#endif +#ifdef CONFIG_ELPLUS /* 3c505 */ + {elplus_probe, 0}, +#endif +#ifdef CONFIG_SK_G16 + {SK_init, 0}, +#endif +#ifdef CONFIG_NI5010 + {ni5010_probe, 0}, +#endif +#ifdef CONFIG_NI52 + {ni52_probe, 0}, +#endif +#ifdef CONFIG_NI65 + {ni65_probe, 0}, +#endif + {NULL, 0}, +}; + +static struct devprobe parport_probes[] __initdata = { +#ifdef CONFIG_DE600 /* D-Link DE-600 adapter */ + {de600_probe, 0}, +#endif +#ifdef CONFIG_DE620 /* D-Link DE-620 adapter */ + {de620_probe, 0}, +#endif + {NULL, 0}, +}; + +static struct devprobe m68k_probes[] __initdata = { +#ifdef CONFIG_ATARILANCE /* Lance-based Atari ethernet boards */ + {atarilance_probe, 0}, +#endif +#ifdef CONFIG_SUN3LANCE /* sun3 onboard Lance chip */ + {sun3lance_probe, 0}, +#endif +#ifdef CONFIG_SUN3_82586 /* sun3 onboard Intel 82586 chip */ + {sun3_82586_probe, 0}, +#endif +#ifdef CONFIG_APNE /* A1200 PCMCIA NE2000 */ + {apne_probe, 0}, +#endif +#ifdef CONFIG_ATARI_BIONET /* Atari Bionet Ethernet board */ + {bionet_probe, 0}, +#endif +#ifdef CONFIG_ATARI_PAMSNET /* Atari PAMsNet Ethernet board */ + {pamsnet_probe, 0}, +#endif +#ifdef CONFIG_HPLANCE /* HP300 internal Ethernet */ + {hplance_probe, 0}, +#endif +#ifdef CONFIG_MVME147_NET /* MVME147 internal Ethernet */ + {mvme147lance_probe, 0}, +#endif +#ifdef CONFIG_MACMACE /* Mac 68k Quadra AV builtin Ethernet */ + {mace_probe, 0}, +#endif +#ifdef CONFIG_MACSONIC /* Mac SONIC-based Ethernet of all sorts */ + {macsonic_probe, 0}, +#endif +#ifdef CONFIG_MAC8390 /* NuBus NS8390-based cards */ + {mac8390_probe, 0}, +#endif +#ifdef CONFIG_MAC89x0 + {mac89x0_probe, 0}, +#endif + {NULL, 0}, +}; + + +static struct devprobe sgi_probes[] __initdata = { +#ifdef CONFIG_SGISEEQ + {sgiseeq_probe, 0}, +#endif + {NULL, 0}, +}; + +static struct devprobe mips_probes[] __initdata = { +#ifdef CONFIG_MIPS_JAZZ_SONIC + {sonic_probe, 0}, +#endif +#ifdef CONFIG_BAGETLANCE /* Lance-based Baget ethernet boards */ + {bagetlance_probe, 0}, +#endif + {NULL, 0}, +}; + +/* + * Unified ethernet device probe, segmented per architecture and + * per bus interface. This drives the legacy devices only for now. + */ + +static int __init ethif_probe(struct net_device *dev) +{ + unsigned long base_addr = dev->base_addr; + + /* + * Backwards compatibility - historically an I/O base of 1 was + * used to indicate not to probe for this ethN interface + */ + if (base_addr == 1) + return 1; /* ENXIO */ + + /* + * The arch specific probes are 1st so that any on-board ethernet + * will be probed before other ISA/EISA/MCA/PCI bus cards. + */ + if (probe_list(dev, m68k_probes) == 0) + return 0; + if (probe_list(dev, mips_probes) == 0) + return 0; + if (probe_list(dev, sgi_probes) == 0) + return 0; + if (probe_list(dev, eisa_probes) == 0) + return 0; + if (probe_list(dev, mca_probes) == 0) + return 0; + /* + * Backwards compatibility - an I/O of 0xffe0 was used to indicate + * that we shouldn't do a bunch of potentially risky ISA probes + * for ethN (N>1). Since the widespread use of modules, *nobody* + * compiles a kernel with all the ISA drivers built in anymore, + * and so we should delete this check in linux 2.3 - Paul G. + */ + if (base_addr != 0xffe0 && probe_list(dev, isa_probes) == 0) + return 0; + if (probe_list(dev, parport_probes) == 0) + return 0; + return -ENODEV; +} + +#ifdef CONFIG_FDDI +static int __init fddiif_probe(struct net_device *dev) +{ + unsigned long base_addr = dev->base_addr; + + if (base_addr == 1) + return 1; /* ENXIO */ + + if (1 +#ifdef CONFIG_APFDDI + && apfddi_init(dev) +#endif +#ifdef CONFIG_SKFP + && skfp_probe(dev) +#endif + && 1 ) { + return 1; /* -ENODEV or -EAGAIN would be more accurate. */ + } + return 0; +} +#endif + + +#ifdef CONFIG_NET_FC +static int fcif_probe(struct net_device *dev) +{ + if (dev->base_addr == -1) + return 1; + + if (1 +#ifdef CONFIG_IPHASE5526 + && iph5526_probe(dev) +#endif + && 1 ) { + return 1; /* -ENODEV or -EAGAIN would be more accurate. */ + } + return 0; +} +#endif /* CONFIG_NET_FC */ + + +#ifdef CONFIG_ETHERTAP + static struct net_device tap0_dev = { "tap0", 0, 0, 0, 0, NETLINK_TAPBASE, 0, 0, 0, 0, NEXT_DEV, ethertap_probe, }; +# undef NEXT_DEV +# define NEXT_DEV (&tap0_dev) +#endif + +#ifdef CONFIG_SDLA + extern int sdla_init(struct net_device *); + static struct net_device sdla0_dev = { "sdla0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, sdla_init, }; + +# undef NEXT_DEV +# define NEXT_DEV (&sdla0_dev) +#endif + +#if defined(CONFIG_LTPC) + extern int ltpc_probe(struct net_device *); + static struct net_device dev_ltpc = { + "lt0", + 0, 0, 0, 0, + 0x0, 0, + 0, 0, 0, NEXT_DEV, ltpc_probe }; +# undef NEXT_DEV +# define NEXT_DEV (&dev_ltpc) +#endif /* LTPC */ + +#if defined(CONFIG_COPS) + extern int cops_probe(struct net_device *); + static struct net_device cops2_dev = { "lt2", 0, 0, 0, 0, 0x0, 0, 0, 0, 0, NEXT_DEV, cops_probe }; + static struct net_device cops1_dev = { "lt1", 0, 0, 0, 0, 0x0, 0, 0, 0, 0, &cops2_dev, cops_probe }; + static struct net_device cops0_dev = { "lt0", 0, 0, 0, 0, 0x0, 0, 0, 0, 0, &cops1_dev, cops_probe }; +# undef NEXT_DEV +# define NEXT_DEV (&cops0_dev) +#endif /* COPS */ + + +/* The first device defaults to I/O base '0', which means autoprobe. */ +#ifndef ETH0_ADDR +# define ETH0_ADDR 0 +#endif +#ifndef ETH0_IRQ +# define ETH0_IRQ 0 +#endif + +/* "eth0" defaults to autoprobe (== 0), other use a base of 0xffe0 (== -0x20), + which means "don't do ISA probes". Distributions don't ship kernels with + all ISA drivers compiled in anymore, so its probably no longer an issue. */ + +#define ETH_NOPROBE_ADDR 0xffe0 + +static struct net_device eth7_dev = { + "eth%d", 0,0,0,0,ETH_NOPROBE_ADDR /* I/O base*/, 0,0,0,0, NEXT_DEV, ethif_probe }; +static struct net_device eth6_dev = { + "eth%d", 0,0,0,0,ETH_NOPROBE_ADDR /* I/O base*/, 0,0,0,0, ð7_dev, ethif_probe }; +static struct net_device eth5_dev = { + "eth%d", 0,0,0,0,ETH_NOPROBE_ADDR /* I/O base*/, 0,0,0,0, ð6_dev, ethif_probe }; +static struct net_device eth4_dev = { + "eth%d", 0,0,0,0,ETH_NOPROBE_ADDR /* I/O base*/, 0,0,0,0, ð5_dev, ethif_probe }; +static struct net_device eth3_dev = { + "eth%d", 0,0,0,0,ETH_NOPROBE_ADDR /* I/O base*/, 0,0,0,0, ð4_dev, ethif_probe }; +static struct net_device eth2_dev = { + "eth%d", 0,0,0,0,ETH_NOPROBE_ADDR /* I/O base*/, 0,0,0,0, ð3_dev, ethif_probe }; +static struct net_device eth1_dev = { + "eth%d", 0,0,0,0,ETH_NOPROBE_ADDR /* I/O base*/, 0,0,0,0, ð2_dev, ethif_probe }; + +static struct net_device eth0_dev = { + "eth%d", 0, 0, 0, 0, ETH0_ADDR, ETH0_IRQ, 0, 0, 0, ð1_dev, ethif_probe }; + +# undef NEXT_DEV +# define NEXT_DEV (ð0_dev) + + + +#ifdef CONFIG_TR +/* Token-ring device probe */ +extern int ibmtr_probe(struct net_device *); +extern int smctr_probe(struct net_device *); + +static int +trif_probe(struct net_device *dev) +{ + if (1 +#ifdef CONFIG_IBMTR + && ibmtr_probe(dev) +#endif +#ifdef CONFIG_SMCTR + && smctr_probe(dev) +#endif + && 1 ) { + return 1; /* -ENODEV or -EAGAIN would be more accurate. */ + } + return 0; +} +static struct net_device tr7_dev = { + "tr%d",0,0,0,0,0,0,0,0,0, NEXT_DEV, trif_probe }; +static struct net_device tr6_dev = { + "tr%d",0,0,0,0,0,0,0,0,0, &tr7_dev, trif_probe }; +static struct net_device tr5_dev = { + "tr%d",0,0,0,0,0,0,0,0,0, &tr6_dev, trif_probe }; +static struct net_device tr4_dev = { + "tr%d",0,0,0,0,0,0,0,0,0, &tr5_dev, trif_probe }; +static struct net_device tr3_dev = { + "tr%d",0,0,0,0,0,0,0,0,0, &tr4_dev, trif_probe }; +static struct net_device tr2_dev = { + "tr%d",0,0,0,0,0,0,0,0,0, &tr3_dev, trif_probe }; +static struct net_device tr1_dev = { + "tr%d",0,0,0,0,0,0,0,0,0, &tr2_dev, trif_probe }; +static struct net_device tr0_dev = { + "tr%d",0,0,0,0,0,0,0,0,0, &tr1_dev, trif_probe }; +# undef NEXT_DEV +# define NEXT_DEV (&tr0_dev) + +#endif + +#ifdef CONFIG_FDDI + static struct net_device fddi7_dev = + {"fddi7", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, fddiif_probe}; + static struct net_device fddi6_dev = + {"fddi6", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi7_dev, fddiif_probe}; + static struct net_device fddi5_dev = + {"fddi5", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi6_dev, fddiif_probe}; + static struct net_device fddi4_dev = + {"fddi4", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi5_dev, fddiif_probe}; + static struct net_device fddi3_dev = + {"fddi3", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi4_dev, fddiif_probe}; + static struct net_device fddi2_dev = + {"fddi2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi3_dev, fddiif_probe}; + static struct net_device fddi1_dev = + {"fddi1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi2_dev, fddiif_probe}; + static struct net_device fddi0_dev = + {"fddi0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi1_dev, fddiif_probe}; +#undef NEXT_DEV +#define NEXT_DEV (&fddi0_dev) +#endif + + +#ifdef CONFIG_NET_FC + static struct net_device fc1_dev = { + "fc1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, fcif_probe}; + static struct net_device fc0_dev = { + "fc0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fc1_dev, fcif_probe}; +# undef NEXT_DEV +# define NEXT_DEV (&fc0_dev) +#endif + + +#ifdef CONFIG_SBNI + static struct net_device sbni7_dev = + {"sbni7", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, sbni_probe}; + static struct net_device sbni6_dev = + {"sbni6", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni7_dev, sbni_probe}; + static struct net_device sbni5_dev = + {"sbni5", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni6_dev, sbni_probe}; + static struct net_device sbni4_dev = + {"sbni4", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni5_dev, sbni_probe}; + static struct net_device sbni3_dev = + {"sbni3", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni4_dev, sbni_probe}; + static struct net_device sbni2_dev = + {"sbni2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni3_dev, sbni_probe}; + static struct net_device sbni1_dev = + {"sbni1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni2_dev, sbni_probe}; + static struct net_device sbni0_dev = + {"sbni0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni1_dev, sbni_probe}; + +#undef NEXT_DEV +#define NEXT_DEV (&sbni0_dev) +#endif + +/* + * The loopback device is global so it can be directly referenced + * by the network code. Also, it must be first on device list. + */ + +extern int loopback_init(struct net_device *dev); +struct net_device loopback_dev = + {"lo", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, loopback_init}; + +/* + * The @dev_base list is protected by @dev_base_lock and the rtln + * semaphore. + * + * Pure readers hold dev_base_lock for reading. + * + * Writers must hold the rtnl semaphore while they loop through the + * dev_base list, and hold dev_base_lock for writing when they do the + * actual updates. This allows pure readers to access the list even + * while a writer is preparing to update it. + * + * To put it another way, dev_base_lock is held for writing only to + * protect against pure readers; the rtnl semaphore provides the + * protection against other writers. + * + * See, for example usages, register_netdevice() and + * unregister_netdevice(), which must be called with the rtnl + * semaphore held. + */ +struct net_device *dev_base = &loopback_dev; +rwlock_t dev_base_lock = RW_LOCK_UNLOCKED; + diff -urN linux-2.4.19-rmk7-pxa2/drivers/net/cs89x0.c linux-2.4.19-rmk7-pxa2-dimm/drivers/net/cs89x0.c --- linux-2.4.19-rmk7-pxa2/drivers/net/cs89x0.c Wed Nov 5 09:22:10 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/net/cs89x0.c Wed Sep 10 14:31:33 2003 @@ -81,9 +81,6 @@ : Make `version[]' __initdata : Uninlined the read/write reg/word functions. - Oskar Schirmer : oskar@scara.com - : HiCO.SH4 (superh) support added (irq#1, cs89x0_media=) - */ /* Always include 'config.h' first in case the user wants to turn on @@ -98,13 +95,13 @@ * Note that even if DMA is turned off we still support the 'dma' and 'use_dma' * module options so we don't break any startup scripts. */ -#define ALLOW_DMA 1 +#define ALLOW_DMA 0 /* * Set this to zero to remove all the debug statements via * dead code elimination */ -#define DEBUGGING 1 +#define DEBUGGING 0 /* Sources: @@ -130,6 +127,7 @@ #include #include #include +#include #if ALLOW_DMA #include #endif @@ -156,19 +154,15 @@ /* The cs8900 has 4 IRQ pins, software selectable. cs8900_irq_map maps them to system IRQ numbers. This mapping is card specific and is set to the configuration of the Cirrus Eval board for this chip. */ -#ifdef CONFIG_ARCH_CLPS7500 -static unsigned int netcard_portlist[] __initdata = - { 0x80090303, 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0}; -static unsigned int cs8900_irq_map[] = {12,0,0,0}; -#elif defined(CONFIG_SH_HICOSH4) -static unsigned int netcard_portlist[] __initdata = - { 0x0300, 0}; -static unsigned int cs8900_irq_map[] = {1,0,0,0}; -#else -static unsigned int netcard_portlist[] __initdata = - { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0}; -static unsigned int cs8900_irq_map[] = {10,11,12,5}; -#endif +//#ifdef CONFIG_ARCH_CLPS7500 +//static unsigned int netcard_portlist[] __initdata = + // { 0xf0000303, 0}; +static unsigned int cs8900_irq_map[] = {14,19,0,0}; +//#else +//static unsigned int netcard_portlist[] __initdata = +// { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0}; +//static unsigned int cs8900_irq_map[] = {10,11,12,5}; +//#endif #if DEBUGGING static unsigned int net_debug = DEBUGGING; @@ -229,7 +223,8 @@ static struct net_device_stats *net_get_stats(struct net_device *dev); static void reset_chip(struct net_device *dev); static int get_eeprom_data(struct net_device *dev, int off, int len, int *buffer); -static int get_eeprom_cksum(int off, int len, int *buffer); +//static int get_eeprom_cksum(int off, int len, int *buffer); +static int get_mac_address(struct net_device *dev); static int set_mac_address(struct net_device *dev, void *addr); static void count_rx_errors(int status, struct net_local *lp); #if ALLOW_DMA @@ -255,20 +250,6 @@ __setup("cs89x0_dma=", dma_fn); #endif /* !defined(MODULE) && (ALLOW_DMA != 0) */ -#ifndef MODULE -static int g_cs89x0_media__force; - -static int __init media_fn(char *str) -{ - if (!strcmp(str, "rj45")) g_cs89x0_media__force = FORCE_RJ45; - else if (!strcmp(str, "aui")) g_cs89x0_media__force = FORCE_AUI; - else if (!strcmp(str, "bnc")) g_cs89x0_media__force = FORCE_BNC; - return 1; -} - -__setup("cs89x0_media=", media_fn); -#endif - /* Check for a network adaptor of this type, and return '0' iff one exists. If dev->base_addr == 0, probe all likely locations. @@ -277,10 +258,11 @@ (detachable devices only). Return 0 on success. */ +#define ETH_NOPROBE_ADDR 0xffe0 int __init cs89x0_probe(struct net_device *dev) { - int i; +// int i; int base_addr = dev ? dev->base_addr : 0; SET_MODULE_OWNER(dev); @@ -288,16 +270,19 @@ if (net_debug) printk("cs89x0:cs89x0_probe(0x%x)\n", base_addr); - if (base_addr > 0x1ff) /* Check a single specified location. */ + if (base_addr != ETH_NOPROBE_ADDR) /* Check a single specified location. */ return cs89x0_probe1(dev, base_addr); else if (base_addr != 0) /* Don't probe at all. */ return -ENXIO; - +/* for (i = 0; netcard_portlist[i]; i++) { + + printk("cs89x0_probe1 at device addr: 0x%x\n", netcard_portlist[i]); + if (cs89x0_probe1(dev, netcard_portlist[i]) == 0) return 0; } - printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n"); +*/ printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n"); return -ENODEV; } @@ -358,7 +343,7 @@ return 0; } -static int __init +/*static int __init get_eeprom_cksum(int off, int len, int *buffer) { int i, cksum; @@ -371,7 +356,7 @@ return 0; return -1; } - +*/ /* This is the real probe routine. Linux has a history of friendly device probes on the ISA bus. A good device probes avoids doing writes, and verifies that the correct device exists and functions. @@ -385,7 +370,7 @@ static unsigned version_printed; int i; unsigned rev_type = 0; - int eeprom_buff[CHKSUM_LEN]; +// int eeprom_buff[CHKSUM_LEN]; int retval; /* Initialize the device structure. */ @@ -405,9 +390,6 @@ lp->dmasize = 16; /* Could make this an option... */ } #endif -#ifndef MODULE - lp->force = g_cs89x0_media__force; -#endif } lp = (struct net_local *)dev->priv; @@ -419,19 +401,11 @@ goto out1; } -#ifdef CONFIG_SH_HICOSH4 - /* truely reset the chip */ - outw(0x0114, ioaddr + ADD_PORT); - outw(0x0040, ioaddr + DATA_PORT); -#endif - /* if they give us an odd I/O address, then do ONE write to the address port, to get it back to address zero, where we expect to find the EISA signature word. An IO with a base of 0x3 will skip the test for the ADD_PORT. */ if (ioaddr & 1) { - if (net_debug > 1) - printk(KERN_INFO "%s: odd ioaddr 0x%x\n", dev->name, ioaddr); if ((ioaddr & 2) != 2) if ((inw((ioaddr & ~3)+ ADD_PORT) & ADD_MASK) != ADD_SIG) { printk(KERN_ERR "%s: bad signature 0x%x\n", @@ -439,12 +413,12 @@ retval = -ENODEV; goto out2; } + ioaddr &= ~3; outw(PP_ChipID, ioaddr + ADD_PORT); } -printk("PP_addr=0x%x\n", inw(ioaddr + ADD_PORT)); - - if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) { + + if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) { printk(KERN_ERR "%s: incorrect signature 0x%x\n", dev->name, inw(ioaddr + DATA_PORT)); retval = -ENODEV; @@ -456,6 +430,7 @@ /* get the chip type */ rev_type = readreg(dev, PRODUCT_ID_ADD); + lp->chip_type = rev_type &~ REVISON_BITS; lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; @@ -485,39 +460,6 @@ EEPROM read on reset. So, if the chip says it read the EEPROM the driver will always do *something* instead of complain that adapter_cnf is 0. */ - -#ifdef CONFIG_SH_HICOSH4 - if (1) { - /* For the HiCO.SH4 board, things are different: we don't - have EEPROM, but there is some data in flash, so we go - get it there directly (MAC). */ - __u16 *confd; - short cnt; - if (((* (volatile __u32 *) 0xa0013ff0) & 0x00ffffff) - == 0x006c3000) { - confd = (__u16*) 0xa0013fc0; - } else { - confd = (__u16*) 0xa001ffc0; - } - cnt = (*confd++ & 0x00ff) >> 1; - while (--cnt > 0) { - __u16 j = *confd++; - - switch (j & 0x0fff) { - case PP_IA: - for (i = 0; i < ETH_ALEN/2; i++) { - dev->dev_addr[i*2] = confd[i] & 0xFF; - dev->dev_addr[i*2+1] = confd[i] >> 8; - } - break; - } - j = (j >> 12) + 1; - confd += j; - cnt -= j; - } - } else -#endif - if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) == (EEPROM_OK|EEPROM_PRESENT)) { /* Load the MAC. */ @@ -571,36 +513,31 @@ printk("\n"); /* First check to see if an EEPROM is attached. */ -#ifdef CONFIG_SH_HICOSH4 /* no EEPROM on HiCO, don't hazzle with it here */ - if (1) { - printk(KERN_NOTICE "cs89x0: No EEPROM on HiCO.SH4\n"); - } else -#endif - if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0) +/* if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0) printk(KERN_WARNING "cs89x0: No EEPROM, relying on command line....\n"); else if (get_eeprom_data(dev, START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) { printk(KERN_WARNING "\ncs89x0: EEPROM read failed, relying on command line.\n"); } else if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) { - /* Check if the chip was able to read its own configuration starting - at 0 in the EEPROM*/ + // Check if the chip was able to read its own configuration starting + // at 0 in the EEPROM if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) != (EEPROM_OK|EEPROM_PRESENT)) printk(KERN_WARNING "cs89x0: Extended EEPROM checksum bad and no Cirrus EEPROM, relying on command line\n"); } else { - /* This reads an extended EEPROM that is not documented - in the CS8900 datasheet. */ + This reads an extended EEPROM that is not documented +// in the CS8900 datasheet. - /* get transmission control word but keep the autonegotiation bits */ + // get transmission control word but keep the autonegotiation bits if (!lp->auto_neg_cnf) lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2]; - /* Store adapter configuration */ + // Store adapter configuration if (!lp->adapter_cnf) lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2]; - /* Store ISA configuration */ + // Store ISA configuration lp->isa_config = eeprom_buff[ISA_CNF_OFFSET/2]; dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8; - /* eeprom_buff has 32-bit ints, so we can't just memcpy it */ - /* store the initial memory base address */ + // eeprom_buff has 32-bit ints, so we can't just memcpy it + // store the initial memory base address for (i = 0; i < ETH_ALEN/2; i++) { dev->dev_addr[i*2] = eeprom_buff[i]; dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8; @@ -609,6 +546,9 @@ printk(KERN_DEBUG "%s: new adapter_cnf: 0x%x\n", dev->name, lp->adapter_cnf); } +*/ + lp->force = FORCE_RJ45|FORCE_AUTO; + lp->auto_neg_cnf |= IMM_BIT; /* allow them to force multiple transceivers. If they force multiple, autosense */ { @@ -622,7 +562,7 @@ else if (lp->force & FORCE_BNC) {lp->adapter_cnf |= A_CNF_MEDIA_10B_2; } } - if (net_debug > 1) +// if (net_debug > 1) printk(KERN_DEBUG "%s: after force 0x%x, adapter_cnf=0x%x\n", dev->name, lp->force, lp->adapter_cnf); @@ -671,7 +611,7 @@ dev->irq = i; } - printk(" IRQ %d", dev->irq); + printk(" IRQ %d", IRQ_GPIO(dev->irq)); #if ALLOW_DMA if (lp->use_dma) { @@ -684,11 +624,14 @@ printk(", programmed I/O"); } + // extract mac addr, has been set in bootloader + get_mac_address( dev); + /* print the ethernet address. */ - printk(", MAC"); + printk(", MAC "); for (i = 0; i < ETH_ALEN; i++) { - printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); + printk("%s%02x", i ? ":" : "", dev->dev_addr[i]); } dev->open = net_open; @@ -1088,12 +1031,12 @@ for (i = 0; i != sizeof(cs8900_irq_map)/sizeof(cs8900_irq_map[0]); i++) if (cs8900_irq_map[i] == irq) break; + /* Not found */ - if (i == sizeof(cs8900_irq_map)/sizeof(cs8900_irq_map[0])) - i = 3; - writereg(dev, PP_CS8900_ISAINT, i); + if (i < sizeof(cs8900_irq_map)/sizeof(cs8900_irq_map[0])) + writereg(dev, PP_CS8900_ISAINT, i); } else { - writereg(dev, PP_CS8920_ISAINT, irq); + printk("cs89x0::write_irq: invalid chip type\n"); } } @@ -1115,7 +1058,6 @@ int i; int ret; -#ifndef CONFIG_SH_HICOSH4 /* uses irq#1, so this wont work */ if (dev->irq < 2) { /* Allow interrupts to be generated by the chip */ /* Cirrus' release had this: */ @@ -1126,7 +1068,7 @@ writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON); for (i = 2; i < CS8920_NO_INTS; i++) { - if ((1 << i) & lp->irq_map) { + if ((1 << dev->irq) & lp->irq_map) { if (request_irq(i, net_interrupt, 0, dev->name, dev) == 0) { dev->irq = i; write_irq(dev, lp->chip_type, i); @@ -1142,16 +1084,13 @@ ret = -EAGAIN; goto bad_out; } - } - else -#endif - { - if (((1 << dev->irq) & lp->irq_map) == 0) { - printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n", - dev->name, dev->irq, lp->irq_map); - ret = -EAGAIN; - goto bad_out; - } + } else { +// if (((1 << dev->irq) & lp->irq_map) == 0) { +// printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n", + // dev->name, dev->irq, lp->irq_map); +// ret = -EAGAIN; +// goto bad_out; +// } /* FIXME: Cirrus' release had this: */ writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ ); /* And 2.3.47 had this: */ @@ -1159,14 +1098,20 @@ writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON); #endif write_irq(dev, lp->chip_type, dev->irq); - ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev); + + set_GPIO_IRQ_edge( dev->irq, GPIO_RISING_EDGE); // set GPIO pin as IRQ line + dev->irq = IRQ_GPIO( dev->irq); // convert GPIO pin number to IRQ + + ret = request_irq(dev->irq, &net_interrupt, SA_SHIRQ, dev->name, dev); if (ret) { - if (net_debug) - printk(KERN_DEBUG "cs89x0: request_irq(%d) failed\n", dev->irq); +// if (net_debug) + printk(KERN_DEBUG "cs89x0: request_irq(%d) failed\n", dev->irq); goto bad_out; } } +// printk(KERN_DEBUG "cs89x0::net_open: request_irq OK\n"); + #if ALLOW_DMA if (lp->use_dma) { if (lp->isa_config & ANY_ISA_DMA) { @@ -1563,7 +1508,7 @@ static int net_close(struct net_device *dev) { - struct net_local *lp = (struct net_local *)dev->priv; +// struct net_local *lp = (struct net_local *)dev->priv; netif_stop_queue(dev); @@ -1629,6 +1574,18 @@ spin_unlock_irqrestore(&lp->lock, flags); } +static int get_mac_address(struct net_device *dev) +{ + int i; + + for (i=0; i < ETH_ALEN/2; i++) + { + unsigned int addr = readreg(dev, PP_IA+i*2); + dev->dev_addr[i*2] = addr & 0xFF; + dev->dev_addr[i*2+1] = addr >> 8; + } + return 0; +} static int set_mac_address(struct net_device *dev, void *addr) { @@ -1636,12 +1593,10 @@ if (netif_running(dev)) return -EBUSY; - if (net_debug) { - printk("%s: Setting MAC address to ", dev->name); - for (i = 0; i < 6; i++) - printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]); - printk(".\n"); - } + + for (i = 0; i < 6; i++) + dev->dev_addr[i] = ((unsigned char *)addr)[i]; + /* set the Ethernet address */ for (i=0; i < ETH_ALEN/2; i++) writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); diff -urN linux-2.4.19-rmk7-pxa2/drivers/net/cs89x0.h linux-2.4.19-rmk7-pxa2-dimm/drivers/net/cs89x0.h --- linux-2.4.19-rmk7-pxa2/drivers/net/cs89x0.h Sat Aug 3 02:39:44 2002 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/net/cs89x0.h Wed Sep 10 10:52:42 2003 @@ -326,14 +326,14 @@ #define RX_FRAME_PORT 0x0000 #define TX_FRAME_PORT RX_FRAME_PORT -#define TX_CMD_PORT 0x0004 +#define TX_CMD_PORT 0x0008 #define TX_NOW 0x0000 /* Tx packet after 5 bytes copied */ #define TX_AFTER_381 0x0040 /* Tx packet after 381 bytes copied */ #define TX_AFTER_ALL 0x00c0 /* Tx packet after all bytes copied */ -#define TX_LEN_PORT 0x0006 -#define ISQ_PORT 0x0008 -#define ADD_PORT 0x000A -#define DATA_PORT 0x000C +#define TX_LEN_PORT 0x000c +#define ISQ_PORT 0x0010 +#define ADD_PORT 0x0014 +#define DATA_PORT 0x0018 #define EEPROM_WRITE_EN 0x00F0 #define EEPROM_WRITE_DIS 0x0000 @@ -437,11 +437,7 @@ #define IRQ_MAP_EEPROM_DATA 0x0046 /* Offset into eeprom for the IRQ map */ #define IRQ_MAP_LEN 0x0004 /* No of bytes to read for the IRQ map */ #define PNP_IRQ_FRMT 0x0022 /* PNP small item IRQ format */ -#ifdef CONFIG_SH_HICOSH4 -#define CS8900_IRQ_MAP 0x0002 /* HiCO-SH4 board has its IRQ on #1 */ -#else #define CS8900_IRQ_MAP 0x1c20 /* This IRQ map is fixed */ -#endif #define CS8920_NO_INTS 0x0F /* Max CS8920 interrupt select # */ diff -urN linux-2.4.19-rmk7-pxa2/drivers/net/cs89x0.orig.c linux-2.4.19-rmk7-pxa2-dimm/drivers/net/cs89x0.orig.c --- linux-2.4.19-rmk7-pxa2/drivers/net/cs89x0.orig.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/net/cs89x0.orig.c Mon Sep 8 12:10:29 2003 @@ -0,0 +1,1831 @@ +/* cs89x0.c: A Crystal Semiconductor (Now Cirrus Logic) CS89[02]0 + * driver for linux. + */ + +/* + Written 1996 by Russell Nelson, with reference to skeleton.c + written 1993-1994 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + The author may be reached at nelson@crynwr.com, Crynwr + Software, 521 Pleasant Valley Rd., Potsdam, NY 13676 + + Changelog: + + Mike Cruse : mcruse@cti-ltd.com + : Changes for Linux 2.0 compatibility. + : Added dev_id parameter in net_interrupt(), + : request_irq() and free_irq(). Just NULL for now. + + Mike Cruse : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros + : in net_open() and net_close() so kerneld would know + : that the module is in use and wouldn't eject the + : driver prematurely. + + Mike Cruse : Rewrote init_module() and cleanup_module using 8390.c + : as an example. Disabled autoprobing in init_module(), + : not a good thing to do to other devices while Linux + : is running from all accounts. + + Russ Nelson : Jul 13 1998. Added RxOnly DMA support. + + Melody Lee : Aug 10 1999. Changes for Linux 2.2.5 compatibility. + : email: ethernet@crystal.cirrus.com + + Alan Cox : Removed 1.2 support, added 2.1 extra counters. + + Andrew Morton : andrewm@uow.edu.au + : Kernel 2.3.48 + : Handle kmalloc() failures + : Other resource allocation fixes + : Add SMP locks + : Integrate Russ Nelson's ALLOW_DMA functionality back in. + : If ALLOW_DMA is true, make DMA runtime selectable + : Folded in changes from Cirrus (Melody Lee + : ) + : Don't call netif_wake_queue() in net_send_packet() + : Fixed an out-of-mem bug in dma_rx() + : Updated Documentation/cs89x0.txt + + Andrew Morton : andrewm@uow.edu.au / Kernel 2.3.99-pre1 + : Use skb_reserve to longword align IP header (two places) + : Remove a delay loop from dma_rx() + : Replace '100' with HZ + : Clean up a couple of skb API abuses + : Added 'cs89x0_dma=N' kernel boot option + : Correctly initialise lp->lock in non-module compile + + Andrew Morton : andrewm@uow.edu.au / Kernel 2.3.99-pre4-1 + : MOD_INC/DEC race fix (see + : http://www.uwsg.indiana.edu/hypermail/linux/kernel/0003.3/1532.html) + + Andrew Morton : andrewm@uow.edu.au / Kernel 2.4.0-test7-pre2 + : Enhanced EEPROM support to cover more devices, + : abstracted IRQ mapping to support CONFIG_ARCH_CLPS7500 arch + : (Jason Gunthorpe ) + + Andrew Morton : Kernel 2.4.0-test11-pre4 + : Use dev->name in request_*() (Andrey Panin) + : Fix an error-path memleak in init_module() + : Preserve return value from request_irq() + : Fix type of `media' module parm (Keith Owens) + : Use SET_MODULE_OWNER() + : Tidied up strange request_irq() abuse in net_open(). + + Andrew Morton : Kernel 2.4.3-pre1 + : Request correct number of pages for DMA (Hugh Dickens) + : Select PP_ChipID _after_ unregister_netdev in cleanup_module() + : because unregister_netdev() calls get_stats. + : Make `version[]' __initdata + : Uninlined the read/write reg/word functions. + + Oskar Schirmer : oskar@scara.com + : HiCO.SH4 (superh) support added (irq#1, cs89x0_media=) + +*/ + +/* Always include 'config.h' first in case the user wants to turn on + or override something. */ +#include +#include +#include + +/* + * Set this to zero to disable DMA code + * + * Note that even if DMA is turned off we still support the 'dma' and 'use_dma' + * module options so we don't break any startup scripts. + */ +#define ALLOW_DMA 1 + +/* + * Set this to zero to remove all the debug statements via + * dead code elimination + */ +#define DEBUGGING 1 + +/* + Sources: + + Crynwr packet driver epktisa. + + Crystal Semiconductor data sheets. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ALLOW_DMA +#include +#endif +#include +#include + +#include +#include +#include + +#include "cs89x0.h" + +static char version[] __initdata = +"cs89x0.c: v2.4.3-pre1 Russell Nelson , Andrew Morton \n"; + +/* First, a few definitions that the brave might change. + A zero-terminated list of I/O addresses to be probed. Some special flags.. + Addr & 1 = Read back the address port, look for signature and reset + the page window before probing + Addr & 3 = Reset the page window and probe + The CLPS eval board has the Cirrus chip at 0x80090300, in ARM IO space, + but it is possible that a Cirrus board could be plugged into the ISA + slots. */ +/* The cs8900 has 4 IRQ pins, software selectable. cs8900_irq_map maps + them to system IRQ numbers. This mapping is card specific and is set to + the configuration of the Cirrus Eval board for this chip. */ +#ifdef CONFIG_ARCH_CLPS7500 +static unsigned int netcard_portlist[] __initdata = + { 0x80090303, 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0}; +static unsigned int cs8900_irq_map[] = {12,0,0,0}; +#elif defined(CONFIG_SH_HICOSH4) +static unsigned int netcard_portlist[] __initdata = + { 0x0300, 0}; +static unsigned int cs8900_irq_map[] = {1,0,0,0}; +#else +static unsigned int netcard_portlist[] __initdata = + { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0}; +static unsigned int cs8900_irq_map[] = {10,11,12,5}; +#endif + +#if DEBUGGING +static unsigned int net_debug = DEBUGGING; +#else +#define net_debug 0 /* gcc will remove all the debug code for us */ +#endif + +/* The number of low I/O ports used by the ethercard. */ +#define NETCARD_IO_EXTENT 16 + +/* we allow the user to override various values normally set in the EEPROM */ +#define FORCE_RJ45 0x0001 /* pick one of these three */ +#define FORCE_AUI 0x0002 +#define FORCE_BNC 0x0004 + +#define FORCE_AUTO 0x0010 /* pick one of these three */ +#define FORCE_HALF 0x0020 +#define FORCE_FULL 0x0030 + +/* Information that need to be kept for each board. */ +struct net_local { + struct net_device_stats stats; + int chip_type; /* one of: CS8900, CS8920, CS8920M */ + char chip_revision; /* revision letter of the chip ('A'...) */ + int send_cmd; /* the proper send command: TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */ + int auto_neg_cnf; /* auto-negotiation word from EEPROM */ + int adapter_cnf; /* adapter configuration from EEPROM */ + int isa_config; /* ISA configuration from EEPROM */ + int irq_map; /* IRQ map from EEPROM */ + int rx_mode; /* what mode are we in? 0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */ + int curr_rx_cfg; /* a copy of PP_RxCFG */ + int linectl; /* either 0 or LOW_RX_SQUELCH, depending on configuration. */ + int send_underrun; /* keep track of how many underruns in a row we get */ + int force; /* force various values; see FORCE* above. */ + spinlock_t lock; +#if ALLOW_DMA + int use_dma; /* Flag: we're using dma */ + int dma; /* DMA channel */ + int dmasize; /* 16 or 64 */ + unsigned char *dma_buff; /* points to the beginning of the buffer */ + unsigned char *end_dma_buff; /* points to the end of the buffer */ + unsigned char *rx_dma_ptr; /* points to the next packet */ +#endif +}; + +/* Index to functions, as function prototypes. */ + +extern int cs89x0_probe(struct net_device *dev); + +static int cs89x0_probe1(struct net_device *dev, int ioaddr); +static int net_open(struct net_device *dev); +static int net_send_packet(struct sk_buff *skb, struct net_device *dev); +static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void set_multicast_list(struct net_device *dev); +static void net_timeout(struct net_device *dev); +static void net_rx(struct net_device *dev); +static int net_close(struct net_device *dev); +static struct net_device_stats *net_get_stats(struct net_device *dev); +static void reset_chip(struct net_device *dev); +static int get_eeprom_data(struct net_device *dev, int off, int len, int *buffer); +static int get_eeprom_cksum(int off, int len, int *buffer); +static int set_mac_address(struct net_device *dev, void *addr); +static void count_rx_errors(int status, struct net_local *lp); +#if ALLOW_DMA +static void get_dma_channel(struct net_device *dev); +static void release_dma_buff(struct net_local *lp); +#endif + +/* Example routines you must write ;->. */ +#define tx_done(dev) 1 + +/* + * Permit 'cs89x0_dma=N' in the kernel boot environment + */ +#if !defined(MODULE) && (ALLOW_DMA != 0) +static int g_cs89x0_dma; + +static int __init dma_fn(char *str) +{ + g_cs89x0_dma = simple_strtol(str,NULL,0); + return 1; +} + +__setup("cs89x0_dma=", dma_fn); +#endif /* !defined(MODULE) && (ALLOW_DMA != 0) */ + +#ifndef MODULE +static int g_cs89x0_media__force; + +static int __init media_fn(char *str) +{ + if (!strcmp(str, "rj45")) g_cs89x0_media__force = FORCE_RJ45; + else if (!strcmp(str, "aui")) g_cs89x0_media__force = FORCE_AUI; + else if (!strcmp(str, "bnc")) g_cs89x0_media__force = FORCE_BNC; + return 1; +} + +__setup("cs89x0_media=", media_fn); +#endif + + +/* Check for a network adaptor of this type, and return '0' iff one exists. + If dev->base_addr == 0, probe all likely locations. + If dev->base_addr == 1, always return failure. + If dev->base_addr == 2, allocate space for the device and return success + (detachable devices only). + Return 0 on success. + */ + +int __init cs89x0_probe(struct net_device *dev) +{ + int i; + int base_addr = dev ? dev->base_addr : 0; + + SET_MODULE_OWNER(dev); + + if (net_debug) + printk("cs89x0:cs89x0_probe(0x%x)\n", base_addr); + + if (base_addr > 0x1ff) /* Check a single specified location. */ + return cs89x0_probe1(dev, base_addr); + else if (base_addr != 0) /* Don't probe at all. */ + return -ENXIO; + + for (i = 0; netcard_portlist[i]; i++) { + if (cs89x0_probe1(dev, netcard_portlist[i]) == 0) + return 0; + } + printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n"); + return -ENODEV; +} + +static int +readreg(struct net_device *dev, int portno) +{ + outw(portno, dev->base_addr + ADD_PORT); + return inw(dev->base_addr + DATA_PORT); +} + +static void +writereg(struct net_device *dev, int portno, int value) +{ + outw(portno, dev->base_addr + ADD_PORT); + outw(value, dev->base_addr + DATA_PORT); +} + +static int +readword(struct net_device *dev, int portno) +{ + return inw(dev->base_addr + portno); +} + +static void +writeword(struct net_device *dev, int portno, int value) +{ + outw(value, dev->base_addr + portno); +} + +static int __init +wait_eeprom_ready(struct net_device *dev) +{ + int timeout = jiffies; + /* check to see if the EEPROM is ready, a timeout is used - + just in case EEPROM is ready when SI_BUSY in the + PP_SelfST is clear */ + while(readreg(dev, PP_SelfST) & SI_BUSY) + if (jiffies - timeout >= 40) + return -1; + return 0; +} + +static int __init +get_eeprom_data(struct net_device *dev, int off, int len, int *buffer) +{ + int i; + + if (net_debug > 3) printk("EEPROM data from %x for %x:\n",off,len); + for (i = 0; i < len; i++) { + if (wait_eeprom_ready(dev) < 0) return -1; + /* Now send the EEPROM read command and EEPROM location to read */ + writereg(dev, PP_EECMD, (off + i) | EEPROM_READ_CMD); + if (wait_eeprom_ready(dev) < 0) return -1; + buffer[i] = readreg(dev, PP_EEData); + if (net_debug > 3) printk("%04x ", buffer[i]); + } + if (net_debug > 3) printk("\n"); + return 0; +} + +static int __init +get_eeprom_cksum(int off, int len, int *buffer) +{ + int i, cksum; + + cksum = 0; + for (i = 0; i < len; i++) + cksum += buffer[i]; + cksum &= 0xffff; + if (cksum == 0) + return 0; + return -1; +} + +/* This is the real probe routine. Linux has a history of friendly device + probes on the ISA bus. A good device probes avoids doing writes, and + verifies that the correct device exists and functions. + Return 0 on success. + */ + +static int __init +cs89x0_probe1(struct net_device *dev, int ioaddr) +{ + struct net_local *lp; + static unsigned version_printed; + int i; + unsigned rev_type = 0; + int eeprom_buff[CHKSUM_LEN]; + int retval; + + /* Initialize the device structure. */ + if (dev->priv == NULL) { + dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + if (dev->priv == 0) { + retval = -ENOMEM; + goto out; + } + lp = (struct net_local *)dev->priv; + memset(lp, 0, sizeof(*lp)); + spin_lock_init(&lp->lock); +#if !defined(MODULE) && (ALLOW_DMA != 0) + if (g_cs89x0_dma) { + lp->use_dma = 1; + lp->dma = g_cs89x0_dma; + lp->dmasize = 16; /* Could make this an option... */ + } +#endif +#ifndef MODULE + lp->force = g_cs89x0_media__force; +#endif + } + lp = (struct net_local *)dev->priv; + + /* Grab the region so we can find another board if autoIRQ fails. */ + if (!request_region(ioaddr & ~3, NETCARD_IO_EXTENT, dev->name)) { + printk(KERN_ERR "%s: request_region(0x%x, 0x%x) failed\n", + dev->name, ioaddr, NETCARD_IO_EXTENT); + retval = -EBUSY; + goto out1; + } + +#ifdef CONFIG_SH_HICOSH4 + /* truely reset the chip */ + outw(0x0114, ioaddr + ADD_PORT); + outw(0x0040, ioaddr + DATA_PORT); +#endif + + /* if they give us an odd I/O address, then do ONE write to + the address port, to get it back to address zero, where we + expect to find the EISA signature word. An IO with a base of 0x3 + will skip the test for the ADD_PORT. */ + if (ioaddr & 1) { + if (net_debug > 1) + printk(KERN_INFO "%s: odd ioaddr 0x%x\n", dev->name, ioaddr); + if ((ioaddr & 2) != 2) + if ((inw((ioaddr & ~3)+ ADD_PORT) & ADD_MASK) != ADD_SIG) { + printk(KERN_ERR "%s: bad signature 0x%x\n", + dev->name, inw((ioaddr & ~3)+ ADD_PORT)); + retval = -ENODEV; + goto out2; + } + ioaddr &= ~3; + outw(PP_ChipID, ioaddr + ADD_PORT); + } +printk("PP_addr=0x%x\n", inw(ioaddr + ADD_PORT)); + + if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) { + printk(KERN_ERR "%s: incorrect signature 0x%x\n", + dev->name, inw(ioaddr + DATA_PORT)); + retval = -ENODEV; + goto out2; + } + + /* Fill in the 'dev' fields. */ + dev->base_addr = ioaddr; + + /* get the chip type */ + rev_type = readreg(dev, PRODUCT_ID_ADD); + lp->chip_type = rev_type &~ REVISON_BITS; + lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; + + /* Check the chip type and revision in order to set the correct send command + CS8920 revision C and CS8900 revision F can use the faster send. */ + lp->send_cmd = TX_AFTER_381; + if (lp->chip_type == CS8900 && lp->chip_revision >= 'F') + lp->send_cmd = TX_NOW; + if (lp->chip_type != CS8900 && lp->chip_revision >= 'C') + lp->send_cmd = TX_NOW; + + if (net_debug && version_printed++ == 0) + printk(version); + + printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#3lx ", + dev->name, + lp->chip_type==CS8900?'0':'2', + lp->chip_type==CS8920M?"M":"", + lp->chip_revision, + dev->base_addr); + + reset_chip(dev); + + /* Here we read the current configuration of the chip. If there + is no Extended EEPROM then the idea is to not disturb the chip + configuration, it should have been correctly setup by automatic + EEPROM read on reset. So, if the chip says it read the EEPROM + the driver will always do *something* instead of complain that + adapter_cnf is 0. */ + +#ifdef CONFIG_SH_HICOSH4 + if (1) { + /* For the HiCO.SH4 board, things are different: we don't + have EEPROM, but there is some data in flash, so we go + get it there directly (MAC). */ + __u16 *confd; + short cnt; + if (((* (volatile __u32 *) 0xa0013ff0) & 0x00ffffff) + == 0x006c3000) { + confd = (__u16*) 0xa0013fc0; + } else { + confd = (__u16*) 0xa001ffc0; + } + cnt = (*confd++ & 0x00ff) >> 1; + while (--cnt > 0) { + __u16 j = *confd++; + + switch (j & 0x0fff) { + case PP_IA: + for (i = 0; i < ETH_ALEN/2; i++) { + dev->dev_addr[i*2] = confd[i] & 0xFF; + dev->dev_addr[i*2+1] = confd[i] >> 8; + } + break; + } + j = (j >> 12) + 1; + confd += j; + cnt -= j; + } + } else +#endif + + if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) == + (EEPROM_OK|EEPROM_PRESENT)) { + /* Load the MAC. */ + for (i=0; i < ETH_ALEN/2; i++) { + unsigned int Addr; + Addr = readreg(dev, PP_IA+i*2); + dev->dev_addr[i*2] = Addr & 0xFF; + dev->dev_addr[i*2+1] = Addr >> 8; + } + + /* Load the Adapter Configuration. + Note: Barring any more specific information from some + other source (ie EEPROM+Schematics), we would not know + how to operate a 10Base2 interface on the AUI port. + However, since we do read the status of HCB1 and use + settings that always result in calls to control_dc_dc(dev,0) + a BNC interface should work if the enable pin + (dc/dc converter) is on HCB1. It will be called AUI + however. */ + + lp->adapter_cnf = 0; + i = readreg(dev, PP_LineCTL); + /* Preserve the setting of the HCB1 pin. */ + if ((i & (HCB1 | HCB1_ENBL)) == (HCB1 | HCB1_ENBL)) + lp->adapter_cnf |= A_CNF_DC_DC_POLARITY; + /* Save the sqelch bit */ + if ((i & LOW_RX_SQUELCH) == LOW_RX_SQUELCH) + lp->adapter_cnf |= A_CNF_EXTND_10B_2 | A_CNF_LOW_RX_SQUELCH; + /* Check if the card is in 10Base-t only mode */ + if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == 0) + lp->adapter_cnf |= A_CNF_10B_T | A_CNF_MEDIA_10B_T; + /* Check if the card is in AUI only mode */ + if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUI_ONLY) + lp->adapter_cnf |= A_CNF_AUI | A_CNF_MEDIA_AUI; + /* Check if the card is in Auto mode. */ + if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUTO_AUI_10BASET) + lp->adapter_cnf |= A_CNF_AUI | A_CNF_10B_T | + A_CNF_MEDIA_AUI | A_CNF_MEDIA_10B_T | A_CNF_MEDIA_AUTO; + + if (net_debug > 1) + printk(KERN_INFO "%s: PP_LineCTL=0x%x, adapter_cnf=0x%x\n", + dev->name, i, lp->adapter_cnf); + + /* IRQ. Other chips already probe, see below. */ + if (lp->chip_type == CS8900) + lp->isa_config = readreg(dev, PP_CS8900_ISAINT) & INT_NO_MASK; + + printk( "[Cirrus EEPROM] "); + } + + printk("\n"); + + /* First check to see if an EEPROM is attached. */ +#ifdef CONFIG_SH_HICOSH4 /* no EEPROM on HiCO, don't hazzle with it here */ + if (1) { + printk(KERN_NOTICE "cs89x0: No EEPROM on HiCO.SH4\n"); + } else +#endif + if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0) + printk(KERN_WARNING "cs89x0: No EEPROM, relying on command line....\n"); + else if (get_eeprom_data(dev, START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) { + printk(KERN_WARNING "\ncs89x0: EEPROM read failed, relying on command line.\n"); + } else if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) { + /* Check if the chip was able to read its own configuration starting + at 0 in the EEPROM*/ + if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) != + (EEPROM_OK|EEPROM_PRESENT)) + printk(KERN_WARNING "cs89x0: Extended EEPROM checksum bad and no Cirrus EEPROM, relying on command line\n"); + + } else { + /* This reads an extended EEPROM that is not documented + in the CS8900 datasheet. */ + + /* get transmission control word but keep the autonegotiation bits */ + if (!lp->auto_neg_cnf) lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2]; + /* Store adapter configuration */ + if (!lp->adapter_cnf) lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2]; + /* Store ISA configuration */ + lp->isa_config = eeprom_buff[ISA_CNF_OFFSET/2]; + dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8; + + /* eeprom_buff has 32-bit ints, so we can't just memcpy it */ + /* store the initial memory base address */ + for (i = 0; i < ETH_ALEN/2; i++) { + dev->dev_addr[i*2] = eeprom_buff[i]; + dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8; + } + if (net_debug > 1) + printk(KERN_DEBUG "%s: new adapter_cnf: 0x%x\n", + dev->name, lp->adapter_cnf); + } + + /* allow them to force multiple transceivers. If they force multiple, autosense */ + { + int count = 0; + if (lp->force & FORCE_RJ45) {lp->adapter_cnf |= A_CNF_10B_T; count++; } + if (lp->force & FORCE_AUI) {lp->adapter_cnf |= A_CNF_AUI; count++; } + if (lp->force & FORCE_BNC) {lp->adapter_cnf |= A_CNF_10B_2; count++; } + if (count > 1) {lp->adapter_cnf |= A_CNF_MEDIA_AUTO; } + else if (lp->force & FORCE_RJ45){lp->adapter_cnf |= A_CNF_MEDIA_10B_T; } + else if (lp->force & FORCE_AUI) {lp->adapter_cnf |= A_CNF_MEDIA_AUI; } + else if (lp->force & FORCE_BNC) {lp->adapter_cnf |= A_CNF_MEDIA_10B_2; } + } + + if (net_debug > 1) + printk(KERN_DEBUG "%s: after force 0x%x, adapter_cnf=0x%x\n", + dev->name, lp->force, lp->adapter_cnf); + + /* FIXME: We don't let you set dc-dc polarity or low RX squelch from the command line: add it here */ + + /* FIXME: We don't let you set the IMM bit from the command line: add it to lp->auto_neg_cnf here */ + + /* FIXME: we don't set the Ethernet address on the command line. Use + ifconfig IFACE hw ether AABBCCDDEEFF */ + + printk(KERN_INFO "cs89x0 media %s%s%s", + (lp->adapter_cnf & A_CNF_10B_T)?"RJ-45,":"", + (lp->adapter_cnf & A_CNF_AUI)?"AUI,":"", + (lp->adapter_cnf & A_CNF_10B_2)?"BNC,":""); + + lp->irq_map = 0xffff; + + /* If this is a CS8900 then no pnp soft */ + if (lp->chip_type != CS8900 && + /* Check if the ISA IRQ has been set */ + (i = readreg(dev, PP_CS8920_ISAINT) & 0xff, + (i != 0 && i < CS8920_NO_INTS))) { + if (!dev->irq) + dev->irq = i; + } else { + i = lp->isa_config & INT_NO_MASK; + if (lp->chip_type == CS8900) { + /* Translate the IRQ using the IRQ mapping table. */ + if (i >= sizeof(cs8900_irq_map)/sizeof(cs8900_irq_map[0])) + printk("\ncs89x0: invalid ISA interrupt number %d\n", i); + else + i = cs8900_irq_map[i]; + + lp->irq_map = CS8900_IRQ_MAP; /* fixed IRQ map for CS8900 */ + } else { + int irq_map_buff[IRQ_MAP_LEN/2]; + + if (get_eeprom_data(dev, IRQ_MAP_EEPROM_DATA, + IRQ_MAP_LEN/2, + irq_map_buff) >= 0) { + if ((irq_map_buff[0] & 0xff) == PNP_IRQ_FRMT) + lp->irq_map = (irq_map_buff[0]>>8) | (irq_map_buff[1] << 8); + } + } + if (!dev->irq) + dev->irq = i; + } + + printk(" IRQ %d", dev->irq); + +#if ALLOW_DMA + if (lp->use_dma) { + get_dma_channel(dev); + printk(", DMA %d", dev->dma); + } + else +#endif + { + printk(", programmed I/O"); + } + + /* print the ethernet address. */ + printk(", MAC"); + for (i = 0; i < ETH_ALEN; i++) + { + printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); + } + + dev->open = net_open; + dev->stop = net_close; + dev->tx_timeout = net_timeout; + dev->watchdog_timeo = HZ; + dev->hard_start_xmit = net_send_packet; + dev->get_stats = net_get_stats; + dev->set_multicast_list = set_multicast_list; + dev->set_mac_address = set_mac_address; + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + + printk("\n"); + if (net_debug) + printk("cs89x0_probe1() successful\n"); + return 0; +out2: + release_region(ioaddr & ~3, NETCARD_IO_EXTENT); +out1: + kfree(dev->priv); + dev->priv = 0; +out: + return retval; +} + + +/********************************* + * This page contains DMA routines +**********************************/ + +#if ALLOW_DMA + +#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17) + +static void +get_dma_channel(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + if (lp->dma) { + dev->dma = lp->dma; + lp->isa_config |= ISA_RxDMA; + } else { + if ((lp->isa_config & ANY_ISA_DMA) == 0) + return; + dev->dma = lp->isa_config & DMA_NO_MASK; + if (lp->chip_type == CS8900) + dev->dma += 5; + if (dev->dma < 5 || dev->dma > 7) { + lp->isa_config &= ~ANY_ISA_DMA; + return; + } + } + return; +} + +static void +write_dma(struct net_device *dev, int chip_type, int dma) +{ + struct net_local *lp = (struct net_local *)dev->priv; + if ((lp->isa_config & ANY_ISA_DMA) == 0) + return; + if (chip_type == CS8900) { + writereg(dev, PP_CS8900_ISADMA, dma-5); + } else { + writereg(dev, PP_CS8920_ISADMA, dma); + } +} + +static void +set_dma_cfg(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + if (lp->use_dma) { + if ((lp->isa_config & ANY_ISA_DMA) == 0) { + if (net_debug > 3) + printk("set_dma_cfg(): no DMA\n"); + return; + } + if (lp->isa_config & ISA_RxDMA) { + lp->curr_rx_cfg |= RX_DMA_ONLY; + if (net_debug > 3) + printk("set_dma_cfg(): RX_DMA_ONLY\n"); + } else { + lp->curr_rx_cfg |= AUTO_RX_DMA; /* not that we support it... */ + if (net_debug > 3) + printk("set_dma_cfg(): AUTO_RX_DMA\n"); + } + } +} + +static int +dma_bufcfg(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + if (lp->use_dma) + return (lp->isa_config & ANY_ISA_DMA)? RX_DMA_ENBL : 0; + else + return 0; +} + +static int +dma_busctl(struct net_device *dev) +{ + int retval = 0; + struct net_local *lp = (struct net_local *)dev->priv; + if (lp->use_dma) { + if (lp->isa_config & ANY_ISA_DMA) + retval |= RESET_RX_DMA; /* Reset the DMA pointer */ + if (lp->isa_config & DMA_BURST) + retval |= DMA_BURST_MODE; /* Does ISA config specify DMA burst ? */ + if (lp->dmasize == 64) + retval |= RX_DMA_SIZE_64K; /* did they ask for 64K? */ + retval |= MEMORY_ON; /* we need memory enabled to use DMA. */ + } + return retval; +} + +static void +dma_rx(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + struct sk_buff *skb; + int status, length; + unsigned char *bp = lp->rx_dma_ptr; + + status = bp[0] + (bp[1]<<8); + length = bp[2] + (bp[3]<<8); + bp += 4; + if (net_debug > 5) { + printk( "%s: receiving DMA packet at %lx, status %x, length %x\n", + dev->name, (unsigned long)bp, status, length); + } + if ((status & RX_OK) == 0) { + count_rx_errors(status, lp); + goto skip_this_frame; + } + + /* Malloc up new buffer. */ + skb = dev_alloc_skb(length + 2); + if (skb == NULL) { + if (net_debug) /* I don't think we want to do this to a stressed system */ + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + + /* AKPM: advance bp to the next frame */ +skip_this_frame: + bp += (length + 3) & ~3; + if (bp >= lp->end_dma_buff) bp -= lp->dmasize*1024; + lp->rx_dma_ptr = bp; + return; + } + skb_reserve(skb, 2); /* longword align L3 header */ + skb->dev = dev; + + if (bp + length > lp->end_dma_buff) { + int semi_cnt = lp->end_dma_buff - bp; + memcpy(skb_put(skb,semi_cnt), bp, semi_cnt); + memcpy(skb_put(skb,length - semi_cnt), lp->dma_buff, + length - semi_cnt); + } else { + memcpy(skb_put(skb,length), bp, length); + } + bp += (length + 3) & ~3; + if (bp >= lp->end_dma_buff) bp -= lp->dmasize*1024; + lp->rx_dma_ptr = bp; + + if (net_debug > 3) { + printk( "%s: received %d byte DMA packet of type %x\n", + dev->name, length, + (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]); + } + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + dev->last_rx = jiffies; + lp->stats.rx_packets++; + lp->stats.rx_bytes += length; +} + +#endif /* ALLOW_DMA */ + +void __init reset_chip(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + int ioaddr = dev->base_addr; + int reset_start_time; + + writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET); + + /* wait 30 ms */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(30*HZ/1000); + + if (lp->chip_type != CS8900) { + /* Hardware problem requires PNP registers to be reconfigured after a reset */ + outw(PP_CS8920_ISAINT, ioaddr + ADD_PORT); + outb(dev->irq, ioaddr + DATA_PORT); + outb(0, ioaddr + DATA_PORT + 1); + + outw(PP_CS8920_ISAMemB, ioaddr + ADD_PORT); + outb((dev->mem_start >> 16) & 0xff, ioaddr + DATA_PORT); + outb((dev->mem_start >> 8) & 0xff, ioaddr + DATA_PORT + 1); + } + /* Wait until the chip is reset */ + reset_start_time = jiffies; + while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2) + ; +} + + +static void +control_dc_dc(struct net_device *dev, int on_not_off) +{ + struct net_local *lp = (struct net_local *)dev->priv; + unsigned int selfcontrol; + int timenow = jiffies; + /* control the DC to DC convertor in the SelfControl register. + Note: This is hooked up to a general purpose pin, might not + always be a DC to DC convertor. */ + + selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */ + if (((lp->adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off) + selfcontrol |= HCB1; + else + selfcontrol &= ~HCB1; + writereg(dev, PP_SelfCTL, selfcontrol); + + /* Wait for the DC/DC converter to power up - 500ms */ + while (jiffies - timenow < HZ) + ; +} + +#define DETECTED_NONE 0 +#define DETECTED_RJ45H 1 +#define DETECTED_RJ45F 2 +#define DETECTED_AUI 3 +#define DETECTED_BNC 4 + +static int +detect_tp(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + int timenow = jiffies; + int fdx; + + if (net_debug > 1) printk("%s: Attempting TP\n", dev->name); + + /* If connected to another full duplex capable 10-Base-T card the link pulses + seem to be lost when the auto detect bit in the LineCTL is set. + To overcome this the auto detect bit will be cleared whilst testing the + 10-Base-T interface. This would not be necessary for the sparrow chip but + is simpler to do it anyway. */ + writereg(dev, PP_LineCTL, lp->linectl &~ AUI_ONLY); + control_dc_dc(dev, 0); + + /* Delay for the hardware to work out if the TP cable is present - 150ms */ + for (timenow = jiffies; jiffies - timenow < 15; ) + ; + if ((readreg(dev, PP_LineST) & LINK_OK) == 0) + return DETECTED_NONE; + + if (lp->chip_type == CS8900) { + switch (lp->force & 0xf0) { +#if 0 + case FORCE_AUTO: + printk("%s: cs8900 doesn't autonegotiate\n",dev->name); + return DETECTED_NONE; +#endif + /* CS8900 doesn't support AUTO, change to HALF*/ + case FORCE_AUTO: + lp->force &= ~FORCE_AUTO; + lp->force |= FORCE_HALF; + break; + case FORCE_HALF: + break; + case FORCE_FULL: + writereg(dev, PP_TestCTL, readreg(dev, PP_TestCTL) | FDX_8900); + break; + } + fdx = readreg(dev, PP_TestCTL) & FDX_8900; + } else { + switch (lp->force & 0xf0) { + case FORCE_AUTO: + lp->auto_neg_cnf = AUTO_NEG_ENABLE; + break; + case FORCE_HALF: + lp->auto_neg_cnf = 0; + break; + case FORCE_FULL: + lp->auto_neg_cnf = RE_NEG_NOW | ALLOW_FDX; + break; + } + + writereg(dev, PP_AutoNegCTL, lp->auto_neg_cnf & AUTO_NEG_MASK); + + if ((lp->auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) { + printk(KERN_INFO "%s: negotiating duplex...\n",dev->name); + while (readreg(dev, PP_AutoNegST) & AUTO_NEG_BUSY) { + if (jiffies - timenow > 4000) { + printk(KERN_ERR "**** Full / half duplex auto-negotiation timed out ****\n"); + break; + } + } + } + fdx = readreg(dev, PP_AutoNegST) & FDX_ACTIVE; + } + if (fdx) + return DETECTED_RJ45F; + else + return DETECTED_RJ45H; +} + +/* send a test packet - return true if carrier bits are ok */ +static int +send_test_pkt(struct net_device *dev) +{ + char test_packet[] = { 0,0,0,0,0,0, 0,0,0,0,0,0, + 0, 46, /* A 46 in network order */ + 0, 0, /* DSAP=0 & SSAP=0 fields */ + 0xf3, 0 /* Control (Test Req + P bit set) */ }; + long timenow = jiffies; + + writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_TX_ON); + + memcpy(test_packet, dev->dev_addr, ETH_ALEN); + memcpy(test_packet+ETH_ALEN, dev->dev_addr, ETH_ALEN); + + writeword(dev, TX_CMD_PORT, TX_AFTER_ALL); + writeword(dev, TX_LEN_PORT, ETH_ZLEN); + + /* Test to see if the chip has allocated memory for the packet */ + while (jiffies - timenow < 5) + if (readreg(dev, PP_BusST) & READY_FOR_TX_NOW) + break; + if (jiffies - timenow >= 5) + return 0; /* this shouldn't happen */ + + /* Write the contents of the packet */ + outsw(dev->base_addr + TX_FRAME_PORT,test_packet,(ETH_ZLEN+1) >>1); + + if (net_debug > 1) printk("Sending test packet "); + /* wait a couple of jiffies for packet to be received */ + for (timenow = jiffies; jiffies - timenow < 3; ) + ; + if ((readreg(dev, PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) { + if (net_debug > 1) printk("succeeded\n"); + return 1; + } + if (net_debug > 1) printk("failed\n"); + return 0; +} + + +static int +detect_aui(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + if (net_debug > 1) printk("%s: Attempting AUI\n", dev->name); + control_dc_dc(dev, 0); + + writereg(dev, PP_LineCTL, (lp->linectl &~ AUTO_AUI_10BASET) | AUI_ONLY); + + if (send_test_pkt(dev)) + return DETECTED_AUI; + else + return DETECTED_NONE; +} + +static int +detect_bnc(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + if (net_debug > 1) printk("%s: Attempting BNC\n", dev->name); + control_dc_dc(dev, 1); + + writereg(dev, PP_LineCTL, (lp->linectl &~ AUTO_AUI_10BASET) | AUI_ONLY); + + if (send_test_pkt(dev)) + return DETECTED_BNC; + else + return DETECTED_NONE; +} + + +static void +write_irq(struct net_device *dev, int chip_type, int irq) +{ + int i; + + if (chip_type == CS8900) { + /* Search the mapping table for the corresponding IRQ pin. */ + for (i = 0; i != sizeof(cs8900_irq_map)/sizeof(cs8900_irq_map[0]); i++) + if (cs8900_irq_map[i] == irq) + break; + /* Not found */ + if (i == sizeof(cs8900_irq_map)/sizeof(cs8900_irq_map[0])) + i = 3; + writereg(dev, PP_CS8900_ISAINT, i); + } else { + writereg(dev, PP_CS8920_ISAINT, irq); + } +} + +/* Open/initialize the board. This is called (in the current kernel) + sometime after booting when the 'ifconfig' program is run. + + This routine should set everything up anew at each open, even + registers that "should" only need to be set once at boot, so that + there is non-reboot way to recover if something goes wrong. + */ + +/* AKPM: do we need to do any locking here? */ + +static int +net_open(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + int result = 0; + int i; + int ret; + +#ifndef CONFIG_SH_HICOSH4 /* uses irq#1, so this wont work */ + if (dev->irq < 2) { + /* Allow interrupts to be generated by the chip */ +/* Cirrus' release had this: */ +#if 0 + writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ ); +#endif +/* And 2.3.47 had this: */ + writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON); + + for (i = 2; i < CS8920_NO_INTS; i++) { + if ((1 << i) & lp->irq_map) { + if (request_irq(i, net_interrupt, 0, dev->name, dev) == 0) { + dev->irq = i; + write_irq(dev, lp->chip_type, i); + /* writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT); */ + break; + } + } + } + + if (i >= CS8920_NO_INTS) { + writereg(dev, PP_BusCTL, 0); /* disable interrupts. */ + printk(KERN_ERR "cs89x0: can't get an interrupt\n"); + ret = -EAGAIN; + goto bad_out; + } + } + else +#endif + { + if (((1 << dev->irq) & lp->irq_map) == 0) { + printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n", + dev->name, dev->irq, lp->irq_map); + ret = -EAGAIN; + goto bad_out; + } +/* FIXME: Cirrus' release had this: */ + writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ ); +/* And 2.3.47 had this: */ +#if 0 + writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON); +#endif + write_irq(dev, lp->chip_type, dev->irq); + ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev); + if (ret) { + if (net_debug) + printk(KERN_DEBUG "cs89x0: request_irq(%d) failed\n", dev->irq); + goto bad_out; + } + } + +#if ALLOW_DMA + if (lp->use_dma) { + if (lp->isa_config & ANY_ISA_DMA) { + unsigned long flags; + lp->dma_buff = (unsigned char *)__get_dma_pages(GFP_KERNEL, + get_order(lp->dmasize * 1024)); + + if (!lp->dma_buff) { + printk(KERN_ERR "%s: cannot get %dK memory for DMA\n", dev->name, lp->dmasize); + goto release_irq; + } + if (net_debug > 1) { + printk( "%s: dma %lx %lx\n", + dev->name, + (unsigned long)lp->dma_buff, + (unsigned long)virt_to_bus(lp->dma_buff)); + } + if ((unsigned long) lp->dma_buff >= MAX_DMA_ADDRESS || + !dma_page_eq(lp->dma_buff, lp->dma_buff+lp->dmasize*1024-1)) { + printk(KERN_ERR "%s: not usable as DMA buffer\n", dev->name); + goto release_irq; + } + memset(lp->dma_buff, 0, lp->dmasize * 1024); /* Why? */ + if (request_dma(dev->dma, dev->name)) { + printk(KERN_ERR "%s: cannot get dma channel %d\n", dev->name, dev->dma); + goto release_irq; + } + write_dma(dev, lp->chip_type, dev->dma); + lp->rx_dma_ptr = lp->dma_buff; + lp->end_dma_buff = lp->dma_buff + lp->dmasize*1024; + spin_lock_irqsave(&lp->lock, flags); + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + set_dma_mode(dev->dma, 0x14); /* auto_init as well */ + set_dma_addr(dev->dma, virt_to_bus(lp->dma_buff)); + set_dma_count(dev->dma, lp->dmasize*1024); + enable_dma(dev->dma); + spin_unlock_irqrestore(&lp->lock, flags); + } + } +#endif /* ALLOW_DMA */ + + /* set the Ethernet address */ + for (i=0; i < ETH_ALEN/2; i++) + writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); + + /* while we're testing the interface, leave interrupts disabled */ + writereg(dev, PP_BusCTL, MEMORY_ON); + + /* Set the LineCTL quintuplet based on adapter configuration read from EEPROM */ + if ((lp->adapter_cnf & A_CNF_EXTND_10B_2) && (lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH)) + lp->linectl = LOW_RX_SQUELCH; + else + lp->linectl = 0; + + /* check to make sure that they have the "right" hardware available */ + switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) { + case A_CNF_MEDIA_10B_T: result = lp->adapter_cnf & A_CNF_10B_T; break; + case A_CNF_MEDIA_AUI: result = lp->adapter_cnf & A_CNF_AUI; break; + case A_CNF_MEDIA_10B_2: result = lp->adapter_cnf & A_CNF_10B_2; break; + default: result = lp->adapter_cnf & (A_CNF_10B_T | A_CNF_AUI | A_CNF_10B_2); + } + if (!result) { + printk(KERN_ERR "%s: EEPROM is configured for unavailable media\n", dev->name); + release_irq: +#if ALLOW_DMA + release_dma_buff(lp); +#endif + writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON)); + free_irq(dev->irq, dev); + ret = -EAGAIN; + goto bad_out; + } + + /* set the hardware to the configured choice */ + switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) { + case A_CNF_MEDIA_10B_T: + result = detect_tp(dev); + if (result==DETECTED_NONE) { + printk(KERN_WARNING "%s: 10Base-T (RJ-45) has no cable\n", dev->name); + if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */ + result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */ + } + break; + case A_CNF_MEDIA_AUI: + result = detect_aui(dev); + if (result==DETECTED_NONE) { + printk(KERN_WARNING "%s: 10Base-5 (AUI) has no cable\n", dev->name); + if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */ + result = DETECTED_AUI; /* Yes! I don't care if I see a carrrier */ + } + break; + case A_CNF_MEDIA_10B_2: + result = detect_bnc(dev); + if (result==DETECTED_NONE) { + printk(KERN_WARNING "%s: 10Base-2 (BNC) has no cable\n", dev->name); + if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */ + result = DETECTED_BNC; /* Yes! I don't care if I can xmit a packet */ + } + break; + case A_CNF_MEDIA_AUTO: + writereg(dev, PP_LineCTL, lp->linectl | AUTO_AUI_10BASET); + if (lp->adapter_cnf & A_CNF_10B_T) + if ((result = detect_tp(dev)) != DETECTED_NONE) + break; + if (lp->adapter_cnf & A_CNF_AUI) + if ((result = detect_aui(dev)) != DETECTED_NONE) + break; + if (lp->adapter_cnf & A_CNF_10B_2) + if ((result = detect_bnc(dev)) != DETECTED_NONE) + break; + printk(KERN_ERR "%s: no media detected\n", dev->name); + goto release_irq; + } + switch(result) { + case DETECTED_NONE: + printk(KERN_ERR "%s: no network cable attached to configured media\n", dev->name); + goto release_irq; + case DETECTED_RJ45H: + printk(KERN_INFO "%s: using half-duplex 10Base-T (RJ-45)\n", dev->name); + break; + case DETECTED_RJ45F: + printk(KERN_INFO "%s: using full-duplex 10Base-T (RJ-45)\n", dev->name); + break; + case DETECTED_AUI: + printk(KERN_INFO "%s: using 10Base-5 (AUI)\n", dev->name); + break; + case DETECTED_BNC: + printk(KERN_INFO "%s: using 10Base-2 (BNC)\n", dev->name); + break; + } + + /* Turn on both receive and transmit operations */ + writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON); + + /* Receive only error free packets addressed to this card */ + lp->rx_mode = 0; + writereg(dev, PP_RxCTL, DEF_RX_ACCEPT); + + lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL; + + if (lp->isa_config & STREAM_TRANSFER) + lp->curr_rx_cfg |= RX_STREAM_ENBL; +#if ALLOW_DMA + set_dma_cfg(dev); +#endif + writereg(dev, PP_RxCFG, lp->curr_rx_cfg); + + writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | + TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL); + + writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | +#if ALLOW_DMA + dma_bufcfg(dev) | +#endif + TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL); + + /* now that we've got our act together, enable everything */ + writereg(dev, PP_BusCTL, ENABLE_IRQ + | (dev->mem_start?MEMORY_ON : 0) /* turn memory on */ +#if ALLOW_DMA + | dma_busctl(dev) +#endif + ); + netif_start_queue(dev); + if (net_debug > 1) + printk("cs89x0: net_open() succeeded\n"); + return 0; +bad_out: + return ret; +} + +static void net_timeout(struct net_device *dev) +{ + /* If we get here, some higher level has decided we are broken. + There should really be a "kick me" function call instead. */ + if (net_debug > 0) printk("%s: transmit timed out, %s?\n", dev->name, + tx_done(dev) ? "IRQ conflict ?" : "network cable problem"); + /* Try to restart the adaptor. */ + netif_wake_queue(dev); +} + +static int net_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + if (net_debug > 3) { + printk("%s: sent %d byte packet of type %x\n", + dev->name, skb->len, + (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]); + } + + /* keep the upload from being interrupted, since we + ask the chip to start transmitting before the + whole packet has been completely uploaded. */ + + spin_lock_irq(&lp->lock); + netif_stop_queue(dev); + + /* initiate a transmit sequence */ + writeword(dev, TX_CMD_PORT, lp->send_cmd); + writeword(dev, TX_LEN_PORT, skb->len); + + /* Test to see if the chip has allocated memory for the packet */ + if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) { + /* + * Gasp! It hasn't. But that shouldn't happen since + * we're waiting for TxOk, so return 1 and requeue this packet. + */ + + spin_unlock_irq(&lp->lock); + if (net_debug) printk("cs89x0: Tx buffer not free!\n"); + return 1; + } + /* Write the contents of the packet */ + outsw(dev->base_addr + TX_FRAME_PORT,skb->data,(skb->len+1) >>1); + spin_unlock_irq(&lp->lock); + dev->trans_start = jiffies; + dev_kfree_skb (skb); + + /* + * We DO NOT call netif_wake_queue() here. + * We also DO NOT call netif_start_queue(). + * + * Either of these would cause another bottom half run through + * net_send_packet() before this packet has fully gone out. That causes + * us to hit the "Gasp!" above and the send is rescheduled. it runs like + * a dog. We just return and wait for the Tx completion interrupt handler + * to restart the netdevice layer + */ + + return 0; +} + +/* The typical workload of the driver: + Handle the network interface interrupts. */ + +static void net_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + struct net_local *lp; + int ioaddr, status; + + ioaddr = dev->base_addr; + lp = (struct net_local *)dev->priv; + + /* we MUST read all the events out of the ISQ, otherwise we'll never + get interrupted again. As a consequence, we can't have any limit + on the number of times we loop in the interrupt handler. The + hardware guarantees that eventually we'll run out of events. Of + course, if you're on a slow machine, and packets are arriving + faster than you can read them off, you're screwed. Hasta la + vista, baby! */ + while ((status = readword(dev, ISQ_PORT))) { + if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status); + switch(status & ISQ_EVENT_MASK) { + case ISQ_RECEIVER_EVENT: + /* Got a packet(s). */ + net_rx(dev); + break; + case ISQ_TRANSMITTER_EVENT: + lp->stats.tx_packets++; + netif_wake_queue(dev); /* Inform upper layers. */ + if ((status & ( TX_OK | + TX_LOST_CRS | + TX_SQE_ERROR | + TX_LATE_COL | + TX_16_COL)) != TX_OK) { + if ((status & TX_OK) == 0) lp->stats.tx_errors++; + if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++; + if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++; + if (status & TX_LATE_COL) lp->stats.tx_window_errors++; + if (status & TX_16_COL) lp->stats.tx_aborted_errors++; + } + break; + case ISQ_BUFFER_EVENT: + if (status & READY_FOR_TX) { + /* we tried to transmit a packet earlier, + but inexplicably ran out of buffers. + That shouldn't happen since we only ever + load one packet. Shrug. Do the right + thing anyway. */ + netif_wake_queue(dev); /* Inform upper layers. */ + } + if (status & TX_UNDERRUN) { + if (net_debug > 0) printk("%s: transmit underrun\n", dev->name); + lp->send_underrun++; + if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381; + else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL; + /* transmit cycle is done, although + frame wasn't transmitted - this + avoids having to wait for the upper + layers to timeout on us, in the + event of a tx underrun */ + netif_wake_queue(dev); /* Inform upper layers. */ + } +#if ALLOW_DMA + if (lp->use_dma && (status & RX_DMA)) { + int count = readreg(dev, PP_DmaFrameCnt); + while(count) { + if (net_debug > 5) + printk("%s: receiving %d DMA frames\n", dev->name, count); + if (net_debug > 2 && count >1) + printk("%s: receiving %d DMA frames\n", dev->name, count); + dma_rx(dev); + if (--count == 0) + count = readreg(dev, PP_DmaFrameCnt); + if (net_debug > 2 && count > 0) + printk("%s: continuing with %d DMA frames\n", dev->name, count); + } + } +#endif + break; + case ISQ_RX_MISS_EVENT: + lp->stats.rx_missed_errors += (status >>6); + break; + case ISQ_TX_COL_EVENT: + lp->stats.collisions += (status >>6); + break; + } + } +} + +static void +count_rx_errors(int status, struct net_local *lp) +{ + lp->stats.rx_errors++; + if (status & RX_RUNT) lp->stats.rx_length_errors++; + if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++; + if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT))) + /* per str 172 */ + lp->stats.rx_crc_errors++; + if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++; + return; +} + +/* We have a good packet(s), get it/them out of the buffers. */ +static void +net_rx(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + struct sk_buff *skb; + int status, length; + + int ioaddr = dev->base_addr; + status = inw(ioaddr + RX_FRAME_PORT); + length = inw(ioaddr + RX_FRAME_PORT); + + if ((status & RX_OK) == 0) { + count_rx_errors(status, lp); + return; + } + + /* Malloc up new buffer. */ + skb = dev_alloc_skb(length + 2); + if (skb == NULL) { +#if 0 /* Again, this seems a cruel thing to do */ + printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); +#endif + lp->stats.rx_dropped++; + return; + } + skb_reserve(skb, 2); /* longword align L3 header */ + skb->dev = dev; + + insw(ioaddr + RX_FRAME_PORT, skb_put(skb, length), length >> 1); + if (length & 1) + skb->data[length-1] = inw(ioaddr + RX_FRAME_PORT); + + if (net_debug > 3) { + printk( "%s: received %d byte packet of type %x\n", + dev->name, length, + (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]); + } + + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + dev->last_rx = jiffies; + lp->stats.rx_packets++; + lp->stats.rx_bytes += length; +} + +#if ALLOW_DMA +static void release_dma_buff(struct net_local *lp) +{ + if (lp->dma_buff) { + free_pages((unsigned long)(lp->dma_buff), get_order(lp->dmasize * 1024)); + lp->dma_buff = 0; + } +} +#endif + +/* The inverse routine to net_open(). */ +static int +net_close(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + netif_stop_queue(dev); + + writereg(dev, PP_RxCFG, 0); + writereg(dev, PP_TxCFG, 0); + writereg(dev, PP_BufCFG, 0); + writereg(dev, PP_BusCTL, 0); + + free_irq(dev->irq, dev); + +#if ALLOW_DMA + if (lp->use_dma && lp->dma) { + free_dma(dev->dma); + release_dma_buff(lp); + } +#endif + + /* Update the statistics here. */ + return 0; +} + +/* Get the current statistics. This may be called with the card open or + closed. */ +static struct net_device_stats * +net_get_stats(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + unsigned long flags; + + spin_lock_irqsave(&lp->lock, flags); + /* Update the statistics from the device registers. */ + lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6); + lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6); + spin_unlock_irqrestore(&lp->lock, flags); + + return &lp->stats; +} + +static void set_multicast_list(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + unsigned long flags; + + spin_lock_irqsave(&lp->lock, flags); + if(dev->flags&IFF_PROMISC) + { + lp->rx_mode = RX_ALL_ACCEPT; + } + else if((dev->flags&IFF_ALLMULTI)||dev->mc_list) + { + /* The multicast-accept list is initialized to accept-all, and we + rely on higher-level filtering for now. */ + lp->rx_mode = RX_MULTCAST_ACCEPT; + } + else + lp->rx_mode = 0; + + writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode); + + /* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */ + writereg(dev, PP_RxCFG, lp->curr_rx_cfg | + (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0)); + spin_unlock_irqrestore(&lp->lock, flags); +} + + +static int set_mac_address(struct net_device *dev, void *addr) +{ + int i; + + if (netif_running(dev)) + return -EBUSY; + if (net_debug) { + printk("%s: Setting MAC address to ", dev->name); + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]); + printk(".\n"); + } + /* set the Ethernet address */ + for (i=0; i < ETH_ALEN/2; i++) + writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); + + return 0; +} + +#ifdef MODULE + +static struct net_device dev_cs89x0 = { + "", + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL }; + +/* + * Support the 'debug' module parm even if we're compiled for non-debug to + * avoid breaking someone's startup scripts + */ + +static int io; +static int irq; +static int debug; +static char media[8]; +static int duplex=-1; + +static int use_dma; /* These generate unused var warnings if ALLOW_DMA = 0 */ +static int dma; +static int dmasize=16; /* or 64 */ + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(media, "c8"); +MODULE_PARM(duplex, "i"); +MODULE_PARM(dma , "i"); +MODULE_PARM(dmasize , "i"); +MODULE_PARM(use_dma , "i"); +MODULE_PARM_DESC(io, "cs89x0 I/O base address"); +MODULE_PARM_DESC(irq, "cs89x0 IRQ number"); +#if DEBUGGING +MODULE_PARM_DESC(debug, "cs89x0 debug level (0-6)"); +#else +MODULE_PARM_DESC(debug, "(ignored)"); +#endif +MODULE_PARM_DESC(media, "Set cs89x0 adapter(s) media type(s) (rj45,bnc,aui)"); +/* No other value than -1 for duplex seems to be currently interpreted */ +MODULE_PARM_DESC(duplex, "(ignored)"); +#if ALLOW_DMA +MODULE_PARM_DESC(dma , "cs89x0 ISA DMA channel; ignored if use_dma=0"); +MODULE_PARM_DESC(dmasize , "cs89x0 DMA size in kB (16,64); ignored if use_dma=0"); +MODULE_PARM_DESC(use_dma , "cs89x0 using DMA (0-1)"); +#else +MODULE_PARM_DESC(dma , "(ignored)"); +MODULE_PARM_DESC(dmasize , "(ignored)"); +MODULE_PARM_DESC(use_dma , "(ignored)"); +#endif + +MODULE_AUTHOR("Mike Cruse, Russwll Nelson , Andrew Morton "); +MODULE_LICENSE("GPL"); + + +EXPORT_NO_SYMBOLS; + +/* +* media=t - specify media type + or media=2 + or media=aui + or medai=auto +* duplex=0 - specify forced half/full/autonegotiate duplex +* debug=# - debug level + + +* Default Chip Configuration: + * DMA Burst = enabled + * IOCHRDY Enabled = enabled + * UseSA = enabled + * CS8900 defaults to half-duplex if not specified on command-line + * CS8920 defaults to autoneg if not specified on command-line + * Use reset defaults for other config parameters + +* Assumptions: + * media type specified is supported (circuitry is present) + * if memory address is > 1MB, then required mem decode hw is present + * if 10B-2, then agent other than driver will enable DC/DC converter + (hw or software util) + + +*/ + +int +init_module(void) +{ + struct net_local *lp; + int ret = 0; + +#if DEBUGGING + net_debug = debug; +#else + debug = 0; +#endif + + dev_cs89x0.irq = irq; + dev_cs89x0.base_addr = io; + + dev_cs89x0.init = cs89x0_probe; + dev_cs89x0.priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + if (dev_cs89x0.priv == 0) { + printk(KERN_ERR "cs89x0.c: Out of memory.\n"); + return -ENOMEM; + } + memset(dev_cs89x0.priv, 0, sizeof(struct net_local)); + lp = (struct net_local *)dev_cs89x0.priv; + +#if ALLOW_DMA + if (use_dma) { + lp->use_dma = use_dma; + lp->dma = dma; + lp->dmasize = dmasize; + } +#endif + + spin_lock_init(&lp->lock); + + /* boy, they'd better get these right */ + if (!strcmp(media, "rj45")) + lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T; + else if (!strcmp(media, "aui")) + lp->adapter_cnf = A_CNF_MEDIA_AUI | A_CNF_AUI; + else if (!strcmp(media, "bnc")) + lp->adapter_cnf = A_CNF_MEDIA_10B_2 | A_CNF_10B_2; + else + lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T; + + if (duplex==-1) + lp->auto_neg_cnf = AUTO_NEG_ENABLE; + + if (io == 0) { + printk(KERN_ERR "cs89x0.c: Module autoprobing not allowed.\n"); + printk(KERN_ERR "cs89x0.c: Append io=0xNNN\n"); + ret = -EPERM; + goto out; + } + +#if ALLOW_DMA + if (use_dma && dmasize != 16 && dmasize != 64) { + printk(KERN_ERR "cs89x0.c: dma size must be either 16K or 64K, not %dK\n", dmasize); + ret = -EPERM; + goto out; + } +#endif + + if (register_netdev(&dev_cs89x0) != 0) { + printk(KERN_ERR "cs89x0.c: No card found at 0x%x\n", io); + ret = -ENXIO; + goto out; + } +out: + if (ret) + kfree(dev_cs89x0.priv); + return ret; +} + +void +cleanup_module(void) +{ + if (dev_cs89x0.priv != NULL) { + /* Free up the private structure, or leak memory :-) */ + unregister_netdev(&dev_cs89x0); + outw(PP_ChipID, dev_cs89x0.base_addr + ADD_PORT); + kfree(dev_cs89x0.priv); + dev_cs89x0.priv = NULL; /* gets re-allocated by cs89x0_probe1 */ + /* If we don't do this, we can't re-insmod it later. */ + release_region(dev_cs89x0.base_addr, NETCARD_IO_EXTENT); + } +} +#endif /* MODULE */ + +/* + * Local variables: + * version-control: t + * kept-new-versions: 5 + * c-indent-level: 8 + * tab-width: 8 + * End: + * + */ diff -urN linux-2.4.19-rmk7-pxa2/drivers/net/cs89x0.orig.h linux-2.4.19-rmk7-pxa2-dimm/drivers/net/cs89x0.orig.h --- linux-2.4.19-rmk7-pxa2/drivers/net/cs89x0.orig.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/net/cs89x0.orig.h Sat Aug 3 02:39:44 2002 @@ -0,0 +1,469 @@ +/* Copyright, 1988-1992, Russell Nelson, Crynwr Software + + 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, version 1. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#define PP_ChipID 0x0000 /* offset 0h -> Corp -ID */ + /* offset 2h -> Model/Product Number */ + /* offset 3h -> Chip Revision Number */ + +#define PP_ISAIOB 0x0020 /* IO base address */ +#define PP_CS8900_ISAINT 0x0022 /* ISA interrupt select */ +#define PP_CS8920_ISAINT 0x0370 /* ISA interrupt select */ +#define PP_CS8900_ISADMA 0x0024 /* ISA Rec DMA channel */ +#define PP_CS8920_ISADMA 0x0374 /* ISA Rec DMA channel */ +#define PP_ISASOF 0x0026 /* ISA DMA offset */ +#define PP_DmaFrameCnt 0x0028 /* ISA DMA Frame count */ +#define PP_DmaByteCnt 0x002A /* ISA DMA Byte count */ +#define PP_CS8900_ISAMemB 0x002C /* Memory base */ +#define PP_CS8920_ISAMemB 0x0348 /* */ + +#define PP_ISABootBase 0x0030 /* Boot Prom base */ +#define PP_ISABootMask 0x0034 /* Boot Prom Mask */ + +/* EEPROM data and command registers */ +#define PP_EECMD 0x0040 /* NVR Interface Command register */ +#define PP_EEData 0x0042 /* NVR Interface Data Register */ +#define PP_DebugReg 0x0044 /* Debug Register */ + +#define PP_RxCFG 0x0102 /* Rx Bus config */ +#define PP_RxCTL 0x0104 /* Receive Control Register */ +#define PP_TxCFG 0x0106 /* Transmit Config Register */ +#define PP_TxCMD 0x0108 /* Transmit Command Register */ +#define PP_BufCFG 0x010A /* Bus configuration Register */ +#define PP_LineCTL 0x0112 /* Line Config Register */ +#define PP_SelfCTL 0x0114 /* Self Command Register */ +#define PP_BusCTL 0x0116 /* ISA bus control Register */ +#define PP_TestCTL 0x0118 /* Test Register */ +#define PP_AutoNegCTL 0x011C /* Auto Negotiation Ctrl */ + +#define PP_ISQ 0x0120 /* Interrupt Status */ +#define PP_RxEvent 0x0124 /* Rx Event Register */ +#define PP_TxEvent 0x0128 /* Tx Event Register */ +#define PP_BufEvent 0x012C /* Bus Event Register */ +#define PP_RxMiss 0x0130 /* Receive Miss Count */ +#define PP_TxCol 0x0132 /* Transmit Collision Count */ +#define PP_LineST 0x0134 /* Line State Register */ +#define PP_SelfST 0x0136 /* Self State register */ +#define PP_BusST 0x0138 /* Bus Status */ +#define PP_TDR 0x013C /* Time Domain Reflectometry */ +#define PP_AutoNegST 0x013E /* Auto Neg Status */ +#define PP_TxCommand 0x0144 /* Tx Command */ +#define PP_TxLength 0x0146 /* Tx Length */ +#define PP_LAF 0x0150 /* Hash Table */ +#define PP_IA 0x0158 /* Physical Address Register */ + +#define PP_RxStatus 0x0400 /* Receive start of frame */ +#define PP_RxLength 0x0402 /* Receive Length of frame */ +#define PP_RxFrame 0x0404 /* Receive frame pointer */ +#define PP_TxFrame 0x0A00 /* Transmit frame pointer */ + +/* Primary I/O Base Address. If no I/O base is supplied by the user, then this */ +/* can be used as the default I/O base to access the PacketPage Area. */ +#define DEFAULTIOBASE 0x0300 +#define FIRST_IO 0x020C /* First I/O port to check */ +#define LAST_IO 0x037C /* Last I/O port to check (+10h) */ +#define ADD_MASK 0x3000 /* Mask it use of the ADD_PORT register */ +#define ADD_SIG 0x3000 /* Expected ID signature */ + +/* On Macs, we only need use the ISA I/O stuff until we do MEMORY_ON */ +#ifdef CONFIG_MAC +#define LCSLOTBASE 0xfee00000 +#define MMIOBASE 0x40000 +#endif + +#define CHIP_EISA_ID_SIG 0x630E /* Product ID Code for Crystal Chip (CS8900 spec 4.3) */ + +#ifdef IBMEIPKT +#define EISA_ID_SIG 0x4D24 /* IBM */ +#define PART_NO_SIG 0x1010 /* IBM */ +#define MONGOOSE_BIT 0x0000 /* IBM */ +#else +#define EISA_ID_SIG 0x630E /* PnP Vendor ID (same as chip id for Crystal board) */ +#define PART_NO_SIG 0x4000 /* ID code CS8920 board (PnP Vendor Product code) */ +#define MONGOOSE_BIT 0x2000 /* PART_NO_SIG + MONGOOSE_BUT => ID of mongoose */ +#endif + +#define PRODUCT_ID_ADD 0x0002 /* Address of product ID */ + +/* Mask to find out the types of registers */ +#define REG_TYPE_MASK 0x001F + +/* Eeprom Commands */ +#define ERSE_WR_ENBL 0x00F0 +#define ERSE_WR_DISABLE 0x0000 + +/* Defines Control/Config register quintuplet numbers */ +#define RX_BUF_CFG 0x0003 +#define RX_CONTROL 0x0005 +#define TX_CFG 0x0007 +#define TX_COMMAND 0x0009 +#define BUF_CFG 0x000B +#define LINE_CONTROL 0x0013 +#define SELF_CONTROL 0x0015 +#define BUS_CONTROL 0x0017 +#define TEST_CONTROL 0x0019 + +/* Defines Status/Count registers quintuplet numbers */ +#define RX_EVENT 0x0004 +#define TX_EVENT 0x0008 +#define BUF_EVENT 0x000C +#define RX_MISS_COUNT 0x0010 +#define TX_COL_COUNT 0x0012 +#define LINE_STATUS 0x0014 +#define SELF_STATUS 0x0016 +#define BUS_STATUS 0x0018 +#define TDR 0x001C + +/* PP_RxCFG - Receive Configuration and Interrupt Mask bit definition - Read/write */ +#define SKIP_1 0x0040 +#define RX_STREAM_ENBL 0x0080 +#define RX_OK_ENBL 0x0100 +#define RX_DMA_ONLY 0x0200 +#define AUTO_RX_DMA 0x0400 +#define BUFFER_CRC 0x0800 +#define RX_CRC_ERROR_ENBL 0x1000 +#define RX_RUNT_ENBL 0x2000 +#define RX_EXTRA_DATA_ENBL 0x4000 + +/* PP_RxCTL - Receive Control bit definition - Read/write */ +#define RX_IA_HASH_ACCEPT 0x0040 +#define RX_PROM_ACCEPT 0x0080 +#define RX_OK_ACCEPT 0x0100 +#define RX_MULTCAST_ACCEPT 0x0200 +#define RX_IA_ACCEPT 0x0400 +#define RX_BROADCAST_ACCEPT 0x0800 +#define RX_BAD_CRC_ACCEPT 0x1000 +#define RX_RUNT_ACCEPT 0x2000 +#define RX_EXTRA_DATA_ACCEPT 0x4000 +#define RX_ALL_ACCEPT (RX_PROM_ACCEPT|RX_BAD_CRC_ACCEPT|RX_RUNT_ACCEPT|RX_EXTRA_DATA_ACCEPT) +/* Default receive mode - individually addressed, broadcast, and error free */ +#define DEF_RX_ACCEPT (RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT) + +/* PP_TxCFG - Transmit Configuration Interrupt Mask bit definition - Read/write */ +#define TX_LOST_CRS_ENBL 0x0040 +#define TX_SQE_ERROR_ENBL 0x0080 +#define TX_OK_ENBL 0x0100 +#define TX_LATE_COL_ENBL 0x0200 +#define TX_JBR_ENBL 0x0400 +#define TX_ANY_COL_ENBL 0x0800 +#define TX_16_COL_ENBL 0x8000 + +/* PP_TxCMD - Transmit Command bit definition - Read-only */ +#define TX_START_4_BYTES 0x0000 +#define TX_START_64_BYTES 0x0040 +#define TX_START_128_BYTES 0x0080 +#define TX_START_ALL_BYTES 0x00C0 +#define TX_FORCE 0x0100 +#define TX_ONE_COL 0x0200 +#define TX_TWO_PART_DEFF_DISABLE 0x0400 +#define TX_NO_CRC 0x1000 +#define TX_RUNT 0x2000 + +/* PP_BufCFG - Buffer Configuration Interrupt Mask bit definition - Read/write */ +#define GENERATE_SW_INTERRUPT 0x0040 +#define RX_DMA_ENBL 0x0080 +#define READY_FOR_TX_ENBL 0x0100 +#define TX_UNDERRUN_ENBL 0x0200 +#define RX_MISS_ENBL 0x0400 +#define RX_128_BYTE_ENBL 0x0800 +#define TX_COL_COUNT_OVRFLOW_ENBL 0x1000 +#define RX_MISS_COUNT_OVRFLOW_ENBL 0x2000 +#define RX_DEST_MATCH_ENBL 0x8000 + +/* PP_LineCTL - Line Control bit definition - Read/write */ +#define SERIAL_RX_ON 0x0040 +#define SERIAL_TX_ON 0x0080 +#define AUI_ONLY 0x0100 +#define AUTO_AUI_10BASET 0x0200 +#define MODIFIED_BACKOFF 0x0800 +#define NO_AUTO_POLARITY 0x1000 +#define TWO_PART_DEFDIS 0x2000 +#define LOW_RX_SQUELCH 0x4000 + +/* PP_SelfCTL - Software Self Control bit definition - Read/write */ +#define POWER_ON_RESET 0x0040 +#define SW_STOP 0x0100 +#define SLEEP_ON 0x0200 +#define AUTO_WAKEUP 0x0400 +#define HCB0_ENBL 0x1000 +#define HCB1_ENBL 0x2000 +#define HCB0 0x4000 +#define HCB1 0x8000 + +/* PP_BusCTL - ISA Bus Control bit definition - Read/write */ +#define RESET_RX_DMA 0x0040 +#define MEMORY_ON 0x0400 +#define DMA_BURST_MODE 0x0800 +#define IO_CHANNEL_READY_ON 0x1000 +#define RX_DMA_SIZE_64K 0x2000 +#define ENABLE_IRQ 0x8000 + +/* PP_TestCTL - Test Control bit definition - Read/write */ +#define LINK_OFF 0x0080 +#define ENDEC_LOOPBACK 0x0200 +#define AUI_LOOPBACK 0x0400 +#define BACKOFF_OFF 0x0800 +#define FDX_8900 0x4000 +#define FAST_TEST 0x8000 + +/* PP_RxEvent - Receive Event Bit definition - Read-only */ +#define RX_IA_HASHED 0x0040 +#define RX_DRIBBLE 0x0080 +#define RX_OK 0x0100 +#define RX_HASHED 0x0200 +#define RX_IA 0x0400 +#define RX_BROADCAST 0x0800 +#define RX_CRC_ERROR 0x1000 +#define RX_RUNT 0x2000 +#define RX_EXTRA_DATA 0x4000 + +#define HASH_INDEX_MASK 0x0FC00 + +/* PP_TxEvent - Transmit Event Bit definition - Read-only */ +#define TX_LOST_CRS 0x0040 +#define TX_SQE_ERROR 0x0080 +#define TX_OK 0x0100 +#define TX_LATE_COL 0x0200 +#define TX_JBR 0x0400 +#define TX_16_COL 0x8000 +#define TX_SEND_OK_BITS (TX_OK|TX_LOST_CRS) +#define TX_COL_COUNT_MASK 0x7800 + +/* PP_BufEvent - Buffer Event Bit definition - Read-only */ +#define SW_INTERRUPT 0x0040 +#define RX_DMA 0x0080 +#define READY_FOR_TX 0x0100 +#define TX_UNDERRUN 0x0200 +#define RX_MISS 0x0400 +#define RX_128_BYTE 0x0800 +#define TX_COL_OVRFLW 0x1000 +#define RX_MISS_OVRFLW 0x2000 +#define RX_DEST_MATCH 0x8000 + +/* PP_LineST - Ethernet Line Status bit definition - Read-only */ +#define LINK_OK 0x0080 +#define AUI_ON 0x0100 +#define TENBASET_ON 0x0200 +#define POLARITY_OK 0x1000 +#define CRS_OK 0x4000 + +/* PP_SelfST - Chip Software Status bit definition */ +#define ACTIVE_33V 0x0040 +#define INIT_DONE 0x0080 +#define SI_BUSY 0x0100 +#define EEPROM_PRESENT 0x0200 +#define EEPROM_OK 0x0400 +#define EL_PRESENT 0x0800 +#define EE_SIZE_64 0x1000 + +/* PP_BusST - ISA Bus Status bit definition */ +#define TX_BID_ERROR 0x0080 +#define READY_FOR_TX_NOW 0x0100 + +/* PP_AutoNegCTL - Auto Negotiation Control bit definition */ +#define RE_NEG_NOW 0x0040 +#define ALLOW_FDX 0x0080 +#define AUTO_NEG_ENABLE 0x0100 +#define NLP_ENABLE 0x0200 +#define FORCE_FDX 0x8000 +#define AUTO_NEG_BITS (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE) +#define AUTO_NEG_MASK (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE|ALLOW_FDX|RE_NEG_NOW) + +/* PP_AutoNegST - Auto Negotiation Status bit definition */ +#define AUTO_NEG_BUSY 0x0080 +#define FLP_LINK 0x0100 +#define FLP_LINK_GOOD 0x0800 +#define LINK_FAULT 0x1000 +#define HDX_ACTIVE 0x4000 +#define FDX_ACTIVE 0x8000 + +/* The following block defines the ISQ event types */ +#define ISQ_RECEIVER_EVENT 0x04 +#define ISQ_TRANSMITTER_EVENT 0x08 +#define ISQ_BUFFER_EVENT 0x0c +#define ISQ_RX_MISS_EVENT 0x10 +#define ISQ_TX_COL_EVENT 0x12 + +#define ISQ_EVENT_MASK 0x003F /* ISQ mask to find out type of event */ +#define ISQ_HIST 16 /* small history buffer */ +#define AUTOINCREMENT 0x8000 /* Bit mask to set bit-15 for autoincrement */ + +#define TXRXBUFSIZE 0x0600 +#define RXDMABUFSIZE 0x8000 +#define RXDMASIZE 0x4000 +#define TXRX_LENGTH_MASK 0x07FF + +/* rx options bits */ +#define RCV_WITH_RXON 1 /* Set SerRx ON */ +#define RCV_COUNTS 2 /* Use Framecnt1 */ +#define RCV_PONG 4 /* Pong respondent */ +#define RCV_DONG 8 /* Dong operation */ +#define RCV_POLLING 0x10 /* Poll RxEvent */ +#define RCV_ISQ 0x20 /* Use ISQ, int */ +#define RCV_AUTO_DMA 0x100 /* Set AutoRxDMAE */ +#define RCV_DMA 0x200 /* Set RxDMA only */ +#define RCV_DMA_ALL 0x400 /* Copy all DMA'ed */ +#define RCV_FIXED_DATA 0x800 /* Every frame same */ +#define RCV_IO 0x1000 /* Use ISA IO only */ +#define RCV_MEMORY 0x2000 /* Use ISA Memory */ + +#define RAM_SIZE 0x1000 /* The card has 4k bytes or RAM */ +#define PKT_START PP_TxFrame /* Start of packet RAM */ + +#define RX_FRAME_PORT 0x0000 +#define TX_FRAME_PORT RX_FRAME_PORT +#define TX_CMD_PORT 0x0004 +#define TX_NOW 0x0000 /* Tx packet after 5 bytes copied */ +#define TX_AFTER_381 0x0040 /* Tx packet after 381 bytes copied */ +#define TX_AFTER_ALL 0x00c0 /* Tx packet after all bytes copied */ +#define TX_LEN_PORT 0x0006 +#define ISQ_PORT 0x0008 +#define ADD_PORT 0x000A +#define DATA_PORT 0x000C + +#define EEPROM_WRITE_EN 0x00F0 +#define EEPROM_WRITE_DIS 0x0000 +#define EEPROM_WRITE_CMD 0x0100 +#define EEPROM_READ_CMD 0x0200 + +/* Receive Header */ +/* Description of header of each packet in receive area of memory */ +#define RBUF_EVENT_LOW 0 /* Low byte of RxEvent - status of received frame */ +#define RBUF_EVENT_HIGH 1 /* High byte of RxEvent - status of received frame */ +#define RBUF_LEN_LOW 2 /* Length of received data - low byte */ +#define RBUF_LEN_HI 3 /* Length of received data - high byte */ +#define RBUF_HEAD_LEN 4 /* Length of this header */ + +#define CHIP_READ 0x1 /* Used to mark state of the repins code (chip or dma) */ +#define DMA_READ 0x2 /* Used to mark state of the repins code (chip or dma) */ + +/* for bios scan */ +/* */ +#ifdef CSDEBUG +/* use these values for debugging bios scan */ +#define BIOS_START_SEG 0x00000 +#define BIOS_OFFSET_INC 0x0010 +#else +#define BIOS_START_SEG 0x0c000 +#define BIOS_OFFSET_INC 0x0200 +#endif + +#define BIOS_LAST_OFFSET 0x0fc00 + +/* Byte offsets into the EEPROM configuration buffer */ +#define ISA_CNF_OFFSET 0x6 +#define TX_CTL_OFFSET (ISA_CNF_OFFSET + 8) /* 8900 eeprom */ +#define AUTO_NEG_CNF_OFFSET (ISA_CNF_OFFSET + 8) /* 8920 eeprom */ + + /* the assumption here is that the bits in the eeprom are generally */ + /* in the same position as those in the autonegctl register. */ + /* Of course the IMM bit is not in that register so it must be */ + /* masked out */ +#define EE_FORCE_FDX 0x8000 +#define EE_NLP_ENABLE 0x0200 +#define EE_AUTO_NEG_ENABLE 0x0100 +#define EE_ALLOW_FDX 0x0080 +#define EE_AUTO_NEG_CNF_MASK (EE_FORCE_FDX|EE_NLP_ENABLE|EE_AUTO_NEG_ENABLE|EE_ALLOW_FDX) + +#define IMM_BIT 0x0040 /* ignore missing media */ + +#define ADAPTER_CNF_OFFSET (AUTO_NEG_CNF_OFFSET + 2) +#define A_CNF_10B_T 0x0001 +#define A_CNF_AUI 0x0002 +#define A_CNF_10B_2 0x0004 +#define A_CNF_MEDIA_TYPE 0x0060 +#define A_CNF_MEDIA_AUTO 0x0000 +#define A_CNF_MEDIA_10B_T 0x0020 +#define A_CNF_MEDIA_AUI 0x0040 +#define A_CNF_MEDIA_10B_2 0x0060 +#define A_CNF_DC_DC_POLARITY 0x0080 +#define A_CNF_NO_AUTO_POLARITY 0x2000 +#define A_CNF_LOW_RX_SQUELCH 0x4000 +#define A_CNF_EXTND_10B_2 0x8000 + +#define PACKET_PAGE_OFFSET 0x8 + +/* Bit definitions for the ISA configuration word from the EEPROM */ +#define INT_NO_MASK 0x000F +#define DMA_NO_MASK 0x0070 +#define ISA_DMA_SIZE 0x0200 +#define ISA_AUTO_RxDMA 0x0400 +#define ISA_RxDMA 0x0800 +#define DMA_BURST 0x1000 +#define STREAM_TRANSFER 0x2000 +#define ANY_ISA_DMA (ISA_AUTO_RxDMA | ISA_RxDMA) + +/* DMA controller registers */ +#define DMA_BASE 0x00 /* DMA controller base */ +#define DMA_BASE_2 0x0C0 /* DMA controller base */ + +#define DMA_STAT 0x0D0 /* DMA controller status register */ +#define DMA_MASK 0x0D4 /* DMA controller mask register */ +#define DMA_MODE 0x0D6 /* DMA controller mode register */ +#define DMA_RESETFF 0x0D8 /* DMA controller first/last flip flop */ + +/* DMA data */ +#define DMA_DISABLE 0x04 /* Disable channel n */ +#define DMA_ENABLE 0x00 /* Enable channel n */ +/* Demand transfers, incr. address, auto init, writes, ch. n */ +#define DMA_RX_MODE 0x14 +/* Demand transfers, incr. address, auto init, reads, ch. n */ +#define DMA_TX_MODE 0x18 + +#define DMA_SIZE (16*1024) /* Size of dma buffer - 16k */ + +#define CS8900 0x0000 +#define CS8920 0x4000 +#define CS8920M 0x6000 +#define REVISON_BITS 0x1F00 +#define EEVER_NUMBER 0x12 +#define CHKSUM_LEN 0x14 +#define CHKSUM_VAL 0x0000 +#define START_EEPROM_DATA 0x001c /* Offset into eeprom for start of data */ +#define IRQ_MAP_EEPROM_DATA 0x0046 /* Offset into eeprom for the IRQ map */ +#define IRQ_MAP_LEN 0x0004 /* No of bytes to read for the IRQ map */ +#define PNP_IRQ_FRMT 0x0022 /* PNP small item IRQ format */ +#ifdef CONFIG_SH_HICOSH4 +#define CS8900_IRQ_MAP 0x0002 /* HiCO-SH4 board has its IRQ on #1 */ +#else +#define CS8900_IRQ_MAP 0x1c20 /* This IRQ map is fixed */ +#endif + +#define CS8920_NO_INTS 0x0F /* Max CS8920 interrupt select # */ + +#define PNP_ADD_PORT 0x0279 +#define PNP_WRITE_PORT 0x0A79 + +#define GET_PNP_ISA_STRUCT 0x40 +#define PNP_ISA_STRUCT_LEN 0x06 +#define PNP_CSN_CNT_OFF 0x01 +#define PNP_RD_PORT_OFF 0x02 +#define PNP_FUNCTION_OK 0x00 +#define PNP_WAKE 0x03 +#define PNP_RSRC_DATA 0x04 +#define PNP_RSRC_READY 0x01 +#define PNP_STATUS 0x05 +#define PNP_ACTIVATE 0x30 +#define PNP_CNF_IO_H 0x60 +#define PNP_CNF_IO_L 0x61 +#define PNP_CNF_INT 0x70 +#define PNP_CNF_DMA 0x74 +#define PNP_CNF_MEM 0x48 + +#define BIT0 1 +#define BIT15 0x8000 + diff -urN linux-2.4.19-rmk7-pxa2/drivers/net/ne.c linux-2.4.19-rmk7-pxa2-dimm/drivers/net/ne.c --- linux-2.4.19-rmk7-pxa2/drivers/net/ne.c Sat Aug 3 02:39:44 2002 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/net/ne.c Sat Oct 4 15:27:46 2003 @@ -8,8 +8,9 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 This driver should work with many programmed-I/O 8390-based ethernet boards. Currently it supports the NE1000, NE2000, many clones, @@ -47,11 +48,13 @@ #include #include #include +#include #include #include #include #include + #include "8390.h" /* Some defines that people can play with if so inclined. */ @@ -75,17 +78,18 @@ }; #endif -static struct isapnp_device_id isapnp_clone_list[] __initdata = { +static struct isapnp_device_id +isapnp_clone_list[] __initdata = { { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('E','D','I'), ISAPNP_FUNCTION(0x0216), - (long) "NN NE2000" }, + (unsigned long)"NN NE2000" }, { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P','N','P'), ISAPNP_FUNCTION(0x80d6), - (long) "Generic PNP" }, - { } /* terminate list */ + (unsigned long)"Generic PNP" }, + {0} }; -MODULE_DEVICE_TABLE(isapnp, isapnp_clone_list); +MODULE_DEVICE_TABLE(isapnp, (struct isapnp_device_id *)isapnp_clone_list); #ifdef SUPPORT_NE_BAD_CLONES /* A list of bad clones that we none-the-less recognize. */ @@ -112,10 +116,10 @@ /* ---- No user-serviceable parts below ---- */ #define NE_BASE (dev->base_addr) -#define NE_CMD 0x00 -#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */ -#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */ -#define NE_IO_EXTENT 0x20 +#define NE_CMD EI_SHIFT(0x00) +#define NE_DATAPORT EI_SHIFT(0x10) /* NatSemi-defined port window offset. */ +#define NE_RESET EI_SHIFT(0x1f) /* Issue a read to reset, a write to clear. */ +#define NE_IO_EXTENT EI_SHIFT(0x20) #define NE1SM_START_PG 0x20 /* First page of TX buffer */ #define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */ @@ -137,7 +141,15 @@ static void ne_block_output(struct net_device *dev, const int count, const unsigned char *buf, const int start_page); - +static int __init init_mac_addr(char *str) +{ +// g_cs89x0_dma = simple_strtol(str,NULL,0); + printk("ne: init_mac_addr\n"); + return 1; +} + +__setup("mac", init_mac_addr); + /* Probe for various non-shared-memory ethercards. NEx000-clone boards have a Station Address PROM (SAPROM) in the packet @@ -209,12 +221,12 @@ /* found it */ dev->base_addr = idev->resource[0].start; dev->irq = idev->irq_resource[0].start; - printk(KERN_INFO "ne.c: ISAPnP reports %s at i/o %#lx, irq %d.\n", - (char *) isapnp_clone_list[i].driver_data, + printk(KERN_INFO "\n ne.c: ISAPnP reports %s at i/o %#lx, irq %d.\n", + (char*)isapnp_clone_list[i].driver_data, dev->base_addr, dev->irq); if (ne_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */ - printk(KERN_ERR "ne.c: Probe of ISAPnP card at %#lx failed.\n", dev->base_addr); + printk(KERN_ERR "\n ne.c: Probe of ISAPnP card at %#lx failed.\n", dev->base_addr); return -ENXIO; } ei_status.priv = (unsigned long)idev; @@ -237,7 +249,7 @@ int start_page, stop_page; int neX000, ctron, copam, bad_card; int reg0, ret; - static unsigned version_printed; + static unsigned version_printed = 0; if (!request_region(ioaddr, NE_IO_EXTENT, dev->name)) return -EBUSY; @@ -252,22 +264,22 @@ { int regd; outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); - regd = inb_p(ioaddr + 0x0d); - outb_p(0xff, ioaddr + 0x0d); + regd = inb_p(ioaddr + EN0_COUNTER0); + outb_p(0xff, ioaddr + EN0_COUNTER0); outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */ if (inb_p(ioaddr + EN0_COUNTER0) != 0) { outb_p(reg0, ioaddr); - outb_p(regd, ioaddr + 0x0d); /* Restore the old values. */ + outb_p(regd, ioaddr + EN0_COUNTER0); /* Restore the old values. */ ret = -ENODEV; goto err_out; } } if (ei_debug && version_printed++ == 0) - printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); + printk(KERN_INFO "\n %s" KERN_INFO "%s", version1, version2); - printk(KERN_INFO "NE*000 ethercard probe at %#3x:", ioaddr); + printk(KERN_INFO "\n NE*000 ethercard probe at %#3x:", ioaddr); /* A user with a poor card that fails to ack the reset, or that does not have a valid 0x57,0x57 signature can still use this @@ -288,10 +300,10 @@ while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0) if (jiffies - reset_start_time > 2*HZ/100) { if (bad_card) { - printk(" (warning: no reset ack)"); + printk(" \n (warning: no reset ack)"); break; } else { - printk(" not found (no reset ack).\n"); + printk("\n not found (no reset ack).\n"); ret = -ENODEV; goto err_out; } @@ -326,17 +338,32 @@ outb_p(program_seq[i].value, ioaddr + program_seq[i].offset); } - for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) { + + for(i = 0; i < 32 ; i+=2) { SA_prom[i] = inb(ioaddr + NE_DATAPORT); SA_prom[i+1] = inb(ioaddr + NE_DATAPORT); if (SA_prom[i] != SA_prom[i+1]) wordlength = 1; } + wordlength = 2; /* Added by Mark Lee */ + if (wordlength == 2) { for (i = 0; i < 16; i++) SA_prom[i] = SA_prom[i+i]; + +/* Added by Mark Lee +++++ */ + SA_prom[0] = 0x88; + SA_prom[1] = 0x79; + SA_prom[2] = 0x01; + SA_prom[3] = 0x01; + SA_prom[4] = 0x01; + SA_prom[5] = 0x01; + SA_prom[14] = 0x57; + SA_prom[15] = 0x57; +/* Added by Mark Lee ----- */ + /* We must set the 8390 for word mode. */ outb_p(0x49, ioaddr + EN0_DCFG); start_page = NESM_START_PG; @@ -382,13 +409,13 @@ } if (bad_clone_list[i].name8 == NULL) { - printk(" not found (invalid signature %2.2x %2.2x).\n", + printk("\n not found (invalid signature %2.2x %2.2x).\n", SA_prom[14], SA_prom[15]); ret = -ENXIO; goto err_out; } #else - printk(" not found.\n"); + printk("\n not found.\n"); ret = -ENXIO; goto err_out; #endif @@ -405,14 +432,14 @@ outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */ dev->irq = probe_irq_off(cookie); if (ei_debug > 2) - printk(" autoirq is %d\n", dev->irq); + printk("\n autoirq is %d\n", dev->irq); } else if (dev->irq == 2) /* Fixup for users that don't know that IRQ 2 is really IRQ 9, or don't know which one to set. */ dev->irq = 9; if (! dev->irq) { - printk(" failed to detect IRQ line.\n"); + printk("\n failed to detect IRQ line.\n"); ret = -EAGAIN; goto err_out; } @@ -481,7 +508,7 @@ static int ne_close(struct net_device *dev) { if (ei_debug > 1) - printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name); + printk(KERN_DEBUG "\n%s: Shutting down ethercard.\n", dev->name); ei_close(dev); return 0; } @@ -494,7 +521,7 @@ unsigned long reset_start_time = jiffies; if (ei_debug > 1) - printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies); + printk(KERN_DEBUG "\n resetting the 8390 t=%ld...", jiffies); /* DON'T change these to inb_p/outb_p or reset will fail on clones. */ outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); @@ -505,7 +532,7 @@ /* This check _should_not_ be necessary, omit eventually. */ while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0) if (jiffies - reset_start_time > 2*HZ/100) { - printk(KERN_WARNING "%s: ne_reset_8390() did not complete.\n", dev->name); + printk(KERN_WARNING "\n %s: ne_reset_8390() did not complete.\n", dev->name); break; } outb_p(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */ @@ -523,7 +550,7 @@ if (ei_status.dmaing) { - printk(KERN_EMERG "%s: DMAing conflict in ne_get_8390_hdr " + printk(KERN_EMERG "\n%s: DMAing conflict in ne_get_8390_hdr " "[DMAstat:%d][irqlock:%d].\n", dev->name, ei_status.dmaing, ei_status.irqlock); return; @@ -564,7 +591,7 @@ /* This *shouldn't* happen. If it does, it's the last thing you'll see */ if (ei_status.dmaing) { - printk(KERN_EMERG "%s: DMAing conflict in ne_block_input " + printk(KERN_EMERG "\n%s: DMAing conflict in ne_block_input " "[DMAstat:%d][irqlock:%d].\n", dev->name, ei_status.dmaing, ei_status.irqlock); return; @@ -610,7 +637,7 @@ break; } while (--tries > 0); if (tries <= 0) - printk(KERN_WARNING "%s: RX transfer address mismatch," + printk(KERN_WARNING "\n %s: RX transfer address mismatch," "%#4.4x (expected) vs. %#4.4x (actual).\n", dev->name, ring_offset + xfer_count, addr); } @@ -638,7 +665,7 @@ /* This *shouldn't* happen. If it does, it's the last thing you'll see */ if (ei_status.dmaing) { - printk(KERN_EMERG "%s: DMAing conflict in ne_block_output." + printk(KERN_EMERG "\n%s: DMAing conflict in ne_block_output." "[DMAstat:%d][irqlock:%d]\n", dev->name, ei_status.dmaing, ei_status.irqlock); return; @@ -701,7 +728,7 @@ if (tries <= 0) { - printk(KERN_WARNING "%s: Tx packet transfer address mismatch," + printk(KERN_WARNING "\n%s: Tx packet transfer address mismatch," "%#4.4x (expected) vs. %#4.4x (actual).\n", dev->name, (start_page << 8) + count, addr); if (retries++ == 0) @@ -712,7 +739,7 @@ while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0) if (jiffies - dma_start > 2*HZ/100) { /* 20ms */ - printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name); + printk(KERN_WARNING "\n%s: timeout waiting for Tx RDC.\n", dev->name); ne_reset_8390(dev); NS8390_init(dev,1); break; @@ -734,11 +761,6 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); MODULE_PARM(bad, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); -MODULE_PARM_DESC(io, "I/O base address(es),required"); -MODULE_PARM_DESC(irq, "IRQ number(s)"); -MODULE_PARM_DESC(bad, "Accept card(s) with bad signatures"); -MODULE_DESCRIPTION("NE1000/NE2000 ISA/PnP Ethernet driver"); -MODULE_LICENSE("GPL"); /* This is set up so that no ISA autoprobe takes place. We can't guarantee that the ne2k probe is the last 8390 based probe to take place (as it @@ -763,9 +785,9 @@ return 0; } if (io[this_dev] != 0) - printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", io[this_dev]); + printk(KERN_WARNING "\nne.c: No NE*000 card found at i/o = %#x\n", io[this_dev]); else - printk(KERN_NOTICE "ne.c: You must supply \"io=0xNNN\" value(s) for ISA cards.\n"); + printk(KERN_NOTICE "\n ne.c: You must supply \"io=0xNNN\" value(s) for ISA cards.\n"); return -ENXIO; } return 0; @@ -790,7 +812,6 @@ } } #endif /* MODULE */ - /* * Local variables: diff -urN linux-2.4.19-rmk7-pxa2/drivers/pcmcia/pxa/lubbock.c linux-2.4.19-rmk7-pxa2-dimm/drivers/pcmcia/pxa/lubbock.c --- linux-2.4.19-rmk7-pxa2/drivers/pcmcia/pxa/lubbock.c Wed Nov 5 09:37:58 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/pcmcia/pxa/lubbock.c Wed Oct 1 16:14:18 2003 @@ -24,18 +24,24 @@ #include #include #include -#include +//#include /* * I'd really like to move the INTPOL stuff to arch/arm/mach-sa1100/sa1111.c * ... and maybe even arch/arm/mach-pxa/sa1111.c now too! : ) */ -#define SA1111_IRQMASK_LO(x) (1 << (x - IRQ_SA1111_START)) -#define SA1111_IRQMASK_HI(x) (1 << (x - IRQ_SA1111_START - 32)) +//#define SA1111_IRQMASK_LO(x) (1 << (x - IRQ_SA1111_START)) +//#define SA1111_IRQMASK_HI(x) (1 << (x - IRQ_SA1111_START - 32)) + +#define PCMCIA_SLOT0_IRQ_GPIO 10 +#define PCMCIA_SLOT1_IRQ_GPIO 11 static int lubbock_pcmcia_init(struct pcmcia_init *init){ int return_val=0; + printk("lubbock_pcmcia_init\n"); + +#if 0 /* Set PCMCIA Socket 0 power to standby mode. */ PA_DWR &= ~(GPIO_bit(0) | GPIO_bit(1) | GPIO_bit(2) | GPIO_bit(3)); @@ -68,12 +74,14 @@ "Lubbock PCMCIA (0) BVD1", NULL); return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, "Lubbock CF (1) BVD1", NULL); - - return (return_val<0) ? -1 : 2; +#endif +// return (return_val<0) ? -1 : 2; + return (return_val<0) ? -1 : 2; } static int lubbock_pcmcia_shutdown(void){ - + printk("lubbock_pcmcia_shutdown\n"); +#if 0 free_irq(S0_CD_VALID, NULL); free_irq(S1_CD_VALID, NULL); free_irq(S0_BVD1_STSCHG, NULL); @@ -83,62 +91,63 @@ SA1111_IRQMASK_HI(S1_CD_VALID) | SA1111_IRQMASK_HI(S0_BVD1_STSCHG) | SA1111_IRQMASK_HI(S1_BVD1_STSCHG)); - +#endif return 0; } static int lubbock_pcmcia_socket_state(struct pcmcia_state_array *state_array){ - unsigned long status; +// unsigned long status; int return_val=1; - if(state_array->size<2) return -1; + if(state_array->size<1) return -1; memset(state_array->state, 0, (state_array->size)*sizeof(struct pcmcia_state)); + +// status=PCSR; - status=PCSR; - - state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; + state_array->state[0].detect=1; //((status & PCSR_S0_DETECT)==0)?1:0; - state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; + state_array->state[0].ready=1; //((status & PCSR_S0_READY)==0)?0:1; - state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; + state_array->state[0].bvd1=1; //((status & PCSR_S0_BVD1)==0)?0:1; - state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; + state_array->state[0].bvd2=1; //((status & PCSR_S0_BVD2)==0)?0:1; - state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; + state_array->state[0].wrprot=0; //((status & PCSR_S0_WP)==0)?0:1; - state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; + state_array->state[0].vs_3v=1; //((status & PCSR_S0_VS1)==0)?1:0; - state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; + state_array->state[0].vs_Xv=0; //((status & PCSR_S0_VS2)==0)?1:0; - state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; + state_array->state[1].detect=1; //((status & PCSR_S1_DETECT)==0)?1:0; - state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; + state_array->state[1].ready=1; //((status & PCSR_S1_READY)==0)?0:1; - state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; + state_array->state[1].bvd1=1; //((status & PCSR_S1_BVD1)==0)?0:1; - state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; + state_array->state[1].bvd2=1; //((status & PCSR_S1_BVD2)==0)?0:1; - state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; + state_array->state[1].wrprot=0; //((status & PCSR_S1_WP)==0)?0:1; - state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; + state_array->state[1].vs_3v=1; //((status & PCSR_S1_VS1)==0)?1:0; - state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; + state_array->state[1].vs_Xv=0; //((status & PCSR_S1_VS2)==0)?1:0; return return_val; } static int lubbock_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + printk("lubbock_pcmcia_get_irq_info: %d\n", info->sock); switch(info->sock){ case 0: - info->irq=S0_READY_NINT; + info->irq = IRQ_GPIO(PCMCIA_SLOT0_IRQ_GPIO); break; case 1: - info->irq=S1_READY_NINT; + info->irq = IRQ_GPIO(PCMCIA_SLOT1_IRQ_GPIO); break; default: @@ -151,6 +160,8 @@ static int lubbock_pcmcia_configure_socket(unsigned int sock, socket_state_t *state) { + printk("lubbock_pcmcia_configure_socket: %d\n", sock); +#if 0 unsigned long flags, pccr, gpio, misc_wr, status; int ret=1; @@ -318,6 +329,8 @@ local_irq_restore(flags); return ret; +#endif + return 0; } struct pcmcia_low_level lubbock_pcmcia_ops = { diff -urN linux-2.4.19-rmk7-pxa2/drivers/pcmcia/pxa/lubbock.orig.c linux-2.4.19-rmk7-pxa2-dimm/drivers/pcmcia/pxa/lubbock.orig.c --- linux-2.4.19-rmk7-pxa2/drivers/pcmcia/pxa/lubbock.orig.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/pcmcia/pxa/lubbock.orig.c Mon Sep 8 12:11:28 2003 @@ -0,0 +1,329 @@ +/* + * linux/drivers/pcmcia/pxa/lubbock.c + * + * Author: George Davis + * Created: Jan 10, 2002 + * Copyright: MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Originally based upon linux/drivers/pcmcia/sa1100_neponset.c + * + * Lubbock PCMCIA specific routines. + * + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +/* + * I'd really like to move the INTPOL stuff to arch/arm/mach-sa1100/sa1111.c + * ... and maybe even arch/arm/mach-pxa/sa1111.c now too! : ) + */ +#define SA1111_IRQMASK_LO(x) (1 << (x - IRQ_SA1111_START)) +#define SA1111_IRQMASK_HI(x) (1 << (x - IRQ_SA1111_START - 32)) + +static int lubbock_pcmcia_init(struct pcmcia_init *init){ + int return_val=0; + + /* Set PCMCIA Socket 0 power to standby mode. + */ + PA_DWR &= ~(GPIO_bit(0) | GPIO_bit(1) | GPIO_bit(2) | GPIO_bit(3)); + + /* Set GPIO_A<3:0> to be outputs for PCMCIA (socket 0) power controller. + * Note that this is done only after first initializing GPIO_A<3:0> + * output state above to be certain that we drive signals to the same + * state as the pull-downs connected to these lines. The pull-downs are + * req'd to make sure PCMCIA power is OFF until we can get around to + * setting up the GPIO_A<3:0> state and direction. + */ + PA_DDR &= ~(GPIO_bit(0) | GPIO_bit(1) | GPIO_bit(2) | GPIO_bit(3)); + + /* Set CF Socket 1 power to standby mode. */ + LUB_MISC_WR &= ~(GPIO_bit(15) | GPIO_bit(14)); + + INTPOL1 |= SA1111_IRQMASK_HI(S0_READY_NINT) | + SA1111_IRQMASK_HI(S1_READY_NINT) | + SA1111_IRQMASK_HI(S0_CD_VALID) | + SA1111_IRQMASK_HI(S1_CD_VALID) | + SA1111_IRQMASK_HI(S0_BVD1_STSCHG) | + SA1111_IRQMASK_HI(S1_BVD1_STSCHG); + +#warning what if a request_irq fails? + return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, + "Lubbock PCMCIA (0) CD", NULL); + return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, + "Lubbock CF (1) CD", NULL); + return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "Lubbock PCMCIA (0) BVD1", NULL); + return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "Lubbock CF (1) BVD1", NULL); + + return (return_val<0) ? -1 : 2; +} + +static int lubbock_pcmcia_shutdown(void){ + + free_irq(S0_CD_VALID, NULL); + free_irq(S1_CD_VALID, NULL); + free_irq(S0_BVD1_STSCHG, NULL); + free_irq(S1_BVD1_STSCHG, NULL); + + INTPOL1 &= ~(SA1111_IRQMASK_HI(S0_CD_VALID) | + SA1111_IRQMASK_HI(S1_CD_VALID) | + SA1111_IRQMASK_HI(S0_BVD1_STSCHG) | + SA1111_IRQMASK_HI(S1_BVD1_STSCHG)); + + return 0; +} + +static int lubbock_pcmcia_socket_state(struct pcmcia_state_array + *state_array){ + unsigned long status; + int return_val=1; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + status=PCSR; + + state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; + + state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; + + state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; + + state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; + + state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; + + state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; + + state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; + + state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; + + state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; + + state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; + + state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; + + state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; + + state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; + + state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; + + return return_val; +} + +static int lubbock_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + + switch(info->sock){ + case 0: + info->irq=S0_READY_NINT; + break; + + case 1: + info->irq=S1_READY_NINT; + break; + + default: + return -1; + } + + return 0; +} + +static int +lubbock_pcmcia_configure_socket(unsigned int sock, socket_state_t *state) +{ + unsigned long flags, pccr, gpio, misc_wr, status; + int ret=1; + + local_irq_save(flags); + + pccr=PCCR; + gpio=PA_DWR; + misc_wr = LUB_MISC_WR; + + /* Lubbock uses the Maxim MAX1602, with the following connections: + * + * Socket 0 (PCMCIA): + * MAX1602 Lubbock Register + * Pin Signal + * ----- ------- ---------------------- + * A0VPP S0_PWR0 SA-1111 GPIO A<0> + * A1VPP S0_PWR1 SA-1111 GPIO A<1> + * A0VCC S0_PWR2 SA-1111 GPIO A<2> + * A1VCC S0_PWR3 SA-1111 GPIO A<3> + * VX VCC + * VY +3.3V + * 12IN +12V + * CODE +3.3V Cirrus Code, CODE = High (VY) + * + * Socket 1 (CF): + * MAX1602 Lubbock Register + * Pin Signal + * ----- ------- ---------------------- + * A0VPP GND VPP is not connected + * A1VPP GND VPP is not connected + * A0VCC S1_PWR0 MISC_WR<14> + * A1VCC S1_PWR0 MISC_WR<15> + * VX VCC + * VY +3.3V + * 12IN GND VPP is not connected + * CODE +3.3V Cirrus Code, CODE = High (VY) + * + */ + +again: + switch(sock){ + case 0: + + switch(state->Vcc){ + case 0: + pccr = (pccr & ~PCCR_S0_FLT); + gpio &= ~(GPIO_bit(2) | GPIO_bit(3)); + break; + + case 33: + pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN; + gpio = (gpio & ~(GPIO_bit(2) | GPIO_bit(3))) | GPIO_bit(3); + break; + + case 50: + pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); + gpio = (gpio & ~(GPIO_bit(2) | GPIO_bit(3))) | GPIO_bit(2); + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, state->Vcc); + ret = -1; + break; + } + + switch(state->Vpp){ + case 0: + gpio &= ~(GPIO_bit(0) | GPIO_bit(1)); + break; + + case 120: + gpio = (gpio & ~(GPIO_bit(0) | GPIO_bit(1))) | GPIO_bit(1); + break; + + default: + /* REVISIT: I'm not sure about this? Is this correct? + Is it always safe or do we have potential problems + with bogus combinations of Vcc and Vpp settings? */ + if(state->Vpp == state->Vcc) + gpio = (gpio & ~(GPIO_bit(0) | GPIO_bit(1))) | GPIO_bit(0); + else { + printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, state->Vpp); + ret = -1; + break; + } + } + + pccr = (state->flags&SS_RESET) ? (pccr|PCCR_S0_RST) : (pccr&~PCCR_S0_RST); + + break; + + case 1: + switch(state->Vcc){ + case 0: + pccr = (pccr & ~PCCR_S1_FLT); + misc_wr &= ~((1 << 15) | (1 << 14)); + break; + + case 33: + pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; + misc_wr = (misc_wr & ~(1 << 15)) | (1 << 14); + gpio = (gpio & ~(GPIO_bit(2) | GPIO_bit(3))) | GPIO_bit(2); + break; + + case 50: + pccr = (pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN); + misc_wr = (misc_wr & ~(1 << 15)) | (1 << 14); + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, state->Vcc); + ret = -1; + break; + } + + if(state->Vpp!=state->Vcc && state->Vpp!=0){ + printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, state->Vpp); + ret = -1; + break; + } + + pccr = (state->flags&SS_RESET) ? (pccr|PCCR_S1_RST) : (pccr&~PCCR_S1_RST); + + break; + + default: + ret = -1; + } + + if (ret >= 0) { + PCCR = pccr; + LUB_MISC_WR = misc_wr; + PA_DWR = gpio; + } + + if (ret > 0) { + ret = 0; + /* + * HACK ALERT: + * We can't sense the voltage properly on Lubbock before actually + * applying some power to the socket (catch 22). + * Resense the socket Voltage Sense pins after applying socket power. + */ + if (sock == 0) + status = PCSR & (PCSR_S0_VS1 | PCSR_S0_VS2); + else + status = PCSR & (PCSR_S1_VS1 | PCSR_S1_VS2); + + if ((status == (PCSR_S0_VS1 | PCSR_S0_VS2)) && (state->Vcc == 33)) { + /* Switch to 5V, Configure socket 0 with 5V voltage */ + PA_DWR &= ~(GPIO_bit(0) | GPIO_bit(1) | GPIO_bit(2) | GPIO_bit(3)); + PA_DDR &= ~(GPIO_bit(0) | GPIO_bit(1) | GPIO_bit(2) | GPIO_bit(3)); + state->Vcc = 50; + state->Vpp = 50; + goto again; + } + if ((status == (PCSR_S1_VS1 | PCSR_S1_VS2)) && (state->Vcc == 33)) { + /* Switch to 5V, Configure socket 1 with 5V voltage */ + LUB_MISC_WR &= ~((1 << 15) | (1 << 14)); + state->Vcc = 50; + state->Vpp = 50; + goto again; + } + } + + local_irq_restore(flags); + return ret; +} + +struct pcmcia_low_level lubbock_pcmcia_ops = { + lubbock_pcmcia_init, + lubbock_pcmcia_shutdown, + lubbock_pcmcia_socket_state, + lubbock_pcmcia_get_irq_info, + lubbock_pcmcia_configure_socket +}; diff -urN linux-2.4.19-rmk7-pxa2/drivers/sound/ac97_codec.c linux-2.4.19-rmk7-pxa2-dimm/drivers/sound/ac97_codec.c --- linux-2.4.19-rmk7-pxa2/drivers/sound/ac97_codec.c Wed Nov 5 09:37:58 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/sound/ac97_codec.c Fri Aug 22 16:16:50 2003 @@ -535,6 +535,10 @@ case SOUND_MIXER_CAPS: val = SOUND_CAP_EXCL_INPUT; break; + + case SOUND_MIXER_GENERAL_PURPOSE: + val = codec->codec_read(codec, AC97_GENERAL_PURPOSE); + break; default: /* read a specific mixer */ i = _IOC_NR(cmd); @@ -563,6 +567,16 @@ codec->recmask_io(codec, 0, val); + return 0; + case SOUND_MIXER_GENERAL_PURPOSE: + val = (((~val >> 16 ) & + codec->codec_read( + codec, AC97_GENERAL_PURPOSE)) | + val) & + 0xFFFF; + codec->codec_write( + codec, + AC97_GENERAL_PURPOSE, val); return 0; default: /* write a specific mixer */ i = _IOC_NR(cmd); diff -urN linux-2.4.19-rmk7-pxa2/drivers/sound/ac97_codec.orig.c linux-2.4.19-rmk7-pxa2-dimm/drivers/sound/ac97_codec.orig.c --- linux-2.4.19-rmk7-pxa2/drivers/sound/ac97_codec.orig.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/sound/ac97_codec.orig.c Mon Sep 8 12:11:28 2003 @@ -0,0 +1,1114 @@ + +/* + * ac97_codec.c: Generic AC97 mixer/modem module + * + * Derived from ac97 mixer in maestro and trident driver. + * + * Copyright 2000 Silicon Integrated System Corporation + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ************************************************************************** + * + * The Intel Audio Codec '97 specification is available at the Intel + * audio homepage: http://developer.intel.com/ial/scalableplatforms/audio/ + * + * The specification itself is currently available at: + * ftp://download.intel.com/ial/scalableplatforms/ac97r22.pdf + * + ************************************************************************** + * + * History + * Mar 28, 2002 Randolph Bentson + * corrections to support WM9707 in ViewPad 1000 + * v0.4 Mar 15 2000 Ollie Lho + * dual codecs support verified with 4 channels output + * v0.3 Feb 22 2000 Ollie Lho + * bug fix for record mask setting + * v0.2 Feb 10 2000 Ollie Lho + * add ac97_read_proc for /proc/driver/{vendor}/ac97 + * v0.1 Jan 14 2000 Ollie Lho + * Isolated from trident.c to support multiple ac97 codec + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel); +static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right); +static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ); +static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask); +static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg); + +static int ac97_init_mixer(struct ac97_codec *codec); + +static int wolfson_init00(struct ac97_codec * codec); +static int wolfson_init03(struct ac97_codec * codec); +static int wolfson_init04(struct ac97_codec * codec); +static int tritech_init(struct ac97_codec * codec); +static int tritech_maestro_init(struct ac97_codec * codec); +static int sigmatel_9708_init(struct ac97_codec *codec); +static int sigmatel_9721_init(struct ac97_codec *codec); +static int sigmatel_9744_init(struct ac97_codec *codec); +static int ad1886_init(struct ac97_codec *codec); +static int eapd_control(struct ac97_codec *codec, int); +static int crystal_digital_control(struct ac97_codec *codec, int mode); + + +/* + * AC97 operations. + * + * If you are adding a codec then you should be able to use + * eapd_ops - any codec that supports EAPD amp control (most) + * null_ops - any ancient codec that supports nothing + * + * The three functions are + * init - used for non AC97 standard initialisation + * amplifier - used to do amplifier control (1=on 0=off) + * digital - switch to digital modes (0 = analog) + * + * Not all codecs support all features, not all drivers use all the + * operations yet + */ + +static struct ac97_ops null_ops = { NULL, NULL, NULL }; +static struct ac97_ops default_ops = { NULL, eapd_control, NULL }; +static struct ac97_ops wolfson_ops00 = { wolfson_init00, NULL, NULL }; +static struct ac97_ops wolfson_ops03 = { wolfson_init03, NULL, NULL }; +static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL }; +static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL }; +static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL }; +static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL }; +static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL }; +static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL }; +static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control }; +static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL }; + +/* sorted by vendor/device id */ +static const struct { + u32 id; + char *name; + struct ac97_ops *ops; +} ac97_codec_ids[] = { + {0x41445303, "Analog Devices AD1819", &null_ops}, + {0x41445340, "Analog Devices AD1881", &null_ops}, + {0x41445348, "Analog Devices AD1881A", &null_ops}, + {0x41445360, "Analog Devices AD1885", &default_ops}, + {0x41445361, "Analog Devices AD1886", &ad1886_ops}, + {0x41445460, "Analog Devices AD1885", &default_ops}, + {0x41445461, "Analog Devices AD1886", &ad1886_ops}, + {0x414B4D00, "Asahi Kasei AK4540", &null_ops}, + {0x414B4D01, "Asahi Kasei AK4542", &null_ops}, + {0x414B4D02, "Asahi Kasei AK4543", &null_ops}, + {0x414C4710, "ALC200/200P", &null_ops}, + {0x414C4720, "ALC650", &null_ops}, + {0x43525900, "Cirrus Logic CS4297", &default_ops}, + {0x43525903, "Cirrus Logic CS4297", &default_ops}, + {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops}, + {0x43525914, "Cirrus Logic CS4297A rev B", &default_ops}, + {0x43525923, "Cirrus Logic CS4298", &null_ops}, + {0x4352592B, "Cirrus Logic CS4294", &null_ops}, + {0x4352592D, "Cirrus Logic CS4294", &null_ops}, + {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops}, + {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops}, + {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops}, + {0x4352594d, "Cirrus Logic CS4201" , &null_ops}, + {0x45838308, "ESS Allegro ES1988", &null_ops}, + {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */ + {0x4e534331, "National Semiconductor LM4549", &null_ops}, + {0x50534304, "Philips UCB1400", &default_ops}, + {0x53494c22, "Silicon Laboratory Si3036", &null_ops}, + {0x53494c23, "Silicon Laboratory Si3038", &null_ops}, + {0x545200FF, "TriTech TR?????", &tritech_m_ops}, + {0x54524102, "TriTech TR28022", &null_ops}, + {0x54524103, "TriTech TR28023", &null_ops}, + {0x54524106, "TriTech TR28026", &null_ops}, + {0x54524108, "TriTech TR28028", &tritech_ops}, + {0x54524123, "TriTech TR A5", &null_ops}, + {0x574D4C00, "Wolfson WM9700A", &wolfson_ops00}, + {0x574D4C03, "Wolfson WM9703/WM9707", &wolfson_ops03}, + {0x574D4C04, "Wolfson WM9704M/WM9704Q", &wolfson_ops04}, + {0x83847600, "SigmaTel STAC????", &null_ops}, + {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops}, + {0x83847605, "SigmaTel STAC9704", &null_ops}, + {0x83847608, "SigmaTel STAC9708", &sigmatel_9708_ops}, + {0x83847609, "SigmaTel STAC9721/23", &sigmatel_9721_ops}, + {0x83847644, "SigmaTel STAC9744/45", &sigmatel_9744_ops}, + {0x83847656, "SigmaTel STAC9756/57", &sigmatel_9744_ops}, + {0x83847666, "SigmaTel STAC9750T", &sigmatel_9744_ops}, + {0x83847684, "SigmaTel STAC9783/84?", &null_ops}, + {0x57454301, "Winbond 83971D", &null_ops}, +}; + +static const char *ac97_stereo_enhancements[] = +{ + /* 0 */ "No 3D Stereo Enhancement", + /* 1 */ "Analog Devices Phat Stereo", + /* 2 */ "Creative Stereo Enhancement", + /* 3 */ "National Semi 3D Stereo Enhancement", + /* 4 */ "YAMAHA Ymersion", + /* 5 */ "BBE 3D Stereo Enhancement", + /* 6 */ "Crystal Semi 3D Stereo Enhancement", + /* 7 */ "Qsound QXpander", + /* 8 */ "Spatializer 3D Stereo Enhancement", + /* 9 */ "SRS 3D Stereo Enhancement", + /* 10 */ "Platform Tech 3D Stereo Enhancement", + /* 11 */ "AKM 3D Audio", + /* 12 */ "Aureal Stereo Enhancement", + /* 13 */ "Aztech 3D Enhancement", + /* 14 */ "Binaura 3D Audio Enhancement", + /* 15 */ "ESS Technology Stereo Enhancement", + /* 16 */ "Harman International VMAx", + /* 17 */ "Nvidea 3D Stereo Enhancement", + /* 18 */ "Philips Incredible Sound", + /* 19 */ "Texas Instruments 3D Stereo Enhancement", + /* 20 */ "VLSI Technology 3D Stereo Enhancement", + /* 21 */ "TriTech 3D Stereo Enhancement", + /* 22 */ "Realtek 3D Stereo Enhancement", + /* 23 */ "Samsung 3D Stereo Enhancement", + /* 24 */ "Wolfson Microelectronics 3D Enhancement", + /* 25 */ "Delta Integration 3D Enhancement", + /* 26 */ "SigmaTel 3D Enhancement", + /* 27 */ "Winbond 3D Stereo Enhancement", + /* 28 */ "Rockwell 3D Stereo Enhancement", + /* 29 */ "Reserved 29", + /* 30 */ "Reserved 30", + /* 31 */ "Reserved 31" +}; + +/* this table has default mixer values for all OSS mixers. */ +static struct mixer_defaults { + int mixer; + unsigned int value; +} mixer_defaults[SOUND_MIXER_NRDEVICES] = { + /* all values 0 -> 100 in bytes */ + {SOUND_MIXER_VOLUME, 0x4343}, + {SOUND_MIXER_BASS, 0x4343}, + {SOUND_MIXER_TREBLE, 0x4343}, + {SOUND_MIXER_PCM, 0x4343}, + {SOUND_MIXER_SPEAKER, 0x4343}, + {SOUND_MIXER_LINE, 0x4343}, + {SOUND_MIXER_MIC, 0x0000}, + {SOUND_MIXER_CD, 0x4343}, + {SOUND_MIXER_ALTPCM, 0x4343}, + {SOUND_MIXER_IGAIN, 0x4343}, + {SOUND_MIXER_LINE1, 0x4343}, + {SOUND_MIXER_PHONEIN, 0x4343}, + {SOUND_MIXER_PHONEOUT, 0x4343}, + {SOUND_MIXER_VIDEO, 0x4343}, + {-1,0} +}; + +/* table to scale scale from OSS mixer value to AC97 mixer register value */ +static struct ac97_mixer_hw { + unsigned char offset; + int scale; +} ac97_hw[SOUND_MIXER_NRDEVICES]= { + [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,64}, + [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 16}, + [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 16}, + [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 32}, + [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 16}, + [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 32}, + [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 32}, + [SOUND_MIXER_CD] = {AC97_CD_VOL, 32}, + [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 64}, + [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 16}, + [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 32}, + [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 32}, + [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 64}, + [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 32}, +}; + +/* the following tables allow us to go from OSS <-> ac97 quickly. */ +enum ac97_recsettings { + AC97_REC_MIC=0, + AC97_REC_CD, + AC97_REC_VIDEO, + AC97_REC_AUX, + AC97_REC_LINE, + AC97_REC_STEREO, /* combination of all enabled outputs.. */ + AC97_REC_MONO, /*.. or the mono equivalent */ + AC97_REC_PHONE +}; + +static const unsigned int ac97_rm2oss[] = { + [AC97_REC_MIC] = SOUND_MIXER_MIC, + [AC97_REC_CD] = SOUND_MIXER_CD, + [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO, + [AC97_REC_AUX] = SOUND_MIXER_LINE1, + [AC97_REC_LINE] = SOUND_MIXER_LINE, + [AC97_REC_STEREO]= SOUND_MIXER_IGAIN, + [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN +}; + +/* indexed by bit position */ +static const unsigned int ac97_oss_rm[] = { + [SOUND_MIXER_MIC] = AC97_REC_MIC, + [SOUND_MIXER_CD] = AC97_REC_CD, + [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, + [SOUND_MIXER_LINE1] = AC97_REC_AUX, + [SOUND_MIXER_LINE] = AC97_REC_LINE, + [SOUND_MIXER_IGAIN] = AC97_REC_STEREO, + [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE +}; + +/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows + about that given mixer, and should be holding a spinlock for the card */ +static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel) +{ + u16 val; + int ret = 0; + int scale; + struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; + + val = codec->codec_read(codec , mh->offset); + + if (val & AC97_MUTE) { + ret = 0; + } else if (AC97_STEREO_MASK & (1 << oss_channel)) { + /* nice stereo mixers .. */ + int left,right; + + left = (val >> 8) & 0x7f; + right = val & 0x7f; + + if (oss_channel == SOUND_MIXER_IGAIN) { + right = (right * 100) / mh->scale; + left = (left * 100) / mh->scale; + } else { + /* these may have 5 or 6 bit resolution */ + if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM) + scale = (1 << codec->bit_resolution); + else + scale = mh->scale; + + right = 100 - ((right * 100) / scale); + left = 100 - ((left * 100) / scale); + } + ret = left | (right << 8); + } else if (oss_channel == SOUND_MIXER_SPEAKER) { + ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_PHONEIN) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_PHONEOUT) { + scale = (1 << codec->bit_resolution); + ret = 100 - (((val & 0x1f) * 100) / scale); + } else if (oss_channel == SOUND_MIXER_MIC) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + /* the low bit is optional in the tone sliders and masking + it lets us avoid the 0xf 'bypass'.. */ + } else if (oss_channel == SOUND_MIXER_BASS) { + ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_TREBLE) { + ret = 100 - (((val & 0xe) * 100) / mh->scale); + } + +#ifdef DEBUG + printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), " + "0x%04x -> 0x%04x\n", + oss_channel, codec->id ? "Secondary" : "Primary", + mh->offset, val, ret); +#endif + + return ret; +} + +/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to + make sure all is well in arg land, call with spinlock held */ +static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right) +{ + u16 val = 0; + int scale; + struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; + +#ifdef DEBUG + printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), " + "left vol:%2d, right vol:%2d:", + oss_channel, codec->id ? "Secondary" : "Primary", + mh->offset, left, right); +#endif + + if (AC97_STEREO_MASK & (1 << oss_channel)) { + /* stereo mixers */ + if (left == 0 && right == 0) { + val = AC97_MUTE; + } else { + if (oss_channel == SOUND_MIXER_IGAIN) { + right = (right * mh->scale) / 100; + left = (left * mh->scale) / 100; + if (right >= mh->scale) + right = mh->scale-1; + if (left >= mh->scale) + left = mh->scale-1; + } else { + /* these may have 5 or 6 bit resolution */ + if (oss_channel == SOUND_MIXER_VOLUME || + oss_channel == SOUND_MIXER_ALTPCM) + scale = (1 << codec->bit_resolution); + else + scale = mh->scale; + + right = ((100 - right) * scale) / 100; + left = ((100 - left) * scale) / 100; + if (right >= scale) + right = scale-1; + if (left >= scale) + left = scale-1; + } + val = (left << 8) | right; + } + } else if (oss_channel == SOUND_MIXER_BASS) { + val = codec->codec_read(codec , mh->offset) & ~0x0f00; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= (left << 8) & 0x0e00; + } else if (oss_channel == SOUND_MIXER_TREBLE) { + val = codec->codec_read(codec , mh->offset) & ~0x000f; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= left & 0x000e; + } else if(left == 0) { + val = AC97_MUTE; + } else if (oss_channel == SOUND_MIXER_SPEAKER) { + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val = left << 1; + } else if (oss_channel == SOUND_MIXER_PHONEIN) { + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val = left; + } else if (oss_channel == SOUND_MIXER_PHONEOUT) { + scale = (1 << codec->bit_resolution); + left = ((100 - left) * scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val = left; + } else if (oss_channel == SOUND_MIXER_MIC) { + val = codec->codec_read(codec , mh->offset) & ~0x801f; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= left; + /* the low bit is optional in the tone sliders and masking + it lets us avoid the 0xf 'bypass'.. */ + } +#ifdef DEBUG + printk(" 0x%04x", val); +#endif + + codec->codec_write(codec, mh->offset, val); + +#ifdef DEBUG + val = codec->codec_read(codec, mh->offset); + printk(" -> 0x%04x\n", val); +#endif +} + +/* a thin wrapper for write_mixer */ +static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ) +{ + unsigned int left,right; + + /* cleanse input a little */ + right = ((val >> 8) & 0xff) ; + left = (val & 0xff) ; + + if (right > 100) right = 100; + if (left > 100) left = 100; + + codec->mixer_state[oss_mixer] = (right << 8) | left; + codec->write_mixer(codec, oss_mixer, left, right); +} + +/* read or write the recmask, the ac97 can really have left and right recording + inputs independantly set, but OSS doesn't seem to want us to express that to + the user. the caller guarantees that we have a supported bit set, and they + must be holding the card's spinlock */ +static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask) +{ + unsigned int val; + + if (rw) { + /* read it from the card */ + val = codec->codec_read(codec, AC97_RECORD_SELECT); +#ifdef DEBUG + printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val); +#endif + return (1 << ac97_rm2oss[val & 0x07]); + } + + /* else, write the first set in the mask as the + output */ + /* clear out current set value first (AC97 supports only 1 input!) */ + val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]); + if (mask != val) + mask &= ~val; + + val = ffs(mask); + val = ac97_oss_rm[val-1]; + val |= val << 8; /* set both channels */ + +#ifdef DEBUG + printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val); +#endif + + codec->codec_write(codec, AC97_RECORD_SELECT, val); + + return 0; +}; + +static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg) +{ + int i, val = 0; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); + info.modify_counter = codec->modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* give them the current record source */ + if (!codec->recmask_io) { + val = 0; + } else { + val = codec->recmask_io(codec, 1, 0); + } + break; + + case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ + val = codec->supported_mixers; + break; + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + val = codec->record_sources; + break; + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + val = codec->stereo_mixers; + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: /* read a specific mixer */ + i = _IOC_NR(cmd); + + if (!supported_mixer(codec, i)) + return -EINVAL; + + /* do we ever want to touch the hardware? */ + /* val = codec->read_mixer(codec, i); */ + val = codec->mixer_state[i]; + break; + } + return put_user(val, (int *)arg); + } + + if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) { + codec->modcnt++; + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (!codec->recmask_io) return -EINVAL; + if (!val) return 0; + if (!(val &= codec->record_sources)) return -EINVAL; + + codec->recmask_io(codec, 0, val); + + return 0; + default: /* write a specific mixer */ + i = _IOC_NR(cmd); + + if (!supported_mixer(codec, i)) + return -EINVAL; + + ac97_set_mixer(codec, i, val); + + return 0; + } + } + return -EINVAL; +} + +/* entry point for /proc/driver/controller_vendor/ac97/%d */ +int ac97_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0, cap, extid, val, id1, id2; + struct ac97_codec *codec; + int is_ac97_20 = 0; + + if ((codec = data) == NULL) + return -ENODEV; + + id1 = codec->codec_read(codec, AC97_VENDOR_ID1); + id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + len += sprintf (page+len, "Vendor name : %s\n", codec->name); + len += sprintf (page+len, "Vendor id : %04X %04X\n", id1, id2); + + extid = codec->codec_read(codec, AC97_EXTENDED_ID); + extid &= ~((1<<2)|(1<<4)|(1<<5)|(1<<10)|(1<<11)|(1<<12)|(1<<13)); + len += sprintf (page+len, "AC97 Version : %s\n", + extid ? "2.0 or later" : "1.0"); + if (extid) is_ac97_20 = 1; + + cap = codec->codec_read(codec, AC97_RESET); + len += sprintf (page+len, "Capabilities :%s%s%s%s%s%s\n", + cap & 0x0001 ? " -dedicated MIC PCM IN channel-" : "", + cap & 0x0002 ? " -reserved1-" : "", + cap & 0x0004 ? " -bass & treble-" : "", + cap & 0x0008 ? " -simulated stereo-" : "", + cap & 0x0010 ? " -headphone out-" : "", + cap & 0x0020 ? " -loudness-" : ""); + val = cap & 0x00c0; + len += sprintf (page+len, "DAC resolutions :%s%s%s\n", + " -16-bit-", + val & 0x0040 ? " -18-bit-" : "", + val & 0x0080 ? " -20-bit-" : ""); + val = cap & 0x0300; + len += sprintf (page+len, "ADC resolutions :%s%s%s\n", + " -16-bit-", + val & 0x0100 ? " -18-bit-" : "", + val & 0x0200 ? " -20-bit-" : ""); + len += sprintf (page+len, "3D enhancement : %s\n", + ac97_stereo_enhancements[(cap >> 10) & 0x1f]); + + val = codec->codec_read(codec, AC97_GENERAL_PURPOSE); + len += sprintf (page+len, "POP path : %s 3D\n" + "Sim. stereo : %s\n" + "3D enhancement : %s\n" + "Loudness : %s\n" + "Mono output : %s\n" + "MIC select : %s\n" + "ADC/DAC loopback : %s\n", + val & 0x8000 ? "post" : "pre", + val & 0x4000 ? "on" : "off", + val & 0x2000 ? "on" : "off", + val & 0x1000 ? "on" : "off", + val & 0x0200 ? "MIC" : "MIX", + val & 0x0100 ? "MIC2" : "MIC1", + val & 0x0080 ? "on" : "off"); + + extid = codec->codec_read(codec, AC97_EXTENDED_ID); + cap = extid; + len += sprintf (page+len, "Ext Capabilities :%s%s%s%s%s%s%s\n", + cap & 0x0001 ? " -var rate PCM audio-" : "", + cap & 0x0002 ? " -2x PCM audio out-" : "", + cap & 0x0008 ? " -var rate MIC in-" : "", + cap & 0x0040 ? " -PCM center DAC-" : "", + cap & 0x0080 ? " -PCM surround DAC-" : "", + cap & 0x0100 ? " -PCM LFE DAC-" : "", + cap & 0x0200 ? " -slot/DAC mappings-" : ""); + if (is_ac97_20) { + len += sprintf (page+len, "Front DAC rate : %d\n", + codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)); + } + + return len; +} + +/** + * ac97_probe_codec - Initialize and setup AC97-compatible codec + * @codec: (in/out) Kernel info for a single AC97 codec + * + * Reset the AC97 codec, then initialize the mixer and + * the rest of the @codec structure. + * + * The codec_read and codec_write fields of @codec are + * required to be setup and working when this function + * is called. All other fields are set by this function. + * + * codec_wait field of @codec can optionally be provided + * when calling this function. If codec_wait is not %NULL, + * this function will call codec_wait any time it is + * necessary to wait for the audio chip to reach the + * codec-ready state. If codec_wait is %NULL, then + * the default behavior is to call schedule_timeout. + * Currently codec_wait is used to wait for AC97 codec + * reset to complete. + * + * Returns 1 (true) on success, or 0 (false) on failure. + */ + +int ac97_probe_codec(struct ac97_codec *codec) +{ + u16 id1, id2; + u16 audio, modem; + int i; + + /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should + * be read zero. + * + * FIXME: is the following comment outdated? -jgarzik + * Probing of AC97 in this way is not reliable, it is not even SAFE !! + */ + codec->codec_write(codec, AC97_RESET, 0L); + + /* also according to spec, we wait for codec-ready state */ + if (codec->codec_wait) + codec->codec_wait(codec); + else + udelay(10); + + if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) { + printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n", + codec->id ? "Secondary" : "Primary"); + return 0; + } + + /* probe for Modem Codec */ + codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L); + modem = codec->codec_read(codec, AC97_EXTENDED_MODEM_ID); + + codec->name = NULL; + codec->codec_ops = &null_ops; + + id1 = codec->codec_read(codec, AC97_VENDOR_ID1); + id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) { + if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) { + codec->type = ac97_codec_ids[i].id; + codec->name = ac97_codec_ids[i].name; + codec->codec_ops = ac97_codec_ids[i].ops; + break; + } + } + if (codec->name == NULL) + codec->name = "Unknown"; + printk(KERN_INFO "ac97_codec: AC97 %s codec, id: 0x%04x:" + "0x%04x (%s)\n", audio ? "Audio" : (modem ? "Modem" : ""), + id1, id2, codec->name); + + return ac97_init_mixer(codec); +} + +static int ac97_init_mixer(struct ac97_codec *codec) +{ + u16 cap; + int i; + + cap = codec->codec_read(codec, AC97_RESET); + + /* mixer masks */ + codec->supported_mixers = AC97_SUPPORTED_MASK; + codec->stereo_mixers = AC97_STEREO_MASK; + codec->record_sources = AC97_RECORD_MASK; + if (!(cap & 0x04)) + codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + if (!(cap & 0x10)) + codec->supported_mixers &= ~SOUND_MASK_ALTPCM; + + /* detect bit resolution */ + codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020); + if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020) + codec->bit_resolution = 6; + else + codec->bit_resolution = 5; + + /* generic OSS to AC97 wrapper */ + codec->read_mixer = ac97_read_mixer; + codec->write_mixer = ac97_write_mixer; + codec->recmask_io = ac97_recmask_io; + codec->mixer_ioctl = ac97_mixer_ioctl; + + /* codec specific initialization for 4-6 channel output or secondary codec stuff */ + if (codec->codec_ops->init != NULL) { + codec->codec_ops->init(codec); + } + + /* initialize mixer channel volumes */ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + struct mixer_defaults *md = &mixer_defaults[i]; + if (md->mixer == -1) + break; + if (!supported_mixer(codec, md->mixer)) + continue; + ac97_set_mixer(codec, md->mixer, md->value); + } + + return 1; +} + +#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */ +#define AC97_SIGMATEL_DAC2INVERT 0x6e +#define AC97_SIGMATEL_BIAS1 0x70 +#define AC97_SIGMATEL_BIAS2 0x72 +#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */ +#define AC97_SIGMATEL_CIC1 0x76 +#define AC97_SIGMATEL_CIC2 0x78 + + +static int sigmatel_9708_init(struct ac97_codec * codec) +{ + u16 codec72, codec6c; + + codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000; + codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG); + + if ((codec72==0) && (codec6c==0)) { + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000); + codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007); + } else if ((codec72==0x8000) && (codec6c==0)) { + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001); + codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008); + } else if ((codec72==0x8000) && (codec6c==0x0080)) { + /* nothing */ + } + codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + + +static int sigmatel_9721_init(struct ac97_codec * codec) +{ + /* Only set up secondary codec */ + if (codec->id == 0) + return 0; + + codec->codec_write(codec, AC97_SURROUND_MASTER, 0L); + + /* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link + sloc 3,4 = 0x01, slot 7,8 = 0x00, */ + codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x00); + + /* we don't have the crystal when we are on an AMR card, so use + BIT_CLK as our clock source. Write the magic word ABBA and read + back to enable register 0x78 */ + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_read(codec, AC97_SIGMATEL_CIC1); + + /* sync all the clocks*/ + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x3802); + + return 0; +} + + +static int sigmatel_9744_init(struct ac97_codec * codec) +{ + // patch for SigmaTel + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x0000); // is this correct? --jk + codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0002); + codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + + +static int wolfson_init00(struct ac97_codec * codec) +{ + /* This initialization is suspect, but not known to be wrong. + It was copied from the initialization for the WM9704Q, but + that same sequence is known to fail for the WM9707. Thus + this warning may help someone with hardware to test + this code. */ + codec->codec_write(codec, 0x72, 0x0808); + codec->codec_write(codec, 0x74, 0x0808); + + // patch for DVD noise + codec->codec_write(codec, 0x5a, 0x0200); + + // init vol as PCM vol + codec->codec_write(codec, 0x70, + codec->codec_read(codec, AC97_PCMOUT_VOL)); + + codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000); + return 0; +} + + +static int wolfson_init03(struct ac97_codec * codec) +{ + /* this is known to work for the ViewSonic ViewPad 1000 */ + codec->codec_write(codec, 0x72, 0x0808); + codec->codec_write(codec, 0x20, 0x8000); + return 0; +} + + +static int wolfson_init04(struct ac97_codec * codec) +{ + codec->codec_write(codec, 0x72, 0x0808); + codec->codec_write(codec, 0x74, 0x0808); + + // patch for DVD noise + codec->codec_write(codec, 0x5a, 0x0200); + + // init vol as PCM vol + codec->codec_write(codec, 0x70, + codec->codec_read(codec, AC97_PCMOUT_VOL)); + + codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000); + return 0; +} + + +static int tritech_init(struct ac97_codec * codec) +{ + codec->codec_write(codec, 0x26, 0x0300); + codec->codec_write(codec, 0x26, 0x0000); + codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000); + codec->codec_write(codec, AC97_RESERVED_3A, 0x0000); + return 0; +} + + +/* copied from drivers/sound/maestro.c */ +static int tritech_maestro_init(struct ac97_codec * codec) +{ + /* no idea what this does */ + codec->codec_write(codec, 0x2A, 0x0001); + codec->codec_write(codec, 0x2C, 0x0000); + codec->codec_write(codec, 0x2C, 0XFFFF); + return 0; +} + + + +/* + * Presario700 workaround + * for Jack Sense/SPDIF Register mis-setting causing + * no audible output + * by Santiago Nullo 04/05/2002 + */ + +#define AC97_AD1886_JACK_SENSE 0x72 + +static int ad1886_init(struct ac97_codec * codec) +{ + /* from AD1886 Specs */ + codec->codec_write(codec, AC97_AD1886_JACK_SENSE, 0x0010); + return 0; +} + + + + +/* + * This is basically standard AC97. It should work as a default for + * almost all modern codecs. Note that some cards wire EAPD *backwards* + * That side of it is up to the card driver not us to cope with. + * + */ + +static int eapd_control(struct ac97_codec * codec, int on) +{ + if(on) + codec->codec_write(codec, AC97_POWER_CONTROL, + codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000); + else + codec->codec_write(codec, AC97_POWER_CONTROL, + codec->codec_read(codec, AC97_POWER_CONTROL)&~0x8000); + return 0; +} + +/* + * Crystal digital audio control (CS4299 + */ + +static int crystal_digital_control(struct ac97_codec *codec, int mode) +{ + u16 cv; + + switch(mode) + { + case 0: cv = 0x0; break; /* SPEN off */ + case 1: cv = 0x8004; break; /* 48KHz digital */ + case 2: cv = 0x8104; break; /* 44.1KHz digital */ + default: + return -1; /* Not supported yet(eg AC3) */ + } + codec->codec_write(codec, 0x68, cv); + return 0; +} + +/* copied from drivers/sound/maestro.c */ +#if 0 /* there has been 1 person on the planet with a pt101 that we + know of. If they care, they can put this back in :) */ +static int pt101_init(struct ac97_codec * codec) +{ + printk(KERN_INFO "ac97_codec: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); + /* who knows.. */ + codec->codec_write(codec, 0x2A, 0x0001); + codec->codec_write(codec, 0x2C, 0x0000); + codec->codec_write(codec, 0x2C, 0xFFFF); + codec->codec_write(codec, 0x10, 0x9F1F); + codec->codec_write(codec, 0x12, 0x0808); + codec->codec_write(codec, 0x14, 0x9F1F); + codec->codec_write(codec, 0x16, 0x9F1F); + codec->codec_write(codec, 0x18, 0x0404); + codec->codec_write(codec, 0x1A, 0x0000); + codec->codec_write(codec, 0x1C, 0x0000); + codec->codec_write(codec, 0x02, 0x0404); + codec->codec_write(codec, 0x04, 0x0808); + codec->codec_write(codec, 0x0C, 0x801F); + codec->codec_write(codec, 0x0E, 0x801F); + return 0; +} +#endif + + +EXPORT_SYMBOL(ac97_read_proc); +EXPORT_SYMBOL(ac97_probe_codec); + +/* + * AC97 library support routines + */ + +/** + * ac97_set_dac_rate - set codec rate adaption + * @codec: ac97 code + * @rate: rate in hertz + * + * Set the DAC rate. Assumes the codec supports VRA. The caller is + * expected to have checked this little detail. + */ + +unsigned int ac97_set_dac_rate(struct ac97_codec *codec, unsigned int rate) +{ + unsigned int new_rate = rate; + u32 dacp; + u32 mast_vol, phone_vol, mono_vol, pcm_vol; + u32 mute_vol = 0x8000; /* The mute volume? */ + + if(rate != codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)) + { + /* Mute several registers */ + mast_vol = codec->codec_read(codec, AC97_MASTER_VOL_STEREO); + mono_vol = codec->codec_read(codec, AC97_MASTER_VOL_MONO); + phone_vol = codec->codec_read(codec, AC97_HEADPHONE_VOL); + pcm_vol = codec->codec_read(codec, AC97_PCMOUT_VOL); + codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mute_vol); + codec->codec_write(codec, AC97_MASTER_VOL_MONO, mute_vol); + codec->codec_write(codec, AC97_HEADPHONE_VOL, mute_vol); + codec->codec_write(codec, AC97_PCMOUT_VOL, mute_vol); + + /* Power down the DAC */ + dacp=codec->codec_read(codec, AC97_POWER_CONTROL); + codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0200); + /* Load the rate and read the effective rate */ + codec->codec_write(codec, AC97_PCM_FRONT_DAC_RATE, rate); + new_rate=codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE); + /* Power it back up */ + codec->codec_write(codec, AC97_POWER_CONTROL, dacp); + + /* Restore volumes */ + codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mast_vol); + codec->codec_write(codec, AC97_MASTER_VOL_MONO, mono_vol); + codec->codec_write(codec, AC97_HEADPHONE_VOL, phone_vol); + codec->codec_write(codec, AC97_PCMOUT_VOL, pcm_vol); + } + return new_rate; +} + +EXPORT_SYMBOL(ac97_set_dac_rate); + +/** + * ac97_set_adc_rate - set codec rate adaption + * @codec: ac97 code + * @rate: rate in hertz + * + * Set the ADC rate. Assumes the codec supports VRA. The caller is + * expected to have checked this little detail. + */ + +unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate) +{ + unsigned int new_rate = rate; + u32 dacp; + + if(rate != codec->codec_read(codec, AC97_PCM_LR_ADC_RATE)) + { + /* Power down the ADC */ + dacp=codec->codec_read(codec, AC97_POWER_CONTROL); + codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0100); + /* Load the rate and read the effective rate */ + codec->codec_write(codec, AC97_PCM_LR_ADC_RATE, rate); + new_rate=codec->codec_read(codec, AC97_PCM_LR_ADC_RATE); + /* Power it back up */ + codec->codec_write(codec, AC97_POWER_CONTROL, dacp); + } + return new_rate; +} + +EXPORT_SYMBOL(ac97_set_adc_rate); + +int ac97_save_state(struct ac97_codec *codec) +{ + return 0; +} + +EXPORT_SYMBOL(ac97_save_state); + +int ac97_restore_state(struct ac97_codec *codec) +{ + int i; + unsigned int left, right, val; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!supported_mixer(codec, i)) + continue; + + val = codec->mixer_state[i]; + right = val >> 8; + left = val & 0xff; + codec->write_mixer(codec, i, left, right); + } + return 0; +} + +EXPORT_SYMBOL(ac97_restore_state); + +MODULE_LICENSE("GPL"); diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi/Config.in linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/Config.in --- linux-2.4.19-rmk7-pxa2/drivers/ssi/Config.in Wed Nov 5 09:22:11 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/Config.in Wed Sep 10 16:42:11 2003 @@ -5,7 +5,15 @@ comment 'SSI Bus Drivers' dep_tristate ' CLPS711X SSI support' CONFIG_SSI_CLPS711X $CONFIG_SSI $CONFIG_ARCH_CLPS711X +dep_tristate ' PXA SSPC support' CONFIG_SSI_PXA $CONFIG_SSI $CONFIG_ARCH_PXA + +if [ $CONFIG_SSI_PXA != "n" ];then +bool ' Enable SSPC external clock (GPIO27)' CONFIG_SSPC_EXTCLK +fi comment 'SSI Device Drivers' dep_tristate ' JUNO keyboard support' CONFIG_SSI_JUNO $CONFIG_SSI +dep_tristate ' AD7887 support' CONFIG_SSI_AD7887 $CONFIG_SSI +dep_bool ' MCP2510 CAN controller support' CONFIG_SSI_MCP2510 $CONFIG_SSI_PXA + endmenu diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi/Makefile linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/Makefile --- linux-2.4.19-rmk7-pxa2/drivers/ssi/Makefile Wed Nov 5 09:22:11 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/Makefile Wed Sep 10 16:41:49 2003 @@ -19,9 +19,18 @@ export-objs := list-multi := +# SSI bus drivers obj-$(CONFIG_SSI) += ssi_core.o +obj-$(CONFIG_SSI_PXA) += spi-core.o spi-pxa.o + +# SSI device drivers +# generic char dev driver +#obj-$(CONFIG_SSI_PXA) += spi-dev.o +# chip drivers obj-$(CONFIG_SSI_CLPS711X) += clps711x_ssi1.o -obj-y += juno.o +obj-$(CONFIG_SSI_JUNO) += juno.o +obj-$(CONFIG_SSI_AD7887) += adc7887.o +obj-$(CONFIG_SSI_MCP2510) += mcp2510.o # Extract lists of the multi-part drivers. # The 'int-*' lists are intermediate files used to build the multi's. diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi/mcp2510.c linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/mcp2510.c --- linux-2.4.19-rmk7-pxa2/drivers/ssi/mcp2510.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/mcp2510.c Wed Sep 10 20:42:04 2003 @@ -0,0 +1,173 @@ + +// Voipac MCP2510 driver controller + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-core.h" + +#define MCP2510_DEVICE_MAJOR 91 // CAN device controller +const char MCP2510_DEVICE_NAME[] = "can"; + +#define MCP2510_DEBUG 1 + +static int mcp2510_device = 0; +static struct spi_bus* spi_pxa_bus = NULL; + +static int mcp2510_open(struct inode *inode, struct file *file) +{ +#if MCP2510_DEBUG + printk("mcp2510: open, pos=%llu\n", file->f_pos); +#endif + if( spi_pxa_bus != NULL) + return -EBUSY; + + if( (spi_pxa_bus = spi_open( "pxa")) == NULL) + { + printk("mcp2510_open: spi_open failed\n"); + return -EBUSY; + } + + return 0; +} + +static int mcp2510_release(struct inode *inode, struct file *filp) +{ +#if MCP2510_DEBUG + printk("mcp2510: release\n"); +#endif + + if( spi_pxa_bus != NULL) + spi_release( spi_pxa_bus); + + spi_pxa_bus = NULL; + + return 0; +} + +static ssize_t mcp2510_read(struct file *file, char *buffer, size_t length, loff_t *offset) +{ +#if MCP2510_DEBUG + printk("mcp2510: read, pos=%llu, len=%u, offs=%llu\n", file->f_pos, length, *offset); +#endif + int count = length; + char *rdbuff, *wrbuff; + struct spi_command spi_cmd; + + if( (wrbuff = kmalloc( count, GFP_KERNEL)) == NULL) + return -ENOMEM; + + if( (rdbuff = kmalloc( count, GFP_KERNEL)) == NULL) + return -ENOMEM; + + if( copy_from_user(wrbuff,buffer,count)) + count = -EFAULT; + + if( count > 0) + { + printk( "spi_start: length = %d\n", count); + + spi_cmd.op = SPI_OP_WRITE_READ; + spi_cmd.read_data = rdbuff; + spi_cmd.write_data = wrbuff; + spi_cmd.length = count; + + if( spi_start( spi_pxa_bus, 0, &spi_cmd, 1, 0)) + count = -EFAULT; + + if( count > 0) + copy_to_user(buffer,rdbuff,count); + } + + kfree(wrbuff); + kfree(rdbuff); + + return count; +} + +static ssize_t mcp2510_write(struct file *file, const char *buffer, size_t length, loff_t *offset) +{ +#if MCP2510_DEBUG + printk("mcp2510: write, pos=%llu, len=%u, offs=%llu\n", file->f_pos, length, *offset); +#endif + struct spi_command spi_cmd; + + spi_cmd.op = SPI_OP_WRITE; + spi_cmd.read_data = NULL; + spi_cmd.write_data = buffer; + spi_cmd.length = length; + + if( !spi_start( spi_pxa_bus, 0, &spi_cmd, 1, 0) ) + return length; + + return 0; +} + + +static int mcp2510_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ +#if MCP2510_DEBUG + printk("mcp2510: ioctl cmd=0x%x, arg=0x%lx\n", cmd, arg); +#endif + switch( cmd) + { + case SPI_OP_SET_FRAMELEN: + break; + case SPI_OP_SET_CLKFREQ: + break; + case SPI_OP_SET_BASE_CLOCK: + break; + } + return 0; +} + +static int mcp2510_read_proc(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int len = 0; + + len += sprintf(buf + len, "Voipac MCP2510 CAN controller driver (c) 2003\n\n"); + len += sprintf(buf + len, "major=%u\nminor=%u\n", MCP2510_DEVICE_MAJOR, 0); + + *eof = 1; + + return len; +} + +static struct file_operations mcp2510_fops= +{ + open: mcp2510_open, + release: mcp2510_release, + read: mcp2510_read, + write: mcp2510_write, + ioctl: mcp2510_ioctl +}; + +int mcp2510_init(void) +{ + printk("Voipac MCP2510 CAN controller driver (c) 2003\n"); + + mcp2510_device = devfs_register_chrdev( MCP2510_DEVICE_MAJOR, MCP2510_DEVICE_NAME, &mcp2510_fops); + + create_proc_read_entry( MCP2510_DEVICE_NAME, 0, NULL, mcp2510_read_proc, NULL); + + return 0; +} + +void mcp2510_cleanup(void) +{ +#if MCP2510_DEBUG + printk("mcp2510: cleanup\n"); +#endif + + devfs_unregister_chrdev( MCP2510_DEVICE_MAJOR, MCP2510_DEVICE_NAME); + remove_proc_entry( MCP2510_DEVICE_NAME, NULL); +} + diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-core.c linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-core.c --- linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-core.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-core.c Mon Sep 8 19:07:36 2003 @@ -0,0 +1,454 @@ +/* + * spi-core.c + * + * Copyright (C) 2002 LI-COR, Inc. + * + * Linux SPI core driver + */ + +#include +#include +#include +#include +#define EXPORT_SYMTAB +//#include +#include +#include +#include +#include + +#undef CONFIG_SYSCTL + +#ifdef CONFIG_SYSCTL +#include +#include +#endif + +#include "spi-core.h" + +#ifdef CONFIG_SYSCTL +static struct spi_local { + // Root directory /proc/sys/dev + // Second entry must be null to terminate the table + ctl_table root_table[2]; + + // Directory for this device /proc/sys/dev/ethX + // Again the second entry must be zero to terminate + ctl_table spi_table[2]; + + // This is the parameters (file) table + ctl_table param_table[CTL_SPI_LAST_ENTRY]; + + // Saves the sysctl header returned by register_sysctl_table() + // we send this to unregister_sysctl_table() + struct ctl_table_header *sysctl_header; + + // Parameter variables (files) go here +} spi_local; +static void spi_sysctl_register(struct spi_local *); +static void spi_sysctl_unregister(struct spi_local *); +#endif /* CONFIG_SYSCTL */ + +#define SPI_BUSES_MAX 8 + +static const char version[] = + "SPI core driver, originally written by Chris Lesiak , SYSCTL support by Lothar Wassmann \n\n"; + +static struct list_head spi_bus_list = LIST_HEAD_INIT(spi_bus_list); +static rwlock_t spi_bus_list_lock = RW_LOCK_UNLOCKED; +static struct proc_dir_entry *spi_proc_dir; + +void spi_bus_init(struct spi_bus *bus, struct spi_bus_operations *ops, + const char *name, void *pdata) +{ + bus->b_op = ops; + init_MUTEX(&bus->lock); + INIT_LIST_HEAD(&bus->bus_list); + bus->cmd_target = 0; + bus->cmd_pos = NULL; + bus->cmd_end = NULL; + init_waitqueue_head(&bus->doneq); + bus->private_data = pdata; + bus->working = 0; + bus->name = name; + bus->proc_entry = 0; +}; + +int spi_bus_register(struct spi_bus *new_bus) +{ + struct list_head *tmp; + + write_lock(&spi_bus_list_lock); + list_for_each(tmp, &spi_bus_list) { + struct spi_bus *bus = list_entry(tmp, struct spi_bus, bus_list); + if (0 == strcmp(new_bus->name, bus->name)) { + write_unlock(&spi_bus_list_lock); + printk(KERN_ERR + "spi-core: bus name already exists: %s\n", + new_bus->name); + return -EBUSY; + } + } + + list_add(&new_bus->bus_list, &spi_bus_list); + write_unlock(&spi_bus_list_lock); + + new_bus->proc_entry = proc_mkdir(new_bus->name, spi_proc_dir); + if (!new_bus->proc_entry) { + write_lock(&spi_bus_list_lock); + list_del(&new_bus->bus_list); + write_unlock(&spi_bus_list_lock); + printk(KERN_ERR "spi-core: error making proc dir %s\n", + new_bus->name); + return -EBUSY; + } +// new_bus->proc_entry->owner = THIS_MODULE; + +// MOD_INC_USE_COUNT; + + printk(KERN_INFO "spi-core: bus registered: %s\n", new_bus->name); + + return 0; +} + +void spi_bus_unregister(struct spi_bus *bus) +{ + struct proc_dir_entry *proc_entry = bus->proc_entry; + remove_proc_entry(proc_entry->name, proc_entry->parent); + + write_lock(&spi_bus_list_lock); + list_del(&bus->bus_list); + write_unlock(&spi_bus_list_lock); + +// MOD_DEC_USE_COUNT; + + printk(KERN_INFO "spi-core: bus unregistered: %s\n", bus->name); +} + +struct spi_bus * spi_open(const char *name) +{ + struct list_head *tmp; + + read_lock(&spi_bus_list_lock); + list_for_each(tmp, &spi_bus_list) { + struct spi_bus *bus = list_entry(tmp, struct spi_bus, bus_list); + if (0 == strcmp(name, bus->name)) { + int result = bus->b_op->open(bus); + read_unlock(&spi_bus_list_lock); + if (result != 0) { + return 0; + } + //MOD_INC_USE_COUNT; + return bus; + } + } + read_unlock(&spi_bus_list_lock); + printk(KERN_ERR "spi-core: bus not found: %s\n", name); + return 0; +} + +void spi_release(struct spi_bus *bus) +{ + bus->b_op->release(bus); +// MOD_DEC_USE_COUNT; +} + +int spi_start(struct spi_bus *bus, + int targetnum, struct spi_command *vect, int vect_len, int nonblock) +{ + + if (nonblock) { + if (down_trylock(&bus->lock)) + return -EAGAIN; + } else { + if (down_interruptible(&bus->lock)) + return -ERESTARTSYS; + } + + bus->cmd_pos = vect; + bus->cmd_end = vect + vect_len; + bus->cmd_target = targetnum; + bus->working = 1; + + spi_bus_next(bus); + if (wait_event_interruptible(bus->doneq, !bus->working)) { + bus->b_op->cancel(bus); + up(&bus->lock); + return -ERESTARTSYS; + } + + up(&bus->lock); + + return 0; +} + +int spi_bus_next(struct spi_bus *bus) +{ + if (bus->cmd_pos == bus->cmd_end) { + bus->cmd_pos = NULL; + bus->cmd_end = NULL; + bus->cmd_target = 0; + bus->working = 0; + wake_up(&bus->doneq); + } else { + struct spi_command *command = bus->cmd_pos; + + bus->cmd_pos += 1; + + switch (command->op) { + case SPI_OP_ENABLE: + if (bus->b_op->enable) { + bus->b_op->enable(bus, bus->cmd_target); + } + break; + case SPI_OP_DISABLE: + if (bus->b_op->disable) { + bus->b_op->disable(bus); + } + break; + case SPI_OP_READ: + if (bus->b_op->read) { + bus->b_op->read(bus, command->read_data, + command->length); + } + break; + case SPI_OP_WRITE: + if (bus->b_op->write) { + bus->b_op->write(bus, command->write_data, + command->length); + } + break; + case SPI_OP_WRITE_READ: + if (bus->b_op->write_read) { + bus->b_op->write_read(bus, + command->write_data, + command->read_data, + command->length); + } + break; + case SPI_OP_DELAY: + udelay(command->length); + spi_bus_next(bus); + break; + default: + printk(KERN_ERR "spi-core: command %d is invalid\n", + command->op); + bus->cmd_pos = bus->cmd_end; + if (bus->b_op->disable) { + bus->b_op->disable(bus); + } + break; + } + } + + return 0; +} + +/* + ******************************************************************** + * module init & cleanup + ******************************************************************** + */ + +static int __init spi_core_init(void) +{ + spi_proc_dir = proc_mkdir("spi", proc_bus); + if (!spi_proc_dir) { + printk(KERN_ERR "spi-core: unable to create /proc/bus/spi\n"); + return -EBUSY; + } +// spi_proc_dir->owner = THIS_MODULE; + +#ifdef CONFIG_SYSCTL + spi_sysctl_register(&spi_local); +#endif /* CONFIG_SYSCTL */ + printk(KERN_INFO "spi-core: initialized\n"); + return 0; +} + +static void __exit spi_core_exit(void) +{ +#ifdef CONFIG_SYSCTL + spi_sysctl_unregister(&spi_local); +#endif /* CONFIG_SYSCTL */ + remove_proc_entry(spi_proc_dir->name, spi_proc_dir->parent); + printk(KERN_INFO "spi-core: cleaned up\n"); +} + +#ifdef CONFIG_SYSCTL +static const char spi_info_string[] = +"\n" +"info Provides this information blurb\n" +"version Prints the software version information of this driver\n" +"clk_base base clock from which the SCLK is derived\n" +"clk_source 0=internal; 1=external;\n" +""; + +/*------------------------------------------------------------ + . Sysctl handler for all integer parameters + .-------------------------------------------------------------*/ +static int spi_sysctl_handler(ctl_table *ctl, int write, struct file * filp, + void *buffer, size_t *lenp) +{ + struct spi_local *lp=(struct spi_local*)ctl->extra1; + int *valp = ctl->data; + int val; + int ret; + +#if 1 + // nothing to do here (yet) + switch (ctl->ctl_name) { + // add processing for reading any settings here + default: + // just ignore everything else + } +#endif + // Save old state + val = *valp; + + // Perform the generic integer operation + if ((ret = proc_dointvec(ctl, write, filp, buffer, lenp)) != 0) { + return(ret); + } + // Write changes out to the registers + if (write && *valp != val) { + val = *valp; +#if 1 + switch (ctl->ctl_name) { + // add processing for modifying any settings here +#endif +#if 0 + case CTL_SPI_FILTCAR: + if (val) + lp->rcr_cur_mode |= RCR_FILT_CAR; + else + lp->rcr_cur_mode &= ~RCR_FILT_CAR; + + // Update the EPH block + spi_modify_regbit(0, ioaddr, RCR_REG, RCR_FILT_CAR, val); + break; + + case CTL_SPI_RFDUPLX: + // Disallow changes if in auto-negotiation mode + if (lp->ctl_autoneg) + break; + + if (val) + { + lp->rpc_cur_mode |= RPC_DPLX; + } + else + { + lp->rpc_cur_mode &= ~RPC_DPLX; + } + + // Reconfigure the PHY + spi_phy_configure(bus); + + break; + + case CTL_SPI_REG_EXTR: // External + spi_modify_reg(7, ioaddr, EXT_REG, val); + break; + +#endif + default: + // Just ignore unsupported parameters + break; + } // end switch + + } // end if + + return ret; +} + +/*------------------------------------------------------------ + . Sysctl registration function for all parameters (files) + .-------------------------------------------------------------*/ +static void spi_sysctl_register(struct spi_local *lp) +{ + static int ctl_name = CTL_SPI; + ctl_table* ct; + int i; + + // Make sure the ctl_tables start out as all zeros + memset(lp->root_table, 0, sizeof lp->root_table); + memset(lp->spi_table, 0, sizeof lp->spi_table); + memset(lp->param_table, 0, sizeof lp->param_table); + + // Initialize the root table + ct = lp->root_table; + ct->ctl_name = CTL_BUS; + ct->procname = "bus"; + ct->maxlen = 0; + ct->mode = 0555; + ct->child = lp->spi_table; + // remaining fields are zero + + // Initialize the spi# table (this device's table) + ct = lp->spi_table; + ct->ctl_name = ctl_name++; // Must be unique + ct->procname = "spi"; + ct->maxlen = 0; + ct->mode = 0555; + ct->child = lp->param_table; + // remaining fields are zero + + // Initialize the parameter (files) table + // Make sure the last entry remains null + ct = lp->param_table; + for (i = 0; i < (CTL_SPI_LAST_ENTRY-1); ++i) + { + // Initialize fields common to all table entries + ct[i].proc_handler = spi_sysctl_handler; + ct[i].extra1 = (void*)lp; // Save our local data pointer + } + + // INFO - this is our only string parameter + i = 0; + ct[i].proc_handler = proc_dostring; // use default handler + ct[i].ctl_name = CTL_SPI_INFO; + ct[i].procname = "info"; + ct[i].data = (void*)spi_info_string; + ct[i].maxlen = sizeof spi_info_string; + ct[i].mode = 0444; // Read only + + // VERSION + ++i; + ct[i].proc_handler = proc_dostring; // use default handler + ct[i].ctl_name = CTL_SPI_SWVER; + ct[i].procname = "version"; + ct[i].data = (void*)version; + ct[i].maxlen = sizeof version; + ct[i].mode = 0444; // Read only + + // Register /proc/sys/bus/spi + lp->sysctl_header = register_sysctl_table(lp->root_table, 1); +} + + +/*------------------------------------------------------------ + . Sysctl unregistration when driver is closed + .-------------------------------------------------------------*/ +static void spi_sysctl_unregister(struct spi_local *lp) +{ + unregister_sysctl_table(lp->sysctl_header); +} + +#endif /* endif CONFIG_SYSCTL */ + +//module_init(spi_core_init); +//module_exit(spi_core_exit); + +//EXPORT_SYMBOL(spi_bus_init); +//EXPORT_SYMBOL(spi_bus_register); +//EXPORT_SYMBOL(spi_bus_unregister); +//EXPORT_SYMBOL(spi_bus_next); +//EXPORT_SYMBOL(spi_open); +//EXPORT_SYMBOL(spi_release); +//EXPORT_SYMBOL(spi_start); + +//MODULE_AUTHOR("Chris Lesiak "); +//MODULE_DESCRIPTION("SPI core driver"); +//MODULE_LICENSE("GPL"); + diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-core.h linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-core.h --- linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-core.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-core.h Mon Sep 8 18:38:20 2003 @@ -0,0 +1,136 @@ +/* + * spi-core.h + * + * Interface for the Linux SPI subsystem + * + * Copyright (c) 2002 LI-COR, Inc. + * + * Author: Chris Lesiak + */ + +#ifndef _SPI_CORE_H_ +#define _SPI_CORE_H_ + +#include +#include + +#include + +#ifdef CONFIG_SYSCTL +/* + * Declarations for the sysctl interface, which allows users the ability to + * control the SPI bus global settings + * + */ +#define CTL_SPI (CTL_BUS+1789) // arbitrary and hopefully unused +enum { + CTL_SPI_INFO = 1, // Sysctl files information + CTL_SPI_SWVER, // Driver Software Version Info + // --------------------------------------------------- + CTL_SPI_LAST_ENTRY // Add new entries above the line +}; + +#endif // CONFIG_SYSCTL + +/* + * spi_bus_operations is the interface that each bus driver must implement. + * The enable member asserts the chip select for the target numbe indicated. + * The TARGET_NONE is used to deselect all devices on the bus. + */ + +#define TARGET_NONE -1 + +struct spi_bus; + +struct spi_bus_operations { + int (*open) (struct spi_bus * bus); + void (*enable) (struct spi_bus * bus, int target_num); + void (*disable) (struct spi_bus * bus); + void (*read) (struct spi_bus * bus, u8 * data, size_t length); + void (*write) (struct spi_bus * bus, const u8 * data, size_t length); + void (*write_read) (struct spi_bus * bus, + const u8 * write_data, u8 * read_data, + size_t length); + void (*cancel) (struct spi_bus * bus); + void (*release) (struct spi_bus * bus); +#if 0 + int (*get_sysctl)(struct spi_bus*, int ctl_name); + int (*set_sysctl)(struct spi_bus*, int ctl_name); +#endif +}; + +/* + * spi_bus + * + * No members should be set directly + * private_data can be used by the implementer if the bus driver. + */ + +struct spi_bus { + struct spi_bus_operations *b_op; + struct semaphore lock; + struct list_head bus_list; + int cmd_target; + struct spi_command *cmd_pos; + struct spi_command *cmd_end; + wait_queue_head_t doneq; + void *private_data; + int working; + const char *name; + struct proc_dir_entry *proc_entry; +#ifdef CONFIG_SYSCTL +#endif // CONFIG_SYSCTL +}; + +extern void spi_bus_init(struct spi_bus *bus, + struct spi_bus_operations *ops, + const char *name, void *pdata); + +extern int spi_bus_register(struct spi_bus *bus); +extern void spi_bus_unregister(struct spi_bus *bus); +extern int spi_bus_next(struct spi_bus *bus); + +/* + * Interface for upper level drivers + */ + +#if 1 +typedef enum { + SPI_OP_ENABLE, + SPI_OP_DISABLE, + SPI_OP_READ, + SPI_OP_WRITE, + SPI_OP_WRITE_READ, + SPI_OP_DELAY, + SPI_OP_SET_FRAMELEN, + SPI_OP_SET_CLKFREQ, + SPI_OP_SET_BASE_CLOCK, +/* insert more opcodes BEFORE this line */ + SPI_NUM_OPS +} spi_op_codes_t; +#else +#define SPI_OP_ENABLE 0 +#define SPI_OP_DISABLE 1 +#define SPI_OP_READ 2 +#define SPI_OP_WRITE 3 +#define SPI_OP_WRITE_READ 4 +#define SPI_OP_DELAY 5 +#endif + +struct spi_command { + int op; + u8 *read_data; + const u8 *write_data; + size_t length; +}; + +/* Initilizes our device's sysctl proc filesystem */ + +extern struct spi_bus *spi_open(const char *name); +extern void spi_release(struct spi_bus *bus); + +extern int spi_start(struct spi_bus *bus, + int targetnum, + struct spi_command *vect, int vect_len, int nonblock); + +#endif // _SPI_CORE_H_ diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-dev.c linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-dev.c --- linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-dev.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-dev.c Mon Sep 8 18:38:20 2003 @@ -0,0 +1,486 @@ +/* + spi-dev.c - spi-bus driver, char device interface + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* $Id: */ + +#include +#include +#include +#include +#include +#include +#if LINUX_KERNEL_VERSION >= KERNEL_VERSION(2,4,0) +#include +#endif /* LINUX_KERNEL_VERSION >= KERNEL_VERSION(2,4,0) */ +#ifdef CONFIG_DEVFS_FS +#include +#endif + +#include +#include + +#include "spi-core.h" +#include "spi-dev.h" + +static ssize_t spidev_read (struct file *file, char *buf, size_t count, loff_t *offset); +static ssize_t spidev_write (struct file *file, const char *buf, size_t count, loff_t *offset); +static int spidev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int spidev_open (struct inode *inode, struct file *file); +static int spidev_release (struct inode *inode, struct file *file); + +static int spidev_attach_adapter(struct spi_adapter *adap); +static int spidev_detach_client(struct spi_client *client); +static int spidev_command(struct spi_client *client, unsigned int cmd, void *arg); + +static int __init spidev_init(void); +static void spidev_cleanup(void); + +static struct file_operations spidev_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: spidev_read, + write: spidev_write, + ioctl: spidev_ioctl, + open: spidev_open, + release: spidev_release, +}; + +static struct spi_adapter *spidev_adaps[SPI_DEV_MAX]; +#ifdef CONFIG_DEVFS_FS +static devfs_handle_t devfs_spi[SPI_DEV_MAX]; +static devfs_handle_t devfs_handle = NULL; +#endif + +static struct spi_driver spidev_driver = { + name: "spi-dev dummy driver", + id: SPI_DRIVERID_SPIDEV, + flags: SPI_DF_DUMMY, + attach_adapter: spidev_attach_adapter, + detach_client: spidev_detach_client, + command: spidev_command, +/* inc_use: NULL, + dec_use: NULL, */ +}; + +static struct spi_client spidev_client_template = { + name: "SPI /dev entry", + id: 1, + flags: 0, + addr: -1, +/* adapter: NULL, */ + driver: &spidev_driver, +/* data: NULL */ +}; + +static int spidev_initialized; + +static ssize_t spidev_read (struct file *file, char *buf, size_t count, + loff_t *offset) +{ + char *tmp; + int ret; + +#ifdef DEBUG + struct inode *inode = file->f_dentry->d_inode; +#endif /* DEBUG */ + + struct spi_client *client = (struct spi_client *)file->private_data; + + /* copy user space data to kernel space. */ + tmp = kmalloc(count, GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + +#ifdef DEBUG + printk(KERN_DEBUG "spi-dev.o: spi-%d reading %d bytes.\n",minor(inode->i_rdev), + count); +#endif + + ret = spi_master_recv(client,tmp,count); + if (ret >= 0) + ret = copy_to_user(buf,tmp,count)?-EFAULT:ret; + kfree(tmp); + return ret; +} + +static ssize_t spidev_write (struct file *file, const char *buf, size_t count, + loff_t *offset) +{ + int ret; + char *tmp; + struct spi_client *client = (struct spi_client *)file->private_data; + +#ifdef DEBUG + struct inode *inode = file->f_dentry->d_inode; +#endif /* DEBUG */ + + /* copy user space data to kernel space. */ + tmp = kmalloc(count, GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + if (copy_from_user(tmp,buf,count)) { + kfree(tmp); + return -EFAULT; + } + +#ifdef DEBUG + printk(KERN_DEBUG "spi-dev.o: spi-%d writing %d bytes.\n",minor(inode->i_rdev), + count); +#endif + ret = spi_master_send(client,tmp,count); + kfree(tmp); + return ret; +} + +int spidev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct spi_client *client = (struct spi_client *)file->private_data; + struct spi_rdwr_ioctl_data rdwr_arg; + struct spi_smbus_ioctl_data data_arg; + union spi_smbus_data temp; + struct spi_msg *rdwr_pa; + int i,datasize,res; + unsigned long funcs; + +#ifdef DEBUG + printk(KERN_DEBUG "spi-dev.o: spi-%d ioctl, cmd: 0x%x, arg: %lx.\n", + minor(inode->i_rdev),cmd, arg); +#endif /* DEBUG */ + + switch ( cmd ) { + case SPI_SLAVE: + case SPI_SLAVE_FORCE: + if ((arg > 0x3ff) || + (((client->flags & SPI_M_TEN) == 0) && arg > 0x7f)) + return -EINVAL; + if ((cmd == SPI_SLAVE) && spi_check_addr(client->adapter,arg)) + return -EBUSY; + client->addr = arg; + return 0; + case SPI_TENBIT: + if (arg) + client->flags |= SPI_M_TEN; + else + client->flags &= ~SPI_M_TEN; + return 0; + case SPI_FUNCS: + funcs = spi_get_functionality(client->adapter); + return (copy_to_user((unsigned long *)arg,&funcs, + sizeof(unsigned long)))?-EFAULT:0; + + case SPI_RDWR: + if (copy_from_user(&rdwr_arg, + (struct spi_rdwr_ioctl_data *)arg, + sizeof(rdwr_arg))) + return -EFAULT; + + rdwr_pa = (struct spi_msg *) + kmalloc(rdwr_arg.nmsgs * sizeof(struct spi_msg), + GFP_KERNEL); + + if (rdwr_pa == NULL) return -ENOMEM; + + res = 0; + for( i=0; iadapter, + rdwr_pa, + rdwr_arg.nmsgs); + } + while(i-- > 0) + { + if( res>=0 && (rdwr_pa[i].flags & SPI_M_RD)) + { + if(copy_to_user( + rdwr_arg.msgs[i].buf, + rdwr_pa[i].buf, + rdwr_pa[i].len)) + { + res = -EFAULT; + } + } + kfree(rdwr_pa[i].buf); + } + kfree(rdwr_pa); + return res; + + case SPI_SMBUS: + if (copy_from_user(&data_arg, + (struct spi_smbus_ioctl_data *) arg, + sizeof(struct spi_smbus_ioctl_data))) + return -EFAULT; + if ((data_arg.size != SPI_SMBUS_BYTE) && + (data_arg.size != SPI_SMBUS_QUICK) && + (data_arg.size != SPI_SMBUS_BYTE_DATA) && + (data_arg.size != SPI_SMBUS_WORD_DATA) && + (data_arg.size != SPI_SMBUS_PROC_CALL) && + (data_arg.size != SPI_SMBUS_BLOCK_DATA) && + (data_arg.size != SPI_SMBUS_SPI_BLOCK_DATA)) { +#ifdef DEBUG + printk(KERN_DEBUG "spi-dev.o: size out of range (%x) in ioctl SPI_SMBUS.\n", + data_arg.size); +#endif + return -EINVAL; + } + /* Note that SPI_SMBUS_READ and SPI_SMBUS_WRITE are 0 and 1, + so the check is valid if size==SPI_SMBUS_QUICK too. */ + if ((data_arg.read_write != SPI_SMBUS_READ) && + (data_arg.read_write != SPI_SMBUS_WRITE)) { +#ifdef DEBUG + printk(KERN_DEBUG "spi-dev.o: read_write out of range (%x) in ioctl SPI_SMBUS.\n", + data_arg.read_write); +#endif + return -EINVAL; + } + + /* Note that command values are always valid! */ + + if ((data_arg.size == SPI_SMBUS_QUICK) || + ((data_arg.size == SPI_SMBUS_BYTE) && + (data_arg.read_write == SPI_SMBUS_WRITE))) + /* These are special: we do not use data */ + return spi_smbus_xfer(client->adapter, client->addr, + client->flags, + data_arg.read_write, + data_arg.command, + data_arg.size, NULL); + + if (data_arg.data == NULL) { +#ifdef DEBUG + printk(KERN_DEBUG "spi-dev.o: data is NULL pointer in ioctl SPI_SMBUS.\n"); +#endif + return -EINVAL; + } + + if ((data_arg.size == SPI_SMBUS_BYTE_DATA) || + (data_arg.size == SPI_SMBUS_BYTE)) + datasize = sizeof(data_arg.data->byte); + else if ((data_arg.size == SPI_SMBUS_WORD_DATA) || + (data_arg.size == SPI_SMBUS_PROC_CALL)) + datasize = sizeof(data_arg.data->word); + else /* size == SPI_SMBUS_BLOCK_DATA */ + datasize = sizeof(data_arg.data->block); + + if ((data_arg.size == SPI_SMBUS_PROC_CALL) || + (data_arg.read_write == SPI_SMBUS_WRITE)) { + if (copy_from_user(&temp, data_arg.data, datasize)) + return -EFAULT; + } + res = spi_smbus_xfer(client->adapter,client->addr,client->flags, + data_arg.read_write, + data_arg.command,data_arg.size,&temp); + if (! res && ((data_arg.size == SPI_SMBUS_PROC_CALL) || + (data_arg.read_write == SPI_SMBUS_READ))) { + if (copy_to_user(data_arg.data, &temp, datasize)) + return -EFAULT; + } + return res; + + default: + return spi_control(client,cmd,arg); + } + return 0; +} + +int spidev_open (struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct spi_client *client; + + if ((minor >= SPI_DEV_MAX) || ! (spidev_adaps[minor])) { +#ifdef DEBUG + printk(KERN_DEBUG "spi-dev.o: Trying to open unattached adapter spi-%d\n", + minor); +#endif + return -ENODEV; + } + + /* Note that we here allocate a client for later use, but we will *not* + register this client! Yes, this is safe. No, it is not very clean. */ + if(! (client = kmalloc(sizeof(struct spi_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(client,&spidev_client_template,sizeof(struct spi_client)); + client->adapter = spidev_adaps[minor]; + file->private_data = client; + +#ifdef INC_DEC_USE + if (spidev_adaps[minor]->inc_use) { + spidev_adaps[minor]->inc_use(spidev_adaps[minor]); + } +#endif + MOD_INC_USE_COUNT; + +#ifdef DEBUG + printk(KERN_DEBUG "spi-dev.o: opened spi-%d\n",minor); +#endif + return 0; +} + +static int spidev_release (struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + kfree(file->private_data); + file->private_data=NULL; +#ifdef DEBUG + printk(KERN_DEBUG "spi-dev.o: Closed: spi-%d\n", minor); +#endif + MOD_DEC_USE_COUNT; +#ifdef INC_DEC_USE + lock_kernel(); + if (spidev_adaps[minor]->dec_use) { + spidev_adaps[minor]->dec_use(spidev_adaps[minor]); + } + unlock_kernel(); +#endif + return 0; +} + +int spidev_attach_adapter(struct spi_adapter *adap) +{ + int i; + char name[8]; + + if ((i = spi_adapter_id(adap)) < 0) { + printk(KERN_DEBUG "spi-dev.o: Unknown adapter ?!?\n"); + return -ENODEV; + } + if (i >= SPI_DEV_MAX) { + printk(KERN_DEBUG "spi-dev.o: Adapter number too large?!? (%d)\n",i); + return -ENODEV; + } + + sprintf (name, "%d", i); + if (! spidev_adaps[i]) { + spidev_adaps[i] = adap; +#ifdef CONFIG_DEVFS_FS + devfs_spi[i] = devfs_register (devfs_handle, name, + DEVFS_FL_DEFAULT, SPI_MAJOR, i, + S_IFCHR | S_IRUSR | S_IWUSR, + &spidev_fops, NULL); +#endif + printk(KERN_DEBUG "spi-dev.o: Registered '%s' as minor %d\n",adap->name,i); + } else { + /* This is actually a detach_adapter call! */ +#ifdef CONFIG_DEVFS_FS + devfs_unregister(devfs_spi[i]); +#endif + spidev_adaps[i] = NULL; +#ifdef DEBUG + printk(KERN_DEBUG "spi-dev.o: Adapter unregistered: %s\n",adap->name); +#endif + } + + return 0; +} + +int spidev_detach_client(struct spi_client *client) +{ + return 0; +} + +static int spidev_command(struct spi_client *client, unsigned int cmd, + void *arg) +{ + return -1; +} + +static int __init spidev_init(void) +{ + int res; + + printk(KERN_DEBUG "spi-dev.o: spi char device driver module version %s (%s)\n", + SPI_VERSION, SPI_DATE); + + spidev_initialized = 0; + if (register_chrdev(SPI_MAJOR,"spi",&spidev_fops)) { + printk(KERN_ERR "spi-dev.o: unable to get major %d for spi bus\n", + SPI_MAJOR); + return -EIO; + } +#ifdef CONFIG_DEVFS_FS + devfs_handle = devfs_mk_dir(NULL, "spi", NULL); +#endif + spidev_initialized ++; + + if ((res = spi_add_driver(&spidev_driver))) { + printk(KERN_ERR "spi-dev.o: Driver registration failed, module not inserted.\n"); + spidev_cleanup(); + return res; + } + spidev_initialized ++; + return 0; +} + +static void spidev_cleanup(void) +{ + int res; + + if (spidev_initialized >= 2) { + if ((res = spi_del_driver(&spidev_driver))) { + printk(KERN_ERR "spi-dev.o: Driver deregistration failed, " + "module not removed.\n"); + return; + } + spidev_initialized --; + } + + if (spidev_initialized >= 1) { +#ifdef CONFIG_DEVFS_FS + devfs_unregister(devfs_handle); +#endif + if ((res = unregister_chrdev(SPI_MAJOR,"spi"))) { + printk(KERN_ERR "spi-dev.o: unable to release major %d for spi bus\n", + SPI_MAJOR); + return; + } + spidev_initialized --; + } +} + +MODULE_AUTHOR("Lothar Wassmann "); +MODULE_DESCRIPTION("SPI char device driver"); +MODULE_LICENSE("GPL"); + +module_init(spidev_init); +module_exit(spidev_cleanup); + diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-dev.h linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-dev.h --- linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-dev.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-dev.h Mon Sep 8 18:38:20 2003 @@ -0,0 +1,135 @@ +/* + spi-dev.h - spi-bus driver, char device interface + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* $Id: spi-dev.h,v 1.2 2002/10/03 14:13:21 lothar Exp $ */ + +#ifndef __SPI_DEV_H +#define __SPI_DEV_H + +#include +#include "spi-core.h" + +struct spi_bus_ioctl_data { + char read_write; + __u8 command; + int size; + union spi_bus_data *data; +}; + +struct spi_rdwr_ioctl_data { + struct spi_msg *msgs; + int nmsgs; +}; + +#ifndef __KERNEL__ + +#include + +static inline __s32 spi_bus_access(int file, char read_write, __u8 command, + int size, union spi_bus_data *data) +{ + struct spi_bus_ioctl_data args; + + args.read_write = read_write; + args.command = command; + args.size = size; + args.data = data; + + return ioctl(file, SPI_BUS, &args); +} + +static inline __s32 spi_bus_read_byte(int file) +{ + union spi_bus_data data; + if (spi_bus_access(file, SPI_BUS_READ, 0, SPI_BUS_BYTE, &data)) { + return -1; + } else { + return data.byte; + } +} + +static inline __s32 spi_bus_write_byte(int file, __u8 value) +{ + return spi_bus_access(file, SPI_BUS_WRITE, value, + SPI_BUS_BYTE, NULL); +} + +static inline __s32 spi_bus_read_word(int file, __u8 command) +{ + union spi_bus_data data; + + if (spi_bus_access(file, SPI_BUS_READ, command, SPI_BUS_WORD_DATA, &data)) { + return -1; + } else { + return data.word; + } +} + +static inline __s32 spi_bus_write_word(int file, __u8 command, __u16 value) +{ + union spi_bus_data data; + data.word = value; + return spi_bus_access(file, SPI_BUS_WRITE, command, SPI_BUS_WORD_DATA, &data); +} + +static inline __s32 spi_bus_process_call(int file, __u8 command, __u16 value) +{ + union spi_bus_data data; + data.word = value; + if (spi_bus_access(file, SPI_BUS_WRITE, command, SPI_BUS_PROC_CALL, &data)) { + return -1; + } else { + return data.word; + } +} + +static inline __s32 spi_bus_read_block(int file, __u8 command, __u8 *values) +{ + union spi_bus_data data; + int i; + if (spi_bus_access(file, SPI_BUS_READ, command, SPI_BUS_BLOCK_DATA, &data)) { + return -1; + } else { + for (i = 1; i <= data.block[0]; i++) { + values[i-1] = data.block[i]; + } + return data.block[0]; + } +} + +static inline __s32 spi_bus_write_block(int file, __u8 command, __u8 length, __u8 *values) +{ + union spi_bus_data data; + int i; + __s32 ret; + + if (length > 32) { + length = 32; + } + for (i = 1; i <= length; i++) { + data.block[i] = values[i-1]; + } + data.block[0] = length; + ret = spi_bus_access(file, SPI_BUS_WRITE, command, SPI_BUS_BLOCK_DATA, &data); + + return ret; +} + +#endif // __KERNEL__ + +#endif // __SPI_DEV_H diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-pxa.c linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-pxa.c --- linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-pxa.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-pxa.c Wed Sep 17 13:36:54 2003 @@ -0,0 +1,845 @@ +/* + * drivers/ssi/sspc-pxa.c + * + * Linux PXA SSPC bus driver + * + * derived from Chris Lesiak's + * device driver for SA1100 + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_SYSCTL +#include +#include +#endif + +#include "spi-pxa.h" + +#ifdef CONFIG_ARCH_INPHINITY +static int inphinity_target_select(struct pxa_sspc_dev* sspc_dev, int target); +#endif + +#include + +#ifdef CONFIG_SYSCTL +static void spi_pxa_sysctl_register(struct pxa_sspc_dev*); +static void spi_pxa_sysctl_unregister(struct pxa_sspc_dev*); +#endif /* CONFIG_SYSCTL */ + +static const char version[] = + "SPI bus driver for PXA250, Lothar Wassmann based on SA1100 driver by Chris Lesiak \n\n"; + +/************************************ + * helper routines + ************************************/ +static int pxa_sspc_target_select(struct pxa_sspc_dev* sspc_dev, int target) +{ + int ret=0; + if (target != TARGET_NONE && sspc_dev->target_select) { + ret = sspc_dev->target_select(sspc_dev, target); + } + sspc_dev->target = target; + + return ret; +} + +static int pxa_sspc_setup(struct pxa_sspc_dev* sspc_dev, int target) +{ + int retval=0; + + u32 sscr0=SSCR0_SSE; //SSCR0 & ~(SSCR0_FRF|SSCR0_DSS|SSCR0_ECS); + u32 sscr1=SSCR1 & ~(SSCR1_SPO|SSCR1_SPH); + int clk_base=SSPC_CLK_BASE; + int divisor=-1; + + pxa_sspc_target_select(sspc_dev, target); +#ifdef CONFIG_SSPC_EXTCLK + if (sspc_dev->clk_src != 0) { + clk_base = sspc_dev->clk_base; + } +#endif +#if 1 + if (sspc_dev->framelen == 0) { + sspc_dev->framelen = 8; //16; + } +#endif + if (sspc_dev->clk_freq > 0) { + if (sspc_dev->clk_freq > clk_base/2) { + printk(KERN_WARNING "%s: desired clock freq %d > max. clock %d; using %d\n", + __FUNCTION__, sspc_dev->clk_freq, clk_base/2, clk_base/2); + divisor = 0; + } else if (sspc_dev->clk_freq < clk_base/(2*((SSCR0_SCR>>SSCR0_SCR_SHIFT)+1))) { + printk(KERN_WARNING "%s: desired clock freq %d < min. clock %d\n", + __FUNCTION__, sspc_dev->clk_freq, + clk_base/(2*((SSCR0_SCR>>SSCR0_SCR_SHIFT)+1))); + retval = -EINVAL; + } else { + int i; + //divisor = (clk_base / (sspc_dev->clk_freq * 2)) - 1; + divisor = SSCR0_SCR>>SSCR0_SCR_SHIFT; + for (i=divisor+1;i>0;i>>=1) { + int div=divisor & ~i; + + if (clk_base / (2*(div+1)) <= sspc_dev->clk_freq) { + divisor = div; + } + } + } + if (retval == 0) { + if (divisor == (divisor & (SSCR0_SCR >> SSCR0_SCR_SHIFT))) { + sscr0 |= divisor << SSCR0_SCR_SHIFT; + sspc_dev->clk_div = divisor; + // update `clk_freq' to actually used frequency + if (clk_base / (2*(divisor+1)) != sspc_dev->clk_freq) { + sspc_dev->clk_freq = clk_base / (2*(divisor+1)); + printk(KERN_INFO "%s: actual clock frequency is: %d\n", + __FUNCTION__, sspc_dev->clk_freq); + } + } else { + printk(KERN_WARNING "%s: clock frequency %d out of range [%d..%d]\n", + __FUNCTION__, clk_base / (2*(divisor+1)), + clk_base / (2*((SSCR0_SCR>>SSCR0_SCR_SHIFT)+1)), clk_base); + retval = -EINVAL; + } + } + } else { + divisor = 255; + sspc_dev->clk_div = divisor; + sspc_dev->clk_freq = clk_base / (2*(divisor+1)); + printk(KERN_WARNING "%s: using default clock frequency %d\n", + __FUNCTION__, sspc_dev->clk_freq); + } + + if (sspc_dev->framelen >= SSPC_MIN_FRAME_LEN && sspc_dev->framelen <= SSPC_MAX_FRAME_LEN) { + sscr0 |= sspc_dev->framelen -1; + } else { + printk(KERN_WARNING "%s: frame length %d out of range [%d..%d]\n", __FUNCTION__, + sspc_dev->framelen, SSPC_MIN_FRAME_LEN, SSPC_MAX_FRAME_LEN); + retval = -EINVAL; + } + + sscr1 |= sspc_dev->clock_mode & SSI_CLK_FALLING ? SSCR1_SPO : 0; + sscr1 |= sspc_dev->clock_mode & SSI_CLK_PHASE ? SSCR1_SPH : 0; + + switch(sspc_dev->proto) { + case SSI_SPI: + SSCR0 = sscr0 | SSCR0_FRF_SPI; + break; + case SSI_MICROWIRE: + SSCR0 = sscr0 | SSCR0_FRF_NMW; + if (sspc_dev->framelen == 8) { + sscr1 &= ~SSCR1_MWDS; + } else if (sspc_dev->framelen == 16) { + sscr1 |= SSCR1_MWDS; + } else { + printk(KERN_WARNING "%s: invalid data size %d for Microwire protocol;" + "only 8 or 16 are valid\n", __FUNCTION__, sspc_dev->framelen); + retval = -EINVAL; + } + break; + case SSI_TISSF: + SSCR0 = sscr0 | SSCR0_FRF_SSP; + break; + default: + printk(KERN_WARNING "%s: unsupported protocol %d\n", __FUNCTION__, sspc_dev->proto); + retval = -EINVAL; + } + if (retval == 0) { + SSCR0 = sscr0; + SSCR1 = sscr1; + } + + return retval; +} + +inline static void ssp_enable(const struct pxa_sspc_dev* dev) { + SSSR = SSSR_ROR; // clear receiver overrun flag + SSCR0 |= SSCR0_SSE; +} + +inline static void ssp_enable_rx_intr(const struct pxa_sspc_dev* dev) { + SSCR1 |= SSCR1_RIE; +} + +inline static void ssp_enable_tx_intr(const struct pxa_sspc_dev* dev) { + SSCR1 |= SSCR1_RIE | SSCR1_TIE; +} + +inline static void ssp_disable_intr(const struct pxa_sspc_dev* dev) { + SSCR1 &= ~(SSCR1_RIE | SSCR1_TIE); +} + +inline static void ssp_disable(const struct pxa_sspc_dev* dev) { + SSCR0 &= ~SSCR0_SSE; +} + +inline static void ssp_write(struct pxa_sspc_dev* dev, u16 val) { + + SSDR = val; + dev->write_count += 2; +} + +inline static u16 ssp_read(struct pxa_sspc_dev* dev) { + u32 val32=SSDR; + u16 val = (u16)(val32 & 0xffff); + + dev->write_count -= 2; + + return val; +} + +inline static void ssp_clear_read_fifo(struct pxa_sspc_dev* dev) { + while (SSSR & SSSR_RNE) { + ssp_read(dev); + } +} + +/************************************ + * high level PXA SSPC routines + ************************************/ + +static int pxa_sspc_open(struct spi_bus* bus) { + int retval=0; + + retval = pxa_sspc_setup(bus->private_data, TARGET_NONE); + if (retval == 0) { + MOD_INC_USE_COUNT; + } + + return retval; +} + +static void pxa_sspc_release(struct spi_bus* bus) { + MOD_DEC_USE_COUNT; +} + +static void pxa_sspc_enable(struct spi_bus* bus, int target_num) +{ + struct pxa_sspc_dev* sspc_dev = bus->private_data; + + if (sspc_dev->target != target_num) { + pxa_sspc_setup(sspc_dev, target_num); + } + ssp_enable(sspc_dev); + + spi_bus_next(bus); +} + +static void pxa_sspc_disable(struct spi_bus* bus) +{ + struct pxa_sspc_dev* sspc_dev = bus->private_data; + + ssp_disable(sspc_dev); + + spi_bus_next(bus); +} + +static void pxa_sspc_read(struct spi_bus* bus, u8* data, size_t count) +{ + struct pxa_sspc_dev* sspc_dev = bus->private_data; + + if (data != NULL && count != 0) { + ssp_clear_read_fifo(sspc_dev); + sspc_dev->read_pos = data; + sspc_dev->write_pos = data; + sspc_dev->read_end = data + count; + sspc_dev->write_end = data + count; + sspc_dev->write_count = 0; + sspc_dev->reading = 1; + sspc_dev->preloading = 1; + + ssp_enable_rx_intr(sspc_dev); + + while ((sspc_dev->write_pos < sspc_dev->write_end) && (SSSR & SSSR_TNF)) { + ssp_write(sspc_dev, (*sspc_dev->write_pos++) | + ((*sspc_dev->write_pos++) << 8)); + } + sspc_dev->preloading = 0; + ssp_enable_tx_intr(sspc_dev); + } else { + spi_bus_next(bus); + } +} + +static void pxa_sspc_write(struct spi_bus* bus, const u8* data, size_t count) +{ + struct pxa_sspc_dev* sspc_dev = bus->private_data; + + if (data != NULL && count != 0) { + sspc_dev->write_pos = data; + sspc_dev->write_end = data + count; + sspc_dev->write_count = 0; + sspc_dev->reading = 0; + sspc_dev->preloading = 1; + + ssp_enable_rx_intr(sspc_dev); + + while ((sspc_dev->write_pos < sspc_dev->write_end) && (SSSR & SSSR_TNF)) { + ssp_write(sspc_dev, (*sspc_dev->write_pos++) | + ((*sspc_dev->write_pos++) << 8)); + } + + sspc_dev->preloading = 0; + ssp_enable_tx_intr(sspc_dev); + } else { + spi_bus_next(bus); + } +} + +static void pxa_sspc_write_read(struct spi_bus* bus, + const u8* write_data, + u8* read_data, + size_t count) +{ + struct pxa_sspc_dev* sspc_dev=bus->private_data; + + if (write_data != NULL && read_data != NULL && count != 0) { + ssp_clear_read_fifo(sspc_dev); + sspc_dev->read_pos = read_data; + sspc_dev->write_pos = write_data; + sspc_dev->read_end = read_data + count; + sspc_dev->write_end = write_data + count; + sspc_dev->write_count = 0; + sspc_dev->reading = 1; + sspc_dev->preloading = 1; + + ssp_enable_rx_intr(sspc_dev); + + while ((sspc_dev->write_pos < sspc_dev->write_end) && (SSSR & SSSR_TNF)) { + ssp_write(sspc_dev, (*sspc_dev->write_pos++) | + ((*sspc_dev->write_pos++) << 8)); + } + sspc_dev->preloading = 0; + ssp_enable_tx_intr(sspc_dev); + } else { + spi_bus_next(bus); + } +} + +static void pxa_sspc_cancel(struct spi_bus* bus) { + struct pxa_sspc_dev* sspc_dev=bus->private_data; + + sspc_dev->write_count = 0; + ssp_disable(sspc_dev); + //GPSR = GPIO_DAC0_CE | GPIO_DAC1_CE | GPIO_DAC2_CE; +} + +static void pxa_sspc_interrupt_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + struct pxa_sspc_dev* sspc_dev=dev_id; + + if ((SSCR1 & SSCR1_TIE) && (sspc_dev->write_pos == sspc_dev->write_end)) { + ssp_disable_intr(sspc_dev); + tasklet_schedule(&sspc_dev->tasklet); + } else { + while (SSSR & SSSR_RNE) { + u16 val16 = ssp_read(sspc_dev); + if (sspc_dev->reading && (sspc_dev->read_pos < sspc_dev->read_end)) { + *sspc_dev->read_pos++ = val16 & 0xff; + *sspc_dev->read_pos++ = val16 >> 8; + } + } + + if (!sspc_dev->preloading) { + while ((sspc_dev->write_pos < sspc_dev->write_end) && (SSSR & SSSR_TNF)) { + ssp_write(sspc_dev, (*sspc_dev->write_pos++) | + ((*sspc_dev->write_pos++) << 8)); + } + } + } +} + +static void pxa_sspc_tasklet(unsigned long ptr) +{ + struct pxa_sspc_dev* sspc_dev=(struct pxa_sspc_dev*)ptr; + + while (sspc_dev->write_count > 0) { + if (SSSR & SSSR_RNE) { + u16 val16 = ssp_read(sspc_dev); + if (sspc_dev->reading && (sspc_dev->read_pos < sspc_dev->read_end)) { + *sspc_dev->read_pos++ = val16 & 0xff; + *sspc_dev->read_pos++ = val16 >> 8; + } + } + } + spi_bus_next(&sspc_dev->spibus); +} + +/************************************ + * pxa sspc interface + ************************************/ + +struct spi_bus_operations pxa_sspc_ops = { + pxa_sspc_open, + pxa_sspc_enable, + pxa_sspc_disable, + pxa_sspc_read, + pxa_sspc_write, + pxa_sspc_write_read, + pxa_sspc_cancel, + pxa_sspc_release +}; + + +/* + *************************************************************************** + * Module Initialization + *************************************************************************** + */ + +static struct pxa_sspc_dev* pxa_sspc_device; + +static int __init pxa_sspc_init(void) +{ + int ret; + struct pxa_sspc_dev* sspc_dev; + + sspc_dev = kmalloc(sizeof(struct pxa_sspc_dev), GFP_KERNEL); + if (sspc_dev == NULL) { + printk(KERN_ERR "pxa_sspc: out of memory\n"); + return -ENOMEM; + } + pxa_sspc_device = sspc_dev; + + memset(sspc_dev, 0, sizeof(pxa_sspc_device[0])); + + tasklet_init(&sspc_dev->tasklet, pxa_sspc_tasklet, (unsigned long)sspc_dev); + + ret = request_irq(IRQ_SSP, pxa_sspc_interrupt_handler, + 0, "sspc", sspc_dev); + if (ret != 0) { + kfree(sspc_dev); + printk(KERN_ERR "pxa_sspc: error requesting irq\n"); + return ret; + } + + set_GPIO_mode(GPIO23_SCLK_MD); + set_GPIO_mode(GPIO24_SFRM_MD); + set_GPIO_mode(GPIO25_STXD_MD); + set_GPIO_mode(GPIO26_SRXD_MD); +#ifdef CONFIG_SSPC_EXTCLK + set_GPIO_mode(GPIO27_SEXTCLK_MD); +#endif + sspc_dev->clk_base = SSPC_CLK_BASE; + +#ifdef CONFIG_ARCH_INPHINITY + sspc_dev->target_select = inphinity_target_select; +#endif + spi_bus_init(&sspc_dev->spibus, &pxa_sspc_ops, "pxa", sspc_dev); + + // Register the SSPC bus + ret = spi_bus_register(&sspc_dev->spibus); + if (ret != 0) { + kfree(sspc_dev); + printk(KERN_ERR "sspc-pxa: unable to register bus\n"); + return ret; + } + + printk(KERN_INFO "sspc-pxa: bus registered\n"); + +#ifdef CONFIG_SYSCTL + spi_pxa_sysctl_register(sspc_dev); +#endif /* CONFIG_SYSCTL */ + return 0; +} + +static void __exit pxa_sspc_exit(void) +{ + struct pxa_sspc_dev* sspc_dev = pxa_sspc_device; + +#ifdef CONFIG_SYSCTL + spi_pxa_sysctl_unregister(sspc_dev); +#endif /* CONFIG_SYSCTL */ + SSCR0 = 0; + + GPSR(23) = GPIO_bit(23) | GPIO_bit(24) | GPIO_bit(25) | GPIO_bit(26) | GPIO_bit(27); + + set_GPIO_mode(GPIO23_SCLK|GPIO_OUT); + set_GPIO_mode(GPIO24_SFRM|GPIO_OUT); + set_GPIO_mode(GPIO25_STXD|GPIO_OUT); + set_GPIO_mode(GPIO26_SRXD|GPIO_OUT); + set_GPIO_mode(GPIO27_SEXTCLK|GPIO_OUT); + + free_irq(IRQ_SSP, sspc_dev); + spi_bus_unregister(&sspc_dev->spibus); + kfree(sspc_dev); + + printk(KERN_INFO "sspc-pxa: bus unregistered\n"); +} + +#ifdef CONFIG_SYSCTL +static const char spi_info_string[] = +"\n" +"info Provides this information blurb\n" +"version Prints the software version information of this driver\n" +"clk_base base clock from which the SCLK is derived\n" +"clk_source 0=internal; 1=external;\n" +""; + +/*------------------------------------------------------------ + . Sysctl handler for all integer parameters + .-------------------------------------------------------------*/ +static int spi_pxa_sysctl_handler(ctl_table* ctl, int write, struct file* filp, + void* buffer, size_t* lenp) +{ + struct pxa_sspc_dev* dev=(struct pxa_sspc_dev*)ctl->extra1; + int* valp = ctl->data; + int val; + int ret; + int modified=0; + int target=dev->target; + + switch (ctl->ctl_name) { + case CTL_SPI_PXA_CLKFREQ: + *valp = dev->clk_freq; + break; + case CTL_SPI_PXA_CLKBASE: + *valp = dev->clk_base; + break; +#ifdef CONFIG_SSPC_EXTCLK + case CTL_SPI_PXA_CLKSRC: + *valp = (dev->clock_mode & SSI_CLK_EXT) != 0; + break; +#endif + case CTL_SPI_PXA_CLKPHASE: + *valp = (dev->clock_mode & SSI_CLK_PHASE) != 0; + break; + case CTL_SPI_PXA_CLKEDGE: + *valp = (dev->clock_mode & SSI_CLK_FALLING) != 0; + break; + case CTL_SPI_PXA_CLKDIV: + *valp = dev->clk_div; + break; + case CTL_SPI_PXA_TARGET: + *valp = dev->target; + break; + case CTL_SPI_PXA_DEVICES: + + sprintf(dev->ctl_devices, "%s", "None"); + break; + default: + break; + // Just ignore unsupported parameters + } + + // Save old state + val = *valp; + + // Perform the generic integer operation + if ((ret = proc_dointvec(ctl, write, filp, buffer, lenp)) != 0) { + return(ret); + } + // Write changes out to the registers + if (write && *valp != val) { + val = *valp; + switch (ctl->ctl_name) { + case CTL_SPI_PXA_CLKFREQ: + if (val > 0) { + modified = dev->clk_freq != val; + dev->clk_freq = val; + } else { + printk(KERN_WARNING "%s: invalid value %d\n", + __FUNCTION__, val); + ret = -EINVAL; + } + + break; +#ifdef CONFIG_SSPC_EXTCLK + case CTL_SPI_PXA_CLKBASE: + if (dev->ctl_clk_src) { + if (val > 1) { + modified = dev->clk_base != val; + dev->clk_base = val; + } else { + printk(KERN_WARNING "%s: invalid value %d\n", + __FUNCTION__, val); + ret = -EINVAL; + } + } else { + printk(KERN_WARNING "%s: cannot change base clock in internal clock mode\n", __FUNCTION__); + } + break; +#endif +#ifdef CONFIG_SSPC_EXTCLK + case CTL_SPI_PXA_CLKSRC: + if (val == 1) { + printk(KERN_INFO "%s: setting %s to %d\n", + __FUNCTION__, "clock src", val); + modified = (dev->clock_mode & SSI_CLK_EXT) == 0; + dev->clock_mode |= SSI_CLK_EXT; + dev->clk_src = val; + } else if (val == 0) { + printk(KERN_INFO "%s: setting %s to %d\n", + __FUNCTION__, "clock src", val); + modified = (dev->clock_mode & SSI_CLK_EXT) != 0; + dev->clock_mode &= ~SSI_CLK_EXT; + dev->clk_src = val; + dev->clk_base = SSPC_CLK_BASE; + } else { + printk(KERN_WARNING "%s: invalid boolean value %d\n", + __FUNCTION__, val); + ret = -EINVAL; + break; + } + break; +#endif + case CTL_SPI_PXA_CLKPHASE: + if (val == 1) { + printk(KERN_INFO "%s: setting %s to %d\n", + __FUNCTION__, "clock phase", val); + modified = (dev->clock_mode & SSI_CLK_PHASE) == 0; + dev->clock_mode |= SSI_CLK_PHASE; + //dev->ctl_clk_phase = val; + } else if (val == 0) { + printk(KERN_INFO "%s: setting %s to %d\n", + __FUNCTION__, "clock phase", val); + modified = (dev->clock_mode & SSI_CLK_PHASE) != 0; + dev->clock_mode &= ~SSI_CLK_PHASE; + //dev->ctl_clk_phase = val; + } else { + printk(KERN_WARNING "%s: invalid boolean value %d\n", + __FUNCTION__, val); + ret = -EINVAL; + break; + } + break; + case CTL_SPI_PXA_CLKEDGE: + if (val == 1) { + printk(KERN_INFO "%s: setting %s to %d\n", + __FUNCTION__, "clock edge", val); + modified = (dev->clock_mode & SSI_CLK_FALLING) == 0; + dev->clock_mode |= SSI_CLK_FALLING; + //dev->ctl_clk_edge = val; + } else if (val == 0) { + printk(KERN_INFO "%s: setting %s to %d\n", + __FUNCTION__, "clock edge", val); + modified = (dev->clock_mode & SSI_CLK_FALLING) != 0; + dev->clock_mode &= ~SSI_CLK_FALLING; + //dev->ctl_clk_edge = val; + } else { + printk(KERN_WARNING "%s: invalid boolean value %d\n", + __FUNCTION__, val); + ret = -EINVAL; + break; + } + break; + case CTL_SPI_PXA_CLKDIV: + if (val >= 0 && val <= (SSCR0_SCR>>SSCR0_SCR_SHIFT)) { + modified = dev->clk_div != val; + dev->clk_freq = dev->clk_base / (2*(val+1)); + } else { + printk(KERN_WARNING "%s: invalid value %d\n", + __FUNCTION__, val); + ret = -EINVAL; + } + break; + case CTL_SPI_PXA_TARGET: + modified = dev->target != val; + target = val; + break; + default: + // Just ignore unsupported parameters + break; + } // end switch + + } // end if + + if (modified) { + pxa_sspc_setup(dev, target); + } + + return ret; +} + +/*------------------------------------------------------------ + . Sysctl registration function for all parameters (files) + .-------------------------------------------------------------*/ +static void spi_pxa_sysctl_register(struct pxa_sspc_dev* dev) +{ + static int ctl_name=CTL_SPI_PXA; + ctl_table* ct; + int i; + + // Make sure the ctl_tables start out as all zeros + memset(dev->root_table, 0, sizeof dev->root_table); + memset(dev->spi_table, 0, sizeof dev->spi_table); + memset(dev->param_table, 0, sizeof dev->param_table); + + // Initialize the root table + ct = dev->root_table; + ct->ctl_name = CTL_BUS; + ct->procname = "bus"; + ct->maxlen = 0; + ct->mode = 0555; + ct->child = dev->spi_table; + + ct = dev->spi_table; + ct->ctl_name = CTL_SPI; + ct->procname = "spi"; + ct->maxlen = 0; + ct->mode = 0555; + ct->child = dev->pxa_table; + // remaining fields are zero + + // Initialize the spiX table (this device's table) + ct = dev->pxa_table; + ct->ctl_name = ctl_name++; // Must be unique + ct->procname = dev->spibus.name; + ct->maxlen = 0; + ct->mode = 0555; + ct->child = dev->param_table; + // remaining fields are zero + + // Initialize the parameter (files) table + // Make sure the last entry remains null + ct = dev->param_table; + for (i = 0; i < (CTL_SPI_PXA_ENTRIES-1); ++i) { + // Initialize fields common to all table entries + ct[i].proc_handler = spi_pxa_sysctl_handler; + ct[i].extra1 = (void*)dev; // Save our pxa_sspc_dev data pointer + } + + // INFO - this is our only string parameter + i = 0; + ct[i].proc_handler = proc_dostring; // use default handler + ct[i].ctl_name = CTL_SPI_PXA_INFO; + ct[i].procname = "info"; + ct[i].data = (void*)spi_info_string; + ct[i].maxlen = sizeof spi_info_string; + ct[i].mode = 0444; // Read only + + // VERSION + ++i; + ct[i].proc_handler = proc_dostring; // use default handler + ct[i].ctl_name = CTL_SPI_PXA_SWVER; + ct[i].procname = "version"; + ct[i].data = (void*)version; + ct[i].maxlen = sizeof version; + ct[i].mode = 0444; // Read only + + // CLKFREQ + ++i; + ct[i].ctl_name = CTL_SPI_PXA_CLKFREQ; + ct[i].procname = "clk_freq"; + ct[i].data = (void*)&(dev->ctl_clk_freq); + ct[i].maxlen = sizeof dev->ctl_clk_freq; + ct[i].mode = 0644; // Read by all, write by root + + // CLKBASE + ++i; + ct[i].ctl_name = CTL_SPI_PXA_CLKBASE; + ct[i].procname = "clk_base"; + ct[i].data = (void*)&(dev->ctl_clk_base); + ct[i].maxlen = sizeof dev->ctl_clk_base; +#ifdef CONFIG_SSPC_EXTCLK + ct[i].mode = 0644; // Read by all, write by root +#else + ct[i].mode = 0444; // Read only +#endif + +#ifdef CONFIG_SSPC_EXTCLK + // CLKSRC + ++i; + ct[i].ctl_name = CTL_SPI_PXA_CLKSRC; + ct[i].procname = "clk_src"; + ct[i].data = (void*)&(dev->ctl_clk_src); + ct[i].maxlen = sizeof dev->ctl_clk_src; + ct[i].mode = 0644; // Read by all, write by root +#endif + // CLKEDGE + ++i; + ct[i].ctl_name = CTL_SPI_PXA_CLKEDGE; + ct[i].procname = "clk_falling_edge"; + ct[i].data = (void*)&(dev->ctl_clk_edge); + ct[i].maxlen = sizeof dev->ctl_clk_edge; + ct[i].mode = 0644; // Read by all, write by root + + // CLKPHASE + ++i; + ct[i].ctl_name = CTL_SPI_PXA_CLKPHASE; + ct[i].procname = "clk_phase_shift"; + ct[i].data = (void*)&(dev->ctl_clk_phase); + ct[i].maxlen = sizeof dev->ctl_clk_phase; + ct[i].mode = 0644; // Read by all, write by root + + // CLKDIV + ++i; + ct[i].ctl_name = CTL_SPI_PXA_CLKDIV; + ct[i].procname = "clk_div"; + ct[i].data = (void*)&(dev->ctl_clk_div); + ct[i].maxlen = sizeof dev->ctl_clk_div; + ct[i].mode = 0644; // Read by all, write by root + + // TARGET + ++i; + ct[i].ctl_name = CTL_SPI_PXA_TARGET; + ct[i].procname = "target"; + ct[i].data = (void*)&(dev->ctl_target); + ct[i].maxlen = sizeof dev->ctl_target; + ct[i].mode = 0644; // Read by all, write by root + + // DEVICES + ++i; + ct[i].ctl_name = CTL_SPI_PXA_DEVICES; + ct[i].procname = "devices"; + ct[i].data = (void*)&(dev->ctl_devices); + ct[i].maxlen = sizeof dev->ctl_devices; + ct[i].mode = 0444; // Read only + + if (++i == CTL_SPI_PXA_ENTRIES-1) { + // Register /proc/sys/bus/spi + dev->sysctl_header = register_sysctl_table(dev->root_table, 1); + } else { + printk("%s@%d %s: Internal error: %d entries in ctl_table, %d initialized\n", + __FILE__, __LINE__, __FUNCTION__, CTL_SPI_PXA_ENTRIES, i); + } +} + + +/*------------------------------------------------------------ + . Sysctl unregistration when driver is closed + .-------------------------------------------------------------*/ +static void spi_pxa_sysctl_unregister(struct pxa_sspc_dev* dev) +{ + if (dev->sysctl_header != NULL) { + unregister_sysctl_table(dev->sysctl_header); + } +} + +#endif /* endif CONFIG_SYSCTL */ + +/* + * machine specific code to select a device to talk to over the SSPC interface + */ +#ifdef CONFIG_ARCH_INPHINITY +static int inphinity_target_select(struct pxa_sspc_dev* sspc_dev, int target) +{ + int ret=-EINVAL; + if (target >= 0 && target < 4) { + *(volatile u8*)INPHINITY_ANALOG_BASE = target; + ret = 0; + } + return ret; +} +#endif + + +module_init (pxa_sspc_init); +module_exit (pxa_sspc_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR ("Lothar Wassmann "); +MODULE_DESCRIPTION ("PXA sspc bus driver"); +MODULE_LICENSE("GPL"); + diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-pxa.h linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-pxa.h --- linux-2.4.19-rmk7-pxa2/drivers/ssi/spi-pxa.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/spi-pxa.h Mon Sep 8 19:26:10 2003 @@ -0,0 +1,113 @@ +#ifndef __SPI_PXA_H +#define __SPI_PXA_H + +#include "spi-core.h" + +#ifdef CONFIG_SYSCTL +/* + * Declarations for the sysctl interface, which allows users the ability to + * control the SPI bus global settings + * + */ +#define CTL_SPI_PXA (CTL_SPI+128) // arbitrary and hopefully unused +enum { + CTL_SPI_PXA_INFO = 1, // Sysctl files information + CTL_SPI_PXA_SWVER, // Driver Software Version Info + CTL_SPI_PXA_CLKFREQ, // current device's clock frequency + CTL_SPI_PXA_CLKBASE, // base clock frequency +#ifdef CONFIG_SSPC_EXTCLK + CTL_SPI_PXA_CLKSRC, // base clock source +#endif + CTL_SPI_PXA_CLKEDGE, // clock active edge + CTL_SPI_PXA_CLKPHASE, // clock phase shift + CTL_SPI_PXA_CLKDIV, // set the clock divider value + CTL_SPI_PXA_TARGET, // ID of currently selected target + CTL_SPI_PXA_DEVICES, // list of attached devices +#if 0 + CTL_SPI_PXA_, // +#endif + // --------------------------------------------------- + CTL_SPI_PXA_ENTRIES // Add new entries above the line +}; + +#endif // CONFIG_SYSCTL + +// tags for SSI protocol variants +typedef enum { + SSI_SPI, // Motorola + SSI_MICROWIRE, // National Microwire + SSI_TISSF, // Texas Instruments + SSI_USAR, // USAR + SSI_NUM_PROTOCOLS +} ssi_protocol_t; + +#define SSPC_CLK_BASE 3686400 +#define SSPC_MIN_FRAME_LEN 4 +#define SSPC_MAX_FRAME_LEN 16 + +#define SSI_CLK_FALLING (1<<0) +#define SSI_CLK_PHASE (1<<1) +#define SSI_CLK_EXT (1<<2) + +#define GPIO23_SCLK_MD (23 | GPIO_ALT_FN_2_OUT) +#define GPIO24_SFRM_MD (24 | GPIO_ALT_FN_2_OUT) +#define GPIO25_STXD_MD (25 | GPIO_ALT_FN_2_OUT) +#define GPIO26_SRXD_MD (26 | GPIO_ALT_FN_1_IN) +#define GPIO27_SEXTCLK_MD (27 | GPIO_ALT_FN_1_IN) + +struct pxa_sspc_dev { + struct spi_bus spibus; + int (*target_select)(struct pxa_sspc_dev*, int target_num); + u8* read_pos; + const u8* write_pos; + const u8* read_end; + const u8* write_end; + int write_count; + int reading; + int preloading; + int target; + struct tasklet_struct tasklet; + u16 clock_mode; + u_int clk_base; +#ifdef CONFIG_SSPC_EXTCLK + int clk_src; +#endif + u_int clk_freq; + u_int clk_div; + u_int framelen; + u_int proto; +#ifdef CONFIG_SYSCTL + // Root directory /proc/sys/bus + // Second entry must be null to terminate the table + ctl_table root_table[2]; + + // Directory for this bus /proc/sys/bus/spiX + // Again the second entry must be zero to terminate + ctl_table spi_table[2]; + + // Directory for this bus /proc/sys/bus/spiX/pxa + // Again the second entry must be zero to terminate + ctl_table pxa_table[2]; + + // This is the parameters (file) table + ctl_table param_table[CTL_SPI_PXA_ENTRIES]; + + // Saves the sysctl header returned by register_sysctl_table() + // we send this to unregister_sysctl_table() + struct ctl_table_header* sysctl_header; + + // Parameter variables (files) go here + int ctl_clk_freq; + int ctl_clk_edge; + int ctl_clk_phase; + int ctl_target; + char ctl_devices[1024]; + int ctl_clk_base; +#ifdef CONFIG_SSPC_EXTCLK + int ctl_clk_src; +#endif + int ctl_clk_div; +#endif // CONFIG_SYSCTL +}; + +#endif // __SPI_PXA_H diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi/ssi_bus.h linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/ssi_bus.h --- linux-2.4.19-rmk7-pxa2/drivers/ssi/ssi_bus.h Wed Nov 5 09:22:11 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/ssi_bus.h Mon Sep 8 18:38:20 2003 @@ -14,7 +14,21 @@ void (*exit)(struct ssi_bus *); char *name; u_int devices; + u_int flags; /* clock source */ + u_int clk_base; /* base clock frequency of hardware interface */ }; + +#ifdef CONFIG_ARCH_PXA +#define SSPC_CLK_BASE 3686400 +#define SSPC_MIN_FRAME_LEN 4 +#define SSPC_MAX_FRAME_LEN 16 +#define SSP_FLAG_DMA (1<<0) +struct pxa_ssi_bus { + struct ssi_bus; + u_int flags; + u_int clk_base; +}; +#endif extern int ssi_core_rcv(struct ssi_bus *bus, u_int data); extern int ssi_register_bus(struct ssi_bus *bus); diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi/ssi_core.c linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/ssi_core.c --- linux-2.4.19-rmk7-pxa2/drivers/ssi/ssi_core.c Wed Nov 5 09:22:11 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi/ssi_core.c Mon Sep 8 19:39:43 2003 @@ -14,7 +14,7 @@ #include #include #include -#include +//#include #include #include @@ -56,10 +56,9 @@ /* * Make sure that we currently own the bus */ - if (bus->dev != dev) - BUG(); - - bus->trans(bus, data); + if (bus->dev == dev) + bus->trans(bus, data); + return 0; } diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/Config.in linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/Config.in --- linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/Config.in Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/Config.in Mon Sep 8 12:10:30 2003 @@ -0,0 +1,11 @@ + +mainmenu_option next_comment +comment 'Synchronous Serial Interface' +tristate 'Synchronous Serial Interface Support' CONFIG_SSI + +comment 'SSI Bus Drivers' +dep_tristate ' CLPS711X SSI support' CONFIG_SSI_CLPS711X $CONFIG_SSI $CONFIG_ARCH_CLPS711X + +comment 'SSI Device Drivers' +dep_tristate ' JUNO keyboard support' CONFIG_SSI_JUNO $CONFIG_SSI +endmenu diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/Makefile linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/Makefile --- linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/Makefile Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/Makefile Mon Sep 8 12:10:30 2003 @@ -0,0 +1,50 @@ +# +# Makefile for the SSI drivers +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now inherited from the +# parent makefile. +# + +O_TARGET := ssi.o + +obj-y := +obj-m := +obj-n := +obj- := + +export-objs := +list-multi := + +obj-$(CONFIG_SSI) += ssi_core.o +obj-$(CONFIG_SSI_CLPS711X) += clps711x_ssi1.o +obj-y += juno.o + +# Extract lists of the multi-part drivers. +# The 'int-*' lists are intermediate files used to build the multi's. + +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + +# Files that are both resident and modular; remove from modular. + +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) + +# Take multi-part drivers out of obj-y and put components in. + +obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) + +# Translate to Rules.make lists. + +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) + +include $(TOPDIR)/Rules.make diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/README linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/README --- linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/README Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/README Mon Sep 8 12:10:30 2003 @@ -0,0 +1,86 @@ + Synchronous Serial Interface bus driver + --------------------------------------- + + EEEEE X X PPPP EEEEE RRRR IIIII M M EEEEE N N TTTTT AAA L + E X X P P E R R I MM MM E NN N T A A L + EEEE X PPPP EEEE RRRR I M M M EEEE N N N T AAAAA L + E X X P E R R I M M E N NN T A A L + EEEEE X X P EEEEE R R IIIII M M EEEEE N N T A A LLLLL + +This directory holds the SSI bus drivers. Basically, a SSI bus consists +of the following signals: + + stxd Transmit data + srxd Receive data + sclk Clock + sfrm Frame + Chip selects (1 - n) + +There may be more than one device on a SSI bus, and each device can +have different timing requirements. There are several frame formats: + +1. Texas Instruments Synchronous Serial Frame format + + sclk ____~_~_~_~_~_~_~_~____ + sfrm ____~~_________________ + stxd ------bn..........b0--- + srxd ------bn..........b0--- + + - data latched in on the falling edge of the clock + - data shifted out on the rising edge of the clock + +2. Motorola SPI frame format + + sclk ______~_~_~_~_~_~_~____ + sfrm ~~~~________________~~~ + stxd -----bn..........b0---- + srxd ----.bn..........b0---- + + - data latched in on the rising edge of the clock + - data shifted out on the falling edge of the clock, or falling edge + of sfrm + +3. National Microwire format + + sclk ______~_~_~_~_~_~_~_~_~_~_~_~_~_____ + sfrm ~~~~_____________________________~~~ + stxd -----bn......b0--------------------- + srxd -----------------bn..........b0.---- + + - data latched in on the rising edge of the clock + - data shifted out on the falling edge of the clock + - half duplex, one clock between transmission and reception + +Types of devices +---------------- + +The following types of devices can be found on a SSP bus: + + Sound chips + Keyboard devices + Touch screen devices + +Keyboard +-------- + +TX: +0. Format: cfglen = 8, framelen = 8, clkpol = 1, clk < 250kHz +1. select device +2. keyboard responds asserting key_atn +3. wait 0.1ms to 5ms +4. transmit data byte +5. wait >= 150us +6. repeat step 4 until all data sent +7. deselect device +8. keyboard responds de-asserting key_atn +9. wait >= 120us + +RX: +0. Format: cfglen = 8, framelen = 8, clkpol = 1, clk < 250kHz +1. keyboard asserts key_atn +2. select device after 0.1ms < 5ms +3. read data byte +4. wait 150us +5. if key_atn still asserted, goto 3 +6. deselect device +7. wait >= 120us diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/clps711x_ssi1.c linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/clps711x_ssi1.c --- linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/clps711x_ssi1.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/clps711x_ssi1.c Mon Sep 8 12:10:30 2003 @@ -0,0 +1,237 @@ +/* + * linux/drivers/ssi/clps711x_ssi1.c + * + * SSI bus driver for the CLPS711x SSI1 bus. We support EP7212 + * extensions as well. + * + * Frame sizes can be between 4 and 24 bits. + * Config sizes can be between 4 and 16 bits. + */ +#include +#include +#include + +#include +#include +#include + +#include + +#include "ssi_bus.h" +#include "ssi_dev.h" + +#define EP7212 + +/* + * Port E on the P720T is used for the SPI bus chip selects + * 0 - Keyboard + * 1 - Touch screen + * 2 - CS4224 ADC/DAC + * 7 - idle + */ + +#if 0 +/* + * we place in the transmit buffer: + * + * received data (binary): + * 0xxxxxxxxxxxx000 + * where 'x' is the value + */ +struct ssi_dev ads7846_dev = { + name: "ADS7846", + id: 1, + proto: SSI_SPI, + cfglen: 8, + framelen: 24, + clkpol: 0, + clkfreq: 2500000, +}; + +/* + * we place in the transmit buffer: + * write: <20> ... + * received data discarded + */ +struct ssi_dev cs4224_dev = { + name: "CS4224", + id: 2, + proto: SSI_SPI, + cfglen: 8, + framelen: 8, + clkpol: 0, + clkfreq: 6000000, +}; +#endif + +/* + * Supplement with whatever method your board requires + */ +static void ssi1_select_id(int id) +{ + if (machine_is_p720t()) { + clps_writel(7, PEDDR); + clps_writel(id, PEDR); + } +} + +/* + * Select the specified device. The upper layers will have already + * checked that the bus transmit queue is idle. We need to make sure + * that the interface itself is idle. + */ +static int ssi1_select(struct ssi_bus *bus, struct ssi_dev *dev) +{ + u_int id = dev ? dev->id : 7; + u_int val; + + /* + * Make sure that the interface is idle + */ + do { + val = clps_readl(SYSFLG1); + } while (val & SYSFLG1_SSIBUSY); + + ssi1_select_id(7); + + if (dev) { + /* + * Select clock frequency. This is very rough, + * and assumes that we're operating in PLL mode. + */ + val = clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK; +// if (dev->clkfreq <= 16000) /* <16kHz */ +// val |= SYSCON1_ADCKSEL(0); +// else if (dev->clkfreq < 64000) /* <64kHz */ +// val |= SYSCON1_ADCKSEL(1); +// else if (dev->clkfreq < 128000) /* <128kHz */ + val |= SYSCON1_ADCKSEL(2); +// else /* >= 128kHz */ +// val |= SYSCON1_ADCKSEL(3); + clps_writel(val, SYSCON1); + + bus->cfglen = dev->cfglen; + bus->framelen = dev->framelen; + bus->clkpol = dev->clkpol; + bus->proto = dev->proto; + +#ifdef EP7212 + /* + * Set the clock edge according to the device. + * (set clkpol if the device reads data on the + * falling edge of the clock signal). + */ + val = ep_readl(SYSCON3) & ~SYSCON3_ADCCKNSEN; + if (bus->clkpol && dev->proto != SSI_USAR) + val |= SYSCON3_ADCCKNSEN; + ep_writel(val, SYSCON3); +#endif + + /* + * Select the device + */ + ssi1_select_id(id); + +#ifdef EP7212 + /* + * If we are doing USAR, wait 30us, then set + * the clock line low. + */ + if (dev->proto == SSI_USAR) { + udelay(150); + + val |= SYSCON3_ADCCKNSEN; + ep_writel(val, SYSCON3); + } +#endif + } + + return 0; +} + +static void ssi1_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ssi_bus *bus = (struct ssi_bus *)dev_id; + + /* + * Read the data word and queue it. + */ + ssi_core_rcv(bus, clps_readl(SYNCIO)); +} + +/* + * Enable transmission and/or of some bytes + */ +static int ssi1_trans(struct ssi_bus *bus, u_int data) +{ + u_int syncio; + +#ifdef EP7212 + data <<= 32 - bus->cfglen; + syncio = bus->cfglen | bus->framelen << 7 | data; +#else + syncio = data | bus->framelen << 8; +#endif + + clps_writel(syncio, SYNCIO); + clps_writel(syncio | SYNCIO_TXFRMEN, SYNCIO); + return 0; +} + +/* + * Initialise the SSI bus. + */ +static int ssi1_bus_init(struct ssi_bus *bus) +{ + int retval, val; + + retval = request_irq(IRQ_SSEOTI, ssi1_int, 0, "ssi1", bus); + if (retval) + return retval; + +#ifdef EP7212 + /* + * EP7212 only! Set the configuration command length. + * On the CLPS711x chips, it is fixed at 8 bits. + */ + val = ep_readl(SYSCON3); + val |= SYSCON3_ADCCON; + ep_writel(val, SYSCON3); +#endif + + ssi1_select(bus, NULL); + + PLD_SPI |= PLD_SPI_EN; + + return 0; +} + +static void ssi1_bus_exit(struct ssi_bus *bus) +{ + ssi1_select(bus, NULL); + + PLD_SPI &= ~PLD_SPI_EN; + + free_irq(IRQ_SSEOTI, bus); +} + +struct ssi_bus clps711x_ssi1_bus = { + name: "clps711x_ssi1", + init: ssi1_bus_init, + exit: ssi1_bus_exit, + select: ssi1_select, + trans: ssi1_trans, +}; + +static int __init clps711x_ssi1_init(void) +{ + return ssi_register_bus(&clps711x_ssi1_bus); +} + +static void __exit clps711x_ssi1_exit(void) +{ + ssi_unregister_bus(&clps711x_ssi1_bus); +} + +module_init(clps711x_ssi1_init); +module_exit(clps711x_ssi1_exit); diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/juno.c linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/juno.c --- linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/juno.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/juno.c Mon Sep 8 12:10:30 2003 @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "ssi_bus.h" +#include "ssi_dev.h" + +extern struct ssi_bus clps711x_ssi1_bus; + +static u_int recvbuf[16]; +static volatile u_int ptr, rxed; + +static inline void juno_enable_irq(void) +{ + enable_irq(IRQ_EINT1); +} + +static inline void juno_disable_irq(void) +{ + disable_irq(IRQ_EINT1); +} + +static void juno_rcv(struct ssi_dev *dev, u_int data) +{ + if (ptr < 16) { + recvbuf[ptr] = data; + ptr++; + } else + printk("juno_rcv: %04x\n", data); + rxed = 1; +} + +static void juno_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ssi_dev *dev = dev_id; + + printk("juno_irq\n"); + + ssi_select_device(dev->bus, dev); + + ptr = 0; + do { + rxed = 0; + ssi_transmit_data(dev, 0xff); + while (rxed == 0); + udelay(150); + } while (PLD_INT & PLD_INT_KBD_ATN); + + ssi_select_device(dev->bus, NULL); + + { int i; + printk("juno_rcv: "); + for (i = 0; i < ptr; i++) + printk("%04x ", recvbuf[i]); + printk("\n"); + } +} + +static void juno_command(struct ssi_dev *dev, int cmd, int data) +{ + ssi_transmit_data(dev, cmd); + mdelay(1); + ssi_transmit_data(dev, data); + mdelay(1); + ssi_transmit_data(dev, 0xa0 ^ 0xc0); + mdelay(1); +} + +static int juno_dev_init(struct ssi_dev *dev) +{ + int retval; + + PLD_KBD |= PLD_KBD_EN; + ptr = 16; + + mdelay(20); + + retval = request_irq(IRQ_EINT1, juno_irq, 0, dev->name, dev); + if (retval) + return retval; + + juno_disable_irq(); + + if (ssi_select_device(dev->bus, dev) != 0) { + printk("juno: ssi_select_dev failed\n"); + return -EBUSY; + } + + mdelay(1); + + juno_command(dev, 0x80, 0x20); + + ssi_select_device(dev->bus, NULL); + + juno_enable_irq(); + + return 0; +} + +static struct ssi_dev juno_dev = { + name: "Juno", + id: 0, + proto: SSI_USAR, + cfglen: 8, + framelen: 8, + clkpol: 1, + clkfreq: 250000, + rcv: juno_rcv, + init: juno_dev_init, +}; + +static int __init juno_init(void) +{ + return ssi_register_device(&clps711x_ssi1_bus, &juno_dev); +} + +static void __exit juno_exit(void) +{ + ssi_unregister_device(&juno_dev); +} + +module_init(juno_init); +module_exit(juno_exit); + diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/ssi_bus.h linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/ssi_bus.h --- linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/ssi_bus.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/ssi_bus.h Mon Sep 8 12:10:30 2003 @@ -0,0 +1,21 @@ +#include + +struct ssi_dev; + +struct ssi_bus { + u_char cfglen; + u_char framelen; + u_char clkpol; + u_char proto; + struct ssi_dev *dev; /* current device */ + int (*select)(struct ssi_bus *, struct ssi_dev *); + int (*trans)(struct ssi_bus *, u_int data); + int (*init)(struct ssi_bus *); + void (*exit)(struct ssi_bus *); + char *name; + u_int devices; +}; + +extern int ssi_core_rcv(struct ssi_bus *bus, u_int data); +extern int ssi_register_bus(struct ssi_bus *bus); +extern int ssi_unregister_bus(struct ssi_bus *bus); diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/ssi_core.c linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/ssi_core.c --- linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/ssi_core.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/ssi_core.c Mon Sep 8 12:10:30 2003 @@ -0,0 +1,175 @@ +/* + * linux/drivers/ssi/ssi_core.c + * + * This file provides a common framework to allow multiple SSI devices + * to work together on a single SSI bus. + * + * You can use this in two ways: + * 1. select the device, queue up data, flush the data to the device, + * (optionally) purge the received data, deselect the device. + * 2. select the device, queue up one data word, flush to the device + * read data word, queue up next data word, flush to the device... + * deselect the device. + */ +#include +#include +#include +#include +#include + +#include + +#include "ssi_bus.h" +#include "ssi_dev.h" + +#define DEBUG + +/** + * ssi_core_rcv - pass received SSI data to the device + * @bus: the bus that the data was received from + * @data: the data word that was received + * + * This function is intended to be called by SSI bus device + * drivers to pass received data to the device driver. + */ +int ssi_core_rcv(struct ssi_bus *bus, u_int data) +{ + struct ssi_dev *dev = bus->dev; + + if (dev && dev->rcv) + dev->rcv(dev, data); + + return 0; +} + +/** + * ssi_transmit_data - queue SSI data for later transmission + * @dev: device requesting data to be transmitted + * @data: data word to be transmitted. + * + * Queue one data word of SSI data for later transmission. + */ +int ssi_transmit_data(struct ssi_dev *dev, u_int data) +{ + struct ssi_bus *bus = dev->bus; + + /* + * Make sure that we currently own the bus + */ + if (bus->dev != dev) + BUG(); + + bus->trans(bus, data); + return 0; +} + +/** + * ssi_select_device - select a SSI device for later transactions + * @dev: device to be selected + */ +int ssi_select_device(struct ssi_bus *bus, struct ssi_dev *dev) +{ + int retval; + +#ifdef DEBUG + printk("SSI: selecting device %s on bus %s\n", + dev ? dev->name : "", bus->name); +#endif + + /* + * Select the device if it wasn't already selected. + */ + retval = 0; + if (bus->dev != dev) { + retval = bus->select(bus, dev); + bus->dev = dev; + } + + return retval; +} + +/** + * ssi_register_device - register a SSI device with a SSI bus + * @bus: bus + * @dev: SSI device + */ +int ssi_register_device(struct ssi_bus *bus, struct ssi_dev *dev) +{ + int retval; + + dev->bus = bus; + bus->devices++; + retval = dev->init(dev); + if (retval != 0) { + dev->bus = NULL; + bus->devices--; + } else { +#ifdef DEBUG + printk("SSI: registered new device %s on bus %s\n", dev->name, bus->name); +#endif + } + return retval; +} + +/** + * ssi_unregister_device - unregister a SSI device from a SSI bus + * @dev: SSI device + */ +int ssi_unregister_device(struct ssi_dev *dev) +{ + struct ssi_bus *bus = dev->bus; + + if (bus->dev == dev) + bus->dev = NULL; + + dev->bus = NULL; + bus->devices--; +#ifdef DEBUG + printk("SSI: unregistered device %s on bus %s\n", dev->name, bus->name); +#endif + return 0; +} + +/** + * ssi_register_bus - register a SSI bus driver + * @bus: bus + */ +int ssi_register_bus(struct ssi_bus *bus) +{ + int retval; + + retval = bus->init(bus); + if (retval == 0) { + bus->devices = 0; +#ifdef DEBUG + printk("SSI: registered new bus %s\n", bus->name); +#endif + } + + return retval; +} + +/** + * ssi_unregister_bus - unregister a SSI bus driver + * @bus: bus + */ +int ssi_unregister_bus(struct ssi_bus *bus) +{ + int retval = -EBUSY; + if (bus->devices == 0) { + retval = 0; + } + return retval; +} + +static int __init ssi_init(void) +{ + return 0; +} + +static void __exit ssi_exit(void) +{ +} + +module_init(ssi_init); +module_exit(ssi_exit); diff -urN linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/ssi_dev.h linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/ssi_dev.h --- linux-2.4.19-rmk7-pxa2/drivers/ssi.orig/ssi_dev.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/ssi.orig/ssi_dev.h Mon Sep 8 12:10:30 2003 @@ -0,0 +1,21 @@ +struct ssi_bus; + +#define SSI_SPI 1 +#define SSI_MICROWIRE 2 +#define SSI_TISSF 3 +#define SSI_USAR 4 + +struct ssi_dev { + char *name; + u_int id; + u_int clkfreq; + u_char cfglen; + u_char framelen; + u_char clkpol; + u_char proto; + void (*rcv)(struct ssi_dev *, u_int); + int (*init)(struct ssi_dev *); + struct ssi_bus *bus; +}; + + diff -urN linux-2.4.19-rmk7-pxa2/drivers/video/pxafb.c linux-2.4.19-rmk7-pxa2-dimm/drivers/video/pxafb.c --- linux-2.4.19-rmk7-pxa2/drivers/video/pxafb.c Wed Nov 5 09:37:58 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/video/pxafb.c Sat Oct 4 23:16:26 2003 @@ -748,22 +748,22 @@ // LCCR0_LEN | LCCR0_LDM | LCCR0_BAM | // LCCR0_ERM | LCCR0_LtlEnd | LCCR0_DMADel(0); - new_regs.lccr1 = 0x3030A7F; -// LCCR1_DisWdth(var->xres) + -// LCCR1_HorSnchWdth(var->hsync_len) + -// LCCR1_BegLnDel(var->left_margin) + -// LCCR1_EndLnDel(var->right_margin); - - new_regs.lccr2 = 0x4EF; -// LCCR2_DisHght(var->yres) + -// LCCR2_VrtSnchWdth(var->vsync_len) + -// LCCR2_BegFrmDel(var->upper_margin) + -// LCCR2_EndFrmDel(var->lower_margin); - - new_regs.lccr3 = fbi->lccr3; -// | -// (var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) | -// (var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL) | + new_regs.lccr1 = + LCCR1_DisWdth(var->xres) + + LCCR1_HorSnchWdth(var->hsync_len) + + LCCR1_BegLnDel(var->left_margin) + + LCCR1_EndLnDel(var->right_margin); + + new_regs.lccr2 = + LCCR2_DisHght(var->yres) + + LCCR2_VrtSnchWdth(var->vsync_len) + + LCCR2_BegFrmDel(var->upper_margin) + + LCCR2_EndFrmDel(var->lower_margin); + + new_regs.lccr3 = fbi->lccr3 + | + (var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) | + (var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL); // LCCR3_ACBsCntOff; #endif @@ -873,6 +873,8 @@ { DPRINTK("backlight on\n"); +// GPSR0 = 0x20000; + #ifdef CONFIG_ARCH_PXA_IDP if(machine_is_pxa_idp()) { FB_BACKLIGHT_ON(); @@ -889,6 +891,8 @@ { DPRINTK("backlight off\n"); +// GPCR0 = 0x20000; + #ifdef CONFIG_ARCH_PXA_IDP if(machine_is_pxa_idp()) { FB_BACKLIGHT_OFF(); diff -urN linux-2.4.19-rmk7-pxa2/drivers/video/pxafb.h linux-2.4.19-rmk7-pxa2-dimm/drivers/video/pxafb.h --- linux-2.4.19-rmk7-pxa2/drivers/video/pxafb.h Wed Nov 5 09:37:58 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/drivers/video/pxafb.h Sat Oct 4 15:29:54 2003 @@ -176,10 +176,10 @@ # define PXAFB_BPP_BITS 0x04 #endif -#if defined(CONFIG_ARCH_LUBBOCK) -#define LCD_PIXCLOCK 150000 -#define LCD_BPP PXAFB_BPP -#ifdef CONFIG_FB_PXA_QVGA +//#if defined(CONFIG_ARCH_LUBBOCK) +#define LCD_PIXCLOCK 0 //150000 +#define LCD_BPP 16 //PXAFB_BPP +/*#ifdef CONFIG_FB_PXA_QVGA #define LCD_XRES 320 #define LCD_YRES 240 #define LCD_HORIZONTAL_SYNC_PULSE_WIDTH 51 @@ -191,19 +191,19 @@ #define LCD_SYNC (FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT) #define LCD_LCCR0 0x003008F8 #define LCD_LCCR3 (0x0040FF0C | (PXAFB_BPP_BITS << 24)) -#else +#else*/ #define LCD_XRES 640 #define LCD_YRES 480 #define LCD_HORIZONTAL_SYNC_PULSE_WIDTH 1 -#define LCD_VERTICAL_SYNC_PULSE_WIDTH 1 -#define LCD_BEGIN_OF_LINE_WAIT_COUNT 3 -#define LCD_BEGIN_FRAME_WAIT_COUNT 0 -#define LCD_END_OF_LINE_WAIT_COUNT 3 -#define LCD_END_OF_FRAME_WAIT_COUNT 0 -#define LCD_SYNC (FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT) -#define LCD_LCCR0 0x0030087C -#define LCD_LCCR3 (0x0040FF0C | (PXAFB_BPP_BITS << 24)) -#endif +#define LCD_VERTICAL_SYNC_PULSE_WIDTH 32 //1 +#define LCD_BEGIN_OF_LINE_WAIT_COUNT 1 //3 +#define LCD_BEGIN_FRAME_WAIT_COUNT 1 //0 +#define LCD_END_OF_LINE_WAIT_COUNT 161 //3 +#define LCD_END_OF_FRAME_WAIT_COUNT 32 //0 +#define LCD_SYNC 0 //(FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT) +#define LCD_LCCR0 (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_QDM | LCCR0_PAS | LCCR0_BM | LCCR0_OUM) //0x0030087C +#define LCD_LCCR3 0x04700001 //(0x0040FF0C | (PXAFB_BPP_BITS << 24)) +/*#endif #elif defined (CONFIG_ARCH_PXA_IDP) #define LCD_PIXCLOCK 150000 @@ -235,4 +235,4 @@ #define LCD_LCCR0 (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_QDM | LCCR0_BM | LCCR0_OUM) #define LCD_LCCR3 (LCCR3_PCP | LCCR3_PixClkDiv(0x12) | LCCR3_Bpp(PXAFB_BPP_BITS) | LCCR3_Acb(0x18)) -#endif +#endif*/ diff -urN linux-2.4.19-rmk7-pxa2/include/asm-arm/arch-pxa/keyboard.h linux-2.4.19-rmk7-pxa2-dimm/include/asm-arm/arch-pxa/keyboard.h --- linux-2.4.19-rmk7-pxa2/include/asm-arm/arch-pxa/keyboard.h Wed Nov 5 09:37:58 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/include/asm-arm/arch-pxa/keyboard.h Mon Sep 8 13:12:25 2003 @@ -20,8 +20,10 @@ static inline void kbd_init_hw(void) { +#ifdef CONFIG_SA1111 if (machine_is_lubbock()) sa1111_kbd_init_hw(); +#endif } diff -urN linux-2.4.19-rmk7-pxa2/include/asm-arm/arch-pxa/pxa-regs.h linux-2.4.19-rmk7-pxa2-dimm/include/asm-arm/arch-pxa/pxa-regs.h --- linux-2.4.19-rmk7-pxa2/include/asm-arm/arch-pxa/pxa-regs.h Wed Nov 5 09:37:58 2003 +++ linux-2.4.19-rmk7-pxa2-dimm/include/asm-arm/arch-pxa/pxa-regs.h Mon Sep 8 19:11:53 2003 @@ -1070,14 +1070,44 @@ /* - * SSP Serial Port Registers - */ + * * SSP Serial Port Registers + * */ -#define SSCR0 __REG(0x41000000) /* SSP Control Register 0 */ -#define SSCR1 __REG(0x41000004) /* SSP Control Register 1 */ -#define SSSR __REG(0x41000008) /* SSP Status Register */ -#define SSITR __REG(0x4100000C) /* SSP Interrupt Test Register */ -#define SSDR __REG(0x41000010) /* (Write / Read) SSP Data Write Register/SSP Data Read Register */ +#define SSCR0 __REG(0x41000000) /* SSP Control Register 0 */ +#define SSCR1 __REG(0x41000004) /* SSP Control Register 1 */ +#define SSSR __REG(0x41000008) /* SSP Status Register */ +#define SSITR __REG(0x4100000C) /* SSP Interrupt Test Register */ +#define SSDR __REG(0x41000010) /* (Write / Read) SSP Data Write Register/SSP Data Read Register */ + +#define SSSR_RSRVD (3<<0) /* mask off 'reserved' bits in SSR */ +#define SSSR_TNF (1<<2) /* transmit fifo not full */ +#define SSSR_RNE (1<<3) /* received fifo not empty */ +#define SSSR_BSY (1<<4) /* SSP interface busy */ +#define SSSR_TFS (1<<5) /* transmit fifo service request */ +#define SSSR_RFS (1<<6) /* receive fifo service request */ +#define SSSR_ROR (1<<7) /* receive fifo overrun */ +#define SSSR_TFL (0x0f<<8) /* transmit fifo level */ +#define SSSR_RFL (0x0f<<12) /* receive fifo level */ +#define SSCR0_DSS (0x0f<<0) /* data size select: 0x03..0x0f -> 4..16 bit */ +#define SSCR0_FRF_SHIFT 4 /* frame format shift count */ +#define SSCR0_FRF (3< + +/* + * Supported card ID numbers (Should be somewhere else?) + */ + +#define SNDCARD_ADLIB 1 +#define SNDCARD_SB 2 +#define SNDCARD_PAS 3 +#define SNDCARD_GUS 4 +#define SNDCARD_MPU401 5 +#define SNDCARD_SB16 6 +#define SNDCARD_SB16MIDI 7 +#define SNDCARD_UART6850 8 +#define SNDCARD_GUS16 9 +#define SNDCARD_MSS 10 +#define SNDCARD_PSS 11 +#define SNDCARD_SSCAPE 12 +#define SNDCARD_PSS_MPU 13 +#define SNDCARD_PSS_MSS 14 +#define SNDCARD_SSCAPE_MSS 15 +#define SNDCARD_TRXPRO 16 +#define SNDCARD_TRXPRO_SB 17 +#define SNDCARD_TRXPRO_MPU 18 +#define SNDCARD_MAD16 19 +#define SNDCARD_MAD16_MPU 20 +#define SNDCARD_CS4232 21 +#define SNDCARD_CS4232_MPU 22 +#define SNDCARD_MAUI 23 +#define SNDCARD_PSEUDO_MSS 24 +#define SNDCARD_GUSPNP 25 +#define SNDCARD_UART401 26 +/* Sound card numbers 27 to N are reserved. Don't add more numbers here. */ + +/*********************************** + * IOCTL Commands for /dev/sequencer + */ + +#ifndef _SIOWR +#if defined(_IOWR) && (defined(_AIX) || (!defined(sun) && !defined(sparc) && !defined(__sparc__) && !defined(__INCioctlh) && !defined(__Lynx__))) +/* Use already defined ioctl defines if they exist (except with Sun or Sparc) */ +#define SIOCPARM_MASK IOCPARM_MASK +#define SIOC_VOID IOC_VOID +#define SIOC_OUT IOC_OUT +#define SIOC_IN IOC_IN +#define SIOC_INOUT IOC_INOUT +#define _SIOC_SIZE _IOC_SIZE +#define _SIOC_DIR _IOC_DIR +#define _SIOC_NONE _IOC_NONE +#define _SIOC_READ _IOC_READ +#define _SIOC_WRITE _IOC_WRITE +#define _SIO _IO +#define _SIOR _IOR +#define _SIOW _IOW +#define _SIOWR _IOWR +#else + +/* Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 8191 bytes. + */ +/* #define SIOCTYPE (0xff<<8) */ +#define SIOCPARM_MASK 0x1fff /* parameters must be < 8192 bytes */ +#define SIOC_VOID 0x00000000 /* no parameters */ +#define SIOC_OUT 0x20000000 /* copy out parameters */ +#define SIOC_IN 0x40000000 /* copy in parameters */ +#define SIOC_INOUT (SIOC_IN|SIOC_OUT) +/* the 0x20000000 is so we can distinguish new ioctl's from old */ +#define _SIO(x,y) ((int)(SIOC_VOID|(x<<8)|y)) +#define _SIOR(x,y,t) ((int)(SIOC_OUT|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y)) +#define _SIOW(x,y,t) ((int)(SIOC_IN|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y)) +/* this should be _SIORW, but stdio got there first */ +#define _SIOWR(x,y,t) ((int)(SIOC_INOUT|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y)) +#define _SIOC_SIZE(x) ((x>>16)&SIOCPARM_MASK) +#define _SIOC_DIR(x) (x & 0xf0000000) +#define _SIOC_NONE SIOC_VOID +#define _SIOC_READ SIOC_OUT +#define _SIOC_WRITE SIOC_IN +# endif /* _IOWR */ +#endif /* !_SIOWR */ + +#define SNDCTL_SEQ_RESET _SIO ('Q', 0) +#define SNDCTL_SEQ_SYNC _SIO ('Q', 1) +#define SNDCTL_SYNTH_INFO _SIOWR('Q', 2, struct synth_info) +#define SNDCTL_SEQ_CTRLRATE _SIOWR('Q', 3, int) /* Set/get timer resolution (HZ) */ +#define SNDCTL_SEQ_GETOUTCOUNT _SIOR ('Q', 4, int) +#define SNDCTL_SEQ_GETINCOUNT _SIOR ('Q', 5, int) +#define SNDCTL_SEQ_PERCMODE _SIOW ('Q', 6, int) +#define SNDCTL_FM_LOAD_INSTR _SIOW ('Q', 7, struct sbi_instrument) /* Obsolete. Don't use!!!!!! */ +#define SNDCTL_SEQ_TESTMIDI _SIOW ('Q', 8, int) +#define SNDCTL_SEQ_RESETSAMPLES _SIOW ('Q', 9, int) +#define SNDCTL_SEQ_NRSYNTHS _SIOR ('Q',10, int) +#define SNDCTL_SEQ_NRMIDIS _SIOR ('Q',11, int) +#define SNDCTL_MIDI_INFO _SIOWR('Q',12, struct midi_info) +#define SNDCTL_SEQ_THRESHOLD _SIOW ('Q',13, int) +#define SNDCTL_SYNTH_MEMAVL _SIOWR('Q',14, int) /* in=dev#, out=memsize */ +#define SNDCTL_FM_4OP_ENABLE _SIOW ('Q',15, int) /* in=dev# */ +#define SNDCTL_SEQ_PANIC _SIO ('Q',17) +#define SNDCTL_SEQ_OUTOFBAND _SIOW ('Q',18, struct seq_event_rec) +#define SNDCTL_SEQ_GETTIME _SIOR ('Q',19, int) +#define SNDCTL_SYNTH_ID _SIOWR('Q',20, struct synth_info) +#define SNDCTL_SYNTH_CONTROL _SIOWR('Q',21, struct synth_control) +#define SNDCTL_SYNTH_REMOVESAMPLE _SIOWR('Q',22, struct remove_sample) + +typedef struct synth_control +{ + int devno; /* Synthesizer # */ + char data[4000]; /* Device spesific command/data record */ +}synth_control; + +typedef struct remove_sample +{ + int devno; /* Synthesizer # */ + int bankno; /* MIDI bank # (0=General MIDI) */ + int instrno; /* MIDI instrument number */ +} remove_sample; + +typedef struct seq_event_rec { + unsigned char arr[8]; +} seq_event_rec; + +#define SNDCTL_TMR_TIMEBASE _SIOWR('T', 1, int) +#define SNDCTL_TMR_START _SIO ('T', 2) +#define SNDCTL_TMR_STOP _SIO ('T', 3) +#define SNDCTL_TMR_CONTINUE _SIO ('T', 4) +#define SNDCTL_TMR_TEMPO _SIOWR('T', 5, int) +#define SNDCTL_TMR_SOURCE _SIOWR('T', 6, int) +# define TMR_INTERNAL 0x00000001 +# define TMR_EXTERNAL 0x00000002 +# define TMR_MODE_MIDI 0x00000010 +# define TMR_MODE_FSK 0x00000020 +# define TMR_MODE_CLS 0x00000040 +# define TMR_MODE_SMPTE 0x00000080 +#define SNDCTL_TMR_METRONOME _SIOW ('T', 7, int) +#define SNDCTL_TMR_SELECT _SIOW ('T', 8, int) + +/* + * Some big endian/little endian handling macros + */ + +#if defined(_AIX) || defined(AIX) || defined(sparc) || defined(__sparc__) || defined(HPPA) || defined(PPC) || defined(__mc68000__) +/* Big endian machines */ +# define _PATCHKEY(id) (0xfd00|id) +# define AFMT_S16_NE AFMT_S16_BE +#else +# define _PATCHKEY(id) ((id<<8)|0xfd) +# define AFMT_S16_NE AFMT_S16_LE +#endif + +/* + * Sample loading mechanism for internal synthesizers (/dev/sequencer) + * The following patch_info structure has been designed to support + * Gravis UltraSound. It tries to be universal format for uploading + * sample based patches but is probably too limited. + * + * (PBD) As Hannu guessed, the GUS structure is too limited for + * the WaveFront, but this is the right place for a constant definition. + */ + +struct patch_info { + unsigned short key; /* Use WAVE_PATCH here */ +#define WAVE_PATCH _PATCHKEY(0x04) +#define GUS_PATCH WAVE_PATCH +#define WAVEFRONT_PATCH _PATCHKEY(0x06) + + short device_no; /* Synthesizer number */ + short instr_no; /* Midi pgm# */ + + unsigned int mode; +/* + * The least significant byte has the same format than the GUS .PAT + * files + */ +#define WAVE_16_BITS 0x01 /* bit 0 = 8 or 16 bit wave data. */ +#define WAVE_UNSIGNED 0x02 /* bit 1 = Signed - Unsigned data. */ +#define WAVE_LOOPING 0x04 /* bit 2 = looping enabled-1. */ +#define WAVE_BIDIR_LOOP 0x08 /* bit 3 = Set is bidirectional looping. */ +#define WAVE_LOOP_BACK 0x10 /* bit 4 = Set is looping backward. */ +#define WAVE_SUSTAIN_ON 0x20 /* bit 5 = Turn sustaining on. (Env. pts. 3)*/ +#define WAVE_ENVELOPES 0x40 /* bit 6 = Enable envelopes - 1 */ +#define WAVE_FAST_RELEASE 0x80 /* bit 7 = Shut off immediately after note off */ + /* (use the env_rate/env_offs fields). */ +/* Linux specific bits */ +#define WAVE_VIBRATO 0x00010000 /* The vibrato info is valid */ +#define WAVE_TREMOLO 0x00020000 /* The tremolo info is valid */ +#define WAVE_SCALE 0x00040000 /* The scaling info is valid */ +#define WAVE_FRACTIONS 0x00080000 /* Fraction information is valid */ +/* Reserved bits */ +#define WAVE_ROM 0x40000000 /* For future use */ +#define WAVE_MULAW 0x20000000 /* For future use */ +/* Other bits must be zeroed */ + + int len; /* Size of the wave data in bytes */ + int loop_start, loop_end; /* Byte offsets from the beginning */ + +/* + * The base_freq and base_note fields are used when computing the + * playback speed for a note. The base_note defines the tone frequency + * which is heard if the sample is played using the base_freq as the + * playback speed. + * + * The low_note and high_note fields define the minimum and maximum note + * frequencies for which this sample is valid. It is possible to define + * more than one samples for an instrument number at the same time. The + * low_note and high_note fields are used to select the most suitable one. + * + * The fields base_note, high_note and low_note should contain + * the note frequency multiplied by 1000. For example value for the + * middle A is 440*1000. + */ + + unsigned int base_freq; + unsigned int base_note; + unsigned int high_note; + unsigned int low_note; + int panning; /* -128=left, 127=right */ + int detuning; + +/* New fields introduced in version 1.99.5 */ + + /* Envelope. Enabled by mode bit WAVE_ENVELOPES */ + unsigned char env_rate[ 6 ]; /* GUS HW ramping rate */ + unsigned char env_offset[ 6 ]; /* 255 == 100% */ + + /* + * The tremolo, vibrato and scale info are not supported yet. + * Enable by setting the mode bits WAVE_TREMOLO, WAVE_VIBRATO or + * WAVE_SCALE + */ + + unsigned char tremolo_sweep; + unsigned char tremolo_rate; + unsigned char tremolo_depth; + + unsigned char vibrato_sweep; + unsigned char vibrato_rate; + unsigned char vibrato_depth; + + int scale_frequency; + unsigned int scale_factor; /* from 0 to 2048 or 0 to 2 */ + + int volume; + int fractions; + int reserved1; + int spare[2]; + char data[1]; /* The waveform data starts here */ + }; + +struct sysex_info { + short key; /* Use SYSEX_PATCH or MAUI_PATCH here */ +#define SYSEX_PATCH _PATCHKEY(0x05) +#define MAUI_PATCH _PATCHKEY(0x06) + short device_no; /* Synthesizer number */ + int len; /* Size of the sysex data in bytes */ + unsigned char data[1]; /* Sysex data starts here */ + }; + +/* + * /dev/sequencer input events. + * + * The data written to the /dev/sequencer is a stream of events. Events + * are records of 4 or 8 bytes. The first byte defines the size. + * Any number of events can be written with a write call. There + * is a set of macros for sending these events. Use these macros if you + * want to maximize portability of your program. + * + * Events SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO. Are also input events. + * (All input events are currently 4 bytes long. Be prepared to support + * 8 byte events also. If you receive any event having first byte >= 128, + * it's a 8 byte event. + * + * The events are documented at the end of this file. + * + * Normal events (4 bytes) + * There is also a 8 byte version of most of the 4 byte events. The + * 8 byte one is recommended. + */ +#define SEQ_NOTEOFF 0 +#define SEQ_FMNOTEOFF SEQ_NOTEOFF /* Just old name */ +#define SEQ_NOTEON 1 +#define SEQ_FMNOTEON SEQ_NOTEON +#define SEQ_WAIT TMR_WAIT_ABS +#define SEQ_PGMCHANGE 3 +#define SEQ_FMPGMCHANGE SEQ_PGMCHANGE +#define SEQ_SYNCTIMER TMR_START +#define SEQ_MIDIPUTC 5 +#define SEQ_DRUMON 6 /*** OBSOLETE ***/ +#define SEQ_DRUMOFF 7 /*** OBSOLETE ***/ +#define SEQ_ECHO TMR_ECHO /* For synching programs with output */ +#define SEQ_AFTERTOUCH 9 +#define SEQ_CONTROLLER 10 + +/******************************************* + * Midi controller numbers + ******************************************* + * Controllers 0 to 31 (0x00 to 0x1f) and + * 32 to 63 (0x20 to 0x3f) are continuous + * controllers. + * In the MIDI 1.0 these controllers are sent using + * two messages. Controller numbers 0 to 31 are used + * to send the MSB and the controller numbers 32 to 63 + * are for the LSB. Note that just 7 bits are used in MIDI bytes. + */ + +#define CTL_BANK_SELECT 0x00 +#define CTL_MODWHEEL 0x01 +#define CTL_BREATH 0x02 +/* undefined 0x03 */ +#define CTL_FOOT 0x04 +#define CTL_PORTAMENTO_TIME 0x05 +#define CTL_DATA_ENTRY 0x06 +#define CTL_MAIN_VOLUME 0x07 +#define CTL_BALANCE 0x08 +/* undefined 0x09 */ +#define CTL_PAN 0x0a +#define CTL_EXPRESSION 0x0b +/* undefined 0x0c */ +/* undefined 0x0d */ +/* undefined 0x0e */ +/* undefined 0x0f */ +#define CTL_GENERAL_PURPOSE1 0x10 +#define CTL_GENERAL_PURPOSE2 0x11 +#define CTL_GENERAL_PURPOSE3 0x12 +#define CTL_GENERAL_PURPOSE4 0x13 +/* undefined 0x14 - 0x1f */ + +/* undefined 0x20 */ +/* The controller numbers 0x21 to 0x3f are reserved for the */ +/* least significant bytes of the controllers 0x00 to 0x1f. */ +/* These controllers are not recognised by the driver. */ + +/* Controllers 64 to 69 (0x40 to 0x45) are on/off switches. */ +/* 0=OFF and 127=ON (intermediate values are possible) */ +#define CTL_DAMPER_PEDAL 0x40 +#define CTL_SUSTAIN 0x40 /* Alias */ +#define CTL_HOLD 0x40 /* Alias */ +#define CTL_PORTAMENTO 0x41 +#define CTL_SOSTENUTO 0x42 +#define CTL_SOFT_PEDAL 0x43 +/* undefined 0x44 */ +#define CTL_HOLD2 0x45 +/* undefined 0x46 - 0x4f */ + +#define CTL_GENERAL_PURPOSE5 0x50 +#define CTL_GENERAL_PURPOSE6 0x51 +#define CTL_GENERAL_PURPOSE7 0x52 +#define CTL_GENERAL_PURPOSE8 0x53 +/* undefined 0x54 - 0x5a */ +#define CTL_EXT_EFF_DEPTH 0x5b +#define CTL_TREMOLO_DEPTH 0x5c +#define CTL_CHORUS_DEPTH 0x5d +#define CTL_DETUNE_DEPTH 0x5e +#define CTL_CELESTE_DEPTH 0x5e /* Alias for the above one */ +#define CTL_PHASER_DEPTH 0x5f +#define CTL_DATA_INCREMENT 0x60 +#define CTL_DATA_DECREMENT 0x61 +#define CTL_NONREG_PARM_NUM_LSB 0x62 +#define CTL_NONREG_PARM_NUM_MSB 0x63 +#define CTL_REGIST_PARM_NUM_LSB 0x64 +#define CTL_REGIST_PARM_NUM_MSB 0x65 +/* undefined 0x66 - 0x78 */ +/* reserved 0x79 - 0x7f */ + +/* Pseudo controllers (not midi compatible) */ +#define CTRL_PITCH_BENDER 255 +#define CTRL_PITCH_BENDER_RANGE 254 +#define CTRL_EXPRESSION 253 /* Obsolete */ +#define CTRL_MAIN_VOLUME 252 /* Obsolete */ +#define SEQ_BALANCE 11 +#define SEQ_VOLMODE 12 + +/* + * Volume mode decides how volumes are used + */ + +#define VOL_METHOD_ADAGIO 1 +#define VOL_METHOD_LINEAR 2 + +/* + * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as + * input events. + */ + +/* + * Event codes 0xf0 to 0xfc are reserved for future extensions. + */ + +#define SEQ_FULLSIZE 0xfd /* Long events */ +/* + * SEQ_FULLSIZE events are used for loading patches/samples to the + * synthesizer devices. These events are passed directly to the driver + * of the associated synthesizer device. There is no limit to the size + * of the extended events. These events are not queued but executed + * immediately when the write() is called (execution can take several + * seconds of time). + * + * When a SEQ_FULLSIZE message is written to the device, it must + * be written using exactly one write() call. Other events cannot + * be mixed to the same write. + * + * For FM synths (YM3812/OPL3) use struct sbi_instrument and write it to the + * /dev/sequencer. Don't write other data together with the instrument structure + * Set the key field of the structure to FM_PATCH. The device field is used to + * route the patch to the corresponding device. + * + * For wave table use struct patch_info. Initialize the key field + * to WAVE_PATCH. + */ +#define SEQ_PRIVATE 0xfe /* Low level HW dependent events (8 bytes) */ +#define SEQ_EXTENDED 0xff /* Extended events (8 bytes) OBSOLETE */ + +/* + * Record for FM patches + */ + +typedef unsigned char sbi_instr_data[32]; + +struct sbi_instrument { + unsigned short key; /* FM_PATCH or OPL3_PATCH */ +#define FM_PATCH _PATCHKEY(0x01) +#define OPL3_PATCH _PATCHKEY(0x03) + short device; /* Synth# (0-4) */ + int channel; /* Program# to be initialized */ + sbi_instr_data operators; /* Register settings for operator cells (.SBI format) */ + }; + +struct synth_info { /* Read only */ + char name[30]; + int device; /* 0-N. INITIALIZE BEFORE CALLING */ + int synth_type; +#define SYNTH_TYPE_FM 0 +#define SYNTH_TYPE_SAMPLE 1 +#define SYNTH_TYPE_MIDI 2 /* Midi interface */ + + int synth_subtype; +#define FM_TYPE_ADLIB 0x00 +#define FM_TYPE_OPL3 0x01 +#define MIDI_TYPE_MPU401 0x401 + +#define SAMPLE_TYPE_BASIC 0x10 +#define SAMPLE_TYPE_GUS SAMPLE_TYPE_BASIC +#define SAMPLE_TYPE_WAVEFRONT 0x11 + + int perc_mode; /* No longer supported */ + int nr_voices; + int nr_drums; /* Obsolete field */ + int instr_bank_size; + unsigned int capabilities; +#define SYNTH_CAP_PERCMODE 0x00000001 /* No longer used */ +#define SYNTH_CAP_OPL3 0x00000002 /* Set if OPL3 supported */ +#define SYNTH_CAP_INPUT 0x00000004 /* Input (MIDI) device */ + int dummies[19]; /* Reserve space */ + }; + +struct sound_timer_info { + char name[32]; + int caps; + }; + +#define MIDI_CAP_MPU401 1 /* MPU-401 intelligent mode */ + +struct midi_info { + char name[30]; + int device; /* 0-N. INITIALIZE BEFORE CALLING */ + unsigned int capabilities; /* To be defined later */ + int dev_type; + int dummies[18]; /* Reserve space */ + }; + +/******************************************** + * ioctl commands for the /dev/midi## + */ +typedef struct { + unsigned char cmd; + char nr_args, nr_returns; + unsigned char data[30]; + } mpu_command_rec; + +#define SNDCTL_MIDI_PRETIME _SIOWR('m', 0, int) +#define SNDCTL_MIDI_MPUMODE _SIOWR('m', 1, int) +#define SNDCTL_MIDI_MPUCMD _SIOWR('m', 2, mpu_command_rec) + +/******************************************** + * IOCTL commands for /dev/dsp and /dev/audio + */ + +#define SNDCTL_DSP_RESET _SIO ('P', 0) +#define SNDCTL_DSP_SYNC _SIO ('P', 1) +#define SNDCTL_DSP_SPEED _SIOWR('P', 2, int) +#define SNDCTL_DSP_STEREO _SIOWR('P', 3, int) +#define SNDCTL_DSP_GETBLKSIZE _SIOWR('P', 4, int) +#define SNDCTL_DSP_SAMPLESIZE SNDCTL_DSP_SETFMT +#define SNDCTL_DSP_CHANNELS _SIOWR('P', 6, int) +#define SOUND_PCM_WRITE_CHANNELS SNDCTL_DSP_CHANNELS +#define SOUND_PCM_WRITE_FILTER _SIOWR('P', 7, int) +#define SNDCTL_DSP_POST _SIO ('P', 8) +#define SNDCTL_DSP_SUBDIVIDE _SIOWR('P', 9, int) +#define SNDCTL_DSP_SETFRAGMENT _SIOWR('P',10, int) + +/* Audio data formats (Note! U8=8 and S16_LE=16 for compatibility) */ +#define SNDCTL_DSP_GETFMTS _SIOR ('P',11, int) /* Returns a mask */ +#define SNDCTL_DSP_SETFMT _SIOWR('P',5, int) /* Selects ONE fmt*/ +# define AFMT_QUERY 0x00000000 /* Return current fmt */ +# define AFMT_MU_LAW 0x00000001 +# define AFMT_A_LAW 0x00000002 +# define AFMT_IMA_ADPCM 0x00000004 +# define AFMT_U8 0x00000008 +# define AFMT_S16_LE 0x00000010 /* Little endian signed 16*/ +# define AFMT_S16_BE 0x00000020 /* Big endian signed 16 */ +# define AFMT_S8 0x00000040 +# define AFMT_U16_LE 0x00000080 /* Little endian U16 */ +# define AFMT_U16_BE 0x00000100 /* Big endian U16 */ +# define AFMT_MPEG 0x00000200 /* MPEG (2) audio */ +# define AFMT_AC3 0x00000400 /* Dolby Digital AC3 */ + +/* + * Buffer status queries. + */ +typedef struct audio_buf_info { + int fragments; /* # of available fragments (partially usend ones not counted) */ + int fragstotal; /* Total # of fragments allocated */ + int fragsize; /* Size of a fragment in bytes */ + + int bytes; /* Available space in bytes (includes partially used fragments) */ + /* Note! 'bytes' could be more than fragments*fragsize */ + } audio_buf_info; + +#define SNDCTL_DSP_GETOSPACE _SIOR ('P',12, audio_buf_info) +#define SNDCTL_DSP_GETISPACE _SIOR ('P',13, audio_buf_info) +#define SNDCTL_DSP_NONBLOCK _SIO ('P',14) +#define SNDCTL_DSP_GETCAPS _SIOR ('P',15, int) +# define DSP_CAP_REVISION 0x000000ff /* Bits for revision level (0 to 255) */ +# define DSP_CAP_DUPLEX 0x00000100 /* Full duplex record/playback */ +# define DSP_CAP_REALTIME 0x00000200 /* Real time capability */ +# define DSP_CAP_BATCH 0x00000400 /* Device has some kind of */ + /* internal buffers which may */ + /* cause some delays and */ + /* decrease precision of timing */ +# define DSP_CAP_COPROC 0x00000800 /* Has a coprocessor */ + /* Sometimes it's a DSP */ + /* but usually not */ +# define DSP_CAP_TRIGGER 0x00001000 /* Supports SETTRIGGER */ +# define DSP_CAP_MMAP 0x00002000 /* Supports mmap() */ +# define DSP_CAP_MULTI 0x00004000 /* support multiple open */ +# define DSP_CAP_BIND 0x00008000 /* channel binding to front/rear/cneter/lfe */ + + +#define SNDCTL_DSP_GETTRIGGER _SIOR ('P',16, int) +#define SNDCTL_DSP_SETTRIGGER _SIOW ('P',16, int) +# define PCM_ENABLE_INPUT 0x00000001 +# define PCM_ENABLE_OUTPUT 0x00000002 + +typedef struct count_info { + int bytes; /* Total # of bytes processed */ + int blocks; /* # of fragment transitions since last time */ + int ptr; /* Current DMA pointer value */ + } count_info; + +#define SNDCTL_DSP_GETIPTR _SIOR ('P',17, count_info) +#define SNDCTL_DSP_GETOPTR _SIOR ('P',18, count_info) + +typedef struct buffmem_desc { + unsigned *buffer; + int size; + } buffmem_desc; +#define SNDCTL_DSP_MAPINBUF _SIOR ('P', 19, buffmem_desc) +#define SNDCTL_DSP_MAPOUTBUF _SIOR ('P', 20, buffmem_desc) +#define SNDCTL_DSP_SETSYNCRO _SIO ('P', 21) +#define SNDCTL_DSP_SETDUPLEX _SIO ('P', 22) +#define SNDCTL_DSP_GETODELAY _SIOR ('P', 23, int) + +#define SNDCTL_DSP_GETCHANNELMASK _SIOWR('P', 64, int) +#define SNDCTL_DSP_BIND_CHANNEL _SIOWR('P', 65, int) +# define DSP_BIND_QUERY 0x00000000 +# define DSP_BIND_FRONT 0x00000001 +# define DSP_BIND_SURR 0x00000002 +# define DSP_BIND_CENTER_LFE 0x00000004 +# define DSP_BIND_HANDSET 0x00000008 +# define DSP_BIND_MIC 0x00000010 +# define DSP_BIND_MODEM1 0x00000020 +# define DSP_BIND_MODEM2 0x00000040 +# define DSP_BIND_I2S 0x00000080 +# define DSP_BIND_SPDIF 0x00000100 + +#define SNDCTL_DSP_SETSPDIF _SIOW ('P', 66, int) +#define SNDCTL_DSP_GETSPDIF _SIOR ('P', 67, int) +# define SPDIF_PRO 0x0001 +# define SPDIF_N_AUD 0x0002 +# define SPDIF_COPY 0x0004 +# define SPDIF_PRE 0x0008 +# define SPDIF_CC 0x07f0 +# define SPDIF_L 0x0800 +# define SPDIF_DRS 0x4000 +# define SPDIF_V 0x8000 + +/* + * Application's profile defines the way how playback underrun situations should be handled. + * + * APF_NORMAL (the default) and APF_NETWORK make the driver to cleanup the + * playback buffer whenever an underrun occurs. This consumes some time + * prevents looping the existing buffer. + * APF_CPUINTENS is intended to be set by CPU intensive applications which + * are likely to run out of time occasionally. In this mode the buffer cleanup is + * disabled which saves CPU time but also let's the previous buffer content to + * be played during the "pause" after the underrun. + */ +#define SNDCTL_DSP_PROFILE _SIOW ('P', 23, int) +#define APF_NORMAL 0 /* Normal applications */ +#define APF_NETWORK 1 /* Underruns probably caused by an "external" delay */ +#define APF_CPUINTENS 2 /* Underruns probably caused by "overheating" the CPU */ + +#define SOUND_PCM_READ_RATE _SIOR ('P', 2, int) +#define SOUND_PCM_READ_CHANNELS _SIOR ('P', 6, int) +#define SOUND_PCM_READ_BITS _SIOR ('P', 5, int) +#define SOUND_PCM_READ_FILTER _SIOR ('P', 7, int) + +/* Some alias names */ +#define SOUND_PCM_WRITE_BITS SNDCTL_DSP_SETFMT +#define SOUND_PCM_WRITE_RATE SNDCTL_DSP_SPEED +#define SOUND_PCM_POST SNDCTL_DSP_POST +#define SOUND_PCM_RESET SNDCTL_DSP_RESET +#define SOUND_PCM_SYNC SNDCTL_DSP_SYNC +#define SOUND_PCM_SUBDIVIDE SNDCTL_DSP_SUBDIVIDE +#define SOUND_PCM_SETFRAGMENT SNDCTL_DSP_SETFRAGMENT +#define SOUND_PCM_GETFMTS SNDCTL_DSP_GETFMTS +#define SOUND_PCM_SETFMT SNDCTL_DSP_SETFMT +#define SOUND_PCM_GETOSPACE SNDCTL_DSP_GETOSPACE +#define SOUND_PCM_GETISPACE SNDCTL_DSP_GETISPACE +#define SOUND_PCM_NONBLOCK SNDCTL_DSP_NONBLOCK +#define SOUND_PCM_GETCAPS SNDCTL_DSP_GETCAPS +#define SOUND_PCM_GETTRIGGER SNDCTL_DSP_GETTRIGGER +#define SOUND_PCM_SETTRIGGER SNDCTL_DSP_SETTRIGGER +#define SOUND_PCM_SETSYNCRO SNDCTL_DSP_SETSYNCRO +#define SOUND_PCM_GETIPTR SNDCTL_DSP_GETIPTR +#define SOUND_PCM_GETOPTR SNDCTL_DSP_GETOPTR +#define SOUND_PCM_MAPINBUF SNDCTL_DSP_MAPINBUF +#define SOUND_PCM_MAPOUTBUF SNDCTL_DSP_MAPOUTBUF + +/* + * ioctl calls to be used in communication with coprocessors and + * DSP chips. + */ + +typedef struct copr_buffer { + int command; /* Set to 0 if not used */ + int flags; +#define CPF_NONE 0x0000 +#define CPF_FIRST 0x0001 /* First block */ +#define CPF_LAST 0x0002 /* Last block */ + int len; + int offs; /* If required by the device (0 if not used) */ + + unsigned char data[4000]; /* NOTE! 4000 is not 4k */ + } copr_buffer; + +typedef struct copr_debug_buf { + int command; /* Used internally. Set to 0 */ + int parm1; + int parm2; + int flags; + int len; /* Length of data in bytes */ + } copr_debug_buf; + +typedef struct copr_msg { + int len; + unsigned char data[4000]; + } copr_msg; + +#define SNDCTL_COPR_RESET _SIO ('C', 0) +#define SNDCTL_COPR_LOAD _SIOWR('C', 1, copr_buffer) +#define SNDCTL_COPR_RDATA _SIOWR('C', 2, copr_debug_buf) +#define SNDCTL_COPR_RCODE _SIOWR('C', 3, copr_debug_buf) +#define SNDCTL_COPR_WDATA _SIOW ('C', 4, copr_debug_buf) +#define SNDCTL_COPR_WCODE _SIOW ('C', 5, copr_debug_buf) +#define SNDCTL_COPR_RUN _SIOWR('C', 6, copr_debug_buf) +#define SNDCTL_COPR_HALT _SIOWR('C', 7, copr_debug_buf) +#define SNDCTL_COPR_SENDMSG _SIOWR('C', 8, copr_msg) +#define SNDCTL_COPR_RCVMSG _SIOR ('C', 9, copr_msg) + +/********************************************* + * IOCTL commands for /dev/mixer + */ + +/* + * Mixer devices + * + * There can be up to 20 different analog mixer channels. The + * SOUND_MIXER_NRDEVICES gives the currently supported maximum. + * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells + * the devices supported by the particular mixer. + */ + +#define SOUND_MIXER_NRDEVICES 25 +#define SOUND_MIXER_VOLUME 0 +#define SOUND_MIXER_BASS 1 +#define SOUND_MIXER_TREBLE 2 +#define SOUND_MIXER_SYNTH 3 +#define SOUND_MIXER_PCM 4 +#define SOUND_MIXER_SPEAKER 5 +#define SOUND_MIXER_LINE 6 +#define SOUND_MIXER_MIC 7 +#define SOUND_MIXER_CD 8 +#define SOUND_MIXER_IMIX 9 /* Recording monitor */ +#define SOUND_MIXER_ALTPCM 10 +#define SOUND_MIXER_RECLEV 11 /* Recording level */ +#define SOUND_MIXER_IGAIN 12 /* Input gain */ +#define SOUND_MIXER_OGAIN 13 /* Output gain */ +/* + * The AD1848 codec and compatibles have three line level inputs + * (line, aux1 and aux2). Since each card manufacturer have assigned + * different meanings to these inputs, it's inpractical to assign + * specific meanings (line, cd, synth etc.) to them. + */ +#define SOUND_MIXER_LINE1 14 /* Input source 1 (aux1) */ +#define SOUND_MIXER_LINE2 15 /* Input source 2 (aux2) */ +#define SOUND_MIXER_LINE3 16 /* Input source 3 (line) */ +#define SOUND_MIXER_DIGITAL1 17 /* Digital (input) 1 */ +#define SOUND_MIXER_DIGITAL2 18 /* Digital (input) 2 */ +#define SOUND_MIXER_DIGITAL3 19 /* Digital (input) 3 */ +#define SOUND_MIXER_PHONEIN 20 /* Phone input */ +#define SOUND_MIXER_PHONEOUT 21 /* Phone output */ +#define SOUND_MIXER_VIDEO 22 /* Video/TV (audio) in */ +#define SOUND_MIXER_RADIO 23 /* Radio in */ +#define SOUND_MIXER_MONITOR 24 /* Monitor (usually mic) volume */ + +/* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */ +/* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */ +#define SOUND_ONOFF_MIN 28 +#define SOUND_ONOFF_MAX 30 + +/* Note! Number 31 cannot be used since the sign bit is reserved */ +#define SOUND_MIXER_NONE 31 + +/* + * The following unsupported macros are no longer functional. + * Use SOUND_MIXER_PRIVATE# macros in future. + */ +#define SOUND_MIXER_ENHANCE SOUND_MIXER_NONE +#define SOUND_MIXER_MUTE SOUND_MIXER_NONE +#define SOUND_MIXER_LOUD SOUND_MIXER_NONE + + +#define SOUND_DEVICE_LABELS {"Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \ + "Mic ", "CD ", "Mix ", "Pcm2 ", "Rec ", "IGain", "OGain", \ + "Line1", "Line2", "Line3", "Digital1", "Digital2", "Digital3", \ + "PhoneIn", "PhoneOut", "Video", "Radio", "Monitor"} + +#define SOUND_DEVICE_NAMES {"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \ + "mic", "cd", "mix", "pcm2", "rec", "igain", "ogain", \ + "line1", "line2", "line3", "dig1", "dig2", "dig3", \ + "phin", "phout", "video", "radio", "monitor"} + +/* Device bitmask identifiers */ + +#define SOUND_MIXER_RECSRC 0xff /* Arg contains a bit for each recording source */ +#define SOUND_MIXER_DEVMASK 0xfe /* Arg contains a bit for each supported device */ +#define SOUND_MIXER_RECMASK 0xfd /* Arg contains a bit for each supported recording source */ +#define SOUND_MIXER_CAPS 0xfc +# define SOUND_CAP_EXCL_INPUT 0x00000001 /* Only one recording source at a time */ +#define SOUND_MIXER_STEREODEVS 0xfb /* Mixer channels supporting stereo */ +#define SOUND_MIXER_OUTSRC 0xfa /* Arg contains a bit for each input source to output */ +#define SOUND_MIXER_OUTMASK 0xf9 /* Arg contains a bit for each supported input source to output */ + +/* Device mask bits */ + +#define SOUND_MASK_VOLUME (1 << SOUND_MIXER_VOLUME) +#define SOUND_MASK_BASS (1 << SOUND_MIXER_BASS) +#define SOUND_MASK_TREBLE (1 << SOUND_MIXER_TREBLE) +#define SOUND_MASK_SYNTH (1 << SOUND_MIXER_SYNTH) +#define SOUND_MASK_PCM (1 << SOUND_MIXER_PCM) +#define SOUND_MASK_SPEAKER (1 << SOUND_MIXER_SPEAKER) +#define SOUND_MASK_LINE (1 << SOUND_MIXER_LINE) +#define SOUND_MASK_MIC (1 << SOUND_MIXER_MIC) +#define SOUND_MASK_CD (1 << SOUND_MIXER_CD) +#define SOUND_MASK_IMIX (1 << SOUND_MIXER_IMIX) +#define SOUND_MASK_ALTPCM (1 << SOUND_MIXER_ALTPCM) +#define SOUND_MASK_RECLEV (1 << SOUND_MIXER_RECLEV) +#define SOUND_MASK_IGAIN (1 << SOUND_MIXER_IGAIN) +#define SOUND_MASK_OGAIN (1 << SOUND_MIXER_OGAIN) +#define SOUND_MASK_LINE1 (1 << SOUND_MIXER_LINE1) +#define SOUND_MASK_LINE2 (1 << SOUND_MIXER_LINE2) +#define SOUND_MASK_LINE3 (1 << SOUND_MIXER_LINE3) +#define SOUND_MASK_DIGITAL1 (1 << SOUND_MIXER_DIGITAL1) +#define SOUND_MASK_DIGITAL2 (1 << SOUND_MIXER_DIGITAL2) +#define SOUND_MASK_DIGITAL3 (1 << SOUND_MIXER_DIGITAL3) +#define SOUND_MASK_PHONEIN (1 << SOUND_MIXER_PHONEIN) +#define SOUND_MASK_PHONEOUT (1 << SOUND_MIXER_PHONEOUT) +#define SOUND_MASK_RADIO (1 << SOUND_MIXER_RADIO) +#define SOUND_MASK_VIDEO (1 << SOUND_MIXER_VIDEO) +#define SOUND_MASK_MONITOR (1 << SOUND_MIXER_MONITOR) + +/* Obsolete macros */ +#define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE) +#define SOUND_MASK_ENHANCE (1 << SOUND_MIXER_ENHANCE) +#define SOUND_MASK_LOUD (1 << SOUND_MIXER_LOUD) + +#define MIXER_READ(dev) _SIOR('M', dev, int) +#define SOUND_MIXER_READ_VOLUME MIXER_READ(SOUND_MIXER_VOLUME) +#define SOUND_MIXER_READ_BASS MIXER_READ(SOUND_MIXER_BASS) +#define SOUND_MIXER_READ_TREBLE MIXER_READ(SOUND_MIXER_TREBLE) +#define SOUND_MIXER_READ_SYNTH MIXER_READ(SOUND_MIXER_SYNTH) +#define SOUND_MIXER_READ_PCM MIXER_READ(SOUND_MIXER_PCM) +#define SOUND_MIXER_READ_SPEAKER MIXER_READ(SOUND_MIXER_SPEAKER) +#define SOUND_MIXER_READ_LINE MIXER_READ(SOUND_MIXER_LINE) +#define SOUND_MIXER_READ_MIC MIXER_READ(SOUND_MIXER_MIC) +#define SOUND_MIXER_READ_CD MIXER_READ(SOUND_MIXER_CD) +#define SOUND_MIXER_READ_IMIX MIXER_READ(SOUND_MIXER_IMIX) +#define SOUND_MIXER_READ_ALTPCM MIXER_READ(SOUND_MIXER_ALTPCM) +#define SOUND_MIXER_READ_RECLEV MIXER_READ(SOUND_MIXER_RECLEV) +#define SOUND_MIXER_READ_IGAIN MIXER_READ(SOUND_MIXER_IGAIN) +#define SOUND_MIXER_READ_OGAIN MIXER_READ(SOUND_MIXER_OGAIN) +#define SOUND_MIXER_READ_LINE1 MIXER_READ(SOUND_MIXER_LINE1) +#define SOUND_MIXER_READ_LINE2 MIXER_READ(SOUND_MIXER_LINE2) +#define SOUND_MIXER_READ_LINE3 MIXER_READ(SOUND_MIXER_LINE3) + +/* Obsolete macros */ +#define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE) +#define SOUND_MIXER_READ_ENHANCE MIXER_READ(SOUND_MIXER_ENHANCE) +#define SOUND_MIXER_READ_LOUD MIXER_READ(SOUND_MIXER_LOUD) + +#define SOUND_MIXER_READ_RECSRC MIXER_READ(SOUND_MIXER_RECSRC) +#define SOUND_MIXER_READ_DEVMASK MIXER_READ(SOUND_MIXER_DEVMASK) +#define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK) +#define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS) +#define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS) + +#define MIXER_WRITE(dev) _SIOWR('M', dev, int) +#define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME) +#define SOUND_MIXER_WRITE_BASS MIXER_WRITE(SOUND_MIXER_BASS) +#define SOUND_MIXER_WRITE_TREBLE MIXER_WRITE(SOUND_MIXER_TREBLE) +#define SOUND_MIXER_WRITE_SYNTH MIXER_WRITE(SOUND_MIXER_SYNTH) +#define SOUND_MIXER_WRITE_PCM MIXER_WRITE(SOUND_MIXER_PCM) +#define SOUND_MIXER_WRITE_SPEAKER MIXER_WRITE(SOUND_MIXER_SPEAKER) +#define SOUND_MIXER_WRITE_LINE MIXER_WRITE(SOUND_MIXER_LINE) +#define SOUND_MIXER_WRITE_MIC MIXER_WRITE(SOUND_MIXER_MIC) +#define SOUND_MIXER_WRITE_CD MIXER_WRITE(SOUND_MIXER_CD) +#define SOUND_MIXER_WRITE_IMIX MIXER_WRITE(SOUND_MIXER_IMIX) +#define SOUND_MIXER_WRITE_ALTPCM MIXER_WRITE(SOUND_MIXER_ALTPCM) +#define SOUND_MIXER_WRITE_RECLEV MIXER_WRITE(SOUND_MIXER_RECLEV) +#define SOUND_MIXER_WRITE_IGAIN MIXER_WRITE(SOUND_MIXER_IGAIN) +#define SOUND_MIXER_WRITE_OGAIN MIXER_WRITE(SOUND_MIXER_OGAIN) +#define SOUND_MIXER_WRITE_LINE1 MIXER_WRITE(SOUND_MIXER_LINE1) +#define SOUND_MIXER_WRITE_LINE2 MIXER_WRITE(SOUND_MIXER_LINE2) +#define SOUND_MIXER_WRITE_LINE3 MIXER_WRITE(SOUND_MIXER_LINE3) + +/* Obsolete macros */ +#define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE) +#define SOUND_MIXER_WRITE_ENHANCE MIXER_WRITE(SOUND_MIXER_ENHANCE) +#define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD) + +#define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC) + +typedef struct mixer_info +{ + char id[16]; + char name[32]; + int modify_counter; + int fillers[10]; +} mixer_info; + +typedef struct _old_mixer_info /* Obsolete */ +{ + char id[16]; + char name[32]; +} _old_mixer_info; + +#define SOUND_MIXER_INFO _SIOR ('M', 101, mixer_info) +#define SOUND_OLD_MIXER_INFO _SIOR ('M', 101, _old_mixer_info) + +/* + * A mechanism for accessing "proprietary" mixer features. This method + * permits passing 128 bytes of arbitrary data between a mixer application + * and the mixer driver. Interpretation of the record is defined by + * the particular mixer driver. + */ +typedef unsigned char mixer_record[128]; + +#define SOUND_MIXER_ACCESS _SIOWR('M', 102, mixer_record) + +/* + * Two ioctls for special souncard function + */ +#define SOUND_MIXER_AGC _SIOWR('M', 103, int) +#define SOUND_MIXER_3DSE _SIOWR('M', 104, int) + +/* + * The SOUND_MIXER_PRIVATE# commands can be redefined by low level drivers. + * These features can be used when accessing device specific features. + */ +#define SOUND_MIXER_PRIVATE1 _SIOWR('M', 111, int) +#define SOUND_MIXER_PRIVATE2 _SIOWR('M', 112, int) +#define SOUND_MIXER_PRIVATE3 _SIOWR('M', 113, int) +#define SOUND_MIXER_PRIVATE4 _SIOWR('M', 114, int) +#define SOUND_MIXER_PRIVATE5 _SIOWR('M', 115, int) + +/* + * SOUND_MIXER_GETLEVELS and SOUND_MIXER_SETLEVELS calls can be used + * for querying current mixer settings from the driver and for loading + * default volume settings _prior_ activating the mixer (loading + * doesn't affect current state of the mixer hardware). These calls + * are for internal use only. + */ + +typedef struct mixer_vol_table { + int num; /* Index to volume table */ + char name[32]; + int levels[32]; +} mixer_vol_table; + +#define SOUND_MIXER_GETLEVELS _SIOWR('M', 116, mixer_vol_table) +#define SOUND_MIXER_SETLEVELS _SIOWR('M', 117, mixer_vol_table) + +/* + * An ioctl for identifying the driver version. It will return value + * of the SOUND_VERSION macro used when compiling the driver. + * This call was introduced in OSS version 3.6 and it will not work + * with earlier versions (returns EINVAL). + */ +#define OSS_GETVERSION _SIOR ('M', 118, int) + +/* + * Level 2 event types for /dev/sequencer + */ + +/* + * The 4 most significant bits of byte 0 specify the class of + * the event: + * + * 0x8X = system level events, + * 0x9X = device/port specific events, event[1] = device/port, + * The last 4 bits give the subtype: + * 0x02 = Channel event (event[3] = chn). + * 0x01 = note event (event[4] = note). + * (0x01 is not used alone but always with bit 0x02). + * event[2] = MIDI message code (0x80=note off etc.) + * + */ + +#define EV_SEQ_LOCAL 0x80 +#define EV_TIMING 0x81 +#define EV_CHN_COMMON 0x92 +#define EV_CHN_VOICE 0x93 +#define EV_SYSEX 0x94 +/* + * Event types 200 to 220 are reserved for application use. + * These numbers will not be used by the driver. + */ + +/* + * Events for event type EV_CHN_VOICE + */ + +#define MIDI_NOTEOFF 0x80 +#define MIDI_NOTEON 0x90 +#define MIDI_KEY_PRESSURE 0xA0 + +/* + * Events for event type EV_CHN_COMMON + */ + +#define MIDI_CTL_CHANGE 0xB0 +#define MIDI_PGM_CHANGE 0xC0 +#define MIDI_CHN_PRESSURE 0xD0 +#define MIDI_PITCH_BEND 0xE0 + +#define MIDI_SYSTEM_PREFIX 0xF0 + +/* + * Timer event types + */ +#define TMR_WAIT_REL 1 /* Time relative to the prev time */ +#define TMR_WAIT_ABS 2 /* Absolute time since TMR_START */ +#define TMR_STOP 3 +#define TMR_START 4 +#define TMR_CONTINUE 5 +#define TMR_TEMPO 6 +#define TMR_ECHO 8 +#define TMR_CLOCK 9 /* MIDI clock */ +#define TMR_SPP 10 /* Song position pointer */ +#define TMR_TIMESIG 11 /* Time signature */ + +/* + * Local event types + */ +#define LOCL_STARTAUDIO 1 + +#if (!defined(__KERNEL__) && !defined(KERNEL) && !defined(INKERNEL) && !defined(_KERNEL)) || defined(USE_SEQ_MACROS) +/* + * Some convenience macros to simplify programming of the + * /dev/sequencer interface + * + * These macros define the API which should be used when possible. + */ +#define SEQ_DECLAREBUF() SEQ_USE_EXTBUF() + +void seqbuf_dump(void); /* This function must be provided by programs */ + +extern int OSS_init(int seqfd, int buflen); +extern void OSS_seqbuf_dump(int fd, unsigned char *buf, int buflen); +extern void OSS_seq_advbuf(int len, int fd, unsigned char *buf, int buflen); +extern void OSS_seq_needbuf(int len, int fd, unsigned char *buf, int buflen); +extern void OSS_patch_caching(int dev, int chn, int patch, + int fd, unsigned char *buf, int buflen); +extern void OSS_drum_caching(int dev, int chn, int patch, + int fd, unsigned char *buf, int buflen); +extern void OSS_write_patch(int fd, unsigned char *buf, int len); +extern int OSS_write_patch2(int fd, unsigned char *buf, int len); + +#define SEQ_PM_DEFINES int __foo_bar___ +#ifdef OSSLIB +# define SEQ_USE_EXTBUF() \ + extern unsigned char *_seqbuf; \ + extern int _seqbuflen;extern int _seqbufptr +# define SEQ_DEFINEBUF(len) SEQ_USE_EXTBUF();static int _requested_seqbuflen=len +# define _SEQ_ADVBUF(len) OSS_seq_advbuf(len, seqfd, _seqbuf, _seqbuflen) +# define _SEQ_NEEDBUF(len) OSS_seq_needbuf(len, seqfd, _seqbuf, _seqbuflen) +# define SEQ_DUMPBUF() OSS_seqbuf_dump(seqfd, _seqbuf, _seqbuflen) + +# define SEQ_LOAD_GMINSTR(dev, instr) \ + OSS_patch_caching(dev, -1, instr, seqfd, _seqbuf, _seqbuflen) +# define SEQ_LOAD_GMDRUM(dev, drum) \ + OSS_drum_caching(dev, -1, drum, seqfd, _seqbuf, _seqbuflen) +#else /* !OSSLIB */ + +# define SEQ_LOAD_GMINSTR(dev, instr) +# define SEQ_LOAD_GMDRUM(dev, drum) + +# define SEQ_USE_EXTBUF() \ + extern unsigned char _seqbuf[]; \ + extern int _seqbuflen;extern int _seqbufptr + +#ifndef USE_SIMPLE_MACROS +/* Sample seqbuf_dump() implementation: + * + * SEQ_DEFINEBUF (2048); -- Defines a buffer for 2048 bytes + * + * int seqfd; -- The file descriptor for /dev/sequencer. + * + * void + * seqbuf_dump () + * { + * if (_seqbufptr) + * if (write (seqfd, _seqbuf, _seqbufptr) == -1) + * { + * perror ("write /dev/sequencer"); + * exit (-1); + * } + * _seqbufptr = 0; + * } + */ + +#define SEQ_DEFINEBUF(len) unsigned char _seqbuf[len]; int _seqbuflen = len;int _seqbufptr = 0 +#define _SEQ_NEEDBUF(len) if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump() +#define _SEQ_ADVBUF(len) _seqbufptr += len +#define SEQ_DUMPBUF seqbuf_dump +#else +/* + * This variation of the sequencer macros is used just to format one event + * using fixed buffer. + * + * The program using the macro library must define the following macros before + * using this library. + * + * #define _seqbuf name of the buffer (unsigned char[]) + * #define _SEQ_ADVBUF(len) If the applic needs to know the exact + * size of the event, this macro can be used. + * Otherwise this must be defined as empty. + * #define _seqbufptr Define the name of index variable or 0 if + * not required. + */ +#define _SEQ_NEEDBUF(len) /* empty */ +#endif +#endif /* !OSSLIB */ + +#define SEQ_VOLUME_MODE(dev, mode) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_VOLMODE;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (mode);\ + _seqbuf[_seqbufptr+4] = 0;\ + _seqbuf[_seqbufptr+5] = 0;\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +/* + * Midi voice messages + */ + +#define _CHN_VOICE(dev, event, chn, note, parm) \ + {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = EV_CHN_VOICE;\ + _seqbuf[_seqbufptr+1] = (dev);\ + _seqbuf[_seqbufptr+2] = (event);\ + _seqbuf[_seqbufptr+3] = (chn);\ + _seqbuf[_seqbufptr+4] = (note);\ + _seqbuf[_seqbufptr+5] = (parm);\ + _seqbuf[_seqbufptr+6] = (0);\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_START_NOTE(dev, chn, note, vol) \ + _CHN_VOICE(dev, MIDI_NOTEON, chn, note, vol) + +#define SEQ_STOP_NOTE(dev, chn, note, vol) \ + _CHN_VOICE(dev, MIDI_NOTEOFF, chn, note, vol) + +#define SEQ_KEY_PRESSURE(dev, chn, note, pressure) \ + _CHN_VOICE(dev, MIDI_KEY_PRESSURE, chn, note, pressure) + +/* + * Midi channel messages + */ + +#define _CHN_COMMON(dev, event, chn, p1, p2, w14) \ + {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = EV_CHN_COMMON;\ + _seqbuf[_seqbufptr+1] = (dev);\ + _seqbuf[_seqbufptr+2] = (event);\ + _seqbuf[_seqbufptr+3] = (chn);\ + _seqbuf[_seqbufptr+4] = (p1);\ + _seqbuf[_seqbufptr+5] = (p2);\ + *(short *)&_seqbuf[_seqbufptr+6] = (w14);\ + _SEQ_ADVBUF(8);} +/* + * SEQ_SYSEX permits sending of sysex messages. (It may look that it permits + * sending any MIDI bytes but it's absolutely not possible. Trying to do + * so _will_ cause problems with MPU401 intelligent mode). + * + * Sysex messages are sent in blocks of 1 to 6 bytes. Longer messages must be + * sent by calling SEQ_SYSEX() several times (there must be no other events + * between them). First sysex fragment must have 0xf0 in the first byte + * and the last byte (buf[len-1] of the last fragment must be 0xf7. No byte + * between these sysex start and end markers cannot be larger than 0x7f. Also + * lengths of each fragments (except the last one) must be 6. + * + * Breaking the above rules may work with some MIDI ports but is likely to + * cause fatal problems with some other devices (such as MPU401). + */ +#define SEQ_SYSEX(dev, buf, len) \ + {int ii, ll=(len); \ + unsigned char *bufp=buf;\ + if (ll>6)ll=6;\ + _SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = EV_SYSEX;\ + _seqbuf[_seqbufptr+1] = (dev);\ + for(ii=0;ii>8)&0xff);\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} +/* + * The following 5 macros are incorrectly implemented and obsolete. + * Use SEQ_BENDER and SEQ_CONTROL (with proper controller) instead. + */ +#define SEQ_PITCHBEND(dev, voice, value) SEQ_V2_X_CONTROL(dev, voice, CTRL_PITCH_BENDER, value) +#define SEQ_BENDER_RANGE(dev, voice, value) SEQ_V2_X_CONTROL(dev, voice, CTRL_PITCH_BENDER_RANGE, value) +#define SEQ_EXPRESSION(dev, voice, value) SEQ_CONTROL(dev, voice, CTL_EXPRESSION, value*128) +#define SEQ_MAIN_VOLUME(dev, voice, value) SEQ_CONTROL(dev, voice, CTL_MAIN_VOLUME, (value*16383)/100) +#define SEQ_PANNING(dev, voice, pos) SEQ_CONTROL(dev, voice, CTL_PAN, (pos+128) / 2) + +/* + * Timing and syncronization macros + */ + +#define _TIMER_EVENT(ev, parm) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr+0] = EV_TIMING; \ + _seqbuf[_seqbufptr+1] = (ev); \ + _seqbuf[_seqbufptr+2] = 0;\ + _seqbuf[_seqbufptr+3] = 0;\ + *(unsigned int *)&_seqbuf[_seqbufptr+4] = (parm); \ + _SEQ_ADVBUF(8);} + +#define SEQ_START_TIMER() _TIMER_EVENT(TMR_START, 0) +#define SEQ_STOP_TIMER() _TIMER_EVENT(TMR_STOP, 0) +#define SEQ_CONTINUE_TIMER() _TIMER_EVENT(TMR_CONTINUE, 0) +#define SEQ_WAIT_TIME(ticks) _TIMER_EVENT(TMR_WAIT_ABS, ticks) +#define SEQ_DELTA_TIME(ticks) _TIMER_EVENT(TMR_WAIT_REL, ticks) +#define SEQ_ECHO_BACK(key) _TIMER_EVENT(TMR_ECHO, key) +#define SEQ_SET_TEMPO(value) _TIMER_EVENT(TMR_TEMPO, value) +#define SEQ_SONGPOS(pos) _TIMER_EVENT(TMR_SPP, pos) +#define SEQ_TIME_SIGNATURE(sig) _TIMER_EVENT(TMR_TIMESIG, sig) + +/* + * Local control events + */ + +#define _LOCAL_EVENT(ev, parm) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr+0] = EV_SEQ_LOCAL; \ + _seqbuf[_seqbufptr+1] = (ev); \ + _seqbuf[_seqbufptr+2] = 0;\ + _seqbuf[_seqbufptr+3] = 0;\ + *(unsigned int *)&_seqbuf[_seqbufptr+4] = (parm); \ + _SEQ_ADVBUF(8);} + +#define SEQ_PLAYAUDIO(devmask) _LOCAL_EVENT(LOCL_STARTAUDIO, devmask) +/* + * Events for the level 1 interface only + */ + +#define SEQ_MIDIOUT(device, byte) {_SEQ_NEEDBUF(4);\ + _seqbuf[_seqbufptr] = SEQ_MIDIPUTC;\ + _seqbuf[_seqbufptr+1] = (byte);\ + _seqbuf[_seqbufptr+2] = (device);\ + _seqbuf[_seqbufptr+3] = 0;\ + _SEQ_ADVBUF(4);} + +/* + * Patch loading. + */ +#ifdef OSSLIB +# define SEQ_WRPATCH(patchx, len) \ + OSS_write_patch(seqfd, (char*)(patchx), len) +# define SEQ_WRPATCH2(patchx, len) \ + OSS_write_patch2(seqfd, (char*)(patchx), len) +#else +# define SEQ_WRPATCH(patchx, len) \ + {if (_seqbufptr) SEQ_DUMPBUF();\ + if (write(seqfd, (char*)(patchx), len)==-1) \ + perror("Write patch: /dev/sequencer");} +# define SEQ_WRPATCH2(patchx, len) \ + (SEQ_DUMPBUF(), write(seqfd, (char*)(patchx), len)) +#endif + +#endif +#endif