Darryl DeGraff writes:
>
>
> Tim Waugh wrote:
>
> > The 2.2 kernel doesn't support ECP at all, at least not in the
> > drivers/misc parport stuff.
>
> > You'll want to back-port some of the bits from
> > drivers/parport/parport_pc.c found in 2.4-test kernels, it sounds
> > like.
>
> Well, that sounds OK to me. Before I embark on such a project,
> is it reasonable to have to diddle with the SuperIO registers
> to get the ECP configuration setup? (As mentioned before, the
> hardware is not fully set up for this after boot and there is no
> 'bios setttings' that can be changed, like on PC's).
>
Hallo,
I once had a kernel module to read out and change the settings of the
SMC Superio. It was for 2.0. Perhaps it helps
Bye
Uwe Bonnes bon@elektron.ikp.physik.tu-darmstadt.de
Institut fuer Kernphysik Schlossgartenstrasse 9 64289 Darmstadt
--------- Tel. 06151 162516 -------- Fax. 06151 164321 ----------
/*
* Copyright (C) 1996 by Uwe Bonnes
*
* This code adds an entry /proc/smcio to reflect the settings and
* change selected values of the configuration registers of the
* SMC Super I/O Chips FDC37C66[56]
* Only either of these chips is supported at a time in the system
*/
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/major.h>
#include <asm/io.h>
#define NO_SMC_REGS 16
#define PRIMARY_PORT 0x03F0
#define SECONDARY_PORT 0x0370
#define SMC665_COOKIE 0x55
#define SMC666_COOKIE 0x44
#define SMC665 0x65
#define SMC666 0x66
#define SMC_CRD 0xD
#define PROC_BLOCK_SIZE (3*1024) /* 4K page size, but our output routines
* use some slack for overruns
*/
#define SMC_PPADDR_MASK ~0x3
#define SMC_PPADDR_NULL 0x0
#define SMC_PPADDR_3BC 0x1
#define SMC_PPADDR_378 0x2
#define SMC_PPADDR_278 0x3
#define SMC_PPFIFO_MASK 0xf0
#define SMC_PPMODE_MASK ~0x3
#define SMC_PPMODE_SPP 0x0
#define SMC_PPMODE_EPP 0x1
#define SMC_PPMODE_ECP 0x2
#define SMC_PPMODE_ALL 0x3
#define SMC_PPECPMODE_MASK 0x3
#define SMC_PPEPPTYPE_MASK 0x40
#define SMC_UART_PORTSIZE 8
unsigned char smc_io_reg[NO_SMC_REGS], cookie;
int port, once =0; /* cookie and port are global values */
static int read_smcio(struct inode * inode, struct file * file,
char * buf, int count);
static int write_smcio(struct inode * inode, struct file * file,
const char * buf, int count);
static struct file_operations proc_smcio_operations = {
NULL, /* lseek */
read_smcio, /* read */
write_smcio, /* write */
};
struct inode_operations smcio_inode_operations = {
&proc_smcio_operations, /* default scsi directory file-ops */
};
static void read_smcio_reg(int port, unsigned cookie)
{
int i;
cli();
outb(cookie,port);
outb(cookie,port);
sti();
for (i=0; i < NO_SMC_REGS; i++) {
outb((unsigned char) i,port);
smc_io_reg[i] = inb((port+1));
}
outb(0xAA,port);
once++;
}
static void write_smcio_reg(int port, unsigned cookie)
{
int i;
cli();
outb(cookie,port);
outb(cookie,port);
sti();
for (i=0; i < NO_SMC_REGS; i++) {
outb((unsigned char) i,port);
outb(smc_io_reg[i],(port+1));
}
outb(0xAA,port);
}
static int read_smcio(struct inode *inode, struct file *file, char *buf,
int count)
{
unsigned char content[(NO_SMC_REGS*5)+1];
unsigned long p = file->f_pos;
int read,i;
if (count < 0)
return -EINVAL;
if (p >= ((NO_SMC_REGS*5)+1))
return 0;
if (count > ( (NO_SMC_REGS*5)+1 - p))
count = (NO_SMC_REGS*5)+1 - p;
read = 0;
read_smcio_reg(port,cookie);
for (i=0; i < NO_SMC_REGS; i++)
sprintf(content+ i*5, "0x%.2x ",smc_io_reg[i]);
sprintf(content+ (NO_SMC_REGS*5), "\n");
memcpy_tofs(buf,(void *)content +p,count);
read += count;
file->f_pos += read;
return read;
}
/* Check if new comm is compatible with the other using comm4
-1 means error */
static int comm3choose_uart(int value)
{
int addr = (smc_io_reg[1] & 0x60) >>5;
printk("doing search with comm3 used\n");
if ((addr == 0) && (value == 0x238)) return 0;
if ((addr == 1) && (value == 0x2e8)) return 0;
if ((addr == 2) && (value == 0x2e0)) return 0;
if ((addr == 3) && (value == 0x228)) return 0;
printk("uart incompatible addresses 0x%3x\n",
value);
return -1;
}
/* Check if new comm is compatible with the other using comm4
-1 means error */
static int comm4choose_uart(int value)
{
int addr = (smc_io_reg[1] & 0x60) >>5;
printk("doing search with comm4 used\n");
if ((addr == 0) && (value == 0x338)) return 0;
if ((addr == 1) && (value == 0x3e8)) return 0;
if ((addr == 2) && (value == 0x2e8)) return 0;
if ((addr == 3) && (value == 0x220)) return 0;
printk("uart incompatible addresses 0x%3x\n",
value);
return -1;
}
/* Check which comm to use for the requested address, if other comm doesnt matter*/
static int simplechoose_uart( int value)
{
printk("doing simple comm search\n");
smc_io_reg[1] &= ~0x60;
if (value == 0x338)
return 0;
if (value == 0x238)
return 1;
if (value == 0x3e8) {
smc_io_reg[1] |= 0x20;
return 0;
}
if (value == 0x2e8) {
smc_io_reg[1] |= 0x20;
return 1;
}
if (value == 0x2e0) {
smc_io_reg[1] |= 0x40;
return 1;
}
if (value == 0x2e8) {
smc_io_reg[1] |= 0x40;
return 0;
}
if (value == 0x2e0) {
smc_io_reg[1] |= 0x40;
return 1;
}
if (value == 0x220) {
smc_io_reg[1] |= 0x60;
return 0;
}
if (value == 0x228) {
smc_io_reg[1] |= 0x60;
return 1;
}
return -1;
}
/* Check if requested region is valid */
static int checkaddr_uart(int value)
{
if ((value == 0x3f8) || (value == 0x2f8) || (value == 0x338) ||
(value == 0x238) || (value == 0x3e8) || (value == 0x2e8) ||
(value == 0x2e0) || (value == 0x220) || (value == 0x228))
return value;
else
return -1;
}
/* check which port region the uart at present uses */
static int getold_uart(int value)
{
switch (value) {
case 0 :
return 0x3f8;
case 1:
return 0x2f8;
case 2:
switch ((smc_io_reg[1] & 0x60)>>5) {
case 0:
return 0x338;
case 1:
return 0x3e8;
case 2:
return 0x2e8;
default:
return 0x220;
}
default:
switch ((smc_io_reg[1] & 0x60)>>5) {
case 0:
return 0x238;
case 1:
return 0x2e8;
case 2:
return 0x2e0;
default:
return 0x228;
}
}
}
/* Simple interface to the configuration registers of the Chip
* Only some changes are sensible
* Only some registers may be changed (* denotes reset default)
* "smcio ppadr 0x[0|3BC|378|278(*)]" address of parallel port
* change only if not in use by lp
* "smcio ppmode [spp(*)|epp|ecp|all]" change only if not in use by lp
* "smcio ppepptype 1.[7|9(*)]"
* "smcio ppfifothr (val) (*=0)"
* "smcio uart1addr 0x[3F8(*)|2F8|338|238|3E8|2E8|2E0|220|228]"
* "smcio uart1en enable(*)|disable"
* "smcio uart1pwr on(*)|off"
* "smcio uart2addr 0x[3F8|2F8(*)|338|238|3E8|2E8|2E0|220|228]"
* "smcio uart2en enable(*)|disable"
* "smcio uart2pwr on(*)|off"
* "smcio irq pp|oc"
*/
static int write_smcio(struct inode * inode, struct file * file,
const char *buf, int count)
{
char * page, *p;
unsigned char pat=0;
int ret = 0,value;
/* first read actual values */
if(count > PROC_BLOCK_SIZE) {
return(-EOVERFLOW);
}
if (!(page = (char *) __get_free_page(GFP_KERNEL)))
return(-ENOMEM);
memcpy_fromfs(page, buf, count);
if(!page ||strncmp("smcio", page, 5))
return(-EINVAL);
read_smcio_reg(port,cookie);
if(!strncmp("ppadr", page + 6, 5)) {
p = page + 12;
value = simple_strtoul(p, &p, 0);
printk("smcio ppadr 0x%.3x\n", value);
if (register_chrdev(LP_MAJOR,"lp",NULL)) {
printk("/dev/lp in use! Change not allowed\n");
free_page((ulong) page);
return -EBUSY;
}
unregister_chrdev(LP_MAJOR,"lp");
/* other devices, that use the parallel port, should be checked here too!*/
if (value == 0)
pat = SMC_PPADDR_NULL;
else if (value == 0x3BC)
pat = SMC_PPADDR_3BC;
else if (value == 0x378)
pat = SMC_PPADDR_378;
else if (value == 0x278)
pat = SMC_PPADDR_278;
else
ret = -EINVAL;
if (!ret) {
smc_io_reg[1] &= SMC_PPADDR_MASK;
smc_io_reg[1] |= pat;
}
}
if(!strncmp("ppmode", page + 6, 6)) {
if (register_chrdev(LP_MAJOR,"lp",NULL)) {
printk("/dev/lp in use! Change not allowed\n");
free_page((ulong) page);
return -EBUSY;
}
if(!strncmp("spp", page + 13, 3))
pat = SMC_PPMODE_SPP;
else if(!strncmp("epp", page + 13, 3))
pat = SMC_PPMODE_EPP;
else if(!strncmp("ecp", page + 13, 3))
pat = SMC_PPMODE_ECP;
else if(!strncmp("all", page + 13, 3))
pat = SMC_PPMODE_ALL;
else
ret = -EINVAL;
if (!ret) {
printk("smcio ppmode %d\n", pat);
smc_io_reg[4] &= SMC_PPMODE_MASK;
smc_io_reg[4] |= pat;
}
}
if(!strncmp("ppfifothr", page + 6, 9)) {
p = page + 16;
value = simple_strtoul(p, &p, 0);
printk("smcio ppfifothr %d\n", value);
if ((value > 15)||( value < 0))
ret = -EINVAL;
else {
smc_io_reg[0xa] &= SMC_PPFIFO_MASK;
smc_io_reg[0xa] |= (unsigned char) value;
}
}
if(!strncmp("ppepptype", page + 6, 9)) {
p = page + 18;
value = simple_strtoul(p, &p, 0);
printk("smcio ppepptype 1.%d\n", value);
if (value == 7)
smc_io_reg[4] |= SMC_PPEPPTYPE_MASK;
else if (value == 9)
smc_io_reg[4] &= ~SMC_PPEPPTYPE_MASK;
else
ret = -EINVAL;
}
if(!strncmp("uartreset", page + 6, 10)) {
page = page +17;
smc_io_reg[2] &= ~0x3;
printk("smcio uartreset\n");
}
if(!strncmp("uart1addr", page + 6, 9)) {
int value_wanted,oldvalue, otheruart;
p = page + 16;
value_wanted = (simple_strtoul(p, &p, 0));
value = checkaddr_uart(value_wanted);
oldvalue = getold_uart(smc_io_reg[2] & 0x3);
otheruart = getold_uart((smc_io_reg[2] & 0x30) >>4);
if (value == -1) {
printk("uart1addr requested region 0x%3x illegal\n",
value_wanted);
ret = -EADDRINUSE;
}
else if (check_region(value, SMC_UART_PORTSIZE) < 0) {
printk("uart1addr requested region 0x%3x not free\n",
value);
ret =-EADDRINUSE;
}
else if ( (smc_io_reg[2] & 0x4) &&
(check_region(oldvalue, SMC_UART_PORTSIZE) < 0)) {
printk("uart1addr old region 0x%3x not free\n",oldvalue);
ret = -EINVAL;
}
else if ((smc_io_reg[2] & 0x40) && (value == otheruart)) {
printk("uart1addr address 0x%3x is uart2 address\n",
otheruart);
ret = -EADDRINUSE;
}
/* if new address is comm 1 or comm 2, we are nearly done */
else if (value == 0x3f8)
smc_io_reg[2] &= ~0x3;
else if (value == 0x2f8) {
smc_io_reg[2] &= ~0x2;
smc_io_reg[2] |= ~0x1;
}
/* if otheruart is comm 1 or comm 2 , we don't care for it
but set the value straight*/
else if (!(smc_io_reg[2] & 0x20)) {
smc_io_reg[2] &= ~0x1;
smc_io_reg[2] |= 0x2 |simplechoose_uart(value);
}
/* other uart uses comm4 */
else if (( smc_io_reg[2] & 0x30) == 0x30) {
if (comm4choose_uart(value) != -1) {
smc_io_reg[2] &= ~0x1;
smc_io_reg[2] |= 0x2;
}
else
ret = -EADDRINUSE;
}
else {
if (comm3choose_uart(value) != -1)
smc_io_reg[2] |= 0x3;
else
ret = -EADDRINUSE;
}
if (!(ret <0))
printk("smcio uart1addr change from 0x%3x to 0x%3x allowed\n",
oldvalue,value);
}
if(!strncmp("uart2addr", page + 6, 9)) {
int value_wanted,oldvalue, otheruart;
p = page + 16;
value_wanted = (simple_strtoul(p, &p, 0));
value = checkaddr_uart(value_wanted);
oldvalue = getold_uart((smc_io_reg[2] & 0x30)>>4);
otheruart = getold_uart(smc_io_reg[2] & 0x3);
if (value == -1) {
printk("uart2addr requested region 0x%3x illegal\n",
value_wanted);
ret = -EADDRINUSE;
}
else if (check_region(value, SMC_UART_PORTSIZE) < 0) {
printk("uart2addr requested region 0x%3x not free\n",
value);
ret =-EADDRINUSE;
}
else if ( (smc_io_reg[2] & 0x40) &&
(check_region(oldvalue, SMC_UART_PORTSIZE) < 0)) {
printk("uart2addr old region 0x%3x not free\n",oldvalue);
ret = -EINVAL;
}
else if ((smc_io_reg[2] & 0x4) && (value == otheruart)) {
printk("uart2addr address 0x%3x is uart2 address\n",
otheruart);
ret = -EADDRINUSE;
}
/* if new address is comm 1 or comm 2, we are nearly done */
else if (value == 0x3f8)
smc_io_reg[2] &= ~0x30;
else if (value == 0x2f8) {
smc_io_reg[2] &= ~0x20;
smc_io_reg[2] |= ~0x10;
}
/* if otheruart is comm 1 or comm 2 , we don't care for it
but set the value straight*/
else if (!(smc_io_reg[2] & 0x2)) {
smc_io_reg[2] &= ~0x10;
smc_io_reg[2] |= 0x20 | (simplechoose_uart(value)<<4);
}
/* other uart uses comm4 */
else if ( (smc_io_reg[2] & 0x3) == 0x3) {
if (comm4choose_uart(value) != -1) {
smc_io_reg[2] &= ~0x10;
smc_io_reg[2] |= 0x20;
}
else
ret = -EADDRINUSE;
}
else {
if (comm3choose_uart(value) != -1)
smc_io_reg[2] |= 0x30;
else
ret = -EADDRINUSE;
}
if (! (ret < 0))
printk("smcio uart2addr change from 0x%3x to 0x%3x allowed\n",
oldvalue,value);
}
if(!strncmp("uart1pwr", page + 6, 8)) {
int value;
value = getold_uart(smc_io_reg[2] & 0x3);
if (check_region(value, SMC_UART_PORTSIZE) < 0) {
printk("uart1 region at 0x%.3x not free\n",
value);
ret = -EBUSY;
}
else if (!strncmp("off", page + 15, 3)) {
printk("Power down down uart1\n");
smc_io_reg[2] &= ~ 0x8;
}
else if (!strncmp("on", page + 15, 2)) {
printk("Power up for uart1\n");
smc_io_reg[2] |= 0x8;
}
}
if(!strncmp("uart2pwr", page + 6, 5)) {
int value;
value = getold_uart((smc_io_reg[2] & 0x30)>>4);
if (check_region(value, SMC_UART_PORTSIZE) < 0) {
printk("uart2 region at 0x%.3x not free\n",
value);
ret = -EBUSY;
}
else if (!strncmp("off", page + 15, 3)) {
printk("Power down for uart2\n");
smc_io_reg[2] &= ~0x80;
}
else if (!strncmp("on", page + 15, 2)) {
printk("Power up for uart2\n");
smc_io_reg[2] |= 0x80;
}
}
if(!strncmp("uart1en", page + 6, 7)) {
int value;
value = getold_uart(smc_io_reg[2] & 0x3);
if (check_region(value, SMC_UART_PORTSIZE) < 0) {
printk("uart1 region at 0x%.3x not free\n",
value);
ret = -EBUSY;
}
else if (!strncmp("disable", page + 14, 7)) {
printk("Disabling uart1\n");
smc_io_reg[2] &= ~ 0x4;
}
else if (!strncmp("enable", page + 14, 6)) {
printk("Enabling uart1\n");
smc_io_reg[2] |= 0x4;
}
else ret = -EINVAL;
}
if(!strncmp("uart2en", page + 6, 7)) {
int value;
value = getold_uart((smc_io_reg[2] & 0x30)>>4);
if (check_region(value, SMC_UART_PORTSIZE) < 0) {
printk("uart2 region at 0x%.3x not free\n",
value);
ret = -EBUSY;
}
else if (!strncmp("disable", page + 14, 7)) {
printk("Disabling uart2\n");
smc_io_reg[2] &= ~0x40;
}
else if (!strncmp("enable", page + 14, 6)) {
printk("Enabling uart2\n");
smc_io_reg[2] |= 0x40;
}
else ret = -EINVAL;
}
if(!strncmp("irq", page + 6, 3)) {
if(!strncmp("pp", page + 10, 2)) {
printk("IRQ active high, inactive low\n");
smc_io_reg[1] |= 0x10;
}
else if(!strncmp("oc", page + 10, 2)) {
printk("IRQ active low, open collector\n");
smc_io_reg[1] &= ~0x10;
}
else {
printk("IRQ bad value\n");
ret = -EINVAL;
}
}
if (!(ret < 0)) write_smcio_reg(port,cookie);
free_page((ulong) page);
return ( (ret)?ret:count);
}
int smcio_read_mem(char *buf, char **start, off_t offset,
int len, int unused)
{
int i;
len=0;
read_smcio_reg(port,cookie);
for (i=0; i < NO_SMC_REGS; i++)
len+= sprintf(buf+len, "0x%x ",smc_io_reg[i]);
/* enqueue the messages */
len+= sprintf(buf+len, "\n");
return len;
}
struct proc_dir_entry smcio_proc_entry =
{
0, /* low_ino: the inode -- dynamic */
5, "smcio", /* len of name and name */
S_IFREG | S_IRUGO |S_IWUSR, /* mode */
1, 0, 0, /* nlinks, owner, group */
NO_SMC_REGS*5+1, /* size */
&smcio_inode_operations, /* operations -- use default */
&smcio_read_mem, /* function used to read data */
/* nothing more */
};
#ifdef MODULE
#define smcio_init init_module
#endif
int smcio_init(void)
{
if (proc_register_dynamic(&proc_root, &smcio_proc_entry)) {
printk(KERN_INFO "Couldn't get a proc-entry for smcio\n");
return -EIO;
}
port = PRIMARY_PORT;
cookie = SMC665_COOKIE;
read_smcio_reg(port,cookie);
if (smc_io_reg[SMC_CRD] == SMC665 ) return 0;
cookie = SMC666_COOKIE;
read_smcio_reg(port,cookie);
if (smc_io_reg[SMC_CRD] == SMC666 ) return 0;
port = PRIMARY_PORT;
read_smcio_reg(port,cookie);
if (smc_io_reg[SMC_CRD] == SMC666 ) return 0;
printk(KERN_INFO "No SMC Super I/O found.\n");
proc_unregister(&proc_root, smcio_proc_entry.low_ino);
return -EIO;
}
#ifdef MODULE
void cleanup_module(void)
{
proc_unregister(&proc_root, smcio_proc_entry.low_ino);
}
#endif
/*
* Overrides for Emacs so that we get a uniform tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-indent-level: 8
* c-brace-imaginary-offset: 0
* c-brace-offset: -8
* c-argdecl-indent: 8
* c-label-offset: -8
* c-continued-statement-offset: 8
* c-continued-brace-offset: 8
* indent-tabs-mode: nil
* tab-width: 8
* End:
*/
/*
elektron:~/tmp/misc> gcc -O2 -Wall -D__KERNEL__ -I/usr/src/linux/include -DMODULE -DCPU=586 -c -o smcio.o smcio.c
*/
-- To unsubscribe, send mail to: linux-parport-request@torque.net --
-- with the single word "unsubscribe" in the body of the message. --
This archive was generated by hypermail 2b29 : Tue Jul 25 2000 - 10:21:47 EDT