diff options
Diffstat (limited to 'drivers')
191 files changed, 7100 insertions, 6383 deletions
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 8a70a9edabd..6b658d84d52 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -48,8 +48,6 @@ config CRYPTO_DEV_PADLOCK_SHA If unsure say M. The compiled module will be called padlock-sha.ko -source "arch/s390/crypto/Kconfig" - config CRYPTO_DEV_GEODE tristate "Support for the Geode LX AES engine" depends on X86_32 && PCI @@ -83,6 +81,67 @@ config ZCRYPT_MONOLITHIC that contains all parts of the crypto device driver (ap bus, request router and all the card drivers). +config CRYPTO_SHA1_S390 + tristate "SHA1 digest algorithm" + depends on S390 + select CRYPTO_ALGAPI + help + This is the s390 hardware accelerated implementation of the + SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2). + +config CRYPTO_SHA256_S390 + tristate "SHA256 digest algorithm" + depends on S390 + select CRYPTO_ALGAPI + help + This is the s390 hardware accelerated implementation of the + SHA256 secure hash standard (DFIPS 180-2). + + This version of SHA implements a 256 bit hash with 128 bits of + security against collision attacks. + +config CRYPTO_DES_S390 + tristate "DES and Triple DES cipher algorithms" + depends on S390 + select CRYPTO_ALGAPI + select CRYPTO_BLKCIPHER + help + This us the s390 hardware accelerated implementation of the + DES cipher algorithm (FIPS 46-2), and Triple DES EDE (FIPS 46-3). + +config CRYPTO_AES_S390 + tristate "AES cipher algorithms" + depends on S390 + select CRYPTO_ALGAPI + select CRYPTO_BLKCIPHER + help + This is the s390 hardware accelerated implementation of the + AES cipher algorithms (FIPS-197). AES uses the Rijndael + algorithm. + + Rijndael appears to be consistently a very good performer in + both hardware and software across a wide range of computing + environments regardless of its use in feedback or non-feedback + modes. Its key setup time is excellent, and its key agility is + good. Rijndael's very low memory requirements make it very well + suited for restricted-space environments, in which it also + demonstrates excellent performance. Rijndael's operations are + among the easiest to defend against power and timing attacks. + + On s390 the System z9-109 currently only supports the key size + of 128 bit. + +config S390_PRNG + tristate "Pseudo random number generator device driver" + depends on S390 + default "m" + help + Select this option if you want to use the s390 pseudo random number + generator. The PRNG is part of the cryptographic processor functions + and uses triple-DES to generate secure random numbers like the + ANSI X9.17 standard. The PRNG is usable via the char device + /dev/prandom. + config CRYPTO_DEV_HIFN_795X tristate "Driver HIFN 795x crypto accelerator chips" select CRYPTO_DES diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index a37cb6b8593..35812823787 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -1,7 +1,7 @@ -/* ------------------------------------------------------------------------- */ -/* i2c-algo-bit.c i2c driver algorithms for bit-shift adapters */ -/* ------------------------------------------------------------------------- */ -/* Copyright (C) 1995-2000 Simon G. Vogl +/* ------------------------------------------------------------------------- + * i2c-algo-bit.c i2c driver algorithms for bit-shift adapters + * ------------------------------------------------------------------------- + * Copyright (C) 1995-2000 Simon G. Vogl 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 @@ -15,8 +15,8 @@ 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. */ -/* ------------------------------------------------------------------------- */ + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * ------------------------------------------------------------------------- */ /* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki <kmalkki@cc.hut.fi> and Jean Delvare <khali@linux-fr.org> */ @@ -60,26 +60,26 @@ MODULE_PARM_DESC(i2c_debug, /* --- setting states on the bus with the right timing: --------------- */ -#define setsda(adap,val) adap->setsda(adap->data, val) -#define setscl(adap,val) adap->setscl(adap->data, val) -#define getsda(adap) adap->getsda(adap->data) -#define getscl(adap) adap->getscl(adap->data) +#define setsda(adap, val) adap->setsda(adap->data, val) +#define setscl(adap, val) adap->setscl(adap->data, val) +#define getsda(adap) adap->getsda(adap->data) +#define getscl(adap) adap->getscl(adap->data) static inline void sdalo(struct i2c_algo_bit_data *adap) { - setsda(adap,0); + setsda(adap, 0); udelay((adap->udelay + 1) / 2); } static inline void sdahi(struct i2c_algo_bit_data *adap) { - setsda(adap,1); + setsda(adap, 1); udelay((adap->udelay + 1) / 2); } static inline void scllo(struct i2c_algo_bit_data *adap) { - setscl(adap,0); + setscl(adap, 0); udelay(adap->udelay / 2); } @@ -91,22 +91,21 @@ static int sclhi(struct i2c_algo_bit_data *adap) { unsigned long start; - setscl(adap,1); + setscl(adap, 1); /* Not all adapters have scl sense line... */ if (!adap->getscl) goto done; - start=jiffies; - while (! getscl(adap) ) { - /* the hw knows how to read the clock line, - * so we wait until it actually gets high. - * This is safer as some chips may hold it low - * while they are processing data internally. - */ - if (time_after_eq(jiffies, start+adap->timeout)) { + start = jiffies; + while (!getscl(adap)) { + /* This hw knows how to read the clock line, so we wait + * until it actually gets high. This is safer as some + * chips may hold it low ("clock stretching") while they + * are processing data internally. + */ + if (time_after_eq(jiffies, start + adap->timeout)) return -ETIMEDOUT; - } cond_resched(); } #ifdef DEBUG @@ -118,11 +117,11 @@ static int sclhi(struct i2c_algo_bit_data *adap) done: udelay(adap->udelay); return 0; -} +} /* --- other auxiliary functions -------------------------------------- */ -static void i2c_start(struct i2c_algo_bit_data *adap) +static void i2c_start(struct i2c_algo_bit_data *adap) { /* assert: scl, sda are high */ setsda(adap, 0); @@ -130,7 +129,7 @@ static void i2c_start(struct i2c_algo_bit_data *adap) scllo(adap); } -static void i2c_repstart(struct i2c_algo_bit_data *adap) +static void i2c_repstart(struct i2c_algo_bit_data *adap) { /* assert: scl is low */ sdahi(adap); @@ -141,18 +140,18 @@ static void i2c_repstart(struct i2c_algo_bit_data *adap) } -static void i2c_stop(struct i2c_algo_bit_data *adap) +static void i2c_stop(struct i2c_algo_bit_data *adap) { /* assert: scl is low */ sdalo(adap); - sclhi(adap); + sclhi(adap); setsda(adap, 1); udelay(adap->udelay); } -/* send a byte without start cond., look for arbitration, +/* send a byte without start cond., look for arbitration, check ackn. from slave */ /* returns: * 1 if the device acknowledged @@ -167,27 +166,33 @@ static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c) struct i2c_algo_bit_data *adap = i2c_adap->algo_data; /* assert: scl is low */ - for ( i=7 ; i>=0 ; i-- ) { + for (i = 7; i >= 0; i--) { sb = (c >> i) & 1; - setsda(adap,sb); + setsda(adap, sb); udelay((adap->udelay + 1) / 2); - if (sclhi(adap)<0) { /* timed out */ + if (sclhi(adap) < 0) { /* timed out */ bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, " "timeout at bit #%d\n", (int)c, i); return -ETIMEDOUT; - }; - /* do arbitration here: - * if ( sb && ! getsda(adap) ) -> ouch! Get out of here. + } + /* FIXME do arbitration here: + * if (sb && !getsda(adap)) -> ouch! Get out of here. + * + * Report a unique code, so higher level code can retry + * the whole (combined) message and *NOT* issue STOP. */ scllo(adap); } sdahi(adap); - if (sclhi(adap)<0){ /* timeout */ + if (sclhi(adap) < 0) { /* timeout */ bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, " "timeout at ack\n", (int)c); return -ETIMEDOUT; - }; - /* read ack: SDA should be pulled down by slave */ + } + + /* read ack: SDA should be pulled down by slave, or it may + * NAK (usually to report problems with the data we wrote). + */ ack = !getsda(adap); /* ack: sda is pulled low -> success */ bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c, ack ? "A" : "NA"); @@ -198,24 +203,24 @@ static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c) } -static int i2c_inb(struct i2c_adapter *i2c_adap) +static int i2c_inb(struct i2c_adapter *i2c_adap) { /* read byte via i2c port, without start/stop sequence */ /* acknowledge is sent in i2c_read. */ int i; - unsigned char indata=0; + unsigned char indata = 0; struct i2c_algo_bit_data *adap = i2c_adap->algo_data; /* assert: scl is low */ sdahi(adap); - for (i=0;i<8;i++) { - if (sclhi(adap)<0) { /* timeout */ + for (i = 0; i < 8; i++) { + if (sclhi(adap) < 0) { /* timeout */ bit_dbg(1, &i2c_adap->dev, "i2c_inb: timeout at bit " "#%d\n", 7 - i); return -ETIMEDOUT; - }; + } indata *= 2; - if ( getsda(adap) ) + if (getsda(adap)) indata |= 0x01; setscl(adap, 0); udelay(i == 7 ? adap->udelay / 2 : adap->udelay); @@ -228,66 +233,67 @@ static int i2c_inb(struct i2c_adapter *i2c_adap) * Sanity check for the adapter hardware - check the reaction of * the bus lines only if it seems to be idle. */ -static int test_bus(struct i2c_algo_bit_data *adap, char* name) { - int scl,sda; +static int test_bus(struct i2c_algo_bit_data *adap, char *name) +{ + int scl, sda; - if (adap->getscl==NULL) + if (adap->getscl == NULL) pr_info("%s: Testing SDA only, SCL is not readable\n", name); - sda=getsda(adap); - scl=(adap->getscl==NULL?1:getscl(adap)); - if (!scl || !sda ) { + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (!scl || !sda) { printk(KERN_WARNING "%s: bus seems to be busy\n", name); goto bailout; } sdalo(adap); - sda=getsda(adap); - scl=(adap->getscl==NULL?1:getscl(adap)); - if ( 0 != sda ) { + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (sda) { printk(KERN_WARNING "%s: SDA stuck high!\n", name); goto bailout; } - if ( 0 == scl ) { + if (!scl) { printk(KERN_WARNING "%s: SCL unexpected low " "while pulling SDA low!\n", name); goto bailout; - } + } sdahi(adap); - sda=getsda(adap); - scl=(adap->getscl==NULL?1:getscl(adap)); - if ( 0 == sda ) { + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (!sda) { printk(KERN_WARNING "%s: SDA stuck low!\n", name); goto bailout; } - if ( 0 == scl ) { + if (!scl) { printk(KERN_WARNING "%s: SCL unexpected low " "while pulling SDA high!\n", name); goto bailout; } scllo(adap); - sda=getsda(adap); - scl=(adap->getscl==NULL?0:getscl(adap)); - if ( 0 != scl ) { + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 0 : getscl(adap); + if (scl) { printk(KERN_WARNING "%s: SCL stuck high!\n", name); goto bailout; } - if ( 0 == sda ) { + if (!sda) { printk(KERN_WARNING "%s: SDA unexpected low " "while pulling SCL low!\n", name); goto bailout; } - + sclhi(adap); - sda=getsda(adap); - scl=(adap->getscl==NULL?1:getscl(adap)); - if ( 0 == scl ) { + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (!scl) { printk(KERN_WARNING "%s: SCL stuck low!\n", name); goto bailout; } - if ( 0 == sda ) { + if (!sda) { printk(KERN_WARNING "%s: SDA unexpected low " "while pulling SCL high!\n", name); goto bailout; @@ -314,9 +320,10 @@ static int try_address(struct i2c_adapter *i2c_adap, unsigned char addr, int retries) { struct i2c_algo_bit_data *adap = i2c_adap->algo_data; - int i,ret = -1; - for (i=0;i<=retries;i++) { - ret = i2c_outb(i2c_adap,addr); + int i, ret = -1; + + for (i = 0; i <= retries; i++) { + ret = i2c_outb(i2c_adap, addr); if (ret == 1 || i == retries) break; bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n"); @@ -338,20 +345,38 @@ static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) { const unsigned char *temp = msg->buf; int count = msg->len; - unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; + unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; int retval; - int wrcount=0; + int wrcount = 0; while (count > 0) { retval = i2c_outb(i2c_adap, *temp); - if ((retval>0) || (nak_ok && (retval==0))) { /* ok or ignored NAK */ - count--; + + /* OK/ACK; or ignored NAK */ + if ((retval > 0) || (nak_ok && (retval == 0))) { + count--; temp++; wrcount++; - } else { /* arbitration or no acknowledge */ - dev_err(&i2c_adap->dev, "sendbytes: error - bailout.\n"); - return (retval<0)? retval : -EFAULT; - /* got a better one ?? */ + + /* A slave NAKing the master means the slave didn't like + * something about the data it saw. For example, maybe + * the SMBus PEC was wrong. + */ + } else if (retval == 0) { + dev_err(&i2c_adap->dev, "sendbytes: NAK bailout.\n"); + return -EIO; + + /* Timeout; or (someday) lost arbitration + * + * FIXME Lost ARB implies retrying the transaction from + * the first message, after the "winning" master issues + * its STOP. As a rule, upper layer code has no reason + * to know or care about this ... it is *NOT* an error. + */ + } else { + dev_err(&i2c_adap->dev, "sendbytes: error %d\n", + retval); + return retval; } } return wrcount; @@ -376,14 +401,14 @@ static int acknak(struct i2c_adapter *i2c_adap, int is_ack) static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) { int inval; - int rdcount=0; /* counts bytes read */ + int rdcount = 0; /* counts bytes read */ unsigned char *temp = msg->buf; int count = msg->len; const unsigned flags = msg->flags; while (count > 0) { inval = i2c_inb(i2c_adap); - if (inval>=0) { + if (inval >= 0) { *temp = inval; rdcount++; } else { /* read timed out */ @@ -431,7 +456,7 @@ static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) * returns: * 0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set * -x an error occurred (like: -EREMOTEIO if the device did not answer, or - * -ETIMEDOUT, for example if the lines are stuck...) + * -ETIMEDOUT, for example if the lines are stuck...) */ static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) { @@ -443,10 +468,10 @@ static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) int ret, retries; retries = nak_ok ? 0 : i2c_adap->retries; - - if ( (flags & I2C_M_TEN) ) { + + if (flags & I2C_M_TEN) { /* a ten bit address */ - addr = 0xf0 | (( msg->addr >> 7) & 0x03); + addr = 0xf0 | ((msg->addr >> 7) & 0x03); bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr); /* try extended address code...*/ ret = try_address(i2c_adap, addr, retries); @@ -456,33 +481,33 @@ static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) return -EREMOTEIO; } /* the remaining 8 bit address */ - ret = i2c_outb(i2c_adap,msg->addr & 0x7f); + ret = i2c_outb(i2c_adap, msg->addr & 0x7f); if ((ret != 1) && !nak_ok) { /* the chip did not ack / xmission error occurred */ dev_err(&i2c_adap->dev, "died at 2nd address code\n"); return -EREMOTEIO; } - if ( flags & I2C_M_RD ) { + if (flags & I2C_M_RD) { bit_dbg(3, &i2c_adap->dev, "emitting repeated " "start condition\n"); i2c_repstart(adap); /* okay, now switch into reading mode */ addr |= 0x01; ret = try_address(i2c_adap, addr, retries); - if ((ret!=1) && !nak_ok) { + if ((ret != 1) && !nak_ok) { dev_err(&i2c_adap->dev, "died at repeated address code\n"); return -EREMOTEIO; } } } else { /* normal 7bit address */ - addr = ( msg->addr << 1 ); - if (flags & I2C_M_RD ) + addr = msg->addr << 1; + if (flags & I2C_M_RD) addr |= 1; - if (flags & I2C_M_REV_DIR_ADDR ) + if (flags & I2C_M_REV_DIR_ADDR) addr ^= 1; ret = try_address(i2c_adap, addr, retries); - if ((ret!=1) && !nak_ok) + if ((ret != 1) && !nak_ok) return -EREMOTEIO; } @@ -494,15 +519,14 @@ static int bit_xfer(struct i2c_adapter *i2c_adap, { struct i2c_msg *pmsg; struct i2c_algo_bit_data *adap = i2c_adap->algo_data; - - int i,ret; + int i, ret; unsigned short nak_ok; bit_dbg(3, &i2c_adap->dev, "emitting start condition\n"); i2c_start(adap); - for (i=0;i<num;i++) { + for (i = 0; i < num; i++) { pmsg = &msgs[i]; - nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; + nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; if (!(pmsg->flags & I2C_M_NOSTART)) { if (i) { bit_dbg(3, &i2c_adap->dev, "emitting " @@ -517,7 +541,7 @@ static int bit_xfer(struct i2c_adapter *i2c_adap, goto bailout; } } - if (pmsg->flags & I2C_M_RD ) { + if (pmsg->flags & I2C_M_RD) { /* read bytes into buffer*/ ret = readbytes(i2c_adap, pmsg); if (ret >= 1) @@ -551,7 +575,7 @@ bailout: static u32 bit_func(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; @@ -565,8 +589,8 @@ static const struct i2c_algorithm i2c_bit_algo = { .functionality = bit_func, }; -/* - * registering functions to load algorithms at runtime +/* + * registering functions to load algorithms at runtime */ static int i2c_bit_prepare_bus(struct i2c_adapter *adap) { @@ -574,7 +598,7 @@ static int i2c_bit_prepare_bus(struct i2c_adapter *adap) if (bit_test) { int ret = test_bus(bit_adap, adap->name); - if (ret<0) + if (ret < 0) return -ENODEV; } diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c index ab2e6f3498b..8907b019167 100644 --- a/drivers/i2c/algos/i2c-algo-pcf.c +++ b/drivers/i2c/algos/i2c-algo-pcf.c @@ -203,35 +203,6 @@ static int pcf_init_8584 (struct i2c_algo_pcf_data *adap) /* ----- Utility functions */ -static inline int try_address(struct i2c_algo_pcf_data *adap, - unsigned char addr, int retries) -{ - int i, status, ret = -1; - int wfp; - for (i=0;i<retries;i++) { - i2c_outb(adap, addr); - i2c_start(adap); - status = get_pcf(adap, 1); - if ((wfp = wait_for_pin(adap, &status)) >= 0) { - if ((status & I2C_PCF_LRB) == 0) { - i2c_stop(adap); - break; /* success! */ - } - } - if (wfp == -EINTR) { - /* arbitration lost */ - udelay(adap->udelay); - return -EINTR; - } - i2c_stop(adap); - udelay(adap->udelay); - } - DEB2(if (i) printk(KERN_DEBUG "i2c-algo-pcf.o: needed %d retries for %d\n",i, - addr)); - return ret; -} - - static int pcf_sendbytes(struct i2c_adapter *i2c_adap, const char *buf, int count, int last) { @@ -321,47 +292,19 @@ static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, } -static inline int pcf_doAddress(struct i2c_algo_pcf_data *adap, - struct i2c_msg *msg, int retries) +static int pcf_doAddress(struct i2c_algo_pcf_data *adap, + struct i2c_msg *msg) { unsigned short flags = msg->flags; unsigned char addr; - int ret; - if ( (flags & I2C_M_TEN) ) { - /* a ten bit address */ - addr = 0xf0 | (( msg->addr >> 7) & 0x03); - DEB2(printk(KERN_DEBUG "addr0: %d\n",addr)); - /* try extended address code...*/ - ret = try_address(adap, addr, retries); - if (ret!=1) { - printk(KERN_ERR "died at extended address code.\n"); - return -EREMOTEIO; - } - /* the remaining 8 bit address */ - i2c_outb(adap,msg->addr & 0x7f); -/* Status check comes here */ - if (ret != 1) { - printk(KERN_ERR "died at 2nd address code.\n"); - return -EREMOTEIO; - } - if ( flags & I2C_M_RD ) { - i2c_repstart(adap); - /* okay, now switch into reading mode */ - addr |= 0x01; - ret = try_address(adap, addr, retries); - if (ret!=1) { - printk(KERN_ERR "died at extended address code.\n"); - return -EREMOTEIO; - } - } - } else { /* normal 7bit address */ - addr = ( msg->addr << 1 ); - if (flags & I2C_M_RD ) - addr |= 1; - if (flags & I2C_M_REV_DIR_ADDR ) - addr ^= 1; - i2c_outb(adap, addr); - } + + addr = msg->addr << 1; + if (flags & I2C_M_RD) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + i2c_outb(adap, addr); + return 0; } @@ -390,7 +333,7 @@ static int pcf_xfer(struct i2c_adapter *i2c_adap, pmsg->flags & I2C_M_RD ? "read" : "write", pmsg->len, pmsg->addr, i + 1, num);) - ret = pcf_doAddress(adap, pmsg, i2c_adap->retries); + ret = pcf_doAddress(adap, pmsg); /* Send START */ if (i == 0) { @@ -453,7 +396,7 @@ static int pcf_xfer(struct i2c_adapter *i2c_adap, static u32 pcf_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | - I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; + I2C_FUNC_PROTOCOL_MANGLING; } /* -----exported algorithm data: ------------------------------------- */ @@ -475,9 +418,7 @@ int i2c_pcf_add_bus(struct i2c_adapter *adap) /* register new adapter to i2c module... */ adap->algo = &pcf_algo; - - adap->timeout = 100; /* default values, should */ - adap->retries = 3; /* be replaced by defines */ + adap->timeout = 100; if ((rval = pcf_init_8584(pcf_adap))) return rval; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index c466c6cfc2e..8d12b26bb6c 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -182,7 +182,8 @@ config I2C_I801 will be called i2c-i801. config I2C_I810 - tristate "Intel 810/815" + tristate "Intel 810/815 (DEPRECATED)" + default n depends on PCI select I2C_ALGOBIT help @@ -195,6 +196,8 @@ config I2C_I810 i815 i845G + This driver is deprecated in favor of the i810fb and intelfb drivers. + This driver can also be built as a module. If so, the module will be called i2c-i810. @@ -259,20 +262,6 @@ config I2C_IOP3XX This driver can also be built as a module. If so, the module will be called i2c-iop3xx. -config I2C_IXP4XX - tristate "IXP4xx GPIO-Based I2C Interface (DEPRECATED)" - depends on ARCH_IXP4XX - select I2C_ALGOBIT - help - Say Y here if you have an Intel IXP4xx(420,421,422,425) based - system and are using GPIO lines for an I2C bus. - - This support is also available as a module. If so, the module - will be called i2c-ixp4xx. - - This driver is deprecated and will be dropped soon. Use i2c-gpio - instead. - config I2C_IXP2000 tristate "IXP2000 GPIO-Based I2C Interface (DEPRECATED)" depends on ARCH_IXP2000 @@ -396,7 +385,8 @@ config I2C_PASEMI Supports the PA Semi PWRficient on-chip SMBus interfaces. config I2C_PROSAVAGE - tristate "S3/VIA (Pro)Savage" + tristate "S3/VIA (Pro)Savage (DEPRECATED)" + default n depends on PCI select I2C_ALGOBIT help @@ -407,6 +397,8 @@ config I2C_PROSAVAGE S3/VIA KM266/VT8375 aka ProSavage8 S3/VIA KM133/VT8365 aka Savage4 + This driver is deprecated in favor of the savagefb driver. + This support is also available as a module. If so, the module will be called i2c-prosavage. @@ -418,13 +410,16 @@ config I2C_S3C2410 Samsung S3C2410 based System-on-Chip devices. config I2C_SAVAGE4 - tristate "S3 Savage 4" - depends on PCI && EXPERIMENTAL + tristate "S3 Savage 4 (DEPRECATED)" + default n + depends on PCI select I2C_ALGOBIT help If you say yes to this option, support will be included for the S3 Savage 4 I2C interface. + This driver is deprecated in favor of the savagefb driver. + This driver can also be built as a module. If so, the module will be called i2c-savage4. @@ -611,7 +606,7 @@ config I2C_VIAPRO VT8231 VT8233/A VT8235 - VT8237R/A + VT8237R/A/S VT8251 CX700 diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 81d43c27cf9..ea7068f1eb6 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_I2C_I810) += i2c-i810.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o -obj-$(CONFIG_I2C_IXP4XX) += i2c-ixp4xx.o obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index 7490dc1771a..573abe44084 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -334,6 +334,10 @@ static int __devinit amd756_probe(struct pci_dev *pdev, int error; u8 temp; + /* driver_data might come from user-space, so check it */ + if (id->driver_data > ARRAY_SIZE(chipname)) + return -EINVAL; + if (amd756_ioport) { dev_err(&pdev->dev, "Only one device supported " "(you have a strange motherboard, btw)\n"); @@ -405,6 +409,7 @@ static struct pci_driver amd756_driver = { .id_table = amd756_ids, .probe = amd756_probe, .remove = __devexit_p(amd756_remove), + .dynids.use_driver_data = 1, }; static int __init amd756_init(void) diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c index 2f684166c43..1953b26da56 100644 --- a/drivers/i2c/busses/i2c-au1550.c +++ b/drivers/i2c/busses/i2c-au1550.c @@ -30,14 +30,22 @@ #include <linux/delay.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/platform_device.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/i2c.h> +#include <linux/slab.h> #include <asm/mach-au1x00/au1xxx.h> #include <asm/mach-au1x00/au1xxx_psc.h> -#include "i2c-au1550.h" +struct i2c_au1550_data { + u32 psc_base; + int xfer_timeout; + int ack_timeout; + struct i2c_adapter adap; + struct resource *ioarea; +}; static int wait_xfer_done(struct i2c_au1550_data *adap) @@ -105,7 +113,7 @@ wait_master_done(struct i2c_au1550_data *adap) } static int -do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd) +do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q) { volatile psc_smb_t *sp; u32 stat; @@ -134,6 +142,10 @@ do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd) if (rd) addr |= 1; + /* zero-byte xfers stop immediately */ + if (q) + addr |= PSC_SMBTXRX_STP; + /* Put byte into fifo, start up master. */ sp->psc_smbtxrx = addr; @@ -142,7 +154,7 @@ do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd) au_sync(); if (wait_ack(adap)) return -EIO; - return 0; + return (q) ? wait_master_done(adap) : 0; } static u32 @@ -262,7 +274,8 @@ au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) for (i = 0; !err && i < num; i++) { p = &msgs[i]; - err = do_address(adap, p->addr, p->flags & I2C_M_RD); + err = do_address(adap, p->addr, p->flags & I2C_M_RD, + (p->len == 0)); if (err || !p->len) continue; if (p->flags & I2C_M_RD) @@ -294,18 +307,48 @@ static const struct i2c_algorithm au1550_algo = { * Prior to calling us, the 50MHz clock frequency and routing * must have been set up for the PSC indicated by the adapter. */ -int -i2c_au1550_add_bus(struct i2c_adapter *i2c_adap) +static int __devinit +i2c_au1550_probe(struct platform_device *pdev) { - struct i2c_au1550_data *adap = i2c_adap->algo_data; - volatile psc_smb_t *sp; - u32 stat; + struct i2c_au1550_data *priv; + volatile psc_smb_t *sp; + struct resource *r; + u32 stat; + int ret; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + ret = -ENODEV; + goto out; + } + + priv = kzalloc(sizeof(struct i2c_au1550_data), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto out; + } + + priv->ioarea = request_mem_region(r->start, r->end - r->start + 1, + pdev->name); + if (!priv->ioarea) { + ret = -EBUSY; + goto out_mem; + } - i2c_adap->algo = &au1550_algo; + priv->psc_base = r->start; + priv->xfer_timeout = 200; + priv->ack_timeout = 200; + + priv->adap.id = I2C_HW_AU1550_PSC; + priv->adap.nr = pdev->id; + priv->adap.algo = &au1550_algo; + priv->adap.algo_data = priv; + priv->adap.dev.parent = &pdev->dev; + strlcpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name)); /* Now, set up the PSC for SMBus PIO mode. */ - sp = (volatile psc_smb_t *)(adap->psc_base); + sp = (volatile psc_smb_t *)priv->psc_base; sp->psc_ctrl = PSC_CTRL_DISABLE; au_sync(); sp->psc_sel = PSC_SEL_PS_SMBUSMODE; @@ -343,87 +386,87 @@ i2c_au1550_add_bus(struct i2c_adapter *i2c_adap) au_sync(); } while ((stat & PSC_SMBSTAT_DR) == 0); - return i2c_add_adapter(i2c_adap); -} + ret = i2c_add_numbered_adapter(&priv->adap); + if (ret == 0) { + platform_set_drvdata(pdev, priv); + return 0; + } + /* disable the PSC */ + sp->psc_smbcfg = 0; + sp->psc_ctrl = PSC_CTRL_DISABLE; + au_sync(); -int -i2c_au1550_del_bus(struct i2c_adapter *adap) + release_resource(priv->ioarea); + kfree(priv->ioarea); +out_mem: + kfree(priv); +out: + return ret; +} + +static int __devexit +i2c_au1550_remove(struct platform_device *pdev) { - return i2c_del_adapter(adap); + struct i2c_au1550_data *priv = platform_get_drvdata(pdev); + volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base; + + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&priv->adap); + sp->psc_smbcfg = 0; + sp->psc_ctrl = PSC_CTRL_DISABLE; + au_sync(); + release_resource(priv->ioarea); + kfree(priv->ioarea); + kfree(priv); + return 0; } static int -pb1550_reg(struct i2c_client *client) +i2c_au1550_suspend(struct platform_device *pdev, pm_message_t state) { + struct i2c_au1550_data *priv = platform_get_drvdata(pdev); + volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base; + + sp->psc_ctrl = PSC_CTRL_SUSPEND; + au_sync(); return 0; } static int -pb1550_unreg(struct i2c_client *client) +i2c_au1550_resume(struct platform_device *pdev) { + struct i2c_au1550_data *priv = platform_get_drvdata(pdev); + volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base; + + sp->psc_ctrl = PSC_CTRL_ENABLE; + au_sync(); + while (!(sp->psc_smbstat & PSC_SMBSTAT_SR)) + au_sync(); return 0; } -static struct i2c_au1550_data pb1550_i2c_info = { - SMBUS_PSC_BASE, 200, 200 -}; - -static struct i2c_adapter pb1550_board_adapter = { - name: "pb1550 adapter", - id: I2C_HW_AU1550_PSC, - algo: NULL, - algo_data: &pb1550_i2c_info, - client_register: pb1550_reg, - client_unregister: pb1550_unreg, +static struct platform_driver au1xpsc_smbus_driver = { + .driver = { + .name = "au1xpsc_smbus", + .owner = THIS_MODULE, + }, + .probe = i2c_au1550_probe, + .remove = __devexit_p(i2c_au1550_remove), + .suspend = i2c_au1550_suspend, + .resume = i2c_au1550_resume, }; -/* BIG hack to support the control interface on the Wolfson WM8731 - * audio codec on the Pb1550 board. We get an address and two data - * bytes to write, create an i2c message, and send it across the - * i2c transfer function. We do this here because we have access to - * the i2c adapter structure. - */ -static struct i2c_msg wm_i2c_msg; /* We don't want this stuff on the stack */ -static u8 i2cbuf[2]; - -int -pb1550_wm_codec_write(u8 addr, u8 reg, u8 val) -{ - wm_i2c_msg.addr = addr; - wm_i2c_msg.flags = 0; - wm_i2c_msg.buf = i2cbuf; - wm_i2c_msg.len = 2; - i2cbuf[0] = reg; - i2cbuf[1] = val; - - return pb1550_board_adapter.algo->master_xfer(&pb1550_board_adapter, &wm_i2c_msg, 1); -} - static int __init i2c_au1550_init(void) { - printk(KERN_INFO "Au1550 I2C: "); - - /* This is where we would set up a 50MHz clock source - * and routing. On the Pb1550, the SMBus is PSC2, which - * uses a shared clock with USB. This has been already - * configured by Yamon as a 48MHz clock, close enough - * for our work. - */ - if (i2c_au1550_add_bus(&pb1550_board_adapter) < 0) { - printk("failed to initialize.\n"); - return -ENODEV; - } - - printk("initialized.\n"); - return 0; + return platform_driver_register(&au1xpsc_smbus_driver); } static void __exit i2c_au1550_exit(void) { - i2c_au1550_del_bus(&pb1550_board_adapter); + platform_driver_unregister(&au1xpsc_smbus_driver); } MODULE_AUTHOR("Dan Malek, Embedded Edge, LLC."); diff --git a/drivers/i2c/busses/i2c-au1550.h b/drivers/i2c/busses/i2c-au1550.h deleted file mode 100644 index fce15d161ae..00000000000 --- a/drivers/i2c/busses/i2c-au1550.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com> - * 2.6 port by Matt Porter <mporter@kernel.crashing.org> - * - * 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. - */ - -#ifndef I2C_AU1550_H -#define I2C_AU1550_H - -struct i2c_au1550_data { - u32 psc_base; - int xfer_timeout; - int ack_timeout; -}; - -int i2c_au1550_add_bus(struct i2c_adapter *); -int i2c_au1550_del_bus(struct i2c_adapter *); - -#endif /* I2C_AU1550_H */ diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index 67224a424ab..7dbdaeb707a 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -550,6 +550,7 @@ static int i2c_bfin_twi_probe(struct platform_device *dev) p_adap = &iface->adap; p_adap->id = I2C_HW_BLACKFIN; + p_adap->nr = dev->id; strlcpy(p_adap->name, dev->name, sizeof(p_adap->name)); p_adap->algo = &bfin_twi_algorithm; p_adap->algo_data = iface; @@ -576,7 +577,7 @@ static int i2c_bfin_twi_probe(struct platform_device *dev) bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA); SSYNC(); - rc = i2c_add_adapter(p_adap); + rc = i2c_add_numbered_adapter(p_adap); if (rc < 0) free_irq(iface->irq, iface); else diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 67679882ebe..cce5a614758 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -510,7 +510,6 @@ static int davinci_i2c_probe(struct platform_device *pdev) /* FIXME */ adap->timeout = 1; - adap->retries = 1; adap->nr = pdev->id; r = i2c_add_numbered_adapter(adap); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index ac27e5f84eb..aa9157913b9 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -4,6 +4,7 @@ Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker <mdsxyz123@yahoo.com> + Copyright (C) 2007 Jean Delvare <khali@linux-fr.org> 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 @@ -21,25 +22,34 @@ */ /* - SUPPORTED DEVICES PCI ID - 82801AA 2413 - 82801AB 2423 - 82801BA 2443 - 82801CA/CAM 2483 - 82801DB 24C3 (HW PEC supported) - 82801EB 24D3 (HW PEC supported) - 6300ESB 25A4 - ICH6 266A - ICH7 27DA - ESB2 269B - ICH8 283E - ICH9 2930 - Tolapai 5032 - This driver supports several versions of Intel's I/O Controller Hubs (ICH). - For SMBus support, they are similar to the PIIX4 and are part - of Intel's '810' and other chipsets. - See the file Documentation/i2c/busses/i2c-i801 for details. - I2C Block Read and Process Call are not supported. + Supports the following Intel I/O Controller Hubs (ICH): + + I/O Block I2C + region SMBus Block proc. block + Chip name PCI ID size PEC buffer call read + ---------------------------------------------------------------------- + 82801AA (ICH) 0x2413 16 no no no no + 82801AB (ICH0) 0x2423 16 no no no no + 82801BA (ICH2) 0x2443 16 no no no no + 82801CA (ICH3) 0x2483 32 soft no no no + 82801DB (ICH4) 0x24c3 32 hard yes no no + 82801E (ICH5) 0x24d3 32 hard yes yes yes + 6300ESB 0x25a4 32 hard yes yes yes + 82801F (ICH6) 0x266a 32 hard yes yes yes + 6310ESB/6320ESB 0x269b 32 hard yes yes yes + 82801G (ICH7) 0x27da 32 hard yes yes yes + 82801H (ICH8) 0x283e 32 hard yes yes yes + 82801I (ICH9) 0x2930 32 hard yes yes yes + Tolapai 0x5032 32 hard yes ? ? + + Features supported by this driver: + Software PEC no + Hardware PEC yes + Block buffer yes + Block process call transaction no + I2C block read transaction yes (doesn't use the block buffer) + + See the file Documentation/i2c/busses/i2c-i801 for details. */ /* Note: we assume there can only be one I801, with one SMBus interface */ @@ -62,9 +72,9 @@ #define SMBHSTDAT0 (5 + i801_smba) #define SMBHSTDAT1 (6 + i801_smba) #define SMBBLKDAT (7 + i801_smba) -#define SMBPEC (8 + i801_smba) /* ICH4 only */ -#define SMBAUXSTS (12 + i801_smba) /* ICH4 only */ -#define SMBAUXCTL (13 + i801_smba) /* ICH4 only */ +#define SMBPEC (8 + i801_smba) /* ICH3 and later */ +#define SMBAUXSTS (12 + i801_smba) /* ICH4 and later */ +#define SMBAUXCTL (13 + i801_smba) /* ICH4 and later */ /* PCI Address Constants */ #define SMBBAR 4 @@ -91,13 +101,13 @@ #define I801_BYTE 0x04 #define I801_BYTE_DATA 0x08 #define I801_WORD_DATA 0x0C -#define I801_PROC_CALL 0x10 /* later chips only, unimplemented */ +#define I801_PROC_CALL 0x10 /* unimplemented */ #define I801_BLOCK_DATA 0x14 -#define I801_I2C_BLOCK_DATA 0x18 /* unimplemented */ +#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */ #define I801_BLOCK_LAST 0x34 -#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */ +#define I801_I2C_BLOCK_LAST 0x38 /* ICH5 and later */ #define I801_START 0x40 -#define I801_PEC_EN 0x80 /* ICH4 only */ +#define I801_PEC_EN 0x80 /* ICH3 and later */ /* I801 Hosts Status register bits */ #define SMBHSTSTS_BYTE_DONE 0x80 @@ -113,7 +123,12 @@ static unsigned long i801_smba; static unsigned char i801_original_hstcfg; static struct pci_driver i801_driver; static struct pci_dev *I801_dev; -static int isich4; + +#define FEATURE_SMBUS_PEC (1 << 0) +#define FEATURE_BLOCK_BUFFER (1 << 1) +#define FEATURE_BLOCK_PROC (1 << 2) +#define FEATURE_I2C_BLOCK_READ (1 << 3) +static unsigned int i801_features; static int i801_transaction(int xact) { @@ -242,7 +257,8 @@ static int i801_block_transaction_by_block(union i2c_smbus_data *data, } static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data, - char read_write, int hwpec) + char read_write, int command, + int hwpec) { int i, len; int smbcmd; @@ -259,16 +275,24 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data, } for (i = 1; i <= len; i++) { - if (i == len && read_write == I2C_SMBUS_READ) - smbcmd = I801_BLOCK_LAST; - else - smbcmd = I801_BLOCK_DATA; + if (i == len && read_write == I2C_SMBUS_READ) { + if (command == I2C_SMBUS_I2C_BLOCK_DATA) + smbcmd = I801_I2C_BLOCK_LAST; + else + smbcmd = I801_BLOCK_LAST; + } else { + if (command == I2C_SMBUS_I2C_BLOCK_DATA + && read_write == I2C_SMBUS_READ) + smbcmd = I801_I2C_BLOCK_DATA; + else + smbcmd = I801_BLOCK_DATA; + } outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT); dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, " - "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i, + "ADD=%02x, DAT0=%02x, DAT1=%02x, BLKDAT=%02x\n", i, inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), - inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT)); + inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1), inb_p(SMBBLKDAT)); /* Make sure the SMBus host is ready to start transmitting */ temp = inb_p(SMBHSTSTS); @@ -332,7 +356,8 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data, dev_dbg(&I801_dev->dev, "Error: no response!\n"); } - if (i == 1 && read_write == I2C_SMBUS_READ) { + if (i == 1 && read_write == I2C_SMBUS_READ + && command != I2C_SMBUS_I2C_BLOCK_DATA) { len = inb_p(SMBHSTDAT0); if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) return -1; @@ -353,9 +378,9 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data, temp); } dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, " - "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i, + "ADD=%02x, DAT0=%02x, DAT1=%02x, BLKDAT=%02x\n", i, inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), - inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT)); + inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1), inb_p(SMBBLKDAT)); if (result < 0) return result; @@ -384,33 +409,38 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); pci_write_config_byte(I801_dev, SMBHSTCFG, hostc | SMBHSTCFG_I2C_EN); - } else { + } else if (!(i801_features & FEATURE_I2C_BLOCK_READ)) { dev_err(&I801_dev->dev, - "I2C_SMBUS_I2C_BLOCK_READ not DB!\n"); + "I2C block read is unsupported!\n"); return -1; } } - if (read_write == I2C_SMBUS_WRITE) { + if (read_write == I2C_SMBUS_WRITE + || command == I2C_SMBUS_I2C_BLOCK_DATA) { if (data->block[0] < 1) data->block[0] = 1; if (data->block[0] > I2C_SMBUS_BLOCK_MAX) data->block[0] = I2C_SMBUS_BLOCK_MAX; } else { - data->block[0] = 32; /* max for reads */ + data->block[0] = 32; /* max for SMBus block reads */ } - if (isich4 && i801_set_block_buffer_mode() == 0 ) + if ((i801_features & FEATURE_BLOCK_BUFFER) + && !(command == I2C_SMBUS_I2C_BLOCK_DATA + && read_write == I2C_SMBUS_READ) + && i801_set_block_buffer_mode() == 0) result = i801_block_transaction_by_block(data, read_write, hwpec); else result = i801_block_transaction_byte_by_byte(data, read_write, - hwpec); + command, hwpec); if (result == 0 && hwpec) i801_wait_hwpec(); - if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + if (command == I2C_SMBUS_I2C_BLOCK_DATA + && read_write == I2C_SMBUS_WRITE) { /* restore saved configuration register value */ pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); } @@ -426,7 +456,7 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr, int block = 0; int ret, xact = 0; - hwpec = isich4 && (flags & I2C_CLIENT_PEC) + hwpec = (i801_features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA; @@ -462,12 +492,23 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr, xact = I801_WORD_DATA; break; case I2C_SMBUS_BLOCK_DATA: - case I2C_SMBUS_I2C_BLOCK_DATA: outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD); outb_p(command, SMBHSTCMD); block = 1; break; + case I2C_SMBUS_I2C_BLOCK_DATA: + /* NB: page 240 of ICH5 datasheet shows that the R/#W + * bit should be cleared here, even when reading */ + outb_p((addr & 0x7f) << 1, SMBHSTADD); + if (read_write == I2C_SMBUS_READ) { + /* NB: page 240 of ICH5 datasheet also shows + * that DATA1 is the cmd field when reading */ + outb_p(command, SMBHSTDAT1); + } else + outb_p(command, SMBHSTCMD); + block = 1; + break; case I2C_SMBUS_PROC_CALL: default: dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size); @@ -487,7 +528,7 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr, /* Some BIOSes don't like it when PEC is enabled at reboot or resume time, so we forcibly disable it after every transaction. Turn off E32B for the same reason. */ - if (hwpec) + if (hwpec || block) outb_p(inb_p(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL); @@ -514,9 +555,11 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr, static u32 i801_func(struct i2c_adapter *adapter) { return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK - | (isich4 ? I2C_FUNC_SMBUS_PEC : 0); + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | + ((i801_features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) | + ((i801_features & FEATURE_I2C_BLOCK_READ) ? + I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0); } static const struct i2c_algorithm smbus_algorithm = { @@ -556,8 +599,8 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id int err; I801_dev = dev; + i801_features = 0; switch (dev->device) { - case PCI_DEVICE_ID_INTEL_82801DB_3: case PCI_DEVICE_ID_INTEL_82801EB_3: case PCI_DEVICE_ID_INTEL_ESB_4: case PCI_DEVICE_ID_INTEL_ICH6_16: @@ -565,11 +608,13 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id case PCI_DEVICE_ID_INTEL_ESB2_17: case PCI_DEVICE_ID_INTEL_ICH8_5: case PCI_DEVICE_ID_INTEL_ICH9_6: + i801_features |= FEATURE_I2C_BLOCK_READ; + /* fall through */ + case PCI_DEVICE_ID_INTEL_82801DB_3: case PCI_DEVICE_ID_INTEL_TOLAPAI_1: - isich4 = 1; + i801_features |= FEATURE_SMBUS_PEC; + i801_features |= FEATURE_BLOCK_BUFFER; break; - default: - isich4 = 0; } err = pci_enable_device(dev); @@ -610,6 +655,11 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id else dev_dbg(&dev->dev, "SMBus using PCI Interrupt\n"); + /* Clear special mode bits */ + if (i801_features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER)) + outb_p(inb_p(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), + SMBAUXCTL); + /* set up the sysfs linkage to our parent device */ i801_adapter.dev.parent = &dev->dev; @@ -678,9 +728,8 @@ static void __exit i2c_i801_exit(void) pci_unregister_driver(&i801_driver); } -MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, " - "Philip Edelbrock <phil@netroedge.com>, " - "and Mark D. Studebaker <mdsxyz123@yahoo.com>"); +MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>, " + "Jean Delvare <khali@linux-fr.org>"); MODULE_DESCRIPTION("I801 SMBus driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index 9b43ff7270d..7c7eb0cfece 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -6,7 +6,7 @@ * Copyright (c) 2003, 2004 Zultys Technologies. * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> * - * Based on original work by + * Based on original work by * Ian DaSilva <idasilva@mvista.com> * Armin Kuster <akuster@mvista.com> * Matt Porter <mporter@mvista.com> @@ -86,8 +86,8 @@ static void dump_iic_regs(const char* header, struct ibm_iic_private* dev) KERN_DEBUG " sts = 0x%02x, extsts = 0x%02x\n" KERN_DEBUG " clkdiv = 0x%02x, xfrcnt = 0x%02x\n" KERN_DEBUG " xtcntlss = 0x%02x, directcntl = 0x%02x\n", - in_8(&iic->cntl), in_8(&iic->mdcntl), in_8(&iic->sts), - in_8(&iic->extsts), in_8(&iic->clkdiv), in_8(&iic->xfrcnt), + in_8(&iic->cntl), in_8(&iic->mdcntl), in_8(&iic->sts), + in_8(&iic->extsts), in_8(&iic->clkdiv), in_8(&iic->xfrcnt), in_8(&iic->xtcntlss), in_8(&iic->directcntl)); } # define DUMP_REGS(h,dev) dump_iic_regs((h),(dev)) @@ -125,7 +125,7 @@ static inline void iic_interrupt_mode(struct ibm_iic_private* dev, int enable) { out_8(&dev->vaddr->intmsk, enable ? INTRMSK_EIMTC : 0); } - + /* * Initialize IIC interface. */ @@ -134,7 +134,7 @@ static void iic_dev_init(struct ibm_iic_private* dev) volatile struct iic_regs __iomem *iic = dev->vaddr; DBG("%d: init\n", dev->idx); - + /* Clear master address */ out_8(&iic->lmadr, 0); out_8(&iic->hmadr, 0); @@ -160,7 +160,7 @@ static void iic_dev_init(struct ibm_iic_private* dev) /* Clear control register */ out_8(&iic->cntl, 0); - + /* Enable interrupts if possible */ iic_interrupt_mode(dev, dev->irq >= 0); @@ -171,7 +171,7 @@ static void iic_dev_init(struct ibm_iic_private* dev) DUMP_REGS("iic_init", dev); } -/* +/* * Reset IIC interface */ static void iic_dev_reset(struct ibm_iic_private* dev) @@ -179,42 +179,42 @@ static void iic_dev_reset(struct ibm_iic_private* dev) volatile struct iic_regs __iomem *iic = dev->vaddr; int i; u8 dc; - + DBG("%d: soft reset\n", dev->idx); DUMP_REGS("reset", dev); - + /* Place chip in the reset state */ out_8(&iic->xtcntlss, XTCNTLSS_SRST); - + /* Check if bus is free */ - dc = in_8(&iic->directcntl); + dc = in_8(&iic->directcntl); if (!DIRCTNL_FREE(dc)){ DBG("%d: trying to regain bus control\n", dev->idx); - + /* Try to set bus free state */ - out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC); - + out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC); + /* Wait until we regain bus control */ for (i = 0; i < 100; ++i){ dc = in_8(&iic->directcntl); if (DIRCTNL_FREE(dc)) break; - + /* Toggle SCL line */ dc ^= DIRCNTL_SCC; out_8(&iic->directcntl, dc); udelay(10); dc ^= DIRCNTL_SCC; out_8(&iic->directcntl, dc); - + /* be nice */ cond_resched(); } } - + /* Remove reset */ out_8(&iic->xtcntlss, 0); - + /* Reinitialize interface */ iic_dev_init(dev); } @@ -324,14 +324,14 @@ static irqreturn_t iic_handler(int irq, void *dev_id) { struct ibm_iic_private* dev = (struct ibm_iic_private*)dev_id; volatile struct iic_regs __iomem *iic = dev->vaddr; - - DBG2("%d: irq handler, STS = 0x%02x, EXTSTS = 0x%02x\n", + + DBG2("%d: irq handler, STS = 0x%02x, EXTSTS = 0x%02x\n", dev->idx, in_8(&iic->sts), in_8(&iic->extsts)); - + /* Acknowledge IRQ and wakeup iic_wait_for_tc */ out_8(&iic->sts, STS_IRQA | STS_SCMP); wake_up_interruptible(&dev->wq); - + return IRQ_HANDLED; } @@ -341,19 +341,19 @@ static irqreturn_t iic_handler(int irq, void *dev_id) */ static int iic_xfer_result(struct ibm_iic_private* dev) { - volatile struct iic_regs __iomem *iic = dev->vaddr; - + volatile struct iic_regs __iomem *iic = dev->vaddr; + if (unlikely(in_8(&iic->sts) & STS_ERR)){ - DBG("%d: xfer error, EXTSTS = 0x%02x\n", dev->idx, + DBG("%d: xfer error, EXTSTS = 0x%02x\n", dev->idx, in_8(&iic->extsts)); - + /* Clear errors and possible pending IRQs */ - out_8(&iic->extsts, EXTSTS_IRQP | EXTSTS_IRQD | + out_8(&iic->extsts, EXTSTS_IRQP | EXTSTS_IRQD | EXTSTS_LA | EXTSTS_ICT | EXTSTS_XFRA); - + /* Flush master data buffer */ out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB); - + /* Is bus free? * If error happened during combined xfer * IIC interface is usually stuck in some strange @@ -376,11 +376,11 @@ static void iic_abort_xfer(struct ibm_iic_private* dev) { volatile struct iic_regs __iomem *iic = dev->vaddr; unsigned long x; - + DBG("%d: iic_abort_xfer\n", dev->idx); - + out_8(&iic->cntl, CNTL_HMT); - + /* * Wait for the abort command to complete. * It's not worth to be optimized, just poll (timeout >= 1 tick) @@ -405,13 +405,13 @@ static void iic_abort_xfer(struct ibm_iic_private* dev) * Returns the number of transferred bytes or error (<0) */ static int iic_wait_for_tc(struct ibm_iic_private* dev){ - + volatile struct iic_regs __iomem *iic = dev->vaddr; int ret = 0; - + if (dev->irq >= 0){ /* Interrupt mode */ - ret = wait_event_interruptible_timeout(dev->wq, + ret = wait_event_interruptible_timeout(dev->wq, !(in_8(&iic->sts) & STS_PT), dev->adap.timeout * HZ); if (unlikely(ret < 0)) @@ -424,37 +424,37 @@ static int iic_wait_for_tc(struct ibm_iic_private* dev){ else { /* Polling mode */ unsigned long x = jiffies + dev->adap.timeout * HZ; - + while (in_8(&iic->sts) & STS_PT){ if (unlikely(time_after(jiffies, x))){ DBG("%d: poll timeout\n", dev->idx); ret = -ETIMEDOUT; break; } - + if (unlikely(signal_pending(current))){ DBG("%d: poll interrupted\n", dev->idx); ret = -ERESTARTSYS; break; } schedule(); - } + } } - + if (unlikely(ret < 0)) iic_abort_xfer(dev); else ret = iic_xfer_result(dev); - + DBG2("%d: iic_wait_for_tc -> %d\n", dev->idx, ret); - + return ret; } /* * Low level master transfer routine */ -static int iic_xfer_bytes(struct ibm_iic_private* dev, struct i2c_msg* pm, +static int iic_xfer_bytes(struct ibm_iic_private* dev, struct i2c_msg* pm, int combined_xfer) { volatile struct iic_regs __iomem *iic = dev->vaddr; @@ -465,48 +465,48 @@ static int iic_xfer_bytes(struct ibm_iic_private* dev, struct i2c_msg* pm, u8 cntl = (in_8(&iic->cntl) & CNTL_AMD) | CNTL_PT; if (pm->flags & I2C_M_RD) cntl |= CNTL_RW; - + loops = (len + 3) / 4; for (i = 0; i < loops; ++i, len -= 4){ int count = len > 4 ? 4 : len; u8 cmd = cntl | ((count - 1) << CNTL_TCT_SHIFT); - + if (!(cntl & CNTL_RW)) for (j = 0; j < count; ++j) out_8((void __iomem *)&iic->mdbuf, *buf++); - + if (i < loops - 1) cmd |= CNTL_CHT; else if (combined_xfer) cmd |= CNTL_RPST; - + DBG2("%d: xfer_bytes, %d, CNTL = 0x%02x\n", dev->idx, count, cmd); - + /* Start transfer */ out_8(&iic->cntl, cmd); - + /* Wait for completion */ ret = iic_wait_for_tc(dev); if (unlikely(ret < 0)) break; else if (unlikely(ret != count)){ - DBG("%d: xfer_bytes, requested %d, transfered %d\n", + DBG("%d: xfer_bytes, requested %d, transfered %d\n", dev->idx, count, ret); - + /* If it's not a last part of xfer, abort it */ if (combined_xfer || (i < loops - 1)) iic_abort_xfer(dev); - + ret = -EREMOTEIO; - break; + break; } - + if (cntl & CNTL_RW) for (j = 0; j < count; ++j) *buf++ = in_8((void __iomem *)&iic->mdbuf); } - + return ret > 0 ? 0 : ret; } @@ -517,10 +517,10 @@ static inline void iic_address(struct ibm_iic_private* dev, struct i2c_msg* msg) { volatile struct iic_regs __iomem *iic = dev->vaddr; u16 addr = msg->addr; - - DBG2("%d: iic_address, 0x%03x (%d-bit)\n", dev->idx, + + DBG2("%d: iic_address, 0x%03x (%d-bit)\n", dev->idx, addr, msg->flags & I2C_M_TEN ? 10 : 7); - + if (msg->flags & I2C_M_TEN){ out_8(&iic->cntl, CNTL_AMD); out_8(&iic->lmadr, addr); @@ -537,15 +537,15 @@ static inline int iic_invalid_address(const struct i2c_msg* p) return (p->addr > 0x3ff) || (!(p->flags & I2C_M_TEN) && (p->addr > 0x7f)); } -static inline int iic_address_neq(const struct i2c_msg* p1, +static inline int iic_address_neq(const struct i2c_msg* p1, const struct i2c_msg* p2) { - return (p1->addr != p2->addr) + return (p1->addr != p2->addr) || ((p1->flags & I2C_M_TEN) != (p2->flags & I2C_M_TEN)); -} +} /* - * Generic master transfer entrypoint. + * Generic master transfer entrypoint. * Returns the number of processed messages or error (<0) */ static int iic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) @@ -553,20 +553,20 @@ static int iic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) struct ibm_iic_private* dev = (struct ibm_iic_private*)(i2c_get_adapdata(adap)); volatile struct iic_regs __iomem *iic = dev->vaddr; int i, ret = 0; - + DBG2("%d: iic_xfer, %d msg(s)\n", dev->idx, num); - + if (!num) return 0; - + /* Check the sanity of the passed messages. * Uhh, generic i2c layer is more suitable place for such code... */ if (unlikely(iic_invalid_address(&msgs[0]))){ - DBG("%d: invalid address 0x%03x (%d-bit)\n", dev->idx, + DBG("%d: invalid address 0x%03x (%d-bit)\n", dev->idx, msgs[0].addr, msgs[0].flags & I2C_M_TEN ? 10 : 7); return -EINVAL; - } + } for (i = 0; i < num; ++i){ if (unlikely(msgs[i].len <= 0)){ if (num == 1 && !msgs[0].len){ @@ -576,7 +576,7 @@ static int iic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) */ return iic_smbus_quick(dev, &msgs[0]); } - DBG("%d: invalid len %d in msg[%d]\n", dev->idx, + DBG("%d: invalid len %d in msg[%d]\n", dev->idx, msgs[i].len, i); return -EINVAL; } @@ -585,34 +585,34 @@ static int iic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) return -EINVAL; } } - + /* Check bus state */ if (unlikely((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE)){ DBG("%d: iic_xfer, bus is not free\n", dev->idx); - + /* Usually it means something serious has happend. * We *cannot* have unfinished previous transfer * so it doesn't make any sense to try to stop it. - * Probably we were not able to recover from the + * Probably we were not able to recover from the * previous error. * The only *reasonable* thing I can think of here * is soft reset. --ebs */ iic_dev_reset(dev); - + if ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){ DBG("%d: iic_xfer, bus is still not free\n", dev->idx); return -EREMOTEIO; } - } + } else { /* Flush master data buffer (just in case) */ out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB); } - + /* Load slave address */ iic_address(dev, &msgs[0]); - + /* Do real transfer */ for (i = 0; i < num && !ret; ++i) ret = iic_xfer_bytes(dev, &msgs[i], i < num - 1); @@ -648,7 +648,7 @@ static inline u8 iic_clckdiv(unsigned int opb) /* Convert to MHz */ opb /= 1000000; - + if (opb < 20 || opb > 150){ printk(KERN_CRIT "ibm-iic: invalid OPB clock frequency %u MHz\n", opb); @@ -666,7 +666,7 @@ static int __devinit iic_probe(struct ocp_device *ocp){ struct i2c_adapter* adap; struct ocp_func_iic_data* iic_data = ocp->def->additions; int ret; - + if (!iic_data) printk(KERN_WARNING"ibm-iic%d: missing additional data!\n", ocp->def->index); @@ -679,7 +679,7 @@ static int __devinit iic_probe(struct ocp_device *ocp){ dev->idx = ocp->def->index; ocp_set_drvdata(ocp, dev); - + if (!request_mem_region(ocp->def->paddr, sizeof(struct iic_regs), "ibm_iic")) { ret = -EBUSY; @@ -692,7 +692,7 @@ static int __devinit iic_probe(struct ocp_device *ocp){ ret = -ENXIO; goto fail2; } - + init_waitqueue_head(&dev->wq); dev->irq = iic_force_poll ? -1 : ocp->def->irq; @@ -702,29 +702,29 @@ static int __devinit iic_probe(struct ocp_device *ocp){ */ iic_interrupt_mode(dev, 0); if (request_irq(dev->irq, iic_handler, 0, "IBM IIC", dev)){ - printk(KERN_ERR "ibm-iic%d: request_irq %d failed\n", + printk(KERN_ERR "ibm-iic%d: request_irq %d failed\n", dev->idx, dev->irq); - /* Fallback to the polling mode */ + /* Fallback to the polling mode */ dev->irq = -1; } } - + if (dev->irq < 0) - printk(KERN_WARNING "ibm-iic%d: using polling mode\n", + printk(KERN_WARNING "ibm-iic%d: using polling mode\n", dev->idx); - + /* Board specific settings */ dev->fast_mode = iic_force_fast ? 1 : (iic_data ? iic_data->fast_mode : 0); - - /* clckdiv is the same for *all* IIC interfaces, + + /* clckdiv is the same for *all* IIC interfaces, * but I'd rather make a copy than introduce another global. --ebs */ dev->clckdiv = iic_clckdiv(ocp_sys_info.opb_bus_freq); DBG("%d: clckdiv = %d\n", dev->idx, dev->clckdiv); - + /* Initialize IIC interface */ iic_dev_init(dev); - + /* Register it with i2c layer */ adap = &dev->adap; adap->dev.parent = &ocp->dev; @@ -736,7 +736,6 @@ static int __devinit iic_probe(struct ocp_device *ocp){ adap->client_register = NULL; adap->client_unregister = NULL; adap->timeout = 1; - adap->retries = 1; /* * If "dev->idx" is negative we consider it as zero. @@ -750,24 +749,24 @@ static int __devinit iic_probe(struct ocp_device *ocp){ dev->idx); goto fail; } - + printk(KERN_INFO "ibm-iic%d: using %s mode\n", dev->idx, dev->fast_mode ? "fast (400 kHz)" : "standard (100 kHz)"); return 0; -fail: +fail: if (dev->irq >= 0){ iic_interrupt_mode(dev, 0); free_irq(dev->irq, dev); - } + } iounmap(dev->vaddr); -fail2: +fail2: release_mem_region(ocp->def->paddr, sizeof(struct iic_regs)); fail1: ocp_set_drvdata(ocp, NULL); - kfree(dev); + kfree(dev); return ret; } @@ -783,13 +782,13 @@ static void __devexit iic_remove(struct ocp_device *ocp) dev->idx); /* That's *very* bad, just shutdown IRQ ... */ if (dev->irq >= 0){ - iic_interrupt_mode(dev, 0); + iic_interrupt_mode(dev, 0); free_irq(dev->irq, dev); dev->irq = -1; } } else { if (dev->irq >= 0){ - iic_interrupt_mode(dev, 0); + iic_interrupt_mode(dev, 0); free_irq(dev->irq, dev); } iounmap(dev->vaddr); @@ -798,7 +797,7 @@ static void __devexit iic_remove(struct ocp_device *ocp) } } -static struct ocp_device_id ibm_iic_ids[] __devinitdata = +static struct ocp_device_id ibm_iic_ids[] __devinitdata = { { .vendor = OCP_VENDOR_IBM, .function = OCP_FUNC_IIC }, { .vendor = OCP_VENDOR_INVALID } diff --git a/drivers/i2c/busses/i2c-ibm_iic.h b/drivers/i2c/busses/i2c-ibm_iic.h index 59d7b437f7f..fdaa48292cb 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.h +++ b/drivers/i2c/busses/i2c-ibm_iic.h @@ -2,11 +2,11 @@ * drivers/i2c/busses/i2c-ibm_iic.h * * Support for the IIC peripheral on IBM PPC 4xx - * + * * Copyright (c) 2003 Zultys Technologies. * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> * - * Based on original work by + * Based on original work by * Ian DaSilva <idasilva@mvista.com> * Armin Kuster <akuster@mvista.com> * Matt Porter <mporter@mvista.com> @@ -22,7 +22,7 @@ #ifndef __I2C_IBM_IIC_H_ #define __I2C_IBM_IIC_H_ -#include <linux/i2c.h> +#include <linux/i2c.h> struct iic_regs { u16 mdbuf; @@ -58,7 +58,7 @@ struct ibm_iic_private { #define CNTL_TCT_MASK 0x30 #define CNTL_TCT_SHIFT 4 #define CNTL_RPST 0x08 -#define CNTL_CHT 0x04 +#define CNTL_CHT 0x04 #define CNTL_RW 0x02 #define CNTL_PT 0x01 diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index c70146e4c2c..ab41400c883 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -490,7 +490,6 @@ iop3xx_i2c_probe(struct platform_device *pdev) * Default values...should these come in from board code? */ new_adapter->timeout = 100; - new_adapter->retries = 3; new_adapter->algo = &iop3xx_i2c_algo; init_waitqueue_head(&adapter_data->waitq); diff --git a/drivers/i2c/busses/i2c-ixp4xx.c b/drivers/i2c/busses/i2c-ixp4xx.c deleted file mode 100644 index 069ed7f3b39..00000000000 --- a/drivers/i2c/busses/i2c-ixp4xx.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * drivers/i2c/busses/i2c-ixp4xx.c - * - * Intel's IXP4xx XScale NPU chipsets (IXP420, 421, 422, 425) do not have - * an on board I2C controller but provide 16 GPIO pins that are often - * used to create an I2C bus. This driver provides an i2c_adapter - * interface that plugs in under algo_bit and drives the GPIO pins - * as instructed by the alogorithm driver. - * - * Author: Deepak Saxena <dsaxena@plexity.net> - * - * Copyright (c) 2003-2004 MontaVista Software Inc. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * - * NOTE: Since different platforms will use different GPIO pins for - * I2C, this driver uses an IXP4xx-specific platform_data - * pointer to pass the GPIO numbers to the driver. This - * allows us to support all the different IXP4xx platforms - * w/o having to put #ifdefs in this driver. - * - * See arch/arm/mach-ixp4xx/ixdp425.c for an example of building a - * device list and filling in the ixp4xx_i2c_pins data structure - * that is passed as the platform_data to this driver. - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/module.h> -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> - -#include <asm/hardware.h> /* Pick up IXP4xx-specific bits */ - -static inline int ixp4xx_scl_pin(void *data) -{ - return ((struct ixp4xx_i2c_pins*)data)->scl_pin; -} - -static inline int ixp4xx_sda_pin(void *data) -{ - return ((struct ixp4xx_i2c_pins*)data)->sda_pin; -} - -static void ixp4xx_bit_setscl(void *data, int val) -{ - gpio_line_set(ixp4xx_scl_pin(data), 0); - gpio_line_config(ixp4xx_scl_pin(data), - val ? IXP4XX_GPIO_IN : IXP4XX_GPIO_OUT ); -} - -static void ixp4xx_bit_setsda(void *data, int val) -{ - gpio_line_set(ixp4xx_sda_pin(data), 0); - gpio_line_config(ixp4xx_sda_pin(data), - val ? IXP4XX_GPIO_IN : IXP4XX_GPIO_OUT ); -} - -static int ixp4xx_bit_getscl(void *data) -{ - int scl; - - gpio_line_config(ixp4xx_scl_pin(data), IXP4XX_GPIO_IN ); - gpio_line_get(ixp4xx_scl_pin(data), &scl); - - return scl; -} - -static int ixp4xx_bit_getsda(void *data) -{ - int sda; - - gpio_line_config(ixp4xx_sda_pin(data), IXP4XX_GPIO_IN ); - gpio_line_get(ixp4xx_sda_pin(data), &sda); - - return sda; -} - -struct ixp4xx_i2c_data { - struct ixp4xx_i2c_pins *gpio_pins; - struct i2c_adapter adapter; - struct i2c_algo_bit_data algo_data; -}; - -static int ixp4xx_i2c_remove(struct platform_device *plat_dev) -{ - struct ixp4xx_i2c_data *drv_data = platform_get_drvdata(plat_dev); - - platform_set_drvdata(plat_dev, NULL); - - i2c_del_adapter(&drv_data->adapter); - - kfree(drv_data); - - return 0; -} - -static int ixp4xx_i2c_probe(struct platform_device *plat_dev) -{ - int err; - struct ixp4xx_i2c_pins *gpio = plat_dev->dev.platform_data; - struct ixp4xx_i2c_data *drv_data = - kzalloc(sizeof(struct ixp4xx_i2c_data), GFP_KERNEL); - - if(!drv_data) - return -ENOMEM; - - drv_data->gpio_pins = gpio; - - /* - * We could make a lot of these structures static, but - * certain platforms may have multiple GPIO-based I2C - * buses for various device domains, so we need per-device - * algo_data->data. - */ - drv_data->algo_data.data = gpio; - drv_data->algo_data.setsda = ixp4xx_bit_setsda; - drv_data->algo_data.setscl = ixp4xx_bit_setscl; - drv_data->algo_data.getsda = ixp4xx_bit_getsda; - drv_data->algo_data.getscl = ixp4xx_bit_getscl; - drv_data->algo_data.udelay = 10; - drv_data->algo_data.timeout = 100; - - drv_data->adapter.id = I2C_HW_B_IXP4XX; - drv_data->adapter.class = I2C_CLASS_HWMON; - strlcpy(drv_data->adapter.name, plat_dev->dev.driver->name, - sizeof(drv_data->adapter.name)); - drv_data->adapter.algo_data = &drv_data->algo_data; - - drv_data->adapter.dev.parent = &plat_dev->dev; - - gpio_line_config(gpio->scl_pin, IXP4XX_GPIO_IN); - gpio_line_config(gpio->sda_pin, IXP4XX_GPIO_IN); - gpio_line_set(gpio->scl_pin, 0); - gpio_line_set(gpio->sda_pin, 0); - - err = i2c_bit_add_bus(&drv_data->adapter); - if (err) { - printk(KERN_ERR "ERROR: Could not install %s\n", plat_dev->dev.bus_id); - - kfree(drv_data); - return err; - } - - platform_set_drvdata(plat_dev, drv_data); - - return 0; -} - -static struct platform_driver ixp4xx_i2c_driver = { - .probe = ixp4xx_i2c_probe, - .remove = ixp4xx_i2c_remove, - .driver = { - .name = "IXP4XX-I2C", - .owner = THIS_MODULE, - }, -}; - -static int __init ixp4xx_i2c_init(void) -{ - return platform_driver_register(&ixp4xx_i2c_driver); -} - -static void __exit ixp4xx_i2c_exit(void) -{ - platform_driver_unregister(&ixp4xx_i2c_driver); -} - -module_init(ixp4xx_i2c_init); -module_exit(ixp4xx_i2c_exit); - -MODULE_DESCRIPTION("GPIO-based I2C adapter for IXP4xx systems"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>"); - diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index d8de4ac88b7..bbe787b243b 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -180,7 +180,7 @@ static void mpc_i2c_stop(struct mpc_i2c *i2c) static int mpc_write(struct mpc_i2c *i2c, int target, const u8 * data, int length, int restart) { - int i; + int i, result; unsigned timeout = i2c->adap.timeout; u32 flags = restart ? CCR_RSTA : 0; @@ -192,15 +192,17 @@ static int mpc_write(struct mpc_i2c *i2c, int target, /* Write target byte */ writeb((target << 1), i2c->base + MPC_I2C_DR); - if (i2c_wait(i2c, timeout, 1) < 0) - return -1; + result = i2c_wait(i2c, timeout, 1); + if (result < 0) + return result; for (i = 0; i < length; i++) { /* Write data byte */ writeb(data[i], i2c->base + MPC_I2C_DR); - if (i2c_wait(i2c, timeout, 1) < 0) - return -1; + result = i2c_wait(i2c, timeout, 1); + if (result < 0) + return result; } return 0; @@ -210,7 +212,7 @@ static int mpc_read(struct mpc_i2c *i2c, int target, u8 * data, int length, int restart) { unsigned timeout = i2c->adap.timeout; - int i; + int i, result; u32 flags = restart ? CCR_RSTA : 0; /* Start with MEN */ @@ -221,8 +223,9 @@ static int mpc_read(struct mpc_i2c *i2c, int target, /* Write target address byte - this time with the read flag set */ writeb((target << 1) | 1, i2c->base + MPC_I2C_DR); - if (i2c_wait(i2c, timeout, 1) < 0) - return -1; + result = i2c_wait(i2c, timeout, 1); + if (result < 0) + return result; if (length) { if (length == 1) @@ -234,8 +237,9 @@ static int mpc_read(struct mpc_i2c *i2c, int target, } for (i = 0; i < length; i++) { - if (i2c_wait(i2c, timeout, 0) < 0) - return -1; + result = i2c_wait(i2c, timeout, 0); + if (result < 0) + return result; /* Generate txack on next to last byte */ if (i == length - 2) @@ -309,7 +313,6 @@ static struct i2c_adapter mpc_ops = { .algo = &mpc_algo, .class = I2C_CLASS_HWMON, .timeout = 1, - .retries = 1 }; static int fsl_i2c_probe(struct platform_device *pdev) @@ -321,9 +324,9 @@ static int fsl_i2c_probe(struct platform_device *pdev) pdata = (struct fsl_i2c_platform_data *) pdev->dev.platform_data; - if (!(i2c = kzalloc(sizeof(*i2c), GFP_KERNEL))) { + i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + if (!i2c) return -ENOMEM; - } i2c->irq = platform_get_irq(pdev, 0); if (i2c->irq < 0) { diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 1bf590c7416..3dac920e53e 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -351,6 +351,7 @@ static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_ pci_set_drvdata(dev, smbuses); switch(dev->device) { + case PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS: case PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS: case PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS: smbuses[0].blockops = 1; diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index f2552b19ea6..da6639707ea 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -362,8 +362,6 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) omap_i2c_enable_clocks(dev); - /* REVISIT: initialize and use adap->retries. This is an optional - * feature */ if ((r = omap_i2c_wait_for_bb(dev)) < 0) goto out; diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c index ca18e0be490..1603c81e39d 100644 --- a/drivers/i2c/busses/i2c-pasemi.c +++ b/drivers/i2c/busses/i2c-pasemi.c @@ -368,6 +368,7 @@ static int __devinit pasemi_smb_probe(struct pci_dev *dev, smbus->adapter.class = I2C_CLASS_HWMON; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; + smbus->adapter.nr = PCI_FUNC(dev->devfn); /* set up the sysfs linkage to our parent device */ smbus->adapter.dev.parent = &dev->dev; @@ -375,7 +376,7 @@ static int __devinit pasemi_smb_probe(struct pci_dev *dev, reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR | (CLK_100K_DIV & CTL_CLK_M))); - error = i2c_add_adapter(&smbus->adapter); + error = i2c_add_numbered_adapter(&smbus->adapter); if (error) goto out_release_region; diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 167e4137ee2..9bbe96cef71 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -121,10 +121,6 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, { unsigned char temp; - /* match up the function */ - if (PCI_FUNC(PIIX4_dev->devfn) != id->driver_data) - return -ENODEV; - dev_info(&PIIX4_dev->dev, "Found %s device\n", pci_name(PIIX4_dev)); /* Don't access SMBus on IBM systems which get corrupted eeproms */ @@ -389,28 +385,21 @@ static struct i2c_adapter piix4_adapter = { }; static struct pci_device_id piix4_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3), - .driver_data = 3 }, - { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_SMBUS), - .driver_data = 0 }, - { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS), - .driver_data = 0 }, - { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS), - .driver_data = 0 }, - { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS), - .driver_data = 0 }, - { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4), - .driver_data = 0 }, - { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5), - .driver_data = 0 }, - { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6), - .driver_data = 0 }, - { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000SB), - .driver_data = 0 }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3), - .driver_data = 3 }, - { PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3), - .driver_data = 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_OSB4) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB5) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB6) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_HT1000SB) }, { 0, } }; diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 6426a61f8d4..2598d29fd7a 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -65,6 +65,7 @@ struct pxa_i2c { unsigned long iosize; int irq; + int use_pio; }; #define _IBMR(i2c) ((i2c)->reg_base + 0) @@ -163,6 +164,7 @@ static void i2c_pxa_show_state(struct pxa_i2c *i2c, int lno, const char *fname) #define eedbg(lvl, x...) do { if ((lvl) < 1) { printk(KERN_DEBUG "" x); } } while(0) static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret); +static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id); static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why) { @@ -554,6 +556,71 @@ static inline void i2c_pxa_stop_message(struct pxa_i2c *i2c) writel(icr, _ICR(i2c)); } +static int i2c_pxa_pio_set_master(struct pxa_i2c *i2c) +{ + /* make timeout the same as for interrupt based functions */ + long timeout = 2 * DEF_TIMEOUT; + + /* + * Wait for the bus to become free. + */ + while (timeout-- && readl(_ISR(i2c)) & (ISR_IBB | ISR_UB)) { + udelay(1000); + show_state(i2c); + } + + if (timeout <= 0) { + show_state(i2c); + dev_err(&i2c->adap.dev, + "i2c_pxa: timeout waiting for bus free\n"); + return I2C_RETRY; + } + + /* + * Set master mode. + */ + writel(readl(_ICR(i2c)) | ICR_SCLE, _ICR(i2c)); + + return 0; +} + +static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c, + struct i2c_msg *msg, int num) +{ + unsigned long timeout = 500000; /* 5 seconds */ + int ret = 0; + + ret = i2c_pxa_pio_set_master(i2c); + if (ret) + goto out; + + i2c->msg = msg; + i2c->msg_num = num; + i2c->msg_idx = 0; + i2c->msg_ptr = 0; + i2c->irqlogidx = 0; + + i2c_pxa_start_message(i2c); + + while (timeout-- && i2c->msg_num > 0) { + i2c_pxa_handler(0, i2c); + udelay(10); + } + + i2c_pxa_stop_message(i2c); + + /* + * We place the return code in i2c->msg_idx. + */ + ret = i2c->msg_idx; + +out: + if (timeout == 0) + i2c_pxa_scream_blue_murder(i2c, "timeout"); + + return ret; +} + /* * We are protected by the adapter bus mutex. */ @@ -610,6 +677,35 @@ static int i2c_pxa_do_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num) return ret; } +static int i2c_pxa_pio_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct pxa_i2c *i2c = adap->algo_data; + int ret, i; + + /* If the I2C controller is disabled we need to reset it + (probably due to a suspend/resume destroying state). We do + this here as we can then avoid worrying about resuming the + controller before its users. */ + if (!(readl(_ICR(i2c)) & ICR_IUE)) + i2c_pxa_reset(i2c); + + for (i = adap->retries; i >= 0; i--) { + ret = i2c_pxa_do_pio_xfer(i2c, msgs, num); + if (ret != I2C_RETRY) + goto out; + + if (i2c_debug) + dev_dbg(&adap->dev, "Retrying transmission\n"); + udelay(100); + } + i2c_pxa_scream_blue_murder(i2c, "exhausted retries"); + ret = -EREMOTEIO; + out: + i2c_pxa_set_slave(i2c, ret); + return ret; +} + /* * i2c_pxa_master_complete - complete the message and wake up. */ @@ -621,7 +717,8 @@ static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret) i2c->msg_num = 0; if (ret) i2c->msg_idx = ret; - wake_up(&i2c->wait); + if (!i2c->use_pio) + wake_up(&i2c->wait); } static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr) @@ -840,6 +937,37 @@ static const struct i2c_algorithm i2c_pxa_algorithm = { .functionality = i2c_pxa_functionality, }; +static const struct i2c_algorithm i2c_pxa_pio_algorithm = { + .master_xfer = i2c_pxa_pio_xfer, + .functionality = i2c_pxa_functionality, +}; + +static void i2c_pxa_enable(struct platform_device *dev) +{ + if (cpu_is_pxa27x()) { + switch (dev->id) { + case 0: + pxa_gpio_mode(GPIO117_I2CSCL_MD); + pxa_gpio_mode(GPIO118_I2CSDA_MD); + break; + case 1: + local_irq_disable(); + PCFR |= PCFR_PI2CEN; + local_irq_enable(); + break; + } + } +} + +static void i2c_pxa_disable(struct platform_device *dev) +{ + if (cpu_is_pxa27x() && dev->id == 1) { + local_irq_disable(); + PCFR &= ~PCFR_PI2CEN; + local_irq_enable(); + } +} + #define res_len(r) ((r)->end - (r)->start + 1) static int i2c_pxa_probe(struct platform_device *dev) { @@ -864,7 +992,6 @@ static int i2c_pxa_probe(struct platform_device *dev) } i2c->adap.owner = THIS_MODULE; - i2c->adap.algo = &i2c_pxa_algorithm; i2c->adap.retries = 5; spin_lock_init(&i2c->lock); @@ -899,34 +1026,28 @@ static int i2c_pxa_probe(struct platform_device *dev) #endif clk_enable(i2c->clk); -#ifdef CONFIG_PXA27x - switch (dev->id) { - case 0: - pxa_gpio_mode(GPIO117_I2CSCL_MD); - pxa_gpio_mode(GPIO118_I2CSDA_MD); - break; - case 1: - local_irq_disable(); - PCFR |= PCFR_PI2CEN; - local_irq_enable(); - } -#endif + i2c_pxa_enable(dev); - ret = request_irq(irq, i2c_pxa_handler, IRQF_DISABLED, - i2c->adap.name, i2c); - if (ret) - goto ereqirq; + if (plat) { + i2c->adap.class = plat->class; + i2c->use_pio = plat->use_pio; + } + if (i2c->use_pio) { + i2c->adap.algo = &i2c_pxa_pio_algorithm; + } else { + i2c->adap.algo = &i2c_pxa_algorithm; + ret = request_irq(irq, i2c_pxa_handler, IRQF_DISABLED, + i2c->adap.name, i2c); + if (ret) + goto ereqirq; + } i2c_pxa_reset(i2c); i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &dev->dev; - if (plat) { - i2c->adap.class = plat->class; - } - /* * If "dev->id" is negative we consider it as zero. * The reason to do so is to avoid sysfs names that only make @@ -952,17 +1073,11 @@ static int i2c_pxa_probe(struct platform_device *dev) return 0; eadapt: - free_irq(irq, i2c); + if (!i2c->use_pio) + free_irq(irq, i2c); ereqirq: clk_disable(i2c->clk); - -#ifdef CONFIG_PXA27x - if (dev->id == 1) { - local_irq_disable(); - PCFR &= ~PCFR_PI2CEN; - local_irq_enable(); - } -#endif + i2c_pxa_disable(dev); eremap: clk_put(i2c->clk); eclk: @@ -979,18 +1094,12 @@ static int i2c_pxa_remove(struct platform_device *dev) platform_set_drvdata(dev, NULL); i2c_del_adapter(&i2c->adap); - free_irq(i2c->irq, i2c); + if (!i2c->use_pio) + free_irq(i2c->irq, i2c); clk_disable(i2c->clk); clk_put(i2c->clk); - -#ifdef CONFIG_PXA27x - if (dev->id == 1) { - local_irq_disable(); - PCFR &= ~PCFR_PI2CEN; - local_irq_enable(); - } -#endif + i2c_pxa_disable(dev); release_mem_region(i2c->iobase, i2c->iosize); kfree(i2c); diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c index 503a134ec80..8fbbdb4c2f3 100644 --- a/drivers/i2c/busses/i2c-sibyte.c +++ b/drivers/i2c/busses/i2c-sibyte.c @@ -36,14 +36,6 @@ struct i2c_algo_sibyte_data { /* ----- global defines ----------------------------------------------- */ #define SMB_CSR(a,r) ((long)(a->reg_base + r)) -/* ----- global variables --------------------------------------------- */ - -/* module parameters: - */ -static int bit_scan; /* have a look at what's hanging 'round */ -module_param(bit_scan, int, 0); -MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus"); - static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr, unsigned short flags, char read_write, @@ -140,9 +132,8 @@ static const struct i2c_algorithm i2c_sibyte_algo = { /* * registering functions to load algorithms at runtime */ -int i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed) +int __init i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed) { - int i; struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; /* register new adapter to i2c module... */ @@ -152,24 +143,6 @@ int i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed) csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ)); csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL)); - /* scan bus */ - if (bit_scan) { - union i2c_smbus_data data; - int rc; - printk(KERN_INFO " i2c-algo-sibyte.o: scanning bus %s.\n", - i2c_adap->name); - for (i = 0x00; i < 0x7f; i++) { - /* XXXKW is this a realistic probe? */ - rc = smbus_xfer(i2c_adap, i, 0, I2C_SMBUS_READ, 0, - I2C_SMBUS_BYTE_DATA, &data); - if (!rc) { - printk("(%02x)",i); - } else - printk("."); - } - printk("\n"); - } - return i2c_add_adapter(i2c_adap); } diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c index 84df29da1dd..c2a9f8c94f5 100644 --- a/drivers/i2c/busses/i2c-stub.c +++ b/drivers/i2c/busses/i2c-stub.c @@ -1,8 +1,8 @@ /* - i2c-stub.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring + i2c-stub.c - I2C/SMBus chip emulator Copyright (c) 2004 Mark M. Hoffman <mhoffman@lightlink.com> + Copyright (C) 2007 Jean Delvare <khali@linux-fr.org> 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 @@ -37,8 +37,8 @@ MODULE_PARM_DESC(chip_addr, struct stub_chip { u8 pointer; - u8 bytes[256]; - u16 words[256]; + u16 words[256]; /* Byte operations use the LSB as per SMBus + specification */ }; static struct stub_chip *stub_chips; @@ -75,7 +75,7 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, "wrote 0x%02x.\n", addr, command); } else { - data->byte = chip->bytes[chip->pointer++]; + data->byte = chip->words[chip->pointer++] & 0xff; dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " "read 0x%02x.\n", addr, data->byte); @@ -86,12 +86,13 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, case I2C_SMBUS_BYTE_DATA: if (read_write == I2C_SMBUS_WRITE) { - chip->bytes[command] = data->byte; + chip->words[command] &= 0xff00; + chip->words[command] |= data->byte; dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " "wrote 0x%02x at 0x%02x.\n", addr, data->byte, command); } else { - data->byte = chip->bytes[command]; + data->byte = chip->words[command] & 0xff; dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " "read 0x%02x at 0x%02x.\n", addr, data->byte, command); diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index c9ce77f13c0..77b13d027f8 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -4,7 +4,7 @@ Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, Kyösti Mälkki <kmalkki@cc.hut.fi>, Mark D. Studebaker <mdsxyz123@yahoo.com> - Copyright (C) 2005 - 2007 Jean Delvare <khali@linux-fr.org> + Copyright (C) 2005 - 2008 Jean Delvare <khali@linux-fr.org> 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 @@ -35,6 +35,7 @@ VT8235 0x3177 yes VT8237R 0x3227 yes VT8237A 0x3337 yes + VT8237S 0x3372 yes VT8251 0x3287 yes CX700 0x8324 yes @@ -318,6 +319,10 @@ static int __devinit vt596_probe(struct pci_dev *pdev, unsigned char temp; int error = -ENODEV; + /* driver_data might come from user-space, so check it */ + if (id->driver_data & 1 || id->driver_data > 0xff) + return -EINVAL; + /* Determine the address of the SMBus areas */ if (force_addr) { vt596_smba = force_addr & 0xfff0; @@ -389,6 +394,7 @@ found: case PCI_DEVICE_ID_VIA_8251: case PCI_DEVICE_ID_VIA_8237: case PCI_DEVICE_ID_VIA_8237A: + case PCI_DEVICE_ID_VIA_8237S: case PCI_DEVICE_ID_VIA_8235: case PCI_DEVICE_ID_VIA_8233A: case PCI_DEVICE_ID_VIA_8233_0: @@ -440,6 +446,8 @@ static struct pci_device_id vt596_ids[] = { .driver_data = SMBBA3 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237A), .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237S), + .driver_data = SMBBA3 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4), .driver_data = SMBBA1 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8251), @@ -455,6 +463,7 @@ static struct pci_driver vt596_driver = { .name = "vt596_smbus", .id_table = vt596_ids, .probe = vt596_probe, + .dynids.use_driver_data = 1, }; static int __init i2c_vt596_init(void) diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 2e1c24f671c..bd7082c2443 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -4,32 +4,6 @@ menu "Miscellaneous I2C Chip support" -config SENSORS_DS1337 - tristate "Dallas DS1337 and DS1339 Real Time Clock (DEPRECATED)" - depends on EXPERIMENTAL - help - If you say yes here you get support for Dallas Semiconductor - DS1337 and DS1339 real-time clock chips. - - This driver can also be built as a module. If so, the module - will be called ds1337. - - This driver is deprecated and will be dropped soon. Use - rtc-ds1307 instead. - -config SENSORS_DS1374 - tristate "Dallas DS1374 Real Time Clock (DEPRECATED)" - depends on EXPERIMENTAL - help - If you say yes here you get support for Dallas Semiconductor - DS1374 real-time clock chips. - - This driver can also be built as a module. If so, the module - will be called ds1374. - - This driver is deprecated and will be dropped soon. Use - rtc-ds1374 instead. - config DS1682 tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm" depends on EXPERIMENTAL @@ -57,7 +31,7 @@ config SENSORS_PCF8574 default n help If you say yes here you get support for Philips PCF8574 and - PCF8574A chips. + PCF8574A chips. These chips are 8-bit I/O expanders for the I2C bus. This driver can also be built as a module. If so, the module will be called pcf8574. @@ -65,6 +39,20 @@ config SENSORS_PCF8574 These devices are hard to detect and rarely found on mainstream hardware. If unsure, say N. +config PCF8575 + tristate "Philips PCF8575" + default n + help + If you say yes here you get support for Philips PCF8575 chip. + This chip is a 16-bit I/O expander for the I2C bus. Several other + chip manufacturers sell equivalent chips, e.g. Texas Instruments. + + This driver can also be built as a module. If so, the module + will be called pcf8575. + + This device is hard to detect and is rarely found on mainstream + hardware. If unsure, say N. + config SENSORS_PCA9539 tristate "Philips PCA9539 16-bit I/O port" depends on EXPERIMENTAL @@ -100,12 +88,8 @@ config ISP1301_OMAP This driver can also be built as a module. If so, the module will be called isp1301_omap. -# NOTE: This isn't really OMAP-specific, except for the current -# interface location in <include/asm-arm/arch-omap/tps65010.h> -# and having mostly OMAP-specific board support config TPS65010 tristate "TPS6501x Power Management chips" - depends on ARCH_OMAP default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK help If you say yes here you get support for the TPS6501x series of @@ -116,18 +100,6 @@ config TPS65010 This driver can also be built as a module. If so, the module will be called tps65010. -config SENSORS_M41T00 - tristate "ST M41T00 RTC chip (DEPRECATED)" - depends on PPC32 - help - If you say yes here you get support for the ST M41T00 RTC chip. - - This driver can also be built as a module. If so, the module - will be called m41t00. - - This driver is deprecated and will be dropped soon. Use - rtc-ds1307 or rtc-m41t80 instead. - config SENSORS_MAX6875 tristate "Maxim MAX6875 Power supply supervisor" depends on EXPERIMENTAL diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index ca924e10595..501f00cea78 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -2,14 +2,12 @@ # Makefile for miscellaneous I2C chip drivers. # -obj-$(CONFIG_SENSORS_DS1337) += ds1337.o -obj-$(CONFIG_SENSORS_DS1374) += ds1374.o obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o obj-$(CONFIG_SENSORS_MAX6875) += max6875.o -obj-$(CONFIG_SENSORS_M41T00) += m41t00.o obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o +obj-$(CONFIG_PCF8575) += pcf8575.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TPS65010) += tps65010.o diff --git a/drivers/i2c/chips/ds1337.c b/drivers/i2c/chips/ds1337.c deleted file mode 100644 index ec17d6b684a..00000000000 --- a/drivers/i2c/chips/ds1337.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * linux/drivers/i2c/chips/ds1337.c - * - * Copyright (C) 2005 James Chapman <jchapman@katalix.com> - * - * based on linux/drivers/acorn/char/pcf8583.c - * Copyright (C) 2000 Russell King - * - * 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. - * - * Driver for Dallas Semiconductor DS1337 and DS1339 real time clock chip - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/string.h> -#include <linux/rtc.h> /* get the user-level API */ -#include <linux/bcd.h> -#include <linux/list.h> - -/* Device registers */ -#define DS1337_REG_HOUR 2 -#define DS1337_REG_DAY 3 -#define DS1337_REG_DATE 4 -#define DS1337_REG_MONTH 5 -#define DS1337_REG_CONTROL 14 -#define DS1337_REG_STATUS 15 - -/* FIXME - how do we export these interface constants? */ -#define DS1337_GET_DATE 0 -#define DS1337_SET_DATE 1 - -/* - * Functions declaration - */ -static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END }; - -I2C_CLIENT_INSMOD_1(ds1337); - -static int ds1337_attach_adapter(struct i2c_adapter *adapter); -static int ds1337_detect(struct i2c_adapter *adapter, int address, int kind); -static void ds1337_init_client(struct i2c_client *client); -static int ds1337_detach_client(struct i2c_client *client); -static int ds1337_command(struct i2c_client *client, unsigned int cmd, - void *arg); - -/* - * Driver data (common to all clients) - */ -static struct i2c_driver ds1337_driver = { - .driver = { - .name = "ds1337", - }, - .attach_adapter = ds1337_attach_adapter, - .detach_client = ds1337_detach_client, - .command = ds1337_command, -}; - -/* - * Client data (each client gets its own) - */ -struct ds1337_data { - struct i2c_client client; - struct list_head list; -}; - -/* - * Internal variables - */ -static LIST_HEAD(ds1337_clients); - -static inline int ds1337_read(struct i2c_client *client, u8 reg, u8 *value) -{ - s32 tmp = i2c_smbus_read_byte_data(client, reg); - - if (tmp < 0) - return -EIO; - - *value = tmp; - - return 0; -} - -/* - * Chip access functions - */ -static int ds1337_get_datetime(struct i2c_client *client, struct rtc_time *dt) -{ - int result; - u8 buf[7]; - u8 val; - struct i2c_msg msg[2]; - u8 offs = 0; - - if (!dt) { - dev_dbg(&client->dev, "%s: EINVAL: dt=NULL\n", __FUNCTION__); - return -EINVAL; - } - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 1; - msg[0].buf = &offs; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = sizeof(buf); - msg[1].buf = &buf[0]; - - result = i2c_transfer(client->adapter, msg, 2); - - dev_dbg(&client->dev, "%s: [%d] %02x %02x %02x %02x %02x %02x %02x\n", - __FUNCTION__, result, buf[0], buf[1], buf[2], buf[3], - buf[4], buf[5], buf[6]); - - if (result == 2) { - dt->tm_sec = BCD2BIN(buf[0]); - dt->tm_min = BCD2BIN(buf[1]); - val = buf[2] & 0x3f; - dt->tm_hour = BCD2BIN(val); - dt->tm_wday = BCD2BIN(buf[3]) - 1; - dt->tm_mday = BCD2BIN(buf[4]); - val = buf[5] & 0x7f; - dt->tm_mon = BCD2BIN(val) - 1; - dt->tm_year = BCD2BIN(buf[6]); - if (buf[5] & 0x80) - dt->tm_year += 100; - - dev_dbg(&client->dev, "%s: secs=%d, mins=%d, " - "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", - __FUNCTION__, dt->tm_sec, dt->tm_min, - dt->tm_hour, dt->tm_mday, - dt->tm_mon, dt->tm_year, dt->tm_wday); - - return 0; - } - - dev_err(&client->dev, "error reading data! %d\n", result); - return -EIO; -} - -static int ds1337_set_datetime(struct i2c_client *client, struct rtc_time *dt) -{ - int result; - u8 buf[8]; - u8 val; - struct i2c_msg msg[1]; - - if (!dt) { - dev_dbg(&client->dev, "%s: EINVAL: dt=NULL\n", __FUNCTION__); - return -EINVAL; - } - - dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " - "mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__, - dt->tm_sec, dt->tm_min, dt->tm_hour, - dt->tm_mday, dt->tm_mon, dt->tm_year, dt->tm_wday); - - buf[0] = 0; /* reg offset */ - buf[1] = BIN2BCD(dt->tm_sec); - buf[2] = BIN2BCD(dt->tm_min); - buf[3] = BIN2BCD(dt->tm_hour); - buf[4] = BIN2BCD(dt->tm_wday + 1); - buf[5] = BIN2BCD(dt->tm_mday); - buf[6] = BIN2BCD(dt->tm_mon + 1); - val = dt->tm_year; - if (val >= 100) { - val -= 100; - buf[6] |= (1 << 7); - } - buf[7] = BIN2BCD(val); - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = sizeof(buf); - msg[0].buf = &buf[0]; - - result = i2c_transfer(client->adapter, msg, 1); - if (result == 1) - return 0; - - dev_err(&client->dev, "error writing data! %d\n", result); - return -EIO; -} - -static int ds1337_command(struct i2c_client *client, unsigned int cmd, - void *arg) -{ - dev_dbg(&client->dev, "%s: cmd=%d\n", __FUNCTION__, cmd); - - switch (cmd) { - case DS1337_GET_DATE: - return ds1337_get_datetime(client, arg); - - case DS1337_SET_DATE: - return ds1337_set_datetime(client, arg); - - default: - return -EINVAL; - } -} - -/* - * Public API for access to specific device. Useful for low-level - * RTC access from kernel code. - */ -int ds1337_do_command(int bus, int cmd, void *arg) -{ - struct list_head *walk; - struct list_head *tmp; - struct ds1337_data *data; - - list_for_each_safe(walk, tmp, &ds1337_clients) { - data = list_entry(walk, struct ds1337_data, list); - if (data->client.adapter->nr == bus) - return ds1337_command(&data->client, cmd, arg); - } - - return -ENODEV; -} - -static int ds1337_attach_adapter(struct i2c_adapter *adapter) -{ - return i2c_probe(adapter, &addr_data, ds1337_detect); -} - -/* - * The following function does more than just detection. If detection - * succeeds, it also registers the new chip. - */ -static int ds1337_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct ds1337_data *data; - int err = 0; - const char *name = ""; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_I2C)) - goto exit; - - if (!(data = kzalloc(sizeof(struct ds1337_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - INIT_LIST_HEAD(&data->list); - - /* The common I2C client data is placed right before the - * DS1337-specific data. - */ - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &ds1337_driver; - new_client->flags = 0; - - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip. A zero kind means that - * the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - * - * For detection, we read registers that are most likely to cause - * detection failure, i.e. those that have more bits with fixed - * or reserved values. - */ - - /* Default to an DS1337 if forced */ - if (kind == 0) - kind = ds1337; - - if (kind < 0) { /* detection and identification */ - u8 data; - - /* Check that status register bits 6-2 are zero */ - if ((ds1337_read(new_client, DS1337_REG_STATUS, &data) < 0) || - (data & 0x7c)) - goto exit_free; - - /* Check for a valid day register value */ - if ((ds1337_read(new_client, DS1337_REG_DAY, &data) < 0) || - (data == 0) || (data & 0xf8)) - goto exit_free; - - /* Check for a valid date register value */ - if ((ds1337_read(new_client, DS1337_REG_DATE, &data) < 0) || - (data == 0) || (data & 0xc0) || ((data & 0x0f) > 9) || - (data >= 0x32)) - goto exit_free; - - /* Check for a valid month register value */ - if ((ds1337_read(new_client, DS1337_REG_MONTH, &data) < 0) || - (data == 0) || (data & 0x60) || ((data & 0x0f) > 9) || - ((data >= 0x13) && (data <= 0x19))) - goto exit_free; - - /* Check that control register bits 6-5 are zero */ - if ((ds1337_read(new_client, DS1337_REG_CONTROL, &data) < 0) || - (data & 0x60)) - goto exit_free; - - kind = ds1337; - } - - if (kind == ds1337) - name = "ds1337"; - - /* We can fill in the remaining client fields */ - strlcpy(new_client->name, name, I2C_NAME_SIZE); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the DS1337 chip */ - ds1337_init_client(new_client); - - /* Add client to local list */ - list_add(&data->list, &ds1337_clients); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static void ds1337_init_client(struct i2c_client *client) -{ - u8 status, control; - - /* On some boards, the RTC isn't configured by boot firmware. - * Handle that case by starting/configuring the RTC now. - */ - status = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS); - control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); - - if ((status & 0x80) || (control & 0x80)) { - /* RTC not running */ - u8 buf[1+16]; /* First byte is interpreted as address */ - struct i2c_msg msg[1]; - - dev_dbg(&client->dev, "%s: RTC not running!\n", __FUNCTION__); - - /* Initialize all, including STATUS and CONTROL to zero */ - memset(buf, 0, sizeof(buf)); - - /* Write valid values in the date/time registers */ - buf[1+DS1337_REG_DAY] = 1; - buf[1+DS1337_REG_DATE] = 1; - buf[1+DS1337_REG_MONTH] = 1; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = sizeof(buf); - msg[0].buf = &buf[0]; - - i2c_transfer(client->adapter, msg, 1); - } else { - /* Running: ensure that device is set in 24-hour mode */ - s32 val; - - val = i2c_smbus_read_byte_data(client, DS1337_REG_HOUR); - if ((val >= 0) && (val & (1 << 6))) - i2c_smbus_write_byte_data(client, DS1337_REG_HOUR, - val & 0x3f); - } -} - -static int ds1337_detach_client(struct i2c_client *client) -{ - int err; - struct ds1337_data *data = i2c_get_clientdata(client); - - if ((err = i2c_detach_client(client))) - return err; - - list_del(&data->list); - kfree(data); - return 0; -} - -static int __init ds1337_init(void) -{ - return i2c_add_driver(&ds1337_driver); -} - -static void __exit ds1337_exit(void) -{ - i2c_del_driver(&ds1337_driver); -} - -MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); -MODULE_DESCRIPTION("DS1337 RTC driver"); -MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL_GPL(ds1337_do_command); - -module_init(ds1337_init); -module_exit(ds1337_exit); diff --git a/drivers/i2c/chips/ds1374.c b/drivers/i2c/chips/ds1374.c deleted file mode 100644 index 8a2ff0c114d..00000000000 --- a/drivers/i2c/chips/ds1374.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * drivers/i2c/chips/ds1374.c - * - * I2C client/driver for the Maxim/Dallas DS1374 Real-Time Clock - * - * Author: Randy Vinson <rvinson@mvista.com> - * - * Based on the m41t00.c by Mark Greer <mgreer@mvista.com> - * - * 2005 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -/* - * This i2c client/driver wedges between the drivers/char/genrtc.c RTC - * interface and the SMBus interface of the i2c subsystem. - * It would be more efficient to use i2c msgs/i2c_transfer directly but, as - * recommened in .../Documentation/i2c/writing-clients section - * "Sending and receiving", using SMBus level communication is preferred. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/i2c.h> -#include <linux/rtc.h> -#include <linux/bcd.h> -#include <linux/mutex.h> -#include <linux/workqueue.h> - -#define DS1374_REG_TOD0 0x00 -#define DS1374_REG_TOD1 0x01 -#define DS1374_REG_TOD2 0x02 -#define DS1374_REG_TOD3 0x03 -#define DS1374_REG_WDALM0 0x04 -#define DS1374_REG_WDALM1 0x05 -#define DS1374_REG_WDALM2 0x06 -#define DS1374_REG_CR 0x07 -#define DS1374_REG_SR 0x08 -#define DS1374_REG_SR_OSF 0x80 -#define DS1374_REG_TCR 0x09 - -#define DS1374_DRV_NAME "ds1374" - -static DEFINE_MUTEX(ds1374_mutex); - -static struct i2c_driver ds1374_driver; -static struct i2c_client *save_client; - -static unsigned short ignore[] = { I2C_CLIENT_END }; -static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END }; - -static struct i2c_client_address_data addr_data = { - .normal_i2c = normal_addr, - .probe = ignore, - .ignore = ignore, -}; - -static ulong ds1374_read_rtc(void) -{ - ulong time = 0; - int reg = DS1374_REG_WDALM0; - - while (reg--) { - s32 tmp; - if ((tmp = i2c_smbus_read_byte_data(save_client, reg)) < 0) { - dev_warn(&save_client->dev, - "can't read from rtc chip\n"); - return 0; - } - time = (time << 8) | (tmp & 0xff); - } - return time; -} - -static void ds1374_write_rtc(ulong time) -{ - int reg; - - for (reg = DS1374_REG_TOD0; reg < DS1374_REG_WDALM0; reg++) { - if (i2c_smbus_write_byte_data(save_client, reg, time & 0xff) - < 0) { - dev_warn(&save_client->dev, - "can't write to rtc chip\n"); - break; - } - time = time >> 8; - } -} - -static void ds1374_check_rtc_status(void) -{ - s32 tmp; - - tmp = i2c_smbus_read_byte_data(save_client, DS1374_REG_SR); - if (tmp < 0) { - dev_warn(&save_client->dev, - "can't read status from rtc chip\n"); - return; - } - if (tmp & DS1374_REG_SR_OSF) { - dev_warn(&save_client->dev, - "oscillator discontinuity flagged, time unreliable\n"); - tmp &= ~DS1374_REG_SR_OSF; - tmp = i2c_smbus_write_byte_data(save_client, DS1374_REG_SR, - tmp & 0xff); - if (tmp < 0) - dev_warn(&save_client->dev, - "can't clear discontinuity notification\n"); - } -} - -ulong ds1374_get_rtc_time(void) -{ - ulong t1, t2; - int limit = 10; /* arbitrary retry limit */ - - mutex_lock(&ds1374_mutex); - - /* - * Since the reads are being performed one byte at a time using - * the SMBus vs a 4-byte i2c transfer, there is a chance that a - * carry will occur during the read. To detect this, 2 reads are - * performed and compared. - */ - do { - t1 = ds1374_read_rtc(); - t2 = ds1374_read_rtc(); - } while (t1 != t2 && limit--); - - mutex_unlock(&ds1374_mutex); - - if (t1 != t2) { - dev_warn(&save_client->dev, - "can't get consistent time from rtc chip\n"); - t1 = 0; - } - - return t1; -} - -static ulong new_time; - -static void ds1374_set_work(struct work_struct *work) -{ - ulong t1, t2; - int limit = 10; /* arbitrary retry limit */ - - t1 = new_time; - - mutex_lock(&ds1374_mutex); - - /* - * Since the writes are being performed one byte at a time using - * the SMBus vs a 4-byte i2c transfer, there is a chance that a - * carry will occur during the write. To detect this, the write - * value is read back and compared. - */ - do { - ds1374_write_rtc(t1); - t2 = ds1374_read_rtc(); - } while (t1 != t2 && limit--); - - mutex_unlock(&ds1374_mutex); - - if (t1 != t2) - dev_warn(&save_client->dev, - "can't confirm time set from rtc chip\n"); -} - -static struct workqueue_struct *ds1374_workqueue; - -static DECLARE_WORK(ds1374_work, ds1374_set_work); - -int ds1374_set_rtc_time(ulong nowtime) -{ - new_time = nowtime; - - if (in_interrupt()) - queue_work(ds1374_workqueue, &ds1374_work); - else - ds1374_set_work(NULL); - - return 0; -} - -/* - ***************************************************************************** - * - * Driver Interface - * - ***************************************************************************** - */ -static int ds1374_probe(struct i2c_adapter *adap, int addr, int kind) -{ - struct i2c_client *client; - int rc; - - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!client) - return -ENOMEM; - - strncpy(client->name, DS1374_DRV_NAME, I2C_NAME_SIZE); - client->addr = addr; - client->adapter = adap; - client->driver = &ds1374_driver; - - ds1374_workqueue = create_singlethread_workqueue("ds1374"); - if (!ds1374_workqueue) { - kfree(client); - return -ENOMEM; /* most expected reason */ - } - - if ((rc = i2c_attach_client(client)) != 0) { - kfree(client); - return rc; - } - - save_client = client; - - ds1374_check_rtc_status(); - - return 0; -} - -static int ds1374_attach(struct i2c_adapter *adap) -{ - return i2c_probe(adap, &addr_data, ds1374_probe); -} - -static int ds1374_detach(struct i2c_client *client) -{ - int rc; - - if ((rc = i2c_detach_client(client)) == 0) { - kfree(i2c_get_clientdata(client)); - destroy_workqueue(ds1374_workqueue); - } - return rc; -} - -static struct i2c_driver ds1374_driver = { - .driver = { - .name = DS1374_DRV_NAME, - }, - .id = I2C_DRIVERID_DS1374, - .attach_adapter = ds1374_attach, - .detach_client = ds1374_detach, -}; - -static int __init ds1374_init(void) -{ - return i2c_add_driver(&ds1374_driver); -} - -static void __exit ds1374_exit(void) -{ - i2c_del_driver(&ds1374_driver); -} - -module_init(ds1374_init); -module_exit(ds1374_exit); - -MODULE_AUTHOR("Randy Vinson <rvinson@mvista.com>"); -MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC I2C Client Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c index 1a7eeebac50..fde297b21ad 100644 --- a/drivers/i2c/chips/eeprom.c +++ b/drivers/i2c/chips/eeprom.c @@ -35,7 +35,7 @@ #include <linux/mutex.h> /* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54, +static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, I2C_CLIENT_END }; /* Insmod parameters */ diff --git a/drivers/i2c/chips/isp1301_omap.c b/drivers/i2c/chips/isp1301_omap.c index ebfbb2947ae..2a3160153f5 100644 --- a/drivers/i2c/chips/isp1301_omap.c +++ b/drivers/i2c/chips/isp1301_omap.c @@ -100,7 +100,7 @@ struct isp1301 { #if defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE) -#include <asm/arch/tps65010.h> +#include <linux/i2c/tps65010.h> #else diff --git a/drivers/i2c/chips/m41t00.c b/drivers/i2c/chips/m41t00.c deleted file mode 100644 index 3fcb646e207..00000000000 --- a/drivers/i2c/chips/m41t00.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * I2C client/driver for the ST M41T00 family of i2c rtc chips. - * - * Author: Mark A. Greer <mgreer@mvista.com> - * - * 2005, 2006 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -/* - * This i2c client/driver wedges between the drivers/char/genrtc.c RTC - * interface and the SMBus interface of the i2c subsystem. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/i2c.h> -#include <linux/rtc.h> -#include <linux/bcd.h> -#include <linux/workqueue.h> -#include <linux/platform_device.h> -#include <linux/m41t00.h> -#include <asm/time.h> -#include <asm/rtc.h> - -static struct i2c_driver m41t00_driver; -static struct i2c_client *save_client; - -static unsigned short ignore[] = { I2C_CLIENT_END }; -static unsigned short normal_addr[] = { I2C_CLIENT_END, I2C_CLIENT_END }; - -static struct i2c_client_address_data addr_data = { - .normal_i2c = normal_addr, - .probe = ignore, - .ignore = ignore, -}; - -struct m41t00_chip_info { - u8 type; - char *name; - u8 read_limit; - u8 sec; /* Offsets for chip regs */ - u8 min; - u8 hour; - u8 day; - u8 mon; - u8 year; - u8 alarm_mon; - u8 alarm_hour; - u8 sqw; - u8 sqw_freq; -}; - -static struct m41t00_chip_info m41t00_chip_info_tbl[] = { - { - .type = M41T00_TYPE_M41T00, - .name = "m41t00", - .read_limit = 5, - .sec = 0, - .min = 1, - .hour = 2, - .day = 4, - .mon = 5, - .year = 6, - }, - { - .type = M41T00_TYPE_M41T81, - .name = "m41t81", - .read_limit = 1, - .sec = 1, - .min = 2, - .hour = 3, - .day = 5, - .mon = 6, - .year = 7, - .alarm_mon = 0xa, - .alarm_hour = 0xc, - .sqw = 0x13, - }, - { - .type = M41T00_TYPE_M41T85, - .name = "m41t85", - .read_limit = 1, - .sec = 1, - .min = 2, - .hour = 3, - .day = 5, - .mon = 6, - .year = 7, - .alarm_mon = 0xa, - .alarm_hour = 0xc, - .sqw = 0x13, - }, -}; -static struct m41t00_chip_info *m41t00_chip; - -ulong -m41t00_get_rtc_time(void) -{ - s32 sec, min, hour, day, mon, year; - s32 sec1, min1, hour1, day1, mon1, year1; - u8 reads = 0; - u8 buf[8], msgbuf[1] = { 0 }; /* offset into rtc's regs */ - struct i2c_msg msgs[] = { - { - .addr = save_client->addr, - .flags = 0, - .len = 1, - .buf = msgbuf, - }, - { - .addr = save_client->addr, - .flags = I2C_M_RD, - .len = 8, - .buf = buf, - }, - }; - - sec = min = hour = day = mon = year = 0; - - do { - if (i2c_transfer(save_client->adapter, msgs, 2) < 0) - goto read_err; - - sec1 = sec; - min1 = min; - hour1 = hour; - day1 = day; - mon1 = mon; - year1 = year; - - sec = buf[m41t00_chip->sec] & 0x7f; - min = buf[m41t00_chip->min] & 0x7f; - hour = buf[m41t00_chip->hour] & 0x3f; - day = buf[m41t00_chip->day] & 0x3f; - mon = buf[m41t00_chip->mon] & 0x1f; - year = buf[m41t00_chip->year]; - } while ((++reads < m41t00_chip->read_limit) && ((sec != sec1) - || (min != min1) || (hour != hour1) || (day != day1) - || (mon != mon1) || (year != year1))); - - if ((m41t00_chip->read_limit > 1) && ((sec != sec1) || (min != min1) - || (hour != hour1) || (day != day1) || (mon != mon1) - || (year != year1))) - goto read_err; - - sec = BCD2BIN(sec); - min = BCD2BIN(min); - hour = BCD2BIN(hour); - day = BCD2BIN(day); - mon = BCD2BIN(mon); - year = BCD2BIN(year); - - year += 1900; - if (year < 1970) - year += 100; - - return mktime(year, mon, day, hour, min, sec); - -read_err: - dev_err(&save_client->dev, "m41t00_get_rtc_time: Read error\n"); - return 0; -} -EXPORT_SYMBOL_GPL(m41t00_get_rtc_time); - -static void -m41t00_set(void *arg) -{ - struct rtc_time tm; - int nowtime = *(int *)arg; - s32 sec, min, hour, day, mon, year; - u8 wbuf[9], *buf = &wbuf[1], msgbuf[1] = { 0 }; - struct i2c_msg msgs[] = { - { - .addr = save_client->addr, - .flags = 0, - .len = 1, - .buf = msgbuf, - }, - { - .addr = save_client->addr, - .flags = I2C_M_RD, - .len = 8, - .buf = buf, - }, - }; - - to_tm(nowtime, &tm); - tm.tm_year = (tm.tm_year - 1900) % 100; - - sec = BIN2BCD(tm.tm_sec); - min = BIN2BCD(tm.tm_min); - hour = BIN2BCD(tm.tm_hour); - day = BIN2BCD(tm.tm_mday); - mon = BIN2BCD(tm.tm_mon); - year = BIN2BCD(tm.tm_year); - - /* Read reg values into buf[0..7]/wbuf[1..8] */ - if (i2c_transfer(save_client->adapter, msgs, 2) < 0) { - dev_err(&save_client->dev, "m41t00_set: Read error\n"); - return; - } - - wbuf[0] = 0; /* offset into rtc's regs */ - buf[m41t00_chip->sec] = (buf[m41t00_chip->sec] & ~0x7f) | (sec & 0x7f); - buf[m41t00_chip->min] = (buf[m41t00_chip->min] & ~0x7f) | (min & 0x7f); - buf[m41t00_chip->hour] = (buf[m41t00_chip->hour] & ~0x3f) | (hour& 0x3f); - buf[m41t00_chip->day] = (buf[m41t00_chip->day] & ~0x3f) | (day & 0x3f); - buf[m41t00_chip->mon] = (buf[m41t00_chip->mon] & ~0x1f) | (mon & 0x1f); - buf[m41t00_chip->year] = year; - - if (i2c_master_send(save_client, wbuf, 9) < 0) - dev_err(&save_client->dev, "m41t00_set: Write error\n"); -} - -static ulong new_time; -/* well, isn't this API just _lovely_? */ -static void -m41t00_barf(struct work_struct *unusable) -{ - m41t00_set(&new_time); -} - -static struct workqueue_struct *m41t00_wq; -static DECLARE_WORK(m41t00_work, m41t00_barf); - -int -m41t00_set_rtc_time(ulong nowtime) -{ - new_time = nowtime; - - if (in_interrupt()) - queue_work(m41t00_wq, &m41t00_work); - else - m41t00_set(&new_time); - - return 0; -} -EXPORT_SYMBOL_GPL(m41t00_set_rtc_time); - -/* - ***************************************************************************** - * - * platform_data Driver Interface - * - ***************************************************************************** - */ -static int __init -m41t00_platform_probe(struct platform_device *pdev) -{ - struct m41t00_platform_data *pdata; - int i; - - if (pdev && (pdata = pdev->dev.platform_data)) { - normal_addr[0] = pdata->i2c_addr; - - for (i=0; i<ARRAY_SIZE(m41t00_chip_info_tbl); i++) - if (m41t00_chip_info_tbl[i].type == pdata->type) { - m41t00_chip = &m41t00_chip_info_tbl[i]; - m41t00_chip->sqw_freq = pdata->sqw_freq; - return 0; - } - } - return -ENODEV; -} - -static int __exit -m41t00_platform_remove(struct platform_device *pdev) -{ - return 0; -} - -static struct platform_driver m41t00_platform_driver = { - .probe = m41t00_platform_probe, - .remove = m41t00_platform_remove, - .driver = { - .owner = THIS_MODULE, - .name = M41T00_DRV_NAME, - }, -}; - -/* - ***************************************************************************** - * - * Driver Interface - * - ***************************************************************************** - */ -static int -m41t00_probe(struct i2c_adapter *adap, int addr, int kind) -{ - struct i2c_client *client; - int rc; - - if (!i2c_check_functionality(adap, I2C_FUNC_I2C - | I2C_FUNC_SMBUS_BYTE_DATA)) - return 0; - - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!client) - return -ENOMEM; - - strlcpy(client->name, m41t00_chip->name, I2C_NAME_SIZE); - client->addr = addr; - client->adapter = adap; - client->driver = &m41t00_driver; - - if ((rc = i2c_attach_client(client))) - goto attach_err; - - if (m41t00_chip->type != M41T00_TYPE_M41T00) { - /* If asked, disable SQW, set SQW frequency & re-enable */ - if (m41t00_chip->sqw_freq) - if (((rc = i2c_smbus_read_byte_data(client, - m41t00_chip->alarm_mon)) < 0) - || ((rc = i2c_smbus_write_byte_data(client, - m41t00_chip->alarm_mon, rc & ~0x40)) <0) - || ((rc = i2c_smbus_write_byte_data(client, - m41t00_chip->sqw, - m41t00_chip->sqw_freq)) < 0) - || ((rc = i2c_smbus_write_byte_data(client, - m41t00_chip->alarm_mon, rc | 0x40)) <0)) - goto sqw_err; - - /* Make sure HT (Halt Update) bit is cleared */ - if ((rc = i2c_smbus_read_byte_data(client, - m41t00_chip->alarm_hour)) < 0) - goto ht_err; - - if (rc & 0x40) - if ((rc = i2c_smbus_write_byte_data(client, - m41t00_chip->alarm_hour, rc & ~0x40))<0) - goto ht_err; - } - - /* Make sure ST (stop) bit is cleared */ - if ((rc = i2c_smbus_read_byte_data(client, m41t00_chip->sec)) < 0) - goto st_err; - - if (rc & 0x80) - if ((rc = i2c_smbus_write_byte_data(client, m41t00_chip->sec, - rc & ~0x80)) < 0) - goto st_err; - - m41t00_wq = create_singlethread_workqueue(m41t00_chip->name); - save_client = client; - return 0; - -st_err: - dev_err(&client->dev, "m41t00_probe: Can't clear ST bit\n"); - goto attach_err; -ht_err: - dev_err(&client->dev, "m41t00_probe: Can't clear HT bit\n"); - goto attach_err; -sqw_err: - dev_err(&client->dev, "m41t00_probe: Can't set SQW Frequency\n"); -attach_err: - kfree(client); - return rc; -} - -static int -m41t00_attach(struct i2c_adapter *adap) -{ - return i2c_probe(adap, &addr_data, m41t00_probe); -} - -static int -m41t00_detach(struct i2c_client *client) -{ - int rc; - - if ((rc = i2c_detach_client(client)) == 0) { - kfree(client); - destroy_workqueue(m41t00_wq); - } - return rc; -} - -static struct i2c_driver m41t00_driver = { - .driver = { - .name = M41T00_DRV_NAME, - }, - .id = I2C_DRIVERID_STM41T00, - .attach_adapter = m41t00_attach, - .detach_client = m41t00_detach, -}; - -static int __init -m41t00_init(void) -{ - int rc; - - if (!(rc = platform_driver_register(&m41t00_platform_driver))) - rc = i2c_add_driver(&m41t00_driver); - return rc; -} - -static void __exit -m41t00_exit(void) -{ - i2c_del_driver(&m41t00_driver); - platform_driver_unregister(&m41t00_platform_driver); -} - -module_init(m41t00_init); -module_exit(m41t00_exit); - -MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>"); -MODULE_DESCRIPTION("ST Microelectronics M41T00 RTC I2C Client Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/chips/max6875.c b/drivers/i2c/chips/max6875.c index 64692f66637..fb7ea5637ec 100644 --- a/drivers/i2c/chips/max6875.c +++ b/drivers/i2c/chips/max6875.c @@ -34,7 +34,7 @@ #include <linux/mutex.h> /* Do not scan - the MAX6875 access method will write to some EEPROM chips */ -static unsigned short normal_i2c[] = {I2C_CLIENT_END}; +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; /* Insmod parameters */ I2C_CLIENT_INSMOD_1(max6875); diff --git a/drivers/i2c/chips/pcf8574.c b/drivers/i2c/chips/pcf8574.c index 21c6dd69193..b3b830ccf20 100644 --- a/drivers/i2c/chips/pcf8574.c +++ b/drivers/i2c/chips/pcf8574.c @@ -41,9 +41,11 @@ #include <linux/i2c.h> /* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + I2C_CLIENT_END +}; /* Insmod parameters */ I2C_CLIENT_INSMOD_2(pcf8574, pcf8574a); diff --git a/drivers/i2c/chips/pcf8575.c b/drivers/i2c/chips/pcf8575.c new file mode 100644 index 00000000000..3ea08ac0bfa --- /dev/null +++ b/drivers/i2c/chips/pcf8575.c @@ -0,0 +1,214 @@ +/* + pcf8575.c + + About the PCF8575 chip: the PCF8575 is a 16-bit I/O expander for the I2C bus + produced by a.o. Philips Semiconductors. + + Copyright (C) 2006 Michael Hennerich, Analog Devices Inc. + <hennerich@blackfin.uclinux.org> + Based on pcf8574.c. + + Copyright (c) 2007 Bart Van Assche <bart.vanassche@gmail.com>. + Ported this driver from ucLinux to the mainstream Linux kernel. + + 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. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/slab.h> /* kzalloc() */ +#include <linux/sysfs.h> /* sysfs_create_group() */ + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + I2C_CLIENT_END +}; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD; + + +/* Each client has this additional data */ +struct pcf8575_data { + struct i2c_client client; + int write; /* last written value, or error code */ +}; + +static int pcf8575_attach_adapter(struct i2c_adapter *adapter); +static int pcf8575_detect(struct i2c_adapter *adapter, int address, int kind); +static int pcf8575_detach_client(struct i2c_client *client); + +/* This is the driver that will be inserted */ +static struct i2c_driver pcf8575_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "pcf8575", + }, + .attach_adapter = pcf8575_attach_adapter, + .detach_client = pcf8575_detach_client, +}; + +/* following are the sysfs callback functions */ +static ssize_t show_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u16 val; + u8 iopin_state[2]; + + i2c_master_recv(client, iopin_state, 2); + + val = iopin_state[0]; + val |= iopin_state[1] << 8; + + return sprintf(buf, "%u\n", val); +} + +static DEVICE_ATTR(read, S_IRUGO, show_read, NULL); + +static ssize_t show_write(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pcf8575_data *data = dev_get_drvdata(dev); + if (data->write < 0) + return data->write; + return sprintf(buf, "%d\n", data->write); +} + +static ssize_t set_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pcf8575_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + u8 iopin_state[2]; + + if (val > 0xffff) + return -EINVAL; + + data->write = val; + + iopin_state[0] = val & 0xFF; + iopin_state[1] = val >> 8; + + i2c_master_send(client, iopin_state, 2); + + return count; +} + +static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write); + +static struct attribute *pcf8575_attributes[] = { + &dev_attr_read.attr, + &dev_attr_write.attr, + NULL +}; + +static const struct attribute_group pcf8575_attr_group = { + .attrs = pcf8575_attributes, +}; + +/* + * Real code + */ + +static int pcf8575_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_probe(adapter, &addr_data, pcf8575_detect); +} + +/* This function is called by i2c_probe */ +static int pcf8575_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct pcf8575_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + goto exit; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. */ + data = kzalloc(sizeof(struct pcf8575_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + client = &data->client; + i2c_set_clientdata(client, data); + client->addr = address; + client->adapter = adapter; + client->driver = &pcf8575_driver; + strlcpy(client->name, "pcf8575", I2C_NAME_SIZE); + data->write = -EAGAIN; + + /* This is the place to detect whether the chip at the specified + address really is a PCF8575 chip. However, there is no method known + to detect whether an I2C chip is a PCF8575 or any other I2C chip. */ + + /* Tell the I2C layer a new client has arrived */ + err = i2c_attach_client(client); + if (err) + goto exit_free; + + /* Register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &pcf8575_attr_group); + if (err) + goto exit_detach; + + return 0; + +exit_detach: + i2c_detach_client(client); +exit_free: + kfree(data); +exit: + return err; +} + +static int pcf8575_detach_client(struct i2c_client *client) +{ + int err; + + sysfs_remove_group(&client->dev.kobj, &pcf8575_attr_group); + + err = i2c_detach_client(client); + if (err) + return err; + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static int __init pcf8575_init(void) +{ + return i2c_add_driver(&pcf8575_driver); +} + +static void __exit pcf8575_exit(void) +{ + i2c_del_driver(&pcf8575_driver); +} + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>, " + "Bart Van Assche <bart.vanassche@gmail.com>"); +MODULE_DESCRIPTION("pcf8575 driver"); +MODULE_LICENSE("GPL"); + +module_init(pcf8575_init); +module_exit(pcf8575_exit); diff --git a/drivers/i2c/chips/pcf8591.c b/drivers/i2c/chips/pcf8591.c index 4dc36376eb3..865f4409c06 100644 --- a/drivers/i2c/chips/pcf8591.c +++ b/drivers/i2c/chips/pcf8591.c @@ -27,7 +27,7 @@ #include <linux/mutex.h> /* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, +static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; /* Insmod parameters */ diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c index e320994b981..4154a910885 100644 --- a/drivers/i2c/chips/tps65010.c +++ b/drivers/i2c/chips/tps65010.c @@ -31,7 +31,7 @@ #include <linux/seq_file.h> #include <linux/mutex.h> -#include <asm/arch/tps65010.h> +#include <linux/i2c/tps65010.h> /*-------------------------------------------------------------------------*/ diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/i2c/chips/tsl2550.c index 3de4b19ba08..a10fd2791a6 100644 --- a/drivers/i2c/chips/tsl2550.c +++ b/drivers/i2c/chips/tsl2550.c @@ -432,11 +432,32 @@ static int __devexit tsl2550_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM + +static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return tsl2550_set_power_state(client, 0); +} + +static int tsl2550_resume(struct i2c_client *client) +{ + return tsl2550_set_power_state(client, 1); +} + +#else + +#define tsl2550_suspend NULL +#define tsl2550_resume NULL + +#endif /* CONFIG_PM */ + static struct i2c_driver tsl2550_driver = { .driver = { .name = TSL2550_DRV_NAME, .owner = THIS_MODULE, }, + .suspend = tsl2550_suspend, + .resume = tsl2550_resume, .probe = tsl2550_probe, .remove = __devexit_p(tsl2550_remove), }; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index b5e13e405e7..96da22e9a5a 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -33,14 +33,15 @@ #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/completion.h> +#include <linux/hardirq.h> +#include <linux/irqflags.h> #include <asm/uaccess.h> +#include <asm/semaphore.h> #include "i2c-core.h" -static LIST_HEAD(adapters); -static LIST_HEAD(drivers); -static DEFINE_MUTEX(core_lists); +static DEFINE_MUTEX(core_lock); static DEFINE_IDR(i2c_adapter_idr); #define is_newstyle_driver(d) ((d)->probe || (d)->remove) @@ -198,6 +199,25 @@ static struct bus_type i2c_bus_type = { .resume = i2c_device_resume, }; + +/** + * i2c_verify_client - return parameter as i2c_client, or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-I2C device as an i2c_client. + */ +struct i2c_client *i2c_verify_client(struct device *dev) +{ + return (dev->bus == &i2c_bus_type) + ? to_i2c_client(dev) + : NULL; +} +EXPORT_SYMBOL(i2c_verify_client); + + /** * i2c_new_device - instantiate an i2c device for use with a new style driver * @adap: the adapter managing the device @@ -276,6 +296,50 @@ void i2c_unregister_device(struct i2c_client *client) EXPORT_SYMBOL_GPL(i2c_unregister_device); +static int dummy_nop(struct i2c_client *client) +{ + return 0; +} + +static struct i2c_driver dummy_driver = { + .driver.name = "dummy", + .probe = dummy_nop, + .remove = dummy_nop, +}; + +/** + * i2c_new_dummy - return a new i2c device bound to a dummy driver + * @adapter: the adapter managing the device + * @address: seven bit address to be used + * @type: optional label used for i2c_client.name + * Context: can sleep + * + * This returns an I2C client bound to the "dummy" driver, intended for use + * with devices that consume multiple addresses. Examples of such chips + * include various EEPROMS (like 24c04 and 24c08 models). + * + * These dummy devices have two main uses. First, most I2C and SMBus calls + * except i2c_transfer() need a client handle; the dummy will be that handle. + * And second, this prevents the specified address from being bound to a + * different driver. + * + * This returns the new i2c client, which should be saved for later use with + * i2c_unregister_device(); or NULL to indicate an error. + */ +struct i2c_client * +i2c_new_dummy(struct i2c_adapter *adapter, u16 address, const char *type) +{ + struct i2c_board_info info = { + .driver_name = "dummy", + .addr = address, + }; + + if (type) + strlcpy(info.type, type, sizeof info.type); + return i2c_new_device(adapter, &info); +} +EXPORT_SYMBOL_GPL(i2c_new_dummy); + /* ------------------------------------------------------------------------- */ /* I2C bus adapters -- one roots each I2C or SMBUS segment */ @@ -320,18 +384,27 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter) mutex_unlock(&__i2c_board_lock); } +static int i2c_do_add_adapter(struct device_driver *d, void *data) +{ + struct i2c_driver *driver = to_i2c_driver(d); + struct i2c_adapter *adap = data; + + if (driver->attach_adapter) { + /* We ignore the return code; if it fails, too bad */ + driver->attach_adapter(adap); + } + return 0; +} + static int i2c_register_adapter(struct i2c_adapter *adap) { - int res = 0; - struct list_head *item; - struct i2c_driver *driver; + int res = 0, dummy; mutex_init(&adap->bus_lock); mutex_init(&adap->clist_lock); INIT_LIST_HEAD(&adap->clients); - mutex_lock(&core_lists); - list_add_tail(&adap->list, &adapters); + mutex_lock(&core_lock); /* Add the adapter to the driver core. * If the parent pointer is not set up, @@ -356,19 +429,14 @@ static int i2c_register_adapter(struct i2c_adapter *adap) i2c_scan_static_board_info(adap); /* let legacy drivers scan this bus for matching devices */ - list_for_each(item,&drivers) { - driver = list_entry(item, struct i2c_driver, list); - if (driver->attach_adapter) - /* We ignore the return code; if it fails, too bad */ - driver->attach_adapter(adap); - } + dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, + i2c_do_add_adapter); out_unlock: - mutex_unlock(&core_lists); + mutex_unlock(&core_lock); return res; out_list: - list_del(&adap->list); idr_remove(&i2c_adapter_idr, adap->nr); goto out_unlock; } @@ -394,11 +462,11 @@ retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) return -ENOMEM; - mutex_lock(&core_lists); + mutex_lock(&core_lock); /* "above" here means "above or equal to", sigh */ res = idr_get_new_above(&i2c_adapter_idr, adapter, __i2c_first_dynamic_bus_num, &id); - mutex_unlock(&core_lists); + mutex_unlock(&core_lock); if (res < 0) { if (res == -EAGAIN) @@ -443,7 +511,7 @@ retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) return -ENOMEM; - mutex_lock(&core_lists); + mutex_lock(&core_lock); /* "above" here means "above or equal to", sigh; * we need the "equal to" result to force the result */ @@ -452,7 +520,7 @@ retry: status = -EBUSY; idr_remove(&i2c_adapter_idr, id); } - mutex_unlock(&core_lists); + mutex_unlock(&core_lock); if (status == -EAGAIN) goto retry; @@ -462,6 +530,21 @@ retry: } EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); +static int i2c_do_del_adapter(struct device_driver *d, void *data) +{ + struct i2c_driver *driver = to_i2c_driver(d); + struct i2c_adapter *adapter = data; + int res; + + if (!driver->detach_adapter) + return 0; + res = driver->detach_adapter(adapter); + if (res) + dev_err(&adapter->dev, "detach_adapter failed (%d) " + "for driver [%s]\n", res, driver->driver.name); + return res; +} + /** * i2c_del_adapter - unregister I2C adapter * @adap: the adapter being unregistered @@ -473,35 +556,24 @@ EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); int i2c_del_adapter(struct i2c_adapter *adap) { struct list_head *item, *_n; - struct i2c_adapter *adap_from_list; - struct i2c_driver *driver; struct i2c_client *client; int res = 0; - mutex_lock(&core_lists); + mutex_lock(&core_lock); /* First make sure that this adapter was ever added */ - list_for_each_entry(adap_from_list, &adapters, list) { - if (adap_from_list == adap) - break; - } - if (adap_from_list != adap) { + if (idr_find(&i2c_adapter_idr, adap->nr) != adap) { pr_debug("i2c-core: attempting to delete unregistered " "adapter [%s]\n", adap->name); res = -EINVAL; goto out_unlock; } - list_for_each(item,&drivers) { - driver = list_entry(item, struct i2c_driver, list); - if (driver->detach_adapter) - if ((res = driver->detach_adapter(adap))) { - dev_err(&adap->dev, "detach_adapter failed " - "for driver [%s]\n", - driver->driver.name); - goto out_unlock; - } - } + /* Tell drivers about this removal */ + res = bus_for_each_drv(&i2c_bus_type, NULL, adap, + i2c_do_del_adapter); + if (res) + goto out_unlock; /* detach any active clients. This must be done first, because * it can fail; in which case we give up. */ @@ -529,7 +601,6 @@ int i2c_del_adapter(struct i2c_adapter *adap) /* clean up the sysfs representation */ init_completion(&adap->dev_released); device_unregister(&adap->dev); - list_del(&adap->list); /* wait for sysfs to drop all references */ wait_for_completion(&adap->dev_released); @@ -540,7 +611,7 @@ int i2c_del_adapter(struct i2c_adapter *adap) dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); out_unlock: - mutex_unlock(&core_lists); + mutex_unlock(&core_lock); return res; } EXPORT_SYMBOL(i2c_del_adapter); @@ -583,21 +654,23 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) if (res) return res; - mutex_lock(&core_lists); + mutex_lock(&core_lock); - list_add_tail(&driver->list,&drivers); pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name); /* legacy drivers scan i2c busses directly */ if (driver->attach_adapter) { struct i2c_adapter *adapter; - list_for_each_entry(adapter, &adapters, list) { + down(&i2c_adapter_class.sem); + list_for_each_entry(adapter, &i2c_adapter_class.devices, + dev.node) { driver->attach_adapter(adapter); } + up(&i2c_adapter_class.sem); } - mutex_unlock(&core_lists); + mutex_unlock(&core_lock); return 0; } EXPORT_SYMBOL(i2c_register_driver); @@ -609,11 +682,11 @@ EXPORT_SYMBOL(i2c_register_driver); */ void i2c_del_driver(struct i2c_driver *driver) { - struct list_head *item1, *item2, *_n; + struct list_head *item2, *_n; struct i2c_client *client; struct i2c_adapter *adap; - mutex_lock(&core_lists); + mutex_lock(&core_lock); /* new-style driver? */ if (is_newstyle_driver(driver)) @@ -623,8 +696,8 @@ void i2c_del_driver(struct i2c_driver *driver) * attached. If so, detach them to be able to kill the driver * afterwards. */ - list_for_each(item1,&adapters) { - adap = list_entry(item1, struct i2c_adapter, list); + down(&i2c_adapter_class.sem); + list_for_each_entry(adap, &i2c_adapter_class.devices, dev.node) { if (driver->detach_adapter) { if (driver->detach_adapter(adap)) { dev_err(&adap->dev, "detach_adapter failed " @@ -648,40 +721,31 @@ void i2c_del_driver(struct i2c_driver *driver) } } } + up(&i2c_adapter_class.sem); unregister: driver_unregister(&driver->driver); - list_del(&driver->list); pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name); - mutex_unlock(&core_lists); + mutex_unlock(&core_lock); } EXPORT_SYMBOL(i2c_del_driver); /* ------------------------------------------------------------------------- */ -static int __i2c_check_addr(struct i2c_adapter *adapter, unsigned int addr) +static int __i2c_check_addr(struct device *dev, void *addrp) { - struct list_head *item; - struct i2c_client *client; + struct i2c_client *client = i2c_verify_client(dev); + int addr = *(int *)addrp; - list_for_each(item,&adapter->clients) { - client = list_entry(item, struct i2c_client, list); - if (client->addr == addr) - return -EBUSY; - } + if (client && client->addr == addr) + return -EBUSY; return 0; } static int i2c_check_addr(struct i2c_adapter *adapter, int addr) { - int rval; - - mutex_lock(&adapter->clist_lock); - rval = __i2c_check_addr(adapter, addr); - mutex_unlock(&adapter->clist_lock); - - return rval; + return device_for_each_child(&adapter->dev, &addr, __i2c_check_addr); } int i2c_attach_client(struct i2c_client *client) @@ -689,15 +753,6 @@ int i2c_attach_client(struct i2c_client *client) struct i2c_adapter *adapter = client->adapter; int res = 0; - mutex_lock(&adapter->clist_lock); - if (__i2c_check_addr(client->adapter, client->addr)) { - res = -EBUSY; - goto out_unlock; - } - list_add_tail(&client->list,&adapter->clients); - - client->usage_count = 0; - client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; @@ -712,13 +767,17 @@ int i2c_attach_client(struct i2c_client *client) snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id), "%d-%04x", i2c_adapter_id(adapter), client->addr); - dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n", - client->name, client->dev.bus_id); res = device_register(&client->dev); if (res) - goto out_list; + goto out_err; + + mutex_lock(&adapter->clist_lock); + list_add_tail(&client->list, &adapter->clients); mutex_unlock(&adapter->clist_lock); + dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n", + client->name, client->dev.bus_id); + if (adapter->client_register) { if (adapter->client_register(client)) { dev_dbg(&adapter->dev, "client_register " @@ -729,12 +788,9 @@ int i2c_attach_client(struct i2c_client *client) return 0; -out_list: - list_del(&client->list); +out_err: dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x " "(%d)\n", client->name, client->addr, res); -out_unlock: - mutex_unlock(&adapter->clist_lock); return res; } EXPORT_SYMBOL(i2c_attach_client); @@ -744,12 +800,6 @@ int i2c_detach_client(struct i2c_client *client) struct i2c_adapter *adapter = client->adapter; int res = 0; - if (client->usage_count > 0) { - dev_warn(&client->dev, "Client [%s] still busy, " - "can't detach\n", client->name); - return -EBUSY; - } - if (adapter->client_unregister) { res = adapter->client_unregister(client); if (res) { @@ -762,9 +812,10 @@ int i2c_detach_client(struct i2c_client *client) mutex_lock(&adapter->clist_lock); list_del(&client->list); + mutex_unlock(&adapter->clist_lock); + init_completion(&client->released); device_unregister(&client->dev); - mutex_unlock(&adapter->clist_lock); wait_for_completion(&client->released); out: @@ -772,72 +823,58 @@ int i2c_detach_client(struct i2c_client *client) } EXPORT_SYMBOL(i2c_detach_client); -static int i2c_inc_use_client(struct i2c_client *client) +/** + * i2c_use_client - increments the reference count of the i2c client structure + * @client: the client being referenced + * + * Each live reference to a client should be refcounted. The driver model does + * that automatically as part of driver binding, so that most drivers don't + * need to do this explicitly: they hold a reference until they're unbound + * from the device. + * + * A pointer to the client with the incremented reference counter is returned. + */ +struct i2c_client *i2c_use_client(struct i2c_client *client) { - - if (!try_module_get(client->driver->driver.owner)) - return -ENODEV; - if (!try_module_get(client->adapter->owner)) { - module_put(client->driver->driver.owner); - return -ENODEV; - } - - return 0; + get_device(&client->dev); + return client; } +EXPORT_SYMBOL(i2c_use_client); -static void i2c_dec_use_client(struct i2c_client *client) +/** + * i2c_release_client - release a use of the i2c client structure + * @client: the client being no longer referenced + * + * Must be called when a user of a client is finished with it. + */ +void i2c_release_client(struct i2c_client *client) { - module_put(client->driver->driver.owner); - module_put(client->adapter->owner); + put_device(&client->dev); } +EXPORT_SYMBOL(i2c_release_client); -int i2c_use_client(struct i2c_client *client) -{ - int ret; - - ret = i2c_inc_use_client(client); - if (ret) - return ret; - - client->usage_count++; - - return 0; -} -EXPORT_SYMBOL(i2c_use_client); +struct i2c_cmd_arg { + unsigned cmd; + void *arg; +}; -int i2c_release_client(struct i2c_client *client) +static int i2c_cmd(struct device *dev, void *_arg) { - if (!client->usage_count) { - pr_debug("i2c-core: %s used one too many times\n", - __FUNCTION__); - return -EPERM; - } - - client->usage_count--; - i2c_dec_use_client(client); + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_cmd_arg *arg = _arg; + if (client && client->driver && client->driver->command) + client->driver->command(client, arg->cmd, arg->arg); return 0; } -EXPORT_SYMBOL(i2c_release_client); void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg) { - struct list_head *item; - struct i2c_client *client; + struct i2c_cmd_arg cmd_arg; - mutex_lock(&adap->clist_lock); - list_for_each(item,&adap->clients) { - client = list_entry(item, struct i2c_client, list); - if (!try_module_get(client->driver->driver.owner)) - continue; - if (NULL != client->driver->command) { - mutex_unlock(&adap->clist_lock); - client->driver->command(client,cmd,arg); - mutex_lock(&adap->clist_lock); - } - module_put(client->driver->driver.owner); - } - mutex_unlock(&adap->clist_lock); + cmd_arg.cmd = cmd; + cmd_arg.arg = arg; + device_for_each_child(&adap->dev, &cmd_arg, i2c_cmd); } EXPORT_SYMBOL(i2c_clients_command); @@ -848,11 +885,24 @@ static int __init i2c_init(void) retval = bus_register(&i2c_bus_type); if (retval) return retval; - return class_register(&i2c_adapter_class); + retval = class_register(&i2c_adapter_class); + if (retval) + goto bus_err; + retval = i2c_add_driver(&dummy_driver); + if (retval) + goto class_err; + return 0; + +class_err: + class_unregister(&i2c_adapter_class); +bus_err: + bus_unregister(&i2c_bus_type); + return retval; } static void __exit i2c_exit(void) { + i2c_del_driver(&dummy_driver); class_unregister(&i2c_adapter_class); bus_unregister(&i2c_bus_type); } @@ -879,7 +929,15 @@ int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num) } #endif - mutex_lock_nested(&adap->bus_lock, adap->level); + if (in_atomic() || irqs_disabled()) { + ret = mutex_trylock(&adap->bus_lock); + if (!ret) + /* I2C activity is ongoing. */ + return -EAGAIN; + } else { + mutex_lock_nested(&adap->bus_lock, adap->level); + } + ret = adap->algo->master_xfer(adap,msgs,num); mutex_unlock(&adap->bus_lock); @@ -978,7 +1036,7 @@ static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind, } int i2c_probe(struct i2c_adapter *adapter, - struct i2c_client_address_data *address_data, + const struct i2c_client_address_data *address_data, int (*found_proc) (struct i2c_adapter *, int, int)) { int i, err; @@ -987,7 +1045,7 @@ int i2c_probe(struct i2c_adapter *adapter, /* Force entries are done first, and are not affected by ignore entries */ if (address_data->forces) { - unsigned short **forces = address_data->forces; + const unsigned short * const *forces = address_data->forces; int kind; for (kind = 0; forces[kind]; kind++) { @@ -1085,7 +1143,6 @@ i2c_new_probed_device(struct i2c_adapter *adap, return NULL; } - mutex_lock(&adap->clist_lock); for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) { /* Check address validity */ if (addr_list[i] < 0x03 || addr_list[i] > 0x77) { @@ -1095,7 +1152,7 @@ i2c_new_probed_device(struct i2c_adapter *adap, } /* Check address availability */ - if (__i2c_check_addr(adap, addr_list[i])) { + if (i2c_check_addr(adap, addr_list[i])) { dev_dbg(&adap->dev, "Address 0x%02x already in " "use, not probing\n", addr_list[i]); continue; @@ -1123,7 +1180,6 @@ i2c_new_probed_device(struct i2c_adapter *adap, break; } } - mutex_unlock(&adap->clist_lock); if (addr_list[i] == I2C_CLIENT_END) { dev_dbg(&adap->dev, "Probing failed, no device found\n"); @@ -1139,12 +1195,12 @@ struct i2c_adapter* i2c_get_adapter(int id) { struct i2c_adapter *adapter; - mutex_lock(&core_lists); + mutex_lock(&core_lock); adapter = (struct i2c_adapter *)idr_find(&i2c_adapter_idr, id); if (adapter && !try_module_get(adapter->owner)) adapter = NULL; - mutex_unlock(&core_lists); + mutex_unlock(&core_lock); return adapter; } EXPORT_SYMBOL(i2c_get_adapter); diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index df540d5dfaf..393e679d9fa 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -182,27 +182,22 @@ static ssize_t i2cdev_write (struct file *file, const char __user *buf, size_t c return ret; } +static int i2cdev_check(struct device *dev, void *addrp) +{ + struct i2c_client *client = i2c_verify_client(dev); + + if (!client || client->addr != *(unsigned int *)addrp) + return 0; + + return dev->driver ? -EBUSY : 0; +} + /* This address checking function differs from the one in i2c-core in that it considers an address with a registered device, but no - bound driver, as NOT busy. */ + driver bound to it, as NOT busy. */ static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) { - struct list_head *item; - struct i2c_client *client; - int res = 0; - - mutex_lock(&adapter->clist_lock); - list_for_each(item, &adapter->clients) { - client = list_entry(item, struct i2c_client, list); - if (client->addr == addr) { - if (client->driver) - res = -EBUSY; - break; - } - } - mutex_unlock(&adapter->clist_lock); - - return res; + return device_for_each_child(&adapter->dev, &addr, i2cdev_check); } static int i2cdev_ioctl(struct inode *inode, struct file *file, diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index ee01e273a53..64df55e20ab 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -325,7 +325,7 @@ config BLK_DEV_PLATFORM If unsure, say N. config BLK_DEV_CMD640 - bool "CMD640 chipset bugfix/support" + tristate "CMD640 chipset bugfix/support" depends on X86 ---help--- The CMD-Technologies CMD640 IDE chip is used on many common 486 and @@ -359,9 +359,8 @@ config BLK_DEV_CMD640_ENHANCED Otherwise say N. config BLK_DEV_IDEPNP - bool "PNP EIDE support" + tristate "PNP EIDE support" depends on PNP - select IDE_GENERIC help If you have a PnP (Plug and Play) compatible EIDE card and would like the kernel to automatically detect and activate @@ -375,7 +374,19 @@ config BLK_DEV_IDEPCI bool config IDEPCI_PCIBUS_ORDER - def_bool BLK_DEV_IDE=y && BLK_DEV_IDEPCI + bool "Probe IDE PCI devices in the PCI bus order (DEPRECATED)" + depends on BLK_DEV_IDE=y && BLK_DEV_IDEPCI + default y + help + Probe IDE PCI devices in the order in which they appear on the + PCI bus (i.e. 00:1f.1 PCI device before 02:01.0 PCI device) + instead of the order in which IDE PCI host drivers are loaded. + + Please note that this method of assuring stable naming of + IDE devices is unreliable and use other means for achieving + it (i.e. udev). + + If in doubt, say N. # TODO: split it on per host driver config options (or module parameters) config BLK_DEV_OFFBOARD @@ -789,7 +800,7 @@ config BLK_DEV_CELLEB endif config BLK_DEV_IDE_PMAC - bool "Builtin PowerMac IDE support" + tristate "Builtin PowerMac IDE support" depends on PPC_PMAC && IDE=y && BLK_DEV_IDE=y help This driver provides support for the built-in IDE controller on @@ -843,8 +854,9 @@ config BLK_DEV_IDE_AU1XXX_SEQTS_PER_RQ depends on BLK_DEV_IDE_AU1XXX config IDE_ARM - def_bool ARM && (ARCH_CLPS7500 || ARCH_RPC || ARCH_SHARK) - select IDE_GENERIC + tristate "ARM IDE support" + depends on ARM && (ARCH_CLPS7500 || ARCH_RPC || ARCH_SHARK) + default y config BLK_DEV_IDE_ICSIDE tristate "ICS IDE interface support" @@ -876,10 +888,9 @@ config BLK_DEV_IDE_BAST Simtec BAST or the Thorcom VR1000 config ETRAX_IDE - bool "ETRAX IDE support" + tristate "ETRAX IDE support" depends on CRIS && BROKEN select BLK_DEV_IDEDMA - select IDE_GENERIC help Enables the ETRAX IDE driver. @@ -911,17 +922,15 @@ config ETRAX_IDE_G27_RESET endchoice config IDE_H8300 - bool "H8300 IDE support" + tristate "H8300 IDE support" depends on H8300 - select IDE_GENERIC default y help Enables the H8300 IDE driver. config BLK_DEV_GAYLE - bool "Amiga Gayle IDE interface support" + tristate "Amiga Gayle IDE interface support" depends on AMIGA - select IDE_GENERIC help This is the IDE driver for the Amiga Gayle IDE interface. It supports both the `A1200 style' and `A4000 style' of the Gayle IDE interface, @@ -951,9 +960,8 @@ config BLK_DEV_IDEDOUBLER runtime using the "ide=doubler" kernel boot parameter. config BLK_DEV_BUDDHA - bool "Buddha/Catweasel/X-Surf IDE interface support (EXPERIMENTAL)" + tristate "Buddha/Catweasel/X-Surf IDE interface support (EXPERIMENTAL)" depends on ZORRO && EXPERIMENTAL - select IDE_GENERIC help This is the IDE driver for the IDE interfaces on the Buddha, Catweasel and X-Surf expansion boards. It supports up to two interfaces @@ -964,9 +972,8 @@ config BLK_DEV_BUDDHA to one of its IDE interfaces. config BLK_DEV_FALCON_IDE - bool "Falcon IDE interface support" + tristate "Falcon IDE interface support" depends on ATARI - select IDE_GENERIC help This is the IDE driver for the builtin IDE interface on the Atari Falcon. Say Y if you have a Falcon and want to use IDE devices (hard @@ -974,9 +981,8 @@ config BLK_DEV_FALCON_IDE interface. config BLK_DEV_MAC_IDE - bool "Macintosh Quadra/Powerbook IDE interface support" + tristate "Macintosh Quadra/Powerbook IDE interface support" depends on MAC - select IDE_GENERIC help This is the IDE driver for the builtin IDE interface on some m68k Macintosh models. It supports both the `Quadra style' (used in @@ -988,18 +994,16 @@ config BLK_DEV_MAC_IDE builtin IDE interface. config BLK_DEV_Q40IDE - bool "Q40/Q60 IDE interface support" + tristate "Q40/Q60 IDE interface support" depends on Q40 - select IDE_GENERIC help Enable the on-board IDE controller in the Q40/Q60. This should normally be on; disable it only if you are running a custom hard drive subsystem through an expansion card. config BLK_DEV_MPC8xx_IDE - bool "MPC8xx IDE support" + tristate "MPC8xx IDE support" depends on 8xx && (LWMON || IVMS8 || IVML24 || TQM8xxL) && IDE=y && BLK_DEV_IDE=y && !PPC_MERGE - select IDE_GENERIC help This option provides support for IDE on Motorola MPC8xx Systems. Please see 'Type of MPC8xx IDE interface' for details. diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index b181fc67205..0d2da89d15c 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -7,41 +7,37 @@ # Note : at this point, these files are compiled on all systems. # In the future, some of these should be built conditionally. # -# First come modules that register themselves with the core +# link order is important here EXTRA_CFLAGS += -Idrivers/ide -obj-$(CONFIG_BLK_DEV_IDE) += pci/ - ide-core-y += ide.o ide-io.o ide-iops.o ide-lib.o ide-probe.o ide-taskfile.o -ide-core-$(CONFIG_BLK_DEV_CMD640) += pci/cmd640.o - -# Core IDE code - must come before legacy +# core IDE code ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o ide-core-$(CONFIG_IDE_PROC_FS) += ide-proc.o -ide-core-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o ide-core-$(CONFIG_BLK_DEV_IDEACPI) += ide-acpi.o -# built-in only drivers from arm/ -ide-core-$(CONFIG_IDE_ARM) += arm/ide_arm.o +obj-$(CONFIG_BLK_DEV_IDE) += ide-core.o -# built-in only drivers from legacy/ -ide-core-$(CONFIG_BLK_DEV_BUDDHA) += legacy/buddha.o -ide-core-$(CONFIG_BLK_DEV_FALCON_IDE) += legacy/falconide.o -ide-core-$(CONFIG_BLK_DEV_GAYLE) += legacy/gayle.o -ide-core-$(CONFIG_BLK_DEV_MAC_IDE) += legacy/macide.o -ide-core-$(CONFIG_BLK_DEV_Q40IDE) += legacy/q40ide.o +ifeq ($(CONFIG_IDE_ARM), y) + ide-arm-core-y += arm/ide_arm.o + obj-y += ide-arm-core.o +endif -# built-in only drivers from ppc/ -ide-core-$(CONFIG_BLK_DEV_MPC8xx_IDE) += ppc/mpc8xx.o -ide-core-$(CONFIG_BLK_DEV_IDE_PMAC) += ppc/pmac.o +obj-$(CONFIG_BLK_DEV_IDE) += legacy/ pci/ -# built-in only drivers from h8300/ -ide-core-$(CONFIG_IDE_H8300) += h8300/ide-h8300.o +obj-$(CONFIG_IDEPCI_PCIBUS_ORDER) += ide-scan-pci.o -obj-$(CONFIG_BLK_DEV_IDE) += ide-core.o +ifeq ($(CONFIG_BLK_DEV_CMD640), y) + cmd640-core-y += pci/cmd640.o + obj-y += cmd640-core.o +endif + +obj-$(CONFIG_BLK_DEV_IDE) += cris/ ppc/ +obj-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o +obj-$(CONFIG_IDE_H8300) += h8300/ obj-$(CONFIG_IDE_GENERIC) += ide-generic.o obj-$(CONFIG_BLK_DEV_IDEDISK) += ide-disk.o @@ -49,6 +45,20 @@ obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd.o obj-$(CONFIG_BLK_DEV_IDETAPE) += ide-tape.o obj-$(CONFIG_BLK_DEV_IDEFLOPPY) += ide-floppy.o -obj-$(CONFIG_BLK_DEV_IDE) += legacy/ arm/ mips/ -obj-$(CONFIG_BLK_DEV_HD) += legacy/ -obj-$(CONFIG_ETRAX_IDE) += cris/ +ifeq ($(CONFIG_BLK_DEV_IDECS), y) + ide-cs-core-y += legacy/ide-cs.o + obj-y += ide-cs-core.o +endif + +ifeq ($(CONFIG_BLK_DEV_PLATFORM), y) + ide-platform-core-y += legacy/ide_platform.o + obj-y += ide-platform-core.o +endif + +obj-$(CONFIG_BLK_DEV_IDE) += arm/ mips/ + +# old hd driver must be last +ifeq ($(CONFIG_BLK_DEV_HD), y) + hd-core-y += legacy/hd.o + obj-y += hd-core.o +endif diff --git a/drivers/ide/arm/Makefile b/drivers/ide/arm/Makefile index 6a78f0755f2..5f63ad21686 100644 --- a/drivers/ide/arm/Makefile +++ b/drivers/ide/arm/Makefile @@ -3,4 +3,8 @@ obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o obj-$(CONFIG_BLK_DEV_IDE_BAST) += bast-ide.o +ifeq ($(CONFIG_IDE_ARM), m) + obj-m += ide_arm.o +endif + EXTRA_CFLAGS := -Idrivers/ide diff --git a/drivers/ide/arm/bast-ide.c b/drivers/ide/arm/bast-ide.c index 48db6167bb9..45bf9c825f2 100644 --- a/drivers/ide/arm/bast-ide.c +++ b/drivers/ide/arm/bast-ide.c @@ -45,7 +45,7 @@ bastide_register(unsigned int base, unsigned int aux, int irq, hw.io_ports[IDE_CONTROL_OFFSET] = aux + (6 * 0x20); hw.irq = irq; - ide_register_hw(&hw, NULL, 0, hwif); + ide_register_hw(&hw, NULL, hwif); return 0; } diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 673402f4a29..8a5c7205b77 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -287,26 +287,10 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode) ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data); } -static void icside_dma_host_off(ide_drive_t *drive) +static void icside_dma_host_set(ide_drive_t *drive, int on) { } -static void icside_dma_off_quietly(ide_drive_t *drive) -{ - drive->using_dma = 0; -} - -static void icside_dma_host_on(ide_drive_t *drive) -{ -} - -static int icside_dma_on(ide_drive_t *drive) -{ - drive->using_dma = 1; - - return 0; -} - static int icside_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); @@ -422,10 +406,7 @@ static void icside_dma_init(ide_hwif_t *hwif) hwif->dmatable_dma = 0; hwif->set_dma_mode = icside_set_dma_mode; - hwif->dma_host_off = icside_dma_host_off; - hwif->dma_off_quietly = icside_dma_off_quietly; - hwif->dma_host_on = icside_dma_host_on; - hwif->ide_dma_on = icside_dma_on; + hwif->dma_host_set = icside_dma_host_set; hwif->dma_setup = icside_dma_setup; hwif->dma_exec_cmd = icside_dma_exec_cmd; hwif->dma_start = icside_dma_start; diff --git a/drivers/ide/arm/ide_arm.c b/drivers/ide/arm/ide_arm.c index 8957cbadf5c..60f2497542c 100644 --- a/drivers/ide/arm/ide_arm.c +++ b/drivers/ide/arm/ide_arm.c @@ -24,12 +24,25 @@ # define IDE_ARM_IRQ IRQ_HARDDISK #endif -void __init ide_arm_init(void) +static int __init ide_arm_init(void) { + ide_hwif_t *hwif; hw_regs_t hw; + u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; memset(&hw, 0, sizeof(hw)); ide_std_init_ports(&hw, IDE_ARM_IO, IDE_ARM_IO + 0x206); hw.irq = IDE_ARM_IRQ; - ide_register_hw(&hw, NULL, 1, NULL); + + hwif = ide_find_port(hw.io_ports[IDE_DATA_OFFSET]); + if (hwif) { + ide_init_port_hw(hwif, &hw); + idx[0] = hwif->index; + + ide_device_add(idx); + } + + return 0; } + +module_init(ide_arm_init); diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index 0775a3afef4..e6b56d1d48f 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -13,26 +13,18 @@ #include <asm/ecard.h> -static ide_hwif_t * -rapide_locate_hwif(void __iomem *base, void __iomem *ctrl, unsigned int sz, int irq) +static void rapide_setup_ports(hw_regs_t *hw, void __iomem *base, + void __iomem *ctrl, unsigned int sz, int irq) { unsigned long port = (unsigned long)base; - ide_hwif_t *hwif = ide_find_port(port); int i; - if (hwif == NULL) - goto out; - for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { - hwif->io_ports[i] = port; + hw->io_ports[i] = port; port += sz; } - hwif->io_ports[IDE_CONTROL_OFFSET] = (unsigned long)ctrl; - hwif->irq = irq; - hwif->mmio = 1; - default_hwif_mmiops(hwif); -out: - return hwif; + hw->io_ports[IDE_CONTROL_OFFSET] = (unsigned long)ctrl; + hw->irq = irq; } static int __devinit @@ -42,6 +34,7 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) void __iomem *base; int ret; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw; ret = ecard_request_resources(ec); if (ret) @@ -53,11 +46,17 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) goto release; } - hwif = rapide_locate_hwif(base, base + 0x818, 1 << 6, ec->irq); + hwif = ide_find_port((unsigned long)base); if (hwif) { - hwif->hwif_data = base; - hwif->gendev.parent = &ec->dev; - hwif->noprobe = 0; + memset(&hw, 0, sizeof(hw)); + rapide_setup_ports(&hw, base, base + 0x818, 1 << 6, ec->irq); + hw.chipset = ide_generic; + hw.dev = &ec->dev; + + ide_init_port_hw(hwif, &hw); + + hwif->mmio = 1; + default_hwif_mmiops(hwif); idx[0] = hwif->index; diff --git a/drivers/ide/cris/Makefile b/drivers/ide/cris/Makefile index 6176e8d6b2e..20b95960531 100644 --- a/drivers/ide/cris/Makefile +++ b/drivers/ide/cris/Makefile @@ -1,3 +1,3 @@ EXTRA_CFLAGS += -Idrivers/ide -obj-y += ide-cris.o +obj-$(CONFIG_IDE_ETRAX) += ide-cris.o diff --git a/drivers/ide/cris/ide-cris.c b/drivers/ide/cris/ide-cris.c index 325e608d9e6..8c3294c4d23 100644 --- a/drivers/ide/cris/ide-cris.c +++ b/drivers/ide/cris/ide-cris.c @@ -673,9 +673,8 @@ static void cris_ide_input_data (ide_drive_t *drive, void *, unsigned int); static void cris_ide_output_data (ide_drive_t *drive, void *, unsigned int); static void cris_atapi_input_bytes(ide_drive_t *drive, void *, unsigned int); static void cris_atapi_output_bytes(ide_drive_t *drive, void *, unsigned int); -static int cris_dma_on (ide_drive_t *drive); -static void cris_dma_off(ide_drive_t *drive) +static void cris_dma_host_set(ide_drive_t *drive, int on) { } @@ -755,13 +754,11 @@ static void cris_set_dma_mode(ide_drive_t *drive, const u8 speed) cris_ide_set_speed(TYPE_DMA, 0, strobe, hold); } -void __init -init_e100_ide (void) +static int __init init_e100_ide(void) { hw_regs_t hw; - int ide_offsets[IDE_NR_PORTS]; - int h; - int i; + int ide_offsets[IDE_NR_PORTS], h, i; + u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; printk("ide: ETRAX FS built-in ATA DMA controller\n"); @@ -778,9 +775,11 @@ init_e100_ide (void) ide_offsets, 0, 0, cris_ide_ack_intr, ide_default_irq(0)); - ide_register_hw(&hw, NULL, 1, &hwif); + hwif = ide_find_port(hw.io_ports[IDE_DATA_OFFSET]); if (hwif == NULL) continue; + ide_init_port_data(hwif, hwif->index); + ide_init_port_hw(hwif, &hw); hwif->mmio = 1; hwif->chipset = ide_etrax100; hwif->set_pio_mode = &cris_set_pio_mode; @@ -789,6 +788,7 @@ init_e100_ide (void) hwif->ata_output_data = &cris_ide_output_data; hwif->atapi_input_bytes = &cris_atapi_input_bytes; hwif->atapi_output_bytes = &cris_atapi_output_bytes; + hwif->dma_host_set = &cris_dma_host_set; hwif->ide_dma_end = &cris_dma_end; hwif->dma_setup = &cris_dma_setup; hwif->dma_exec_cmd = &cris_dma_exec_cmd; @@ -799,9 +799,6 @@ init_e100_ide (void) hwif->OUTBSYNC = &cris_ide_outbsync; hwif->INB = &cris_ide_inb; hwif->INW = &cris_ide_inw; - hwif->dma_host_off = &cris_dma_off; - hwif->dma_host_on = &cris_dma_on; - hwif->dma_off_quietly = &cris_dma_off; hwif->cbl = ATA_CBL_PATA40; hwif->host_flags |= IDE_HFLAG_NO_ATAPI_DMA; hwif->pio_mask = ATA_PIO4, @@ -809,6 +806,8 @@ init_e100_ide (void) hwif->drives[1].autotune = 1; hwif->ultra_mask = cris_ultra_mask; hwif->mwdma_mask = 0x07; /* Multiword DMA 0-2 */ + + idx[h] = hwif->index; } /* Reset pulse */ @@ -821,14 +820,12 @@ init_e100_ide (void) cris_ide_set_speed(TYPE_PIO, ATA_PIO4_SETUP, ATA_PIO4_STROBE, ATA_PIO4_HOLD); cris_ide_set_speed(TYPE_DMA, 0, ATA_DMA2_STROBE, ATA_DMA2_HOLD); cris_ide_set_speed(TYPE_UDMA, ATA_UDMA2_CYC, ATA_UDMA2_DVS, 0); -} -static int cris_dma_on (ide_drive_t *drive) -{ + ide_device_add(idx); + return 0; } - static cris_dma_descr_type mydescr __attribute__ ((__aligned__(16))); /* @@ -1060,3 +1057,5 @@ static void cris_dma_start(ide_drive_t *drive) LED_DISK_READ(1); } } + +module_init(init_e100_ide); diff --git a/drivers/ide/h8300/Makefile b/drivers/ide/h8300/Makefile new file mode 100644 index 00000000000..5eba16f423f --- /dev/null +++ b/drivers/ide/h8300/Makefile @@ -0,0 +1,2 @@ + +obj-$(CONFIG_IDE_H8300) += ide-h8300.o diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 4a49b5c59ac..4f6d0191cf6 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -84,11 +84,12 @@ static inline void hwif_setup(ide_hwif_t *hwif) hwif->INSL = NULL; } -void __init h8300_ide_init(void) +static int __init h8300_ide_init(void) { hw_regs_t hw; ide_hwif_t *hwif; - int idx; + int index; + u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!request_region(CONFIG_H8300_IDE_BASE, H8300_IDE_GAP*8, "ide-h8300")) goto out_busy; @@ -100,16 +101,28 @@ void __init h8300_ide_init(void) hw_setup(&hw); /* register if */ - idx = ide_register_hw(&hw, NULL, 1, &hwif); - if (idx == -1) { + hwif = ide_find_port(hw.io_ports[IDE_DATA_OFFSET]); + if (hwif == NULL) { printk(KERN_ERR "ide-h8300: IDE I/F register failed\n"); - return; + return -ENOENT; } + index = hwif->index; + ide_init_port_data(hwif, index); + ide_init_port_hw(hwif, &hw); hwif_setup(hwif); - printk(KERN_INFO "ide%d: H8/300 generic IDE interface\n", idx); - return; + printk(KERN_INFO "ide%d: H8/300 generic IDE interface\n", index); + + idx[0] = index; + + ide_device_add(idx); + + return 0; out_busy: printk(KERN_ERR "ide-h8300: IDE I/F resource already used.\n"); + + return -EBUSY; } + +module_init(h8300_ide_init); diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index e0bb0cfa7bd..e888fc35b27 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -386,7 +386,7 @@ static int taskfile_load_raw(ide_drive_t *drive, /* convert gtf to IDE Taskfile */ memcpy(&args.tf_array[7], >f->tfa, 7); - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; + args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; if (ide_noacpitfs) { DEBPRINT("_GTF execution disabled\n"); diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index d8fdd865dea..717e114ced5 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -201,7 +201,7 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq, memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_NO_SELECT_MASK; /* FIXME? */ - task.tf_flags |= (IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE); + task.tf_flags |= (IDE_TFLAG_TF | IDE_TFLAG_DEVICE); if (drive->select.b.lba) { if (lba48) { @@ -219,13 +219,8 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq, tf->lbal = (u8) block; tf->lbam = (u8)(block >> 8); tf->lbah = (u8)(block >> 16); -#ifdef DEBUG - printk("%s: 0x%02x%02x 0x%02x%02x%02x%02x%02x%02x\n", - drive->name, tf->hob_nsect, tf->nsect, - tf->hob_lbah, tf->hob_lbam, tf->hob_lbal, - tf->lbah, tf->lbam, tf->lbal); -#endif - task.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_OUT_HOB); + + task.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_HOB); } else { tf->nsect = nsectors & 0xff; tf->lbal = block; @@ -319,9 +314,9 @@ static u64 idedisk_read_native_max_address(ide_drive_t *drive, int lba48) else tf->command = WIN_READ_NATIVE_MAX; tf->device = ATA_LBA; - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; + args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; if (lba48) - args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_OUT_HOB); + args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_HOB); /* submit command request */ ide_no_data_taskfile(drive, &args); @@ -358,9 +353,9 @@ static u64 idedisk_set_max_address(ide_drive_t *drive, u64 addr_req, int lba48) tf->command = WIN_SET_MAX; } tf->device |= ATA_LBA; - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; + args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; if (lba48) - args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_OUT_HOB); + args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_HOB); /* submit command request */ ide_no_data_taskfile(drive, &args); /* if OK, compute maximum address value */ @@ -500,7 +495,7 @@ static int smart_enable(ide_drive_t *drive) tf->lbam = SMART_LCYL_PASS; tf->lbah = SMART_HCYL_PASS; tf->command = WIN_SMART; - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; + args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; return ide_no_data_taskfile(drive, &args); } @@ -515,7 +510,7 @@ static int get_smart_data(ide_drive_t *drive, u8 *buf, u8 sub_cmd) tf->lbam = SMART_LCYL_PASS; tf->lbah = SMART_HCYL_PASS; tf->command = WIN_SMART; - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; + args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; args.data_phase = TASKFILE_IN; (void) smart_enable(drive); return ide_raw_taskfile(drive, &args, buf, 1); @@ -625,8 +620,10 @@ static int set_multcount(ide_drive_t *drive, int arg) if (drive->special.b.set_multmode) return -EBUSY; + ide_init_drive_cmd (&rq); - rq.cmd_type = REQ_TYPE_ATA_CMD; + rq.cmd_type = REQ_TYPE_ATA_TASKFILE; + drive->mult_req = arg; drive->special.b.set_multmode = 1; (void) ide_do_drive_cmd (drive, &rq, ide_wait); @@ -694,7 +691,7 @@ static int write_cache(ide_drive_t *drive, int arg) args.tf.feature = arg ? SETFEATURES_EN_WCACHE : SETFEATURES_DIS_WCACHE; args.tf.command = WIN_SETFEATURES; - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; + args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; err = ide_no_data_taskfile(drive, &args); if (err == 0) drive->wcache = arg; @@ -714,7 +711,7 @@ static int do_idedisk_flushcache (ide_drive_t *drive) args.tf.command = WIN_FLUSH_CACHE_EXT; else args.tf.command = WIN_FLUSH_CACHE; - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; + args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; return ide_no_data_taskfile(drive, &args); } @@ -729,7 +726,7 @@ static int set_acoustic (ide_drive_t *drive, int arg) args.tf.feature = arg ? SETFEATURES_EN_AAM : SETFEATURES_DIS_AAM; args.tf.nsect = arg; args.tf.command = WIN_SETFEATURES; - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; + args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; ide_no_data_taskfile(drive, &args); drive->acoustic = arg; return 0; @@ -766,7 +763,6 @@ static void idedisk_add_settings(ide_drive_t *drive) ide_add_setting(drive, "bios_head", SETTING_RW, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); ide_add_setting(drive, "bios_sect", SETTING_RW, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); ide_add_setting(drive, "address", SETTING_RW, TYPE_BYTE, 0, 2, 1, 1, &drive->addressing, set_lba_addressing); - ide_add_setting(drive, "bswap", SETTING_READ, TYPE_BYTE, 0, 1, 1, 1, &drive->bswap, NULL); ide_add_setting(drive, "multcount", SETTING_RW, TYPE_BYTE, 0, id->max_multsect, 1, 1, &drive->mult_count, set_multcount); ide_add_setting(drive, "nowerr", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->nowerr, set_nowerr); ide_add_setting(drive, "lun", SETTING_RW, TYPE_INT, 0, 7, 1, 1, &drive->lun, NULL); @@ -975,6 +971,17 @@ static ide_driver_t idedisk_driver = { #endif }; +static int idedisk_set_doorlock(ide_drive_t *drive, int on) +{ + ide_task_t task; + + memset(&task, 0, sizeof(task)); + task.tf.command = on ? WIN_DOORLOCK : WIN_DOORUNLOCK; + task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; + + return ide_no_data_taskfile(drive, &task); +} + static int idedisk_open(struct inode *inode, struct file *filp) { struct gendisk *disk = inode->i_bdev->bd_disk; @@ -989,17 +996,13 @@ static int idedisk_open(struct inode *inode, struct file *filp) idkp->openers++; if (drive->removable && idkp->openers == 1) { - ide_task_t args; - memset(&args, 0, sizeof(ide_task_t)); - args.tf.command = WIN_DOORLOCK; - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; check_disk_change(inode->i_bdev); /* * Ignore the return code from door_lock, * since the open() has already succeeded, * and the door_lock is irrelevant at this point. */ - if (drive->doorlocking && ide_no_data_taskfile(drive, &args)) + if (drive->doorlocking && idedisk_set_doorlock(drive, 1)) drive->doorlocking = 0; } return 0; @@ -1015,11 +1018,7 @@ static int idedisk_release(struct inode *inode, struct file *filp) ide_cacheflush_p(drive); if (drive->removable && idkp->openers == 1) { - ide_task_t args; - memset(&args, 0, sizeof(ide_task_t)); - args.tf.command = WIN_DOORUNLOCK; - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; - if (drive->doorlocking && ide_no_data_taskfile(drive, &args)) + if (drive->doorlocking && idedisk_set_doorlock(drive, 0)) drive->doorlocking = 0; } diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 18c78ad2b31..5bf32038dc4 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -153,13 +153,7 @@ ide_startstop_t ide_dma_intr (ide_drive_t *drive) if (!dma_stat) { struct request *rq = HWGROUP(drive)->rq; - if (rq->rq_disk) { - ide_driver_t *drv; - - drv = *(ide_driver_t **)rq->rq_disk->private_data; - drv->end_request(drive, 1, rq->nr_sectors); - } else - ide_end_request(drive, 1, rq->nr_sectors); + task_end_request(drive, rq, stat); return ide_stopped; } printk(KERN_ERR "%s: dma_intr: bad DMA status (dma_stat=%x)\n", @@ -408,23 +402,29 @@ static int dma_timer_expiry (ide_drive_t *drive) } /** - * ide_dma_host_off - Generic DMA kill + * ide_dma_host_set - Enable/disable DMA on a host * @drive: drive to control * - * Perform the generic IDE controller DMA off operation. This - * works for most IDE bus mastering controllers + * Enable/disable DMA on an IDE controller following generic + * bus-mastering IDE controller behaviour. */ -void ide_dma_host_off(ide_drive_t *drive) +void ide_dma_host_set(ide_drive_t *drive, int on) { ide_hwif_t *hwif = HWIF(drive); u8 unit = (drive->select.b.unit & 0x01); u8 dma_stat = hwif->INB(hwif->dma_status); - hwif->OUTB((dma_stat & ~(1<<(5+unit))), hwif->dma_status); + if (on) + dma_stat |= (1 << (5 + unit)); + else + dma_stat &= ~(1 << (5 + unit)); + + hwif->OUTB(dma_stat, hwif->dma_status); } -EXPORT_SYMBOL(ide_dma_host_off); +EXPORT_SYMBOL_GPL(ide_dma_host_set); +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ /** * ide_dma_off_quietly - Generic DMA kill @@ -438,11 +438,10 @@ void ide_dma_off_quietly(ide_drive_t *drive) drive->using_dma = 0; ide_toggle_bounce(drive, 0); - drive->hwif->dma_host_off(drive); + drive->hwif->dma_host_set(drive, 0); } EXPORT_SYMBOL(ide_dma_off_quietly); -#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ /** * ide_dma_off - disable DMA on a device @@ -455,52 +454,29 @@ EXPORT_SYMBOL(ide_dma_off_quietly); void ide_dma_off(ide_drive_t *drive) { printk(KERN_INFO "%s: DMA disabled\n", drive->name); - drive->hwif->dma_off_quietly(drive); + ide_dma_off_quietly(drive); } EXPORT_SYMBOL(ide_dma_off); -#ifdef CONFIG_BLK_DEV_IDEDMA_PCI -/** - * ide_dma_host_on - Enable DMA on a host - * @drive: drive to enable for DMA - * - * Enable DMA on an IDE controller following generic bus mastering - * IDE controller behaviour - */ - -void ide_dma_host_on(ide_drive_t *drive) -{ - if (drive->using_dma) { - ide_hwif_t *hwif = HWIF(drive); - u8 unit = (drive->select.b.unit & 0x01); - u8 dma_stat = hwif->INB(hwif->dma_status); - - hwif->OUTB((dma_stat|(1<<(5+unit))), hwif->dma_status); - } -} - -EXPORT_SYMBOL(ide_dma_host_on); - /** - * __ide_dma_on - Enable DMA on a device + * ide_dma_on - Enable DMA on a device * @drive: drive to enable DMA on * * Enable IDE DMA for a device on this IDE controller. */ - -int __ide_dma_on (ide_drive_t *drive) + +void ide_dma_on(ide_drive_t *drive) { drive->using_dma = 1; ide_toggle_bounce(drive, 1); - drive->hwif->dma_host_on(drive); - - return 0; + drive->hwif->dma_host_set(drive, 1); } -EXPORT_SYMBOL(__ide_dma_on); +EXPORT_SYMBOL(ide_dma_on); +#ifdef CONFIG_BLK_DEV_IDEDMA_PCI /** * ide_dma_setup - begin a DMA phase * @drive: target device @@ -755,6 +731,7 @@ EXPORT_SYMBOL_GPL(ide_find_dma_mode); static int ide_tune_dma(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; u8 speed; if (noautodma || drive->nodma || (drive->id->capability & 1) == 0) @@ -767,15 +744,21 @@ static int ide_tune_dma(ide_drive_t *drive) if (ide_id_dma_bug(drive)) return 0; - if (drive->hwif->host_flags & IDE_HFLAG_TRUST_BIOS_FOR_DMA) + if (hwif->host_flags & IDE_HFLAG_TRUST_BIOS_FOR_DMA) return config_drive_for_dma(drive); speed = ide_max_dma_mode(drive); - if (!speed) - return 0; + if (!speed) { + /* is this really correct/needed? */ + if ((hwif->host_flags & IDE_HFLAG_CY82C693) && + ide_dma_good_drive(drive)) + return 1; + else + return 0; + } - if (drive->hwif->host_flags & IDE_HFLAG_NO_SET_MODE) + if (hwif->host_flags & IDE_HFLAG_NO_SET_MODE) return 0; if (ide_set_dma_mode(drive, speed)) @@ -820,7 +803,6 @@ err_out: int ide_set_dma(ide_drive_t *drive) { - ide_hwif_t *hwif = drive->hwif; int rc; /* @@ -829,13 +811,15 @@ int ide_set_dma(ide_drive_t *drive) * things, if not checked and cleared. * PARANOIA!!! */ - hwif->dma_off_quietly(drive); + ide_dma_off_quietly(drive); rc = ide_dma_check(drive); if (rc) return rc; - return hwif->ide_dma_on(drive); + ide_dma_on(drive); + + return 0; } #ifdef CONFIG_BLK_DEV_IDEDMA_PCI @@ -972,14 +956,8 @@ void ide_setup_dma(ide_hwif_t *hwif, unsigned long base, unsigned num_ports) if (!(hwif->dma_prdtable)) hwif->dma_prdtable = (hwif->dma_base + 4); - if (!hwif->dma_off_quietly) - hwif->dma_off_quietly = &ide_dma_off_quietly; - if (!hwif->dma_host_off) - hwif->dma_host_off = &ide_dma_host_off; - if (!hwif->ide_dma_on) - hwif->ide_dma_on = &__ide_dma_on; - if (!hwif->dma_host_on) - hwif->dma_host_on = &ide_dma_host_on; + if (!hwif->dma_host_set) + hwif->dma_host_set = &ide_dma_host_set; if (!hwif->dma_setup) hwif->dma_setup = &ide_dma_setup; if (!hwif->dma_exec_cmd) diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index 0f72b98d727..bb30c29f6ec 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -14,10 +14,16 @@ static int __init ide_generic_init(void) { + u8 idx[MAX_HWIFS]; + int i; + if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) ide_get_lock(NULL, NULL); /* for atari only */ - (void)ideprobe_init(); + for (i = 0; i < MAX_HWIFS; i++) + idx[i] = ide_hwifs[i].present ? 0xff : i; + + ide_device_add_all(idx); if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) ide_release_lock(); /* for atari only */ diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 2711b5a6962..6f8f544392a 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -75,7 +75,7 @@ static int __ide_end_request(ide_drive_t *drive, struct request *rq, */ if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) { drive->state = 0; - HWGROUP(drive)->hwif->ide_dma_on(drive); + ide_dma_on(drive); } if (!end_that_request_chunk(rq, uptodate, nr_bytes)) { @@ -219,7 +219,7 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request * * we could be smarter and check for current xfer_speed * in struct drive etc... */ - if (drive->hwif->ide_dma_on == NULL) + if (drive->hwif->dma_host_set == NULL) break; /* * TODO: respect ->using_dma setting @@ -231,7 +231,7 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request * return ide_stopped; out_do_tf: - args->tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; + args->tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; args->data_phase = TASKFILE_NO_DATA; return do_rw_taskfile(drive, args); } @@ -354,7 +354,6 @@ void ide_tf_read(ide_drive_t *drive, ide_task_t *task) void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) { - ide_hwif_t *hwif = HWIF(drive); unsigned long flags; struct request *rq; @@ -362,17 +361,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) rq = HWGROUP(drive)->rq; spin_unlock_irqrestore(&ide_lock, flags); - if (rq->cmd_type == REQ_TYPE_ATA_CMD) { - u8 *args = (u8 *) rq->buffer; - if (rq->errors == 0) - rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); - - if (args) { - args[0] = stat; - args[1] = err; - args[2] = hwif->INB(IDE_NSECTOR_REG); - } - } else if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { + if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { ide_task_t *args = (ide_task_t *) rq->special; if (rq->errors == 0) rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); @@ -383,10 +372,6 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) tf->error = err; tf->status = stat; - args->tf_flags |= (IDE_TFLAG_IN_TF|IDE_TFLAG_IN_DEVICE); - if (args->tf_flags & IDE_TFLAG_LBA48) - args->tf_flags |= IDE_TFLAG_IN_HOB; - ide_tf_read(drive, args); } } else if (blk_pm_request(rq)) { @@ -626,42 +611,6 @@ ide_startstop_t ide_abort(ide_drive_t *drive, const char *msg) return __ide_abort(drive, rq); } -/** - * drive_cmd_intr - drive command completion interrupt - * @drive: drive the completion interrupt occurred on - * - * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD. - * We do any necessary data reading and then wait for the drive to - * go non busy. At that point we may read the error data and complete - * the request - */ - -static ide_startstop_t drive_cmd_intr (ide_drive_t *drive) -{ - struct request *rq = HWGROUP(drive)->rq; - ide_hwif_t *hwif = HWIF(drive); - u8 *args = (u8 *) rq->buffer; - u8 stat = hwif->INB(IDE_STATUS_REG); - int retries = 10; - - local_irq_enable_in_hardirq(); - if (rq->cmd_type == REQ_TYPE_ATA_CMD && - (stat & DRQ_STAT) && args && args[3]) { - u8 io_32bit = drive->io_32bit; - drive->io_32bit = 0; - hwif->ata_input_data(drive, &args[4], args[3] * SECTOR_WORDS); - drive->io_32bit = io_32bit; - while (((stat = hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) && retries--) - udelay(100); - } - - if (!OK_STAT(stat, READY_STAT, BAD_STAT)) - return ide_error(drive, "drive_cmd", stat); - /* calls ide_end_drive_cmd */ - ide_end_drive_cmd(drive, stat, hwif->INB(IDE_ERROR_REG)); - return ide_stopped; -} - static void ide_tf_set_specify_cmd(ide_drive_t *drive, struct ide_taskfile *tf) { tf->nsect = drive->sect; @@ -710,7 +659,7 @@ static ide_startstop_t ide_disk_special(ide_drive_t *drive) return ide_stopped; } - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE | + args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE | IDE_TFLAG_CUSTOM_HANDLER; do_rw_taskfile(drive, &args); @@ -787,7 +736,7 @@ static ide_startstop_t do_special (ide_drive_t *drive) if (hwif->host_flags & IDE_HFLAG_SET_PIO_MODE_KEEP_DMA) { if (keep_dma) - hwif->ide_dma_on(drive); + ide_dma_on(drive); } } @@ -847,16 +796,9 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq) { ide_hwif_t *hwif = HWIF(drive); - u8 *args = rq->buffer; - ide_task_t ltask; - struct ide_taskfile *tf = <ask.tf; - - if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { - ide_task_t *task = rq->special; - - if (task == NULL) - goto done; + ide_task_t *task = rq->special; + if (task) { hwif->data_phase = task->data_phase; switch (hwif->data_phase) { @@ -873,33 +815,6 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, return do_rw_taskfile(drive, task); } - if (args == NULL) - goto done; - - memset(<ask, 0, sizeof(ltask)); - if (rq->cmd_type == REQ_TYPE_ATA_CMD) { -#ifdef DEBUG - printk("%s: DRIVE_CMD\n", drive->name); -#endif - tf->feature = args[2]; - if (args[0] == WIN_SMART) { - tf->nsect = args[3]; - tf->lbal = args[1]; - tf->lbam = 0x4f; - tf->lbah = 0xc2; - ltask.tf_flags = IDE_TFLAG_OUT_TF; - } else { - tf->nsect = args[1]; - ltask.tf_flags = IDE_TFLAG_OUT_FEATURE | - IDE_TFLAG_OUT_NSECT; - } - } - tf->command = args[0]; - ide_tf_load(drive, <ask); - ide_execute_command(drive, args[0], &drive_cmd_intr, WAIT_WORSTCASE, NULL); - return ide_started; - -done: /* * NULL is actually a valid way of waiting for * all current requests to be flushed from the queue. @@ -939,8 +854,7 @@ static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) if (rc) printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name); SELECT_DRIVE(drive); - if (IDE_CONTROL_REG) - HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG); + ide_set_irq(drive, 1); rc = ide_wait_not_busy(HWIF(drive), 100000); if (rc) printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name); @@ -1004,8 +918,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) if (drive->current_speed == 0xff) ide_config_drive_speed(drive, drive->desired_speed); - if (rq->cmd_type == REQ_TYPE_ATA_CMD || - rq->cmd_type == REQ_TYPE_ATA_TASKFILE) + if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) return execute_drive_cmd(drive, rq); else if (blk_pm_request(rq)) { struct request_pm_state *pm = rq->data; @@ -1213,15 +1126,13 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) } again: hwif = HWIF(drive); - if (hwgroup->hwif->sharing_irq && - hwif != hwgroup->hwif && - hwif->io_ports[IDE_CONTROL_OFFSET]) { + if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif) { /* * set nIEN for previous hwif, drives in the * quirk_list may not like intr setups/cleanups */ if (drive->quirk_list != 1) - hwif->OUTB(drive->ctl | 2, IDE_CONTROL_REG); + ide_set_irq(drive, 0); } hwgroup->hwif = hwif; hwgroup->drive = drive; @@ -1334,7 +1245,7 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) */ drive->retry_pio++; drive->state = DMA_PIO_RETRY; - hwif->dma_off_quietly(drive); + ide_dma_off_quietly(drive); /* * un-busy drive etc (hwgroup->busy is cleared on return) and @@ -1679,7 +1590,6 @@ irqreturn_t ide_intr (int irq, void *dev_id) void ide_init_drive_cmd (struct request *rq) { memset(rq, 0, sizeof(*rq)); - rq->cmd_type = REQ_TYPE_ATA_CMD; rq->ref_count = 1; } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index c97c0719ddf..e2a7e95e163 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -619,7 +619,7 @@ no_80w: int ide_ata66_check (ide_drive_t *drive, ide_task_t *args) { if (args->tf.command == WIN_SETFEATURES && - args->tf.lbal > XFER_UDMA_2 && + args->tf.nsect > XFER_UDMA_2 && args->tf.feature == SETFEATURES_XFER) { if (eighty_ninty_three(drive) == 0) { printk(KERN_WARNING "%s: UDMA speeds >UDMA33 cannot " @@ -639,7 +639,7 @@ int ide_ata66_check (ide_drive_t *drive, ide_task_t *args) int set_transfer (ide_drive_t *drive, ide_task_t *args) { if (args->tf.command == WIN_SETFEATURES && - args->tf.lbal >= XFER_SW_DMA_0 && + args->tf.nsect >= XFER_SW_DMA_0 && args->tf.feature == SETFEATURES_XFER && (drive->id->dma_ultra || drive->id->dma_mword || @@ -688,8 +688,7 @@ int ide_driveid_update(ide_drive_t *drive) */ SELECT_MASK(drive, 1); - if (IDE_CONTROL_REG) - hwif->OUTB(drive->ctl,IDE_CONTROL_REG); + ide_set_irq(drive, 1); msleep(50); hwif->OUTB(WIN_IDENTIFY, IDE_COMMAND_REG); timeout = jiffies + WAIT_WORSTCASE; @@ -742,8 +741,8 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) // msleep(50); #ifdef CONFIG_BLK_DEV_IDEDMA - if (hwif->ide_dma_on) /* check if host supports DMA */ - hwif->dma_host_off(drive); + if (hwif->dma_host_set) /* check if host supports DMA */ + hwif->dma_host_set(drive, 0); #endif /* Skip setting PIO flow-control modes on pre-EIDE drives */ @@ -772,13 +771,12 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) SELECT_DRIVE(drive); SELECT_MASK(drive, 0); udelay(1); - if (IDE_CONTROL_REG) - hwif->OUTB(drive->ctl | 2, IDE_CONTROL_REG); + ide_set_irq(drive, 0); hwif->OUTB(speed, IDE_NSECTOR_REG); hwif->OUTB(SETFEATURES_XFER, IDE_FEATURE_REG); hwif->OUTBSYNC(drive, WIN_SETFEATURES, IDE_COMMAND_REG); - if ((IDE_CONTROL_REG) && (drive->quirk_list == 2)) - hwif->OUTB(drive->ctl, IDE_CONTROL_REG); + if (drive->quirk_list == 2) + ide_set_irq(drive, 1); error = __ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT|ERR_STAT, @@ -799,10 +797,11 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) skip: #ifdef CONFIG_BLK_DEV_IDEDMA - if (speed >= XFER_SW_DMA_0) - hwif->dma_host_on(drive); - else if (hwif->ide_dma_on) /* check if host supports DMA */ - hwif->dma_off_quietly(drive); + if ((speed >= XFER_SW_DMA_0 || (hwif->host_flags & IDE_HFLAG_VDMA)) && + drive->using_dma) + hwif->dma_host_set(drive, 1); + else if (hwif->dma_host_set) /* check if host supports DMA */ + ide_dma_off_quietly(drive); #endif switch(speed) { @@ -1012,10 +1011,10 @@ static void check_dma_crc(ide_drive_t *drive) { #ifdef CONFIG_BLK_DEV_IDEDMA if (drive->crc_count) { - drive->hwif->dma_off_quietly(drive); + ide_dma_off_quietly(drive); ide_set_xfer_rate(drive, ide_auto_reduce_xfer(drive)); if (drive->current_speed >= XFER_SW_DMA_0) - (void) HWIF(drive)->ide_dma_on(drive); + ide_dma_on(drive); } else ide_dma_off(drive); #endif diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index a3bd8e8ed6b..9b44fbdfe41 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -454,8 +454,7 @@ int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) static void ide_dump_opcode(ide_drive_t *drive) { struct request *rq; - u8 opcode = 0; - int found = 0; + ide_task_t *task = NULL; spin_lock(&ide_lock); rq = NULL; @@ -464,25 +463,15 @@ static void ide_dump_opcode(ide_drive_t *drive) spin_unlock(&ide_lock); if (!rq) return; - if (rq->cmd_type == REQ_TYPE_ATA_CMD) { - char *args = rq->buffer; - if (args) { - opcode = args[0]; - found = 1; - } - } else if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { - ide_task_t *args = rq->special; - if (args) { - opcode = args->tf.command; - found = 1; - } - } + + if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) + task = rq->special; printk("ide: failed opcode was: "); - if (!found) - printk("unknown\n"); + if (task == NULL) + printk(KERN_CONT "unknown\n"); else - printk("0x%02x\n", opcode); + printk(KERN_CONT "0x%02x\n", task->tf.command); } u64 ide_get_lba_addr(struct ide_taskfile *tf, int lba48) diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index e245521af7b..cbbb0f75be9 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -31,7 +31,6 @@ static int idepnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id { hw_regs_t hw; ide_hwif_t *hwif; - int index; if (!(pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && pnp_irq_valid(dev, 0))) return -1; @@ -41,11 +40,19 @@ static int idepnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id pnp_port_start(dev, 1)); hw.irq = pnp_irq(dev, 0); - index = ide_register_hw(&hw, NULL, 1, &hwif); + hwif = ide_find_port(hw.io_ports[IDE_DATA_OFFSET]); + if (hwif) { + u8 index = hwif->index; + u8 idx[4] = { index, 0xff, 0xff, 0xff }; + + ide_init_port_data(hwif, index); + ide_init_port_hw(hwif, &hw); - if (index != -1) { - printk(KERN_INFO "ide%d: generic PnP IDE interface\n", index); + printk(KERN_INFO "ide%d: generic PnP IDE interface\n", index); pnp_set_drvdata(dev,hwif); + + ide_device_add(idx); + return 0; } @@ -68,12 +75,15 @@ static struct pnp_driver idepnp_driver = { .remove = idepnp_remove, }; -void __init pnpide_init(void) +static int __init pnpide_init(void) { - pnp_register_driver(&idepnp_driver); + return pnp_register_driver(&idepnp_driver); } -void __exit pnpide_exit(void) +static void __exit pnpide_exit(void) { pnp_unregister_driver(&idepnp_driver); } + +module_init(pnpide_init); +module_exit(pnpide_exit); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 0379d1f697c..edf650b20c6 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -235,9 +235,6 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) drive->media = ide_disk; printk("%s DISK drive\n", (id->config == 0x848a) ? "CFA" : "ATA" ); - if (hwif->quirkproc) - drive->quirk_list = hwif->quirkproc(drive); - return; err_misc: @@ -353,22 +350,19 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) * the irq handler isn't expecting. */ if (IDE_CONTROL_REG) { - u8 ctl = drive->ctl | 2; if (!hwif->irq) { autoprobe = 1; cookie = probe_irq_on(); - /* enable device irq */ - ctl &= ~2; } - hwif->OUTB(ctl, IDE_CONTROL_REG); + ide_set_irq(drive, autoprobe); } retval = actual_try_to_identify(drive, cmd); if (autoprobe) { int irq; - /* mask device irq */ - hwif->OUTB(drive->ctl|2, IDE_CONTROL_REG); + + ide_set_irq(drive, 0); /* clear drive IRQ */ (void) hwif->INB(IDE_STATUS_REG); udelay(5); @@ -388,6 +382,20 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) return retval; } +static int ide_busy_sleep(ide_hwif_t *hwif) +{ + unsigned long timeout = jiffies + WAIT_WORSTCASE; + u8 stat; + + do { + msleep(50); + stat = hwif->INB(hwif->io_ports[IDE_STATUS_OFFSET]); + if ((stat & BUSY_STAT) == 0) + return 0; + } while (time_before(jiffies, timeout)); + + return 1; +} /** * do_probe - probe an IDE device @@ -456,7 +464,6 @@ static int do_probe (ide_drive_t *drive, u8 cmd) if ((rc == 1 && cmd == WIN_PIDENTIFY) && ((drive->autotune == IDE_TUNE_DEFAULT) || (drive->autotune == IDE_TUNE_AUTO))) { - unsigned long timeout; printk("%s: no response (status = 0x%02x), " "resetting drive\n", drive->name, hwif->INB(IDE_STATUS_REG)); @@ -464,10 +471,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) hwif->OUTB(drive->select.all, IDE_SELECT_REG); msleep(50); hwif->OUTB(WIN_SRST, IDE_COMMAND_REG); - timeout = jiffies; - while (((hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) && - time_before(jiffies, timeout + WAIT_WORSTCASE)) - msleep(50); + (void)ide_busy_sleep(hwif); rc = try_to_identify(drive, cmd); } if (rc == 1) @@ -495,20 +499,16 @@ static int do_probe (ide_drive_t *drive, u8 cmd) static void enable_nest (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - unsigned long timeout; printk("%s: enabling %s -- ", hwif->name, drive->id->model); SELECT_DRIVE(drive); msleep(50); hwif->OUTB(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG); - timeout = jiffies + WAIT_WORSTCASE; - do { - if (time_after(jiffies, timeout)) { - printk("failed (timeout)\n"); - return; - } - msleep(50); - } while ((hwif->INB(IDE_STATUS_REG)) & BUSY_STAT); + + if (ide_busy_sleep(hwif)) { + printk(KERN_CONT "failed (timeout)\n"); + return; + } msleep(50); @@ -656,8 +656,7 @@ static int wait_hwif_ready(ide_hwif_t *hwif) /* Ignore disks that we will not probe for later. */ if (!drive->noprobe || drive->present) { SELECT_DRIVE(drive); - if (IDE_CONTROL_REG) - hwif->OUTB(drive->ctl, IDE_CONTROL_REG); + ide_set_irq(drive, 1); mdelay(2); rc = ide_wait_not_busy(hwif, 35000); if (rc) @@ -676,19 +675,18 @@ out: /** * ide_undecoded_slave - look for bad CF adapters - * @hwif: interface + * @drive1: drive * * Analyse the drives on the interface and attempt to decide if we * have the same drive viewed twice. This occurs with crap CF adapters * and PCMCIA sometimes. */ -void ide_undecoded_slave(ide_hwif_t *hwif) +void ide_undecoded_slave(ide_drive_t *drive1) { - ide_drive_t *drive0 = &hwif->drives[0]; - ide_drive_t *drive1 = &hwif->drives[1]; + ide_drive_t *drive0 = &drive1->hwif->drives[0]; - if (drive0->present == 0 || drive1->present == 0) + if ((drive1->dn & 1) == 0 || drive0->present == 0) return; /* If the models don't match they are not the same product */ @@ -791,18 +789,11 @@ static void probe_hwif(ide_hwif_t *hwif) } } if (hwif->io_ports[IDE_CONTROL_OFFSET] && hwif->reset) { - unsigned long timeout = jiffies + WAIT_WORSTCASE; - u8 stat; - printk(KERN_WARNING "%s: reset\n", hwif->name); hwif->OUTB(12, hwif->io_ports[IDE_CONTROL_OFFSET]); udelay(10); hwif->OUTB(8, hwif->io_ports[IDE_CONTROL_OFFSET]); - do { - msleep(50); - stat = hwif->INB(hwif->io_ports[IDE_STATUS_OFFSET]); - } while ((stat & BUSY_STAT) && time_after(timeout, jiffies)); - + (void)ide_busy_sleep(hwif); } local_irq_restore(flags); /* @@ -817,8 +808,12 @@ static void probe_hwif(ide_hwif_t *hwif) return; } - if (hwif->fixup) - hwif->fixup(hwif); + for (unit = 0; unit < MAX_DRIVES; unit++) { + ide_drive_t *drive = &hwif->drives[unit]; + + if (drive->present && hwif->quirkproc) + hwif->quirkproc(drive); + } for (unit = 0; unit < MAX_DRIVES; ++unit) { ide_drive_t *drive = &hwif->drives[unit]; @@ -833,7 +828,7 @@ static void probe_hwif(ide_hwif_t *hwif) drive->nice1 = 1; - if (hwif->ide_dma_on) + if (hwif->dma_host_set) ide_set_dma(drive); } } @@ -848,25 +843,6 @@ static void probe_hwif(ide_hwif_t *hwif) } } -static int hwif_init(ide_hwif_t *hwif); -static void hwif_register_devices(ide_hwif_t *hwif); - -static int probe_hwif_init(ide_hwif_t *hwif) -{ - probe_hwif(hwif); - - if (!hwif_init(hwif)) { - printk(KERN_INFO "%s: failed to initialize IDE interface\n", - hwif->name); - return -1; - } - - if (hwif->present) - hwif_register_devices(hwif); - - return 0; -} - #if MAX_HWIFS > 1 /* * save_match() is used to simplify logic in init_irq() below. @@ -1359,54 +1335,63 @@ static void hwif_register_devices(ide_hwif_t *hwif) } } -int ideprobe_init (void) +int ide_device_add_all(u8 *idx) { - unsigned int index; - int probe[MAX_HWIFS]; - - memset(probe, 0, MAX_HWIFS * sizeof(int)); - for (index = 0; index < MAX_HWIFS; ++index) - probe[index] = !ide_hwifs[index].present; - - for (index = 0; index < MAX_HWIFS; ++index) - if (probe[index]) - probe_hwif(&ide_hwifs[index]); - for (index = 0; index < MAX_HWIFS; ++index) - if (probe[index]) - hwif_init(&ide_hwifs[index]); - for (index = 0; index < MAX_HWIFS; ++index) { - if (probe[index]) { - ide_hwif_t *hwif = &ide_hwifs[index]; - if (!hwif->present) - continue; - if (hwif->chipset == ide_unknown || hwif->chipset == ide_forced) - hwif->chipset = ide_generic; - hwif_register_devices(hwif); + ide_hwif_t *hwif; + int i, rc = 0; + + for (i = 0; i < MAX_HWIFS; i++) { + if (idx[i] == 0xff) + continue; + + probe_hwif(&ide_hwifs[idx[i]]); + } + + for (i = 0; i < MAX_HWIFS; i++) { + if (idx[i] == 0xff) + continue; + + hwif = &ide_hwifs[idx[i]]; + + if (hwif_init(hwif) == 0) { + printk(KERN_INFO "%s: failed to initialize IDE " + "interface\n", hwif->name); + rc = -1; + continue; } } - for (index = 0; index < MAX_HWIFS; ++index) - if (probe[index]) - ide_proc_register_port(&ide_hwifs[index]); - return 0; -} -EXPORT_SYMBOL_GPL(ideprobe_init); + for (i = 0; i < MAX_HWIFS; i++) { + if (idx[i] == 0xff) + continue; -int ide_device_add(u8 idx[4]) -{ - int i, rc = 0; + hwif = &ide_hwifs[idx[i]]; - for (i = 0; i < 4; i++) { - if (idx[i] != 0xff) - rc |= probe_hwif_init(&ide_hwifs[idx[i]]); + if (hwif->present) { + if (hwif->chipset == ide_unknown || + hwif->chipset == ide_forced) + hwif->chipset = ide_generic; + hwif_register_devices(hwif); + } } - for (i = 0; i < 4; i++) { + for (i = 0; i < MAX_HWIFS; i++) { if (idx[i] != 0xff) ide_proc_register_port(&ide_hwifs[idx[i]]); } return rc; } +EXPORT_SYMBOL_GPL(ide_device_add_all); + +int ide_device_add(u8 idx[4]) +{ + u8 idx_all[MAX_HWIFS]; + int i; + for (i = 0; i < MAX_HWIFS; i++) + idx_all[i] = (i < 4) ? idx[i] : 0xff; + + return ide_device_add_all(idx_all); +} EXPORT_SYMBOL_GPL(ide_device_add); diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index a4007d30da5..aa663e7f46f 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -346,14 +346,20 @@ static int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int va static int set_xfer_rate (ide_drive_t *drive, int arg) { + ide_task_t task; int err; if (arg < 0 || arg > 70) return -EINVAL; - err = ide_wait_cmd(drive, - WIN_SETFEATURES, (u8) arg, - SETFEATURES_XFER, 0, NULL); + memset(&task, 0, sizeof(task)); + task.tf.command = WIN_SETFEATURES; + task.tf.feature = SETFEATURES_XFER; + task.tf.nsect = (u8)arg; + task.tf_flags = IDE_TFLAG_OUT_FEATURE | IDE_TFLAG_OUT_NSECT | + IDE_TFLAG_IN_NSECT; + + err = ide_no_data_taskfile(drive, &task); if (!err && arg) { ide_set_xfer_rate(drive, (u8) arg); diff --git a/drivers/ide/ide-scan-pci.c b/drivers/ide/ide-scan-pci.c new file mode 100644 index 00000000000..7ffa332d77c --- /dev/null +++ b/drivers/ide/ide-scan-pci.c @@ -0,0 +1,121 @@ +/* + * support for probing IDE PCI devices in the PCI bus order + * + * Copyright (c) 1998-2000 Andre Hedrick <andre@linux-ide.org> + * Copyright (c) 1995-1998 Mark Lord + * + * May be copied or modified under the terms of the GNU General Public License + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/ide.h> + +/* + * Module interfaces + */ + +static int pre_init = 1; /* Before first ordered IDE scan */ +static LIST_HEAD(ide_pci_drivers); + +/* + * __ide_pci_register_driver - attach IDE driver + * @driver: pci driver + * @module: owner module of the driver + * + * Registers a driver with the IDE layer. The IDE layer arranges that + * boot time setup is done in the expected device order and then + * hands the controllers off to the core PCI code to do the rest of + * the work. + * + * Returns are the same as for pci_register_driver + */ + +int __ide_pci_register_driver(struct pci_driver *driver, struct module *module, + const char *mod_name) +{ + if (!pre_init) + return __pci_register_driver(driver, module, mod_name); + driver->driver.owner = module; + list_add_tail(&driver->node, &ide_pci_drivers); + return 0; +} +EXPORT_SYMBOL_GPL(__ide_pci_register_driver); + +/** + * ide_scan_pcidev - find an IDE driver for a device + * @dev: PCI device to check + * + * Look for an IDE driver to handle the device we are considering. + * This is only used during boot up to get the ordering correct. After + * boot up the pci layer takes over the job. + */ + +static int __init ide_scan_pcidev(struct pci_dev *dev) +{ + struct list_head *l; + struct pci_driver *d; + + list_for_each(l, &ide_pci_drivers) { + d = list_entry(l, struct pci_driver, node); + if (d->id_table) { + const struct pci_device_id *id = + pci_match_id(d->id_table, dev); + + if (id != NULL && d->probe(dev, id) >= 0) { + dev->driver = d; + pci_dev_get(dev); + return 1; + } + } + } + return 0; +} + +/** + * ide_scan_pcibus - perform the initial IDE driver scan + * + * Perform the initial bus rather than driver ordered scan of the + * PCI drivers. After this all IDE pci handling becomes standard + * module ordering not traditionally ordered. + */ + +int __init ide_scan_pcibus(void) +{ + struct pci_dev *dev = NULL; + struct pci_driver *d; + struct list_head *l, *n; + + pre_init = 0; + if (!ide_scan_direction) + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev))) + ide_scan_pcidev(dev); + else + while ((dev = pci_get_device_reverse(PCI_ANY_ID, PCI_ANY_ID, + dev))) + ide_scan_pcidev(dev); + + /* + * Hand the drivers over to the PCI layer now we + * are post init. + */ + + list_for_each_safe(l, n, &ide_pci_drivers) { + list_del(l); + d = list_entry(l, struct pci_driver, node); + if (__pci_register_driver(d, d->driver.owner, + d->driver.mod_name)) + printk(KERN_ERR "%s: failed to register %s driver\n", + __FUNCTION__, d->driver.mod_name); + } + + return 0; +} + +static int __init ide_scan_pci(void) +{ + return ide_scan_pcibus(); +} + +module_init(ide_scan_pci); diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 3cbca3f4628..d71a584f076 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1690,6 +1690,11 @@ static int idetape_end_request(ide_drive_t *drive, int uptodate, int nr_sects) if (error) tape->failed_pc = NULL; + if (!blk_special_request(rq)) { + ide_end_request(drive, uptodate, nr_sects); + return 0; + } + spin_lock_irqsave(&tape->spinlock, flags); /* The request was a pipelined data transfer request */ diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 2d63ea9ee61..5eb6fa15dc4 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -35,34 +35,6 @@ #include <asm/uaccess.h> #include <asm/io.h> -static void ata_bswap_data (void *buffer, int wcount) -{ - u16 *p = buffer; - - while (wcount--) { - *p = *p << 8 | *p >> 8; p++; - *p = *p << 8 | *p >> 8; p++; - } -} - -static void taskfile_input_data(ide_drive_t *drive, void *buffer, u32 wcount) -{ - HWIF(drive)->ata_input_data(drive, buffer, wcount); - if (drive->bswap) - ata_bswap_data(buffer, wcount); -} - -static void taskfile_output_data(ide_drive_t *drive, void *buffer, u32 wcount) -{ - if (drive->bswap) { - ata_bswap_data(buffer, wcount); - HWIF(drive)->ata_output_data(drive, buffer, wcount); - ata_bswap_data(buffer, wcount); - } else { - HWIF(drive)->ata_output_data(drive, buffer, wcount); - } -} - void ide_tf_load(ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; @@ -77,10 +49,13 @@ void ide_tf_load(ide_drive_t *drive, ide_task_t *task) "lbam 0x%02x lbah 0x%02x dev 0x%02x cmd 0x%02x\n", drive->name, tf->feature, tf->nsect, tf->lbal, tf->lbam, tf->lbah, tf->device, tf->command); + printk("%s: hob: nsect 0x%02x lbal 0x%02x " + "lbam 0x%02x lbah 0x%02x\n", + drive->name, tf->hob_nsect, tf->hob_lbal, + tf->hob_lbam, tf->hob_lbah); #endif - if (IDE_CONTROL_REG) - hwif->OUTB(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ + ide_set_irq(drive, 1); if ((task->tf_flags & IDE_TFLAG_NO_SELECT_MASK) == 0) SELECT_MASK(drive, 0); @@ -124,7 +99,7 @@ int taskfile_lib_get_identify (ide_drive_t *drive, u8 *buf) args.tf.command = WIN_IDENTIFY; else args.tf.command = WIN_PIDENTIFY; - args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; + args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; args.data_phase = TASKFILE_IN; return ide_raw_taskfile(drive, &args, buf, 1); } @@ -285,7 +260,7 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) return ide_stopped; } -static u8 wait_drive_not_busy(ide_drive_t *drive) +u8 wait_drive_not_busy(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); int retries; @@ -293,8 +268,7 @@ static u8 wait_drive_not_busy(ide_drive_t *drive) /* * Last sector was transfered, wait until drive is ready. - * This can take up to 10 usec, but we will wait max 1 ms - * (drive_cmd_intr() waits that long). + * This can take up to 10 usec, but we will wait max 1 ms. */ for (retries = 0; retries < 100; retries++) { if ((stat = hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) @@ -349,9 +323,9 @@ static void ide_pio_sector(ide_drive_t *drive, unsigned int write) /* do the actual data transfer */ if (write) - taskfile_output_data(drive, buf, SECTOR_WORDS); + hwif->ata_output_data(drive, buf, SECTOR_WORDS); else - taskfile_input_data(drive, buf, SECTOR_WORDS); + hwif->ata_input_data(drive, buf, SECTOR_WORDS); kunmap_atomic(buf, KM_BIO_SRC_IRQ); #ifdef CONFIG_HIGHMEM @@ -371,9 +345,18 @@ static void ide_pio_multi(ide_drive_t *drive, unsigned int write) static void ide_pio_datablock(ide_drive_t *drive, struct request *rq, unsigned int write) { + u8 saved_io_32bit = drive->io_32bit; + if (rq->bio) /* fs request */ rq->errors = 0; + if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { + ide_task_t *task = rq->special; + + if (task->tf_flags & IDE_TFLAG_IO_16BIT) + drive->io_32bit = 0; + } + touch_softlockup_watchdog(); switch (drive->hwif->data_phase) { @@ -385,6 +368,8 @@ static void ide_pio_datablock(ide_drive_t *drive, struct request *rq, ide_pio_sector(drive, write); break; } + + drive->io_32bit = saved_io_32bit; } static ide_startstop_t task_error(ide_drive_t *drive, struct request *rq, @@ -422,27 +407,22 @@ static ide_startstop_t task_error(ide_drive_t *drive, struct request *rq, return ide_error(drive, s, stat); } -static void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat) +void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat) { - HWIF(drive)->cursg = NULL; - if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { - ide_task_t *task = rq->special; + u8 err = drive->hwif->INB(IDE_ERROR_REG); - if (task->tf_flags & IDE_TFLAG_FLAGGED) { - u8 err = drive->hwif->INB(IDE_ERROR_REG); - ide_end_drive_cmd(drive, stat, err); - return; - } + ide_end_drive_cmd(drive, stat, err); + return; } if (rq->rq_disk) { ide_driver_t *drv; drv = *(ide_driver_t **)rq->rq_disk->private_data;; - drv->end_request(drive, 1, rq->hard_nr_sectors); + drv->end_request(drive, 1, rq->nr_sectors); } else - ide_end_request(drive, 1, rq->hard_nr_sectors); + ide_end_request(drive, 1, rq->nr_sectors); } /* @@ -455,7 +435,7 @@ static ide_startstop_t task_in_intr(ide_drive_t *drive) u8 stat = hwif->INB(IDE_STATUS_REG); /* new way for dealing with premature shared PCI interrupts */ - if (!OK_STAT(stat, DATA_READY, BAD_R_STAT)) { + if (!OK_STAT(stat, DRQ_STAT, BAD_R_STAT)) { if (stat & (ERR_STAT | DRQ_STAT)) return task_error(drive, rq, __FUNCTION__, stat); /* No data yet, so wait for another IRQ. */ @@ -468,7 +448,7 @@ static ide_startstop_t task_in_intr(ide_drive_t *drive) /* If it was the last datablock check status and finish transfer. */ if (!hwif->nleft) { stat = wait_drive_not_busy(drive); - if (!OK_STAT(stat, 0, BAD_R_STAT)) + if (!OK_STAT(stat, 0, BAD_STAT)) return task_error(drive, rq, __FUNCTION__, stat); task_end_request(drive, rq, stat); return ide_stopped; @@ -512,7 +492,7 @@ static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq) { ide_startstop_t startstop; - if (ide_wait_stat(&startstop, drive, DATA_READY, + if (ide_wait_stat(&startstop, drive, DRQ_STAT, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n", drive->name, @@ -580,7 +560,6 @@ int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) unsigned int taskin = 0; unsigned int taskout = 0; u16 nsect = 0; - u8 io_32bit = drive->io_32bit; char __user *buf = (char __user *)arg; // printk("IDE Taskfile ...\n"); @@ -633,9 +612,10 @@ int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) args.data_phase = req_task->data_phase; - args.tf_flags = IDE_TFLAG_OUT_DEVICE; + args.tf_flags = IDE_TFLAG_IO_16BIT | IDE_TFLAG_DEVICE | + IDE_TFLAG_IN_TF; if (drive->addressing == 1) - args.tf_flags |= IDE_TFLAG_LBA48; + args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_IN_HOB); if (req_task->out_flags.all) { args.tf_flags |= IDE_TFLAG_FLAGGED; @@ -671,7 +651,6 @@ int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) if (req_task->in_flags.b.data) args.tf_flags |= IDE_TFLAG_IN_DATA; - drive->io_32bit = 0; switch(req_task->data_phase) { case TASKFILE_MULTI_OUT: if (!drive->mult_count) { @@ -767,41 +746,24 @@ abort: // printk("IDE Taskfile ioctl ended. rc = %i\n", err); - drive->io_32bit = io_32bit; - return err; } #endif -int ide_wait_cmd (ide_drive_t *drive, u8 cmd, u8 nsect, u8 feature, u8 sectors, u8 *buf) -{ - struct request rq; - u8 buffer[4]; - - if (!buf) - buf = buffer; - memset(buf, 0, 4 + SECTOR_WORDS * 4 * sectors); - ide_init_drive_cmd(&rq); - rq.buffer = buf; - *buf++ = cmd; - *buf++ = nsect; - *buf++ = feature; - *buf++ = sectors; - return ide_do_drive_cmd(drive, &rq, ide_wait); -} - int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) { - int err = 0; - u8 args[4], *argbuf = args; - u8 xfer_rate = 0; - int argsize = 4; + u8 *buf = NULL; + int bufsize = 0, err = 0; + u8 args[4], xfer_rate = 0; ide_task_t tfargs; struct ide_taskfile *tf = &tfargs.tf; if (NULL == (void *) arg) { struct request rq; + ide_init_drive_cmd(&rq); + rq.cmd_type = REQ_TYPE_ATA_TASKFILE; + return ide_do_drive_cmd(drive, &rq, ide_wait); } @@ -810,23 +772,39 @@ int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) memset(&tfargs, 0, sizeof(ide_task_t)); tf->feature = args[2]; - tf->nsect = args[3]; - tf->lbal = args[1]; + if (args[0] == WIN_SMART) { + tf->nsect = args[3]; + tf->lbal = args[1]; + tf->lbam = 0x4f; + tf->lbah = 0xc2; + tfargs.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_IN_NSECT; + } else { + tf->nsect = args[1]; + tfargs.tf_flags = IDE_TFLAG_OUT_FEATURE | + IDE_TFLAG_OUT_NSECT | IDE_TFLAG_IN_NSECT; + } tf->command = args[0]; + tfargs.data_phase = args[3] ? TASKFILE_IN : TASKFILE_NO_DATA; if (args[3]) { - argsize = 4 + (SECTOR_WORDS * 4 * args[3]); - argbuf = kzalloc(argsize, GFP_KERNEL); - if (argbuf == NULL) + tfargs.tf_flags |= IDE_TFLAG_IO_16BIT; + bufsize = SECTOR_WORDS * 4 * args[3]; + buf = kzalloc(bufsize, GFP_KERNEL); + if (buf == NULL) return -ENOMEM; } + if (set_transfer(drive, &tfargs)) { xfer_rate = args[1]; if (ide_ata66_check(drive, &tfargs)) goto abort; } - err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf); + err = ide_raw_taskfile(drive, &tfargs, buf, args[3]); + + args[0] = tf->status; + args[1] = tf->error; + args[2] = tf->nsect; if (!err && xfer_rate) { /* active-retuning-calls future */ @@ -834,10 +812,13 @@ int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) ide_driveid_update(drive); } abort: - if (copy_to_user((void __user *)arg, argbuf, argsize)) + if (copy_to_user((void __user *)arg, &args, 4)) err = -EFAULT; - if (argsize > 4) - kfree(argbuf); + if (buf) { + if (copy_to_user((void __user *)(arg + 4), buf, bufsize)) + err = -EFAULT; + kfree(buf); + } return err; } @@ -854,7 +835,7 @@ int ide_task_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) memset(&task, 0, sizeof(task)); memcpy(&task.tf_array[7], &args[1], 6); task.tf.command = args[0]; - task.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE; + task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE; err = ide_no_data_taskfile(drive, &task); diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index c6d4f630e18..97894abd9eb 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -95,7 +95,7 @@ DEFINE_MUTEX(ide_cfg_mtx); __cacheline_aligned_in_smp DEFINE_SPINLOCK(ide_lock); #ifdef CONFIG_IDEPCI_PCIBUS_ORDER -static int ide_scan_direction; /* THIS was formerly 2.2.x pci=reverse */ +int ide_scan_direction; /* THIS was formerly 2.2.x pci=reverse */ #endif int noautodma = 0; @@ -116,7 +116,7 @@ EXPORT_SYMBOL(ide_hwifs); /* * Do not even *think* about calling this! */ -static void init_hwif_data(ide_hwif_t *hwif, unsigned int index) +void ide_init_port_data(ide_hwif_t *hwif, unsigned int index) { unsigned int unit; @@ -159,6 +159,7 @@ static void init_hwif_data(ide_hwif_t *hwif, unsigned int index) init_completion(&drive->gendev_rel_comp); } } +EXPORT_SYMBOL_GPL(ide_init_port_data); static void init_hwif_default(ide_hwif_t *hwif, unsigned int index) { @@ -177,8 +178,6 @@ static void init_hwif_default(ide_hwif_t *hwif, unsigned int index) #endif } -extern void ide_arm_init(void); - /* * init_ide_data() sets reasonable default values into all fields * of all instances of the hwifs and drives, but only on the first call. @@ -210,16 +209,13 @@ static void __init init_ide_data (void) /* Initialise all interface structures */ for (index = 0; index < MAX_HWIFS; ++index) { hwif = &ide_hwifs[index]; - init_hwif_data(hwif, index); + ide_init_port_data(hwif, index); init_hwif_default(hwif, index); #if !defined(CONFIG_PPC32) || !defined(CONFIG_PCI) hwif->irq = ide_init_default_irq(hwif->io_ports[IDE_DATA_OFFSET]); #endif } -#ifdef CONFIG_IDE_ARM - ide_arm_init(); -#endif } /** @@ -414,8 +410,6 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif) hwif->cds = tmp_hwif->cds; #endif - hwif->fixup = tmp_hwif->fixup; - hwif->set_pio_mode = tmp_hwif->set_pio_mode; hwif->set_dma_mode = tmp_hwif->set_dma_mode; hwif->mdma_filter = tmp_hwif->mdma_filter; @@ -433,16 +427,13 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif) hwif->atapi_input_bytes = tmp_hwif->atapi_input_bytes; hwif->atapi_output_bytes = tmp_hwif->atapi_output_bytes; + hwif->dma_host_set = tmp_hwif->dma_host_set; hwif->dma_setup = tmp_hwif->dma_setup; hwif->dma_exec_cmd = tmp_hwif->dma_exec_cmd; hwif->dma_start = tmp_hwif->dma_start; hwif->ide_dma_end = tmp_hwif->ide_dma_end; - hwif->ide_dma_on = tmp_hwif->ide_dma_on; - hwif->dma_off_quietly = tmp_hwif->dma_off_quietly; hwif->ide_dma_test_irq = tmp_hwif->ide_dma_test_irq; hwif->ide_dma_clear_irq = tmp_hwif->ide_dma_clear_irq; - hwif->dma_host_on = tmp_hwif->dma_host_on; - hwif->dma_host_off = tmp_hwif->dma_host_off; hwif->dma_lost_irq = tmp_hwif->dma_lost_irq; hwif->dma_timeout = tmp_hwif->dma_timeout; @@ -614,7 +605,7 @@ void ide_unregister(unsigned int index) tmp_hwif = *hwif; /* restore hwif data to pristine status */ - init_hwif_data(hwif, index); + ide_init_port_data(hwif, index); init_hwif_default(hwif, index); ide_hwif_restore(hwif, &tmp_hwif); @@ -680,24 +671,34 @@ void ide_setup_ports ( hw_regs_t *hw, */ } +void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) +{ + memcpy(hwif->io_ports, hw->io_ports, sizeof(hwif->io_ports)); + hwif->irq = hw->irq; + hwif->noprobe = 0; + hwif->chipset = hw->chipset; + hwif->gendev.parent = hw->dev; + hwif->ack_intr = hw->ack_intr; +} +EXPORT_SYMBOL_GPL(ide_init_port_hw); + /** * ide_register_hw - register IDE interface * @hw: hardware registers - * @fixup: fixup function - * @initializing: set while initializing built-in drivers + * @quirkproc: quirkproc function * @hwifp: pointer to returned hwif * * Register an IDE interface, specifying exactly the registers etc. - * Set init=1 iff calling before probes have taken place. * * Returns -1 on error. */ -int ide_register_hw(hw_regs_t *hw, void (*fixup)(ide_hwif_t *), - int initializing, ide_hwif_t **hwifp) +int ide_register_hw(hw_regs_t *hw, void (*quirkproc)(ide_drive_t *), + ide_hwif_t **hwifp) { int index, retry = 1; ide_hwif_t *hwif; + u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; do { for (index = 0; index < MAX_HWIFS; ++index) { @@ -709,8 +710,7 @@ int ide_register_hw(hw_regs_t *hw, void (*fixup)(ide_hwif_t *), hwif = &ide_hwifs[index]; if (hwif->hold) continue; - if ((!hwif->present && !hwif->mate && !initializing) || - (!hwif->io_ports[IDE_DATA_OFFSET] && initializing)) + if (!hwif->present && hwif->mate == NULL) goto found; } for (index = 0; index < MAX_HWIFS; index++) @@ -721,29 +721,23 @@ found: if (hwif->present) ide_unregister(index); else if (!hwif->hold) { - init_hwif_data(hwif, index); + ide_init_port_data(hwif, index); init_hwif_default(hwif, index); } if (hwif->present) return -1; - memcpy(hwif->io_ports, hw->io_ports, sizeof(hwif->io_ports)); - hwif->irq = hw->irq; - hwif->noprobe = 0; - hwif->fixup = fixup; - hwif->chipset = hw->chipset; - hwif->gendev.parent = hw->dev; - hwif->ack_intr = hw->ack_intr; - if (initializing == 0) { - u8 idx[4] = { index, 0xff, 0xff, 0xff }; + ide_init_port_hw(hwif, hw); + hwif->quirkproc = quirkproc; - ide_device_add(idx); - } + idx[0] = index; + + ide_device_add(idx); if (hwifp) *hwifp = hwif; - return (initializing || hwif->present) ? index : -1; + return hwif->present ? index : -1; } EXPORT_SYMBOL(ide_register_hw); @@ -836,7 +830,7 @@ int set_using_dma(ide_drive_t *drive, int arg) if (!drive->id || !(drive->id->capability & 1)) goto out; - if (hwif->ide_dma_on == NULL) + if (hwif->dma_host_set == NULL) goto out; err = -EBUSY; @@ -884,7 +878,10 @@ int set_pio_mode(ide_drive_t *drive, int arg) if (drive->special.b.set_tune) return -EBUSY; + ide_init_drive_cmd(&rq); + rq.cmd_type = REQ_TYPE_ATA_TASKFILE; + drive->tune_req = (u8) arg; drive->special.b.set_tune = 1; (void) ide_do_drive_cmd(drive, &rq, ide_wait); @@ -1066,7 +1063,7 @@ int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device ide_init_hwif_ports(&hw, (unsigned long) args[0], (unsigned long) args[1], NULL); hw.irq = args[2]; - if (ide_register_hw(&hw, NULL, 0, NULL) == -1) + if (ide_register_hw(&hw, NULL, NULL) == -1) return -EIO; return 0; } @@ -1227,26 +1224,12 @@ static int __init match_parm (char *s, const char *keywords[], int vals[], int m return 0; /* zero = nothing matched */ } -#ifdef CONFIG_BLK_DEV_ALI14XX extern int probe_ali14xx; -extern int ali14xx_init(void); -#endif -#ifdef CONFIG_BLK_DEV_UMC8672 extern int probe_umc8672; -extern int umc8672_init(void); -#endif -#ifdef CONFIG_BLK_DEV_DTC2278 extern int probe_dtc2278; -extern int dtc2278_init(void); -#endif -#ifdef CONFIG_BLK_DEV_HT6560B extern int probe_ht6560b; -extern int ht6560b_init(void); -#endif -#ifdef CONFIG_BLK_DEV_QD65XX extern int probe_qd65xx; -extern int qd65xx_init(void); -#endif +extern int cmd640_vlb; static int __initdata is_chipset_set[MAX_HWIFS]; @@ -1323,7 +1306,7 @@ static int __init ide_setup(char *s) if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) { const char *hd_words[] = { "none", "noprobe", "nowerr", "cdrom", "nodma", - "autotune", "noautotune", "minus8", "swapdata", "bswap", + "autotune", "noautotune", "-8", "-9", "-10", "noflush", "remap", "remap63", "scsi", NULL }; unit = s[2] - 'a'; hw = unit / MAX_DRIVES; @@ -1359,10 +1342,6 @@ static int __init ide_setup(char *s) case -7: /* "noautotune" */ drive->autotune = IDE_TUNE_NOAUTO; goto obsolete_option; - case -9: /* "swapdata" */ - case -10: /* "bswap" */ - drive->bswap = 1; - goto done; case -11: /* noflush */ drive->noflush = 1; goto done; @@ -1462,11 +1441,8 @@ static int __init ide_setup(char *s) #endif #ifdef CONFIG_BLK_DEV_CMD640 case -14: /* "cmd640_vlb" */ - { - extern int cmd640_vlb; /* flag for cmd640.c */ cmd640_vlb = 1; goto done; - } #endif #ifdef CONFIG_BLK_DEV_HT6560B case -13: /* "ht6560b" */ @@ -1556,79 +1532,6 @@ done: return 1; } -extern void __init pnpide_init(void); -extern void __exit pnpide_exit(void); -extern void __init h8300_ide_init(void); - -/* - * probe_for_hwifs() finds/initializes "known" IDE interfaces - */ -static void __init probe_for_hwifs (void) -{ -#ifdef CONFIG_IDEPCI_PCIBUS_ORDER - ide_scan_pcibus(ide_scan_direction); -#endif - -#ifdef CONFIG_ETRAX_IDE - { - extern void init_e100_ide(void); - init_e100_ide(); - } -#endif /* CONFIG_ETRAX_IDE */ -#ifdef CONFIG_BLK_DEV_CMD640 - { - extern void ide_probe_for_cmd640x(void); - ide_probe_for_cmd640x(); - } -#endif /* CONFIG_BLK_DEV_CMD640 */ -#ifdef CONFIG_BLK_DEV_IDE_PMAC - { - extern int pmac_ide_probe(void); - (void)pmac_ide_probe(); - } -#endif /* CONFIG_BLK_DEV_IDE_PMAC */ -#ifdef CONFIG_BLK_DEV_GAYLE - { - extern void gayle_init(void); - gayle_init(); - } -#endif /* CONFIG_BLK_DEV_GAYLE */ -#ifdef CONFIG_BLK_DEV_FALCON_IDE - { - extern void falconide_init(void); - falconide_init(); - } -#endif /* CONFIG_BLK_DEV_FALCON_IDE */ -#ifdef CONFIG_BLK_DEV_MAC_IDE - { - extern void macide_init(void); - macide_init(); - } -#endif /* CONFIG_BLK_DEV_MAC_IDE */ -#ifdef CONFIG_BLK_DEV_Q40IDE - { - extern void q40ide_init(void); - q40ide_init(); - } -#endif /* CONFIG_BLK_DEV_Q40IDE */ -#ifdef CONFIG_BLK_DEV_BUDDHA - { - extern void buddha_init(void); - buddha_init(); - } -#endif /* CONFIG_BLK_DEV_BUDDHA */ -#ifdef CONFIG_BLK_DEV_IDEPNP - pnpide_init(); -#endif -#ifdef CONFIG_H8300 - h8300_ide_init(); -#endif -} - -/* - * Probe module - */ - EXPORT_SYMBOL(ide_lock); static int ide_bus_match(struct device *dev, struct device_driver *drv) @@ -1775,30 +1678,6 @@ static int __init ide_init(void) proc_ide_create(); -#ifdef CONFIG_BLK_DEV_ALI14XX - if (probe_ali14xx) - (void)ali14xx_init(); -#endif -#ifdef CONFIG_BLK_DEV_UMC8672 - if (probe_umc8672) - (void)umc8672_init(); -#endif -#ifdef CONFIG_BLK_DEV_DTC2278 - if (probe_dtc2278) - (void)dtc2278_init(); -#endif -#ifdef CONFIG_BLK_DEV_HT6560B - if (probe_ht6560b) - (void)ht6560b_init(); -#endif -#ifdef CONFIG_BLK_DEV_QD65XX - if (probe_qd65xx) - (void)qd65xx_init(); -#endif - - /* Probe for special PCI and other "known" interface chipsets. */ - probe_for_hwifs(); - return 0; } @@ -1834,10 +1713,6 @@ void __exit cleanup_module (void) for (index = 0; index < MAX_HWIFS; ++index) ide_unregister(index); -#ifdef CONFIG_BLK_DEV_IDEPNP - pnpide_exit(); -#endif - proc_ide_destroy(); bus_unregister(&ide_bus_type); diff --git a/drivers/ide/legacy/Makefile b/drivers/ide/legacy/Makefile index 409822349f1..7043ec7d1e0 100644 --- a/drivers/ide/legacy/Makefile +++ b/drivers/ide/legacy/Makefile @@ -1,15 +1,24 @@ +# link order is important here + obj-$(CONFIG_BLK_DEV_ALI14XX) += ali14xx.o +obj-$(CONFIG_BLK_DEV_UMC8672) += umc8672.o obj-$(CONFIG_BLK_DEV_DTC2278) += dtc2278.o obj-$(CONFIG_BLK_DEV_HT6560B) += ht6560b.o obj-$(CONFIG_BLK_DEV_QD65XX) += qd65xx.o -obj-$(CONFIG_BLK_DEV_UMC8672) += umc8672.o -obj-$(CONFIG_BLK_DEV_IDECS) += ide-cs.o +obj-$(CONFIG_BLK_DEV_GAYLE) += gayle.o +obj-$(CONFIG_BLK_DEV_FALCON_IDE) += falconide.o +obj-$(CONFIG_BLK_DEV_MAC_IDE) += macide.o +obj-$(CONFIG_BLK_DEV_Q40IDE) += q40ide.o +obj-$(CONFIG_BLK_DEV_BUDDHA) += buddha.o -obj-$(CONFIG_BLK_DEV_PLATFORM) += ide_platform.o +ifeq ($(CONFIG_BLK_DEV_IDECS), m) + obj-m += ide-cs.o +endif -# Last of all -obj-$(CONFIG_BLK_DEV_HD) += hd.o +ifeq ($(CONFIG_BLK_DEV_PLATFORM), m) + obj-m += ide_platform.o +endif EXTRA_CFLAGS := -Idrivers/ide diff --git a/drivers/ide/legacy/ali14xx.c b/drivers/ide/legacy/ali14xx.c index 38c3a6d63f3..5ec0be4cbad 100644 --- a/drivers/ide/legacy/ali14xx.c +++ b/drivers/ide/legacy/ali14xx.c @@ -231,8 +231,7 @@ int probe_ali14xx = 0; module_param_named(probe, probe_ali14xx, bool, 0); MODULE_PARM_DESC(probe, "probe for ALI M14xx chipsets"); -/* Can be called directly from ide.c. */ -int __init ali14xx_init(void) +static int __init ali14xx_init(void) { if (probe_ali14xx == 0) goto out; @@ -248,9 +247,7 @@ out: return -ENODEV; } -#ifdef MODULE module_init(ali14xx_init); -#endif MODULE_AUTHOR("see local file"); MODULE_DESCRIPTION("support of ALI 14XX IDE chipsets"); diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c index 4a0be251a05..74d28e058f5 100644 --- a/drivers/ide/legacy/buddha.c +++ b/drivers/ide/legacy/buddha.c @@ -112,6 +112,7 @@ typedef enum BuddhaType_Enum { BOARD_BUDDHA, BOARD_CATWEASEL, BOARD_XSURF } BuddhaType; +static const char *buddha_board_name[] = { "Buddha", "Catweasel", "X-Surf" }; /* * Check and acknowledge the interrupt status @@ -143,11 +144,11 @@ static int xsurf_ack_intr(ide_hwif_t *hwif) * Probe for a Buddha or Catweasel IDE interface */ -void __init buddha_init(void) +static int __init buddha_init(void) { hw_regs_t hw; ide_hwif_t *hwif; - int i, index; + int i; struct zorro_dev *z = NULL; u_long buddha_board = 0; @@ -156,6 +157,8 @@ void __init buddha_init(void) while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { unsigned long board; + u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) { buddha_num_hwifs = BUDDHA_NUM_HWIFS; type=BOARD_BUDDHA; @@ -195,7 +198,10 @@ fail_base2: /* X-Surf doesn't have this. IRQs are always on */ if (type != BOARD_XSURF) z_writeb(0, buddha_board+BUDDHA_IRQ_MR); - + + printk(KERN_INFO "ide: %s IDE controller\n", + buddha_board_name[type]); + for(i=0;i<buddha_num_hwifs;i++) { if(type != BOARD_XSURF) { ide_setup_ports(&hw, (buddha_board+buddha_bases[i]), @@ -213,23 +219,23 @@ fail_base2: IRQ_AMIGA_PORTS); } - index = ide_register_hw(&hw, NULL, 1, &hwif); - if (index != -1) { + hwif = ide_find_port(hw.io_ports[IDE_DATA_OFFSET]); + if (hwif) { + u8 index = hwif->index; + + ide_init_port_data(hwif, index); + ide_init_port_hw(hwif, &hw); + hwif->mmio = 1; - printk("ide%d: ", index); - switch(type) { - case BOARD_BUDDHA: - printk("Buddha"); - break; - case BOARD_CATWEASEL: - printk("Catweasel"); - break; - case BOARD_XSURF: - printk("X-Surf"); - break; - } - printk(" IDE interface\n"); - } + + idx[i] = index; + } } + + ide_device_add(idx); } + + return 0; } + +module_init(buddha_init); diff --git a/drivers/ide/legacy/dtc2278.c b/drivers/ide/legacy/dtc2278.c index 24a845d45bd..13eee6da280 100644 --- a/drivers/ide/legacy/dtc2278.c +++ b/drivers/ide/legacy/dtc2278.c @@ -150,8 +150,7 @@ int probe_dtc2278 = 0; module_param_named(probe, probe_dtc2278, bool, 0); MODULE_PARM_DESC(probe, "probe for DTC2278xx chipsets"); -/* Can be called directly from ide.c. */ -int __init dtc2278_init(void) +static int __init dtc2278_init(void) { if (probe_dtc2278 == 0) return -ENODEV; @@ -163,9 +162,7 @@ int __init dtc2278_init(void) return 0; } -#ifdef MODULE module_init(dtc2278_init); -#endif MODULE_AUTHOR("See Local File"); MODULE_DESCRIPTION("support of DTC-2278 VLB IDE chipsets"); diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c index 7d7936f1b90..2860956bdcb 100644 --- a/drivers/ide/legacy/falconide.c +++ b/drivers/ide/legacy/falconide.c @@ -62,19 +62,31 @@ EXPORT_SYMBOL(falconide_intr_lock); * Probe for a Falcon IDE interface */ -void __init falconide_init(void) +static int __init falconide_init(void) { if (MACH_IS_ATARI && ATARIHW_PRESENT(IDE)) { hw_regs_t hw; - int index; + + printk(KERN_INFO "ide: Falcon IDE controller\n"); ide_setup_ports(&hw, ATA_HD_BASE, falconide_offsets, 0, 0, NULL, // falconide_iops, IRQ_MFP_IDE); - index = ide_register_hw(&hw, NULL, 1, NULL); - if (index != -1) - printk("ide%d: Falcon IDE interface\n", index); + hwif = ide_find_port(hw.io_ports[IDE_DATA_OFFSET]); + if (hwif) { + u8 index = hwif->index; + u8 idx[4] = { index, 0xff, 0xff, 0xff }; + + ide_init_port_data(hwif, index); + ide_init_port_hw(hwif, &hw); + + ide_device_add(idx); + } } + + return 0; } + +module_init(falconide_init); diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c index 53331ee1e95..492fa047efc 100644 --- a/drivers/ide/legacy/gayle.c +++ b/drivers/ide/legacy/gayle.c @@ -110,12 +110,13 @@ static int gayle_ack_intr_a1200(ide_hwif_t *hwif) * Probe for a Gayle IDE interface (and optionally for an IDE doubler) */ -void __init gayle_init(void) +static int __init gayle_init(void) { int a4000, i; + u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!MACH_IS_AMIGA) - return; + return -ENODEV; if ((a4000 = AMIGAHW_PRESENT(A4000_IDE)) || AMIGAHW_PRESENT(A1200_IDE)) goto found; @@ -125,15 +126,21 @@ void __init gayle_init(void) NULL)) goto found; #endif - return; + return -ENODEV; found: + printk(KERN_INFO "ide: Gayle IDE controller (A%d style%s)\n", + a4000 ? 4000 : 1200, +#ifdef CONFIG_BLK_DEV_IDEDOUBLER + ide_doubler ? ", IDE doubler" : +#endif + ""); + for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) { unsigned long base, ctrlport, irqport; ide_ack_intr_t *ack_intr; hw_regs_t hw; ide_hwif_t *hwif; - int index; unsigned long phys_base, res_start, res_n; if (a4000) { @@ -165,21 +172,23 @@ found: // &gayle_iops, IRQ_AMIGA_PORTS); - index = ide_register_hw(&hw, NULL, 1, &hwif); - if (index != -1) { + hwif = ide_find_port(base); + if (hwif) { + u8 index = hwif->index; + + ide_init_port_data(hwif, index); + ide_init_port_hw(hwif, &hw); + hwif->mmio = 1; - switch (i) { - case 0: - printk("ide%d: Gayle IDE interface (A%d style)\n", index, - a4000 ? 4000 : 1200); - break; -#ifdef CONFIG_BLK_DEV_IDEDOUBLER - case 1: - printk("ide%d: IDE doubler\n", index); - break; -#endif /* CONFIG_BLK_DEV_IDEDOUBLER */ - } + + idx[i] = index; } else release_mem_region(res_start, res_n); } + + ide_device_add(idx); + + return 0; } + +module_init(gayle_init); diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c index a4245d13f11..8da5031a6d0 100644 --- a/drivers/ide/legacy/ht6560b.c +++ b/drivers/ide/legacy/ht6560b.c @@ -307,8 +307,7 @@ int probe_ht6560b = 0; module_param_named(probe, probe_ht6560b, bool, 0); MODULE_PARM_DESC(probe, "probe for HT6560B chipset"); -/* Can be called directly from ide.c. */ -int __init ht6560b_init(void) +static int __init ht6560b_init(void) { ide_hwif_t *hwif, *mate; static u8 idx[4] = { 0, 1, 0xff, 0xff }; @@ -369,9 +368,7 @@ release_region: return -ENODEV; } -#ifdef MODULE module_init(ht6560b_init); -#endif MODULE_AUTHOR("See Local File"); MODULE_DESCRIPTION("HT-6560B EIDE-controller support"); diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index 03715c05866..f4ea15b3296 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -153,7 +153,7 @@ static int idecs_register(unsigned long io, unsigned long ctl, unsigned long irq hw.irq = irq; hw.chipset = ide_pci; hw.dev = &handle->dev; - return ide_register_hw(&hw, &ide_undecoded_slave, 0, NULL); + return ide_register_hw(&hw, &ide_undecoded_slave, NULL); } /*====================================================================== diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c index 7bb79f53dac..69a0fb0e564 100644 --- a/drivers/ide/legacy/ide_platform.c +++ b/drivers/ide/legacy/ide_platform.c @@ -28,39 +28,27 @@ static struct { int index; } hwif_prop; -static ide_hwif_t *__devinit plat_ide_locate_hwif(void __iomem *base, - void __iomem *ctrl, struct pata_platform_info *pdata, int irq, - int mmio) +static void __devinit plat_ide_setup_ports(hw_regs_t *hw, + void __iomem *base, + void __iomem *ctrl, + struct pata_platform_info *pdata, + int irq) { unsigned long port = (unsigned long)base; - ide_hwif_t *hwif = ide_find_port(port); int i; - if (hwif == NULL) - goto out; - - hwif->io_ports[IDE_DATA_OFFSET] = port; + hw->io_ports[IDE_DATA_OFFSET] = port; port += (1 << pdata->ioport_shift); for (i = IDE_ERROR_OFFSET; i <= IDE_STATUS_OFFSET; i++, port += (1 << pdata->ioport_shift)) - hwif->io_ports[i] = port; - - hwif->io_ports[IDE_CONTROL_OFFSET] = (unsigned long)ctrl; + hw->io_ports[i] = port; - hwif->irq = irq; + hw->io_ports[IDE_CONTROL_OFFSET] = (unsigned long)ctrl; - hwif->chipset = ide_generic; + hw->irq = irq; - if (mmio) { - hwif->mmio = 1; - default_hwif_mmiops(hwif); - } - - hwif_prop.hwif = hwif; - hwif_prop.index = hwif->index; -out: - return hwif; + hw->chipset = ide_generic; } static int __devinit plat_ide_probe(struct platform_device *pdev) @@ -71,6 +59,7 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; int ret = 0; int mmio = 0; + hw_regs_t hw; pdata = pdev->dev.platform_data; @@ -106,15 +95,27 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) res_alt->start, res_alt->end - res_alt->start + 1); } - hwif = plat_ide_locate_hwif(hwif_prop.plat_ide_mapbase, - hwif_prop.plat_ide_alt_mapbase, pdata, res_irq->start, mmio); - + hwif = ide_find_port((unsigned long)hwif_prop.plat_ide_mapbase); if (!hwif) { ret = -ENODEV; goto out; } - hwif->gendev.parent = &pdev->dev; - hwif->noprobe = 0; + + memset(&hw, 0, sizeof(hw)); + plat_ide_setup_ports(&hw, hwif_prop.plat_ide_mapbase, + hwif_prop.plat_ide_alt_mapbase, + pdata, res_irq->start); + hw.dev = &pdev->dev; + + ide_init_port_hw(hwif, &hw); + + if (mmio) { + hwif->mmio = 1; + default_hwif_mmiops(hwif); + } + + hwif_prop.hwif = hwif; + hwif_prop.index = hwif->index; idx[0] = hwif->index; diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c index 5c6aa77c237..782d4c76c0e 100644 --- a/drivers/ide/legacy/macide.c +++ b/drivers/ide/legacy/macide.c @@ -77,15 +77,17 @@ int macide_ack_intr(ide_hwif_t* hwif) return 0; } +static const char *mac_ide_name[] = + { "Quadra", "Powerbook", "Powerbook Baboon" }; + /* * Probe for a Macintosh IDE interface */ -void __init macide_init(void) +static int __init macide_init(void) { hw_regs_t hw; ide_hwif_t *hwif; - int index = -1; switch (macintosh_config->ide_type) { case MAC_IDE_QUADRA: @@ -93,48 +95,50 @@ void __init macide_init(void) 0, 0, macide_ack_intr, // quadra_ide_iops, IRQ_NUBUS_F); - index = ide_register_hw(&hw, NULL, 1, &hwif); break; case MAC_IDE_PB: ide_setup_ports(&hw, IDE_BASE, macide_offsets, 0, 0, macide_ack_intr, // macide_pb_iops, IRQ_NUBUS_C); - index = ide_register_hw(&hw, NULL, 1, &hwif); break; case MAC_IDE_BABOON: ide_setup_ports(&hw, BABOON_BASE, macide_offsets, 0, 0, NULL, // macide_baboon_iops, IRQ_BABOON_1); - index = ide_register_hw(&hw, NULL, 1, &hwif); - if (index == -1) break; - if (macintosh_config->ident == MAC_MODEL_PB190) { + break; + default: + return -ENODEV; + } + + printk(KERN_INFO "ide: Macintosh %s IDE controller\n", + mac_ide_name[macintosh_config->ide_type - 1]); + hwif = ide_find_port(hw.io_ports[IDE_DATA_OFFSET]); + if (hwif) { + u8 index = hwif->index; + u8 idx[4] = { index, 0xff, 0xff, 0xff }; + + ide_init_port_data(hwif, index); + ide_init_port_hw(hwif, &hw); + + if (macintosh_config->ide_type == MAC_IDE_BABOON && + macintosh_config->ident == MAC_MODEL_PB190) { /* Fix breakage in ide-disk.c: drive capacity */ /* is not initialized for drives without a */ /* hardware ID, and we can't get that without */ /* probing the drive which freezes a 190. */ - - ide_drive_t *drive = &ide_hwifs[index].drives[0]; + ide_drive_t *drive = &hwif->drives[0]; drive->capacity64 = drive->cyl*drive->head*drive->sect; - } - break; - - default: - return; - } - if (index != -1) { hwif->mmio = 1; - if (macintosh_config->ide_type == MAC_IDE_QUADRA) - printk(KERN_INFO "ide%d: Macintosh Quadra IDE interface\n", index); - else if (macintosh_config->ide_type == MAC_IDE_PB) - printk(KERN_INFO "ide%d: Macintosh Powerbook IDE interface\n", index); - else if (macintosh_config->ide_type == MAC_IDE_BABOON) - printk(KERN_INFO "ide%d: Macintosh Powerbook Baboon IDE interface\n", index); - else - printk(KERN_INFO "ide%d: Unknown Macintosh IDE interface\n", index); + + ide_device_add(idx); } + + return 0; } + +module_init(macide_init); diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index 6ea46a6723e..f5329730df9 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -111,15 +111,17 @@ static const char *q40_ide_names[Q40IDE_NUM_HWIFS]={ * Probe for Q40 IDE interfaces */ -void __init q40ide_init(void) +static int __init q40ide_init(void) { int i; ide_hwif_t *hwif; - int index; const char *name; + u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!MACH_IS_Q40) - return ; + return -ENODEV; + + printk(KERN_INFO "ide: Q40 IDE controller\n"); for (i = 0; i < Q40IDE_NUM_HWIFS; i++) { hw_regs_t hw; @@ -141,10 +143,20 @@ void __init q40ide_init(void) 0, NULL, // m68kide_iops, q40ide_default_irq(pcide_bases[i])); - index = ide_register_hw(&hw, NULL, 1, &hwif); - // **FIXME** - if (index != -1) + + hwif = ide_find_port(hw.io_ports[IDE_DATA_OFFSET]); + if (hwif) { + ide_init_port_data(hwif, hwif->index); + ide_init_port_hw(hwif, &hw); hwif->mmio = 1; + + idx[i] = hwif->index; + } } + + ide_device_add(idx); + + return 0; } +module_init(q40ide_init); diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c index 912e73853fa..2bac4c1a653 100644 --- a/drivers/ide/legacy/qd65xx.c +++ b/drivers/ide/legacy/qd65xx.c @@ -478,8 +478,7 @@ int probe_qd65xx = 0; module_param_named(probe, probe_qd65xx, bool, 0); MODULE_PARM_DESC(probe, "probe for QD65xx chipsets"); -/* Can be called directly from ide.c. */ -int __init qd65xx_init(void) +static int __init qd65xx_init(void) { if (probe_qd65xx == 0) return -ENODEV; @@ -492,9 +491,7 @@ int __init qd65xx_init(void) return 0; } -#ifdef MODULE module_init(qd65xx_init); -#endif MODULE_AUTHOR("Samuel Thibault"); MODULE_DESCRIPTION("support of qd65xx vlb ide chipset"); diff --git a/drivers/ide/legacy/umc8672.c b/drivers/ide/legacy/umc8672.c index 79577b91687..a1ae1ae6699 100644 --- a/drivers/ide/legacy/umc8672.c +++ b/drivers/ide/legacy/umc8672.c @@ -169,8 +169,7 @@ int probe_umc8672 = 0; module_param_named(probe, probe_umc8672, bool, 0); MODULE_PARM_DESC(probe, "probe for UMC8672 chipset"); -/* Can be called directly from ide.c. */ -int __init umc8672_init(void) +static int __init umc8672_init(void) { if (probe_umc8672 == 0) goto out; @@ -181,9 +180,7 @@ out: return -ENODEV;; } -#ifdef MODULE module_init(umc8672_init); -#endif MODULE_AUTHOR("Wolfram Podien"); MODULE_DESCRIPTION("Support for UMC 8672 IDE chipset"); diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index a4d0d4ca73d..2d3e5115b83 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -395,26 +395,10 @@ static int auide_dma_test_irq(ide_drive_t *drive) return 0; } -static void auide_dma_host_on(ide_drive_t *drive) +static void auide_dma_host_set(ide_drive_t *drive, int on) { } -static int auide_dma_on(ide_drive_t *drive) -{ - drive->using_dma = 1; - - return 0; -} - -static void auide_dma_host_off(ide_drive_t *drive) -{ -} - -static void auide_dma_off_quietly(ide_drive_t *drive) -{ - drive->using_dma = 0; -} - static void auide_dma_lost_irq(ide_drive_t *drive) { printk(KERN_ERR "%s: IRQ lost\n", drive->name); @@ -641,12 +625,13 @@ static int au_ide_probe(struct device *dev) /* FIXME: This might possibly break PCMCIA IDE devices */ hwif = &ide_hwifs[pdev->id]; - hwif->irq = ahwif->irq; - hwif->chipset = ide_au1xxx; memset(&hw, 0, sizeof(hw)); auide_setup_ports(&hw, ahwif); - memcpy(hwif->io_ports, hw.io_ports, sizeof(hwif->io_ports)); + hw.irq = ahwif->irq; + hw.chipset = ide_au1xxx; + + ide_init_port_hw(hwif, &hw); hwif->ultra_mask = 0x0; /* Disable Ultra DMA */ #ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA @@ -660,7 +645,6 @@ static int au_ide_probe(struct device *dev) hwif->pio_mask = ATA_PIO4; hwif->host_flags = IDE_HFLAG_POST_SET_MODE; - hwif->noprobe = 0; hwif->drives[0].unmask = 1; hwif->drives[1].unmask = 1; @@ -682,29 +666,25 @@ static int au_ide_probe(struct device *dev) hwif->set_dma_mode = &auide_set_dma_mode; #ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA - hwif->dma_off_quietly = &auide_dma_off_quietly; hwif->dma_timeout = &auide_dma_timeout; hwif->mdma_filter = &auide_mdma_filter; + hwif->dma_host_set = &auide_dma_host_set; hwif->dma_exec_cmd = &auide_dma_exec_cmd; hwif->dma_start = &auide_dma_start; hwif->ide_dma_end = &auide_dma_end; hwif->dma_setup = &auide_dma_setup; hwif->ide_dma_test_irq = &auide_dma_test_irq; - hwif->dma_host_off = &auide_dma_host_off; - hwif->dma_host_on = &auide_dma_host_on; hwif->dma_lost_irq = &auide_dma_lost_irq; - hwif->ide_dma_on = &auide_dma_on; -#else /* !CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA */ +#endif hwif->channel = 0; - hwif->hold = 1; hwif->select_data = 0; /* no chipset-specific code */ hwif->config_data = 0; /* no chipset-specific code */ hwif->drives[0].autotune = 1; /* 1=autotune, 2=noautotune, 0=default */ hwif->drives[1].autotune = 1; -#endif + hwif->drives[0].no_io_32bit = 1; hwif->drives[1].no_io_32bit = 1; diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c index 521edd41b57..8b3959dfa2b 100644 --- a/drivers/ide/mips/swarm.c +++ b/drivers/ide/mips/swarm.c @@ -117,6 +117,7 @@ static int __devinit swarm_ide_probe(struct device *dev) default_hwif_mmiops(hwif); /* Prevent resource map manipulation. */ hwif->mmio = 1; + hwif->chipset = ide_generic; hwif->noprobe = 0; for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) diff --git a/drivers/ide/pci/Makefile b/drivers/ide/pci/Makefile index 95d1ea8f1f1..94803253e8a 100644 --- a/drivers/ide/pci/Makefile +++ b/drivers/ide/pci/Makefile @@ -36,4 +36,8 @@ obj-$(CONFIG_BLK_DEV_VIA82CXXX) += via82cxxx.o # Must appear at the end of the block obj-$(CONFIG_BLK_DEV_GENERIC) += generic.o +ifeq ($(CONFIG_BLK_DEV_CMD640), m) + obj-m += cmd640.o +endif + EXTRA_CFLAGS := -Idrivers/ide diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c index 5ae26564fb7..491871984aa 100644 --- a/drivers/ide/pci/atiixp.c +++ b/drivers/ide/pci/atiixp.c @@ -1,5 +1,5 @@ /* - * linux/drivers/ide/pci/atiixp.c Version 0.03 Aug 3 2007 + * linux/drivers/ide/pci/atiixp.c Version 0.05 Nov 9 2007 * * Copyright (C) 2003 ATI Inc. <hyu@ati.com> * Copyright (C) 2004,2007 Bartlomiej Zolnierkiewicz @@ -43,47 +43,8 @@ static atiixp_ide_timing mdma_timing[] = { { 0x02, 0x00 }, }; -static int save_mdma_mode[4]; - static DEFINE_SPINLOCK(atiixp_lock); -static void atiixp_dma_host_on(ide_drive_t *drive) -{ - struct pci_dev *dev = drive->hwif->pci_dev; - unsigned long flags; - u16 tmp16; - - spin_lock_irqsave(&atiixp_lock, flags); - - pci_read_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, &tmp16); - if (save_mdma_mode[drive->dn]) - tmp16 &= ~(1 << drive->dn); - else - tmp16 |= (1 << drive->dn); - pci_write_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, tmp16); - - spin_unlock_irqrestore(&atiixp_lock, flags); - - ide_dma_host_on(drive); -} - -static void atiixp_dma_host_off(ide_drive_t *drive) -{ - struct pci_dev *dev = drive->hwif->pci_dev; - unsigned long flags; - u16 tmp16; - - spin_lock_irqsave(&atiixp_lock, flags); - - pci_read_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, &tmp16); - tmp16 &= ~(1 << drive->dn); - pci_write_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, tmp16); - - spin_unlock_irqrestore(&atiixp_lock, flags); - - ide_dma_host_off(drive); -} - /** * atiixp_set_pio_mode - set host controller for PIO mode * @drive: drive @@ -132,26 +93,33 @@ static void atiixp_set_dma_mode(ide_drive_t *drive, const u8 speed) int timing_shift = (drive->dn & 2) ? 16 : 0 + (drive->dn & 1) ? 0 : 8; u32 tmp32; u16 tmp16; + u16 udma_ctl = 0; spin_lock_irqsave(&atiixp_lock, flags); - save_mdma_mode[drive->dn] = 0; + pci_read_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, &udma_ctl); + if (speed >= XFER_UDMA_0) { pci_read_config_word(dev, ATIIXP_IDE_UDMA_MODE, &tmp16); tmp16 &= ~(0x07 << (drive->dn * 4)); tmp16 |= ((speed & 0x07) << (drive->dn * 4)); pci_write_config_word(dev, ATIIXP_IDE_UDMA_MODE, tmp16); - } else { - if ((speed >= XFER_MW_DMA_0) && (speed <= XFER_MW_DMA_2)) { - save_mdma_mode[drive->dn] = speed; - pci_read_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, &tmp32); - tmp32 &= ~(0xff << timing_shift); - tmp32 |= (mdma_timing[speed & 0x03].recover_width << timing_shift) | - (mdma_timing[speed & 0x03].command_width << (timing_shift + 4)); - pci_write_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, tmp32); - } + + udma_ctl |= (1 << drive->dn); + } else if (speed >= XFER_MW_DMA_0) { + u8 i = speed & 0x03; + + pci_read_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, &tmp32); + tmp32 &= ~(0xff << timing_shift); + tmp32 |= (mdma_timing[i].recover_width << timing_shift) | + (mdma_timing[i].command_width << (timing_shift + 4)); + pci_write_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, tmp32); + + udma_ctl &= ~(1 << drive->dn); } + pci_write_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, udma_ctl); + spin_unlock_irqrestore(&atiixp_lock, flags); } @@ -181,9 +149,6 @@ static void __devinit init_hwif_atiixp(ide_hwif_t *hwif) hwif->cbl = ATA_CBL_PATA80; else hwif->cbl = ATA_CBL_PATA40; - - hwif->dma_host_on = &atiixp_dma_host_on; - hwif->dma_host_off = &atiixp_dma_host_off; } static const struct ide_port_info atiixp_pci_info[] __devinitdata = { diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index 4aa48104e0c..da3565e0071 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -706,9 +706,9 @@ static int pci_conf2(void) } /* - * Probe for a cmd640 chipset, and initialize it if found. Called from ide.c + * Probe for a cmd640 chipset, and initialize it if found. */ -int __init ide_probe_for_cmd640x (void) +static int __init cmd640x_init(void) { #ifdef CONFIG_BLK_DEV_CMD640_ENHANCED int second_port_toggled = 0; @@ -717,6 +717,7 @@ int __init ide_probe_for_cmd640x (void) const char *bus_type, *port2; unsigned int index; u8 b, cfr; + u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (cmd640_vlb && probe_for_cmd640_vlb()) { bus_type = "VLB"; @@ -769,6 +770,8 @@ int __init ide_probe_for_cmd640x (void) cmd_hwif0->set_pio_mode = &cmd640_set_pio_mode; #endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ + idx[0] = cmd_hwif0->index; + /* * Ensure compatibility by always using the slowest timings * for access to the drive's command register block, @@ -826,6 +829,8 @@ int __init ide_probe_for_cmd640x (void) cmd_hwif1->pio_mask = ATA_PIO5; cmd_hwif1->set_pio_mode = &cmd640_set_pio_mode; #endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ + + idx[1] = cmd_hwif1->index; } printk(KERN_INFO "%s: %sserialized, secondary interface %s\n", cmd_hwif1->name, cmd_hwif0->serialized ? "" : "not ", port2); @@ -872,6 +877,13 @@ int __init ide_probe_for_cmd640x (void) #ifdef CMD640_DUMP_REGS cmd640_dump_regs(); #endif + + ide_device_add(idx); + return 1; } +module_param_named(probe_vlb, cmd640_vlb, bool, 0); +MODULE_PARM_DESC(probe_vlb, "probe for VLB version of CMD640 chipset"); + +module_init(cmd640x_init); diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index 0b1e9479f01..cd4eb9def15 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -1,5 +1,5 @@ /* - * linux/drivers/ide/pci/cmd64x.c Version 1.52 Dec 24, 2007 + * linux/drivers/ide/pci/cmd64x.c Version 1.53 Dec 24, 2007 * * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines. * Due to massive hardware bugs, UltraDMA is only supported @@ -22,8 +22,6 @@ #include <asm/io.h> -#define DISPLAY_CMD64X_TIMINGS - #define CMD_DEBUG 0 #if CMD_DEBUG @@ -37,11 +35,6 @@ */ #define CFR 0x50 #define CFR_INTR_CH0 0x04 -#define CNTRL 0x51 -#define CNTRL_ENA_1ST 0x04 -#define CNTRL_ENA_2ND 0x08 -#define CNTRL_DIS_RA0 0x40 -#define CNTRL_DIS_RA1 0x80 #define CMDTIM 0x52 #define ARTTIM0 0x53 @@ -60,108 +53,13 @@ #define MRDMODE 0x71 #define MRDMODE_INTR_CH0 0x04 #define MRDMODE_INTR_CH1 0x08 -#define MRDMODE_BLK_CH0 0x10 -#define MRDMODE_BLK_CH1 0x20 -#define BMIDESR0 0x72 #define UDIDETCR0 0x73 #define DTPR0 0x74 #define BMIDECR1 0x78 #define BMIDECSR 0x79 -#define BMIDESR1 0x7A #define UDIDETCR1 0x7B #define DTPR1 0x7C -#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_IDE_PROC_FS) -#include <linux/stat.h> -#include <linux/proc_fs.h> - -static u8 cmd64x_proc = 0; - -#define CMD_MAX_DEVS 5 - -static struct pci_dev *cmd_devs[CMD_MAX_DEVS]; -static int n_cmd_devs; - -static char * print_cmd64x_get_info (char *buf, struct pci_dev *dev, int index) -{ - char *p = buf; - u8 reg72 = 0, reg73 = 0; /* primary */ - u8 reg7a = 0, reg7b = 0; /* secondary */ - u8 reg50 = 1, reg51 = 1, reg57 = 0, reg71 = 0; /* extra */ - - p += sprintf(p, "\nController: %d\n", index); - p += sprintf(p, "PCI-%x Chipset.\n", dev->device); - - (void) pci_read_config_byte(dev, CFR, ®50); - (void) pci_read_config_byte(dev, CNTRL, ®51); - (void) pci_read_config_byte(dev, ARTTIM23, ®57); - (void) pci_read_config_byte(dev, MRDMODE, ®71); - (void) pci_read_config_byte(dev, BMIDESR0, ®72); - (void) pci_read_config_byte(dev, UDIDETCR0, ®73); - (void) pci_read_config_byte(dev, BMIDESR1, ®7a); - (void) pci_read_config_byte(dev, UDIDETCR1, ®7b); - - /* PCI0643/6 originally didn't have the primary channel enable bit */ - if ((dev->device == PCI_DEVICE_ID_CMD_643) || - (dev->device == PCI_DEVICE_ID_CMD_646 && dev->revision < 3)) - reg51 |= CNTRL_ENA_1ST; - - p += sprintf(p, "---------------- Primary Channel " - "---------------- Secondary Channel ------------\n"); - p += sprintf(p, " %s %s\n", - (reg51 & CNTRL_ENA_1ST) ? "enabled " : "disabled", - (reg51 & CNTRL_ENA_2ND) ? "enabled " : "disabled"); - p += sprintf(p, "---------------- drive0 --------- drive1 " - "-------- drive0 --------- drive1 ------\n"); - p += sprintf(p, "DMA enabled: %s %s" - " %s %s\n", - (reg72 & 0x20) ? "yes" : "no ", (reg72 & 0x40) ? "yes" : "no ", - (reg7a & 0x20) ? "yes" : "no ", (reg7a & 0x40) ? "yes" : "no "); - p += sprintf(p, "UltraDMA mode: %s (%c) %s (%c)", - ( reg73 & 0x01) ? " on" : "off", - ((reg73 & 0x30) == 0x30) ? ((reg73 & 0x04) ? '3' : '0') : - ((reg73 & 0x30) == 0x20) ? ((reg73 & 0x04) ? '3' : '1') : - ((reg73 & 0x30) == 0x10) ? ((reg73 & 0x04) ? '4' : '2') : - ((reg73 & 0x30) == 0x00) ? ((reg73 & 0x04) ? '5' : '2') : '?', - ( reg73 & 0x02) ? " on" : "off", - ((reg73 & 0xC0) == 0xC0) ? ((reg73 & 0x08) ? '3' : '0') : - ((reg73 & 0xC0) == 0x80) ? ((reg73 & 0x08) ? '3' : '1') : - ((reg73 & 0xC0) == 0x40) ? ((reg73 & 0x08) ? '4' : '2') : - ((reg73 & 0xC0) == 0x00) ? ((reg73 & 0x08) ? '5' : '2') : '?'); - p += sprintf(p, " %s (%c) %s (%c)\n", - ( reg7b & 0x01) ? " on" : "off", - ((reg7b & 0x30) == 0x30) ? ((reg7b & 0x04) ? '3' : '0') : - ((reg7b & 0x30) == 0x20) ? ((reg7b & 0x04) ? '3' : '1') : - ((reg7b & 0x30) == 0x10) ? ((reg7b & 0x04) ? '4' : '2') : - ((reg7b & 0x30) == 0x00) ? ((reg7b & 0x04) ? '5' : '2') : '?', - ( reg7b & 0x02) ? " on" : "off", - ((reg7b & 0xC0) == 0xC0) ? ((reg7b & 0x08) ? '3' : '0') : - ((reg7b & 0xC0) == 0x80) ? ((reg7b & 0x08) ? '3' : '1') : - ((reg7b & 0xC0) == 0x40) ? ((reg7b & 0x08) ? '4' : '2') : - ((reg7b & 0xC0) == 0x00) ? ((reg7b & 0x08) ? '5' : '2') : '?'); - p += sprintf(p, "Interrupt: %s, %s %s, %s\n", - (reg71 & MRDMODE_BLK_CH0 ) ? "blocked" : "enabled", - (reg50 & CFR_INTR_CH0 ) ? "pending" : "clear ", - (reg71 & MRDMODE_BLK_CH1 ) ? "blocked" : "enabled", - (reg57 & ARTTIM23_INTR_CH1) ? "pending" : "clear "); - - return (char *)p; -} - -static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count) -{ - char *p = buffer; - int i; - - for (i = 0; i < n_cmd_devs; i++) { - struct pci_dev *dev = cmd_devs[i]; - p = print_cmd64x_get_info(p, dev, i); - } - return p-buffer; /* => must be less than 4k! */ -} - -#endif /* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_IDE_PROC_FS) */ - static u8 quantize_timing(int timing, int quant) { return (timing + quant - 1) / quant; @@ -472,16 +370,6 @@ static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev, const cha mrdmode &= ~0x30; (void) pci_write_config_byte(dev, MRDMODE, (mrdmode | 0x02)); -#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_IDE_PROC_FS) - - cmd_devs[n_cmd_devs++] = dev; - - if (!cmd64x_proc) { - cmd64x_proc = 1; - ide_pci_create_host_proc("cmd64x", cmd64x_get_info); - } -#endif /* DISPLAY_CMD64X_TIMINGS && CONFIG_IDE_PROC_FS */ - return 0; } diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index d1a91bcb5b2..6ec00b8d7ec 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -71,7 +71,6 @@ static void cs5520_set_pio_mode(ide_drive_t *drive, const u8 pio) ide_hwif_t *hwif = HWIF(drive); struct pci_dev *pdev = hwif->pci_dev; int controller = drive->dn > 1 ? 1 : 0; - u8 reg; /* FIXME: if DMA = 1 do we need to set the DMA bit here ? */ @@ -91,11 +90,6 @@ static void cs5520_set_pio_mode(ide_drive_t *drive, const u8 pio) pci_write_config_byte(pdev, 0x66 + 4*controller + (drive->dn&1), (cs5520_pio_clocks[pio].recovery << 4) | (cs5520_pio_clocks[pio].assert)); - - /* Set the DMA enable/disable flag */ - reg = inb(hwif->dma_base + 0x02 + 8*controller); - reg |= 1<<((drive->dn&1)+5); - outb(reg, hwif->dma_base + 0x02 + 8*controller); } static void cs5520_set_dma_mode(ide_drive_t *drive, const u8 speed) @@ -109,13 +103,14 @@ static void cs5520_set_dma_mode(ide_drive_t *drive, const u8 speed) * We wrap the DMA activate to set the vdma flag. This is needed * so that the IDE DMA layer issues PIO not DMA commands over the * DMA channel + * + * ATAPI is harder so disable it for now using IDE_HFLAG_NO_ATAPI_DMA */ - -static int cs5520_dma_on(ide_drive_t *drive) + +static void cs5520_dma_host_set(ide_drive_t *drive, int on) { - /* ATAPI is harder so leave it for now */ - drive->vdma = 1; - return 0; + drive->vdma = on; + ide_dma_host_set(drive, on); } static void __devinit init_hwif_cs5520(ide_hwif_t *hwif) @@ -126,7 +121,7 @@ static void __devinit init_hwif_cs5520(ide_hwif_t *hwif) if (hwif->dma_base == 0) return; - hwif->ide_dma_on = &cs5520_dma_on; + hwif->dma_host_set = &cs5520_dma_host_set; } #define DECLARE_CS_DEV(name_str) \ diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c index 1cd4e9cb052..3ec4c659a37 100644 --- a/drivers/ide/pci/cy82c693.c +++ b/drivers/ide/pci/cy82c693.c @@ -1,5 +1,5 @@ /* - * linux/drivers/ide/pci/cy82c693.c Version 0.42 Oct 23, 2007 + * linux/drivers/ide/pci/cy82c693.c Version 0.44 Nov 8, 2007 * * Copyright (C) 1998-2000 Andreas S. Krebs (akrebs@altavista.net), Maintainer * Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org>, Integrator @@ -176,17 +176,12 @@ static void compute_clocks (u8 pio, pio_clocks_t *p_pclk) * set DMA mode a specific channel for CY82C693 */ -static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single) +static void cy82c693_set_dma_mode(ide_drive_t *drive, const u8 mode) { - u8 index = 0, data = 0; + ide_hwif_t *hwif = drive->hwif; + u8 single = (mode & 0x10) >> 4, index = 0, data = 0; - if (mode>2) /* make sure we set a valid mode */ - mode = 2; - - if (mode > drive->id->tDMA) /* to be absolutly sure we have a valid mode */ - mode = drive->id->tDMA; - - index = (HWIF(drive)->channel==0) ? CY82_INDEX_CHANNEL0 : CY82_INDEX_CHANNEL1; + index = hwif->channel ? CY82_INDEX_CHANNEL1 : CY82_INDEX_CHANNEL0; #if CY82C693_DEBUG_LOGS /* for debug let's show the previous values */ @@ -199,7 +194,7 @@ static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single) (data&0x3), ((data>>2)&1)); #endif /* CY82C693_DEBUG_LOGS */ - data = (u8)mode|(u8)(single<<2); + data = (mode & 3) | (single << 2); outb(index, CY82_INDEX_PORT); outb(data, CY82_DATA_PORT); @@ -207,7 +202,7 @@ static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single) #if CY82C693_DEBUG_INFO printk(KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n", drive->name, HWIF(drive)->channel, drive->select.b.unit, - mode, single); + mode & 3, single); #endif /* CY82C693_DEBUG_INFO */ /* @@ -230,39 +225,6 @@ static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single) #endif /* CY82C693_DEBUG_INFO */ } -/* - * used to set DMA mode for CY82C693 (single and multi modes) - */ -static int cy82c693_ide_dma_on (ide_drive_t *drive) -{ - struct hd_driveid *id = drive->id; - -#if CY82C693_DEBUG_INFO - printk (KERN_INFO "dma_on: %s\n", drive->name); -#endif /* CY82C693_DEBUG_INFO */ - - if (id != NULL) { - /* Enable DMA on any drive that has DMA - * (multi or single) enabled - */ - if (id->field_valid & 2) { /* regular DMA */ - int mmode, smode; - - mmode = id->dma_mword & (id->dma_mword >> 8); - smode = id->dma_1word & (id->dma_1word >> 8); - - if (mmode != 0) { - /* enable multi */ - cy82c693_dma_enable(drive, (mmode >> 1), 0); - } else if (smode != 0) { - /* enable single */ - cy82c693_dma_enable(drive, (smode >> 1), 1); - } - } - } - return __ide_dma_on(drive); -} - static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio) { ide_hwif_t *hwif = HWIF(drive); @@ -429,11 +391,7 @@ static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const c static void __devinit init_hwif_cy82c693(ide_hwif_t *hwif) { hwif->set_pio_mode = &cy82c693_set_pio_mode; - - if (hwif->dma_base == 0) - return; - - hwif->ide_dma_on = &cy82c693_ide_dma_on; + hwif->set_dma_mode = &cy82c693_set_dma_mode; } static void __devinit init_iops_cy82c693(ide_hwif_t *hwif) @@ -454,11 +412,11 @@ static const struct ide_port_info cy82c693_chipset __devinitdata = { .init_iops = init_iops_cy82c693, .init_hwif = init_hwif_cy82c693, .chipset = ide_cy82c693, - .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_TRUST_BIOS_FOR_DMA | + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_CY82C693 | IDE_HFLAG_BOOTABLE, .pio_mask = ATA_PIO4, - .swdma_mask = ATA_SWDMA2_ONLY, - .mwdma_mask = ATA_MWDMA2_ONLY, + .swdma_mask = ATA_SWDMA2, + .mwdma_mask = ATA_MWDMA2, }; static int __devinit cy82c693_init_one(struct pci_dev *dev, const struct pci_device_id *id) diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c index 83829081640..26aa492071b 100644 --- a/drivers/ide/pci/delkin_cb.c +++ b/drivers/ide/pci/delkin_cb.c @@ -80,7 +80,7 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) hw.irq = dev->irq; hw.chipset = ide_pci; /* this enables IRQ sharing */ - rc = ide_register_hw(&hw, &ide_undecoded_slave, 0, &hwif); + rc = ide_register_hw(&hw, &ide_undecoded_slave, &hwif); if (rc < 0) { printk(KERN_ERR "delkin_cb: ide_register_hw failed (%d)\n", rc); pci_disable_device(dev); diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 3777fb8c804..12685939a81 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -725,15 +725,18 @@ static void hpt3xx_set_pio_mode(ide_drive_t *drive, const u8 pio) hpt3xx_set_mode(drive, XFER_PIO_0 + pio); } -static int hpt3xx_quirkproc(ide_drive_t *drive) +static void hpt3xx_quirkproc(ide_drive_t *drive) { struct hd_driveid *id = drive->id; const char **list = quirk_drives; while (*list) - if (strstr(id->model, *list++)) - return 1; - return 0; + if (strstr(id->model, *list++)) { + drive->quirk_list = 1; + return; + } + + drive->quirk_list = 0; } static void hpt3xx_maskproc(ide_drive_t *drive, int mask) diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index 99b7d763b6c..e610a5340fd 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -431,33 +431,29 @@ static u8 __devinit ata66_it821x(ide_hwif_t *hwif) } /** - * it821x_fixup - post init callback - * @hwif: interface + * it821x_quirkproc - post init callback + * @drive: drive * - * This callback is run after the drives have been probed but + * This callback is run after the drive has been probed but * before anything gets attached. It allows drivers to do any * final tuning that is needed, or fixups to work around bugs. */ -static void __devinit it821x_fixups(ide_hwif_t *hwif) +static void __devinit it821x_quirkproc(ide_drive_t *drive) { - struct it821x_dev *itdev = ide_get_hwifdata(hwif); - int i; + struct it821x_dev *itdev = ide_get_hwifdata(drive->hwif); + struct hd_driveid *id = drive->id; + u16 *idbits = (u16 *)drive->id; - if(!itdev->smart) { + if (!itdev->smart) { /* * If we are in pass through mode then not much * needs to be done, but we do bother to clear the * IRQ mask as we may well be in PIO (eg rev 0x10) * for now and we know unmasking is safe on this chipset. */ - for (i = 0; i < 2; i++) { - ide_drive_t *drive = &hwif->drives[i]; - if(drive->present) - drive->unmask = 1; - } - return; - } + drive->unmask = 1; + } else { /* * Perform fixups on smart mode. We need to "lose" some * capabilities the firmware lacks but does not filter, and @@ -465,16 +461,6 @@ static void __devinit it821x_fixups(ide_hwif_t *hwif) * in RAID mode. */ - for(i = 0; i < 2; i++) { - ide_drive_t *drive = &hwif->drives[i]; - struct hd_driveid *id; - u16 *idbits; - - if(!drive->present) - continue; - id = drive->id; - idbits = (u16 *)drive->id; - /* Check for RAID v native */ if(strstr(id->model, "Integrated Technology Express")) { /* In raid mode the ident block is slightly buggy @@ -537,6 +523,8 @@ static void __devinit init_hwif_it821x(ide_hwif_t *hwif) struct it821x_dev *idev = kzalloc(sizeof(struct it821x_dev), GFP_KERNEL); u8 conf; + hwif->quirkproc = &it821x_quirkproc; + if (idev == NULL) { printk(KERN_ERR "it821x: out of memory, falling back to legacy behaviour.\n"); return; @@ -633,7 +621,6 @@ static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev, const cha .name = name_str, \ .init_chipset = init_chipset_it821x, \ .init_hwif = init_hwif_it821x, \ - .fixup = it821x_fixups, \ .host_flags = IDE_HFLAG_BOOTABLE, \ .pio_mask = ATA_PIO4, \ } diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c index ef4a99b99d1..89d2363a1eb 100644 --- a/drivers/ide/pci/pdc202xx_new.c +++ b/drivers/ide/pci/pdc202xx_new.c @@ -203,14 +203,17 @@ static u8 pdcnew_cable_detect(ide_hwif_t *hwif) return ATA_CBL_PATA80; } -static int pdcnew_quirkproc(ide_drive_t *drive) +static void pdcnew_quirkproc(ide_drive_t *drive) { const char **list, *model = drive->id->model; for (list = pdc_quirk_drives; *list != NULL; list++) - if (strstr(model, *list) != NULL) - return 2; - return 0; + if (strstr(model, *list) != NULL) { + drive->quirk_list = 2; + return; + } + + drive->quirk_list = 0; } static void pdcnew_reset(ide_drive_t *drive) diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index 67b2781e221..3a1e081fe39 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -176,14 +176,17 @@ static void pdc_old_disable_66MHz_clock(ide_hwif_t *hwif) outb(clock & ~(hwif->channel ? 0x08 : 0x02), clock_reg); } -static int pdc202xx_quirkproc (ide_drive_t *drive) +static void pdc202xx_quirkproc(ide_drive_t *drive) { const char **list, *model = drive->id->model; for (list = pdc_quirk_drives; *list != NULL; list++) - if (strstr(model, *list) != NULL) - return 2; - return 0; + if (strstr(model, *list) != NULL) { + drive->quirk_list = 2; + return; + } + + drive->quirk_list = 0; } static void pdc202xx_old_ide_dma_start(ide_drive_t *drive) diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c index fef20bd4aa7..32fdf53379f 100644 --- a/drivers/ide/pci/sc1200.c +++ b/drivers/ide/pci/sc1200.c @@ -220,9 +220,9 @@ static void sc1200_set_pio_mode(ide_drive_t *drive, const u8 pio) } if (mode != -1) { printk("SC1200: %s: changing (U)DMA mode\n", drive->name); - hwif->dma_off_quietly(drive); - if (ide_set_dma_mode(drive, mode) == 0) - hwif->dma_host_on(drive); + ide_dma_off_quietly(drive); + if (ide_set_dma_mode(drive, mode) == 0 && drive->using_dma) + hwif->dma_host_set(drive, 1); return; } diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index e9bd269547b..877c09bf482 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -164,25 +164,12 @@ static void svwks_set_dma_mode(ide_drive_t *drive, const u8 speed) ultra_timing &= ~(0x0F << (4*unit)); ultra_enable &= ~(0x01 << drive->dn); - switch(speed) { - case XFER_MW_DMA_2: - case XFER_MW_DMA_1: - case XFER_MW_DMA_0: - dma_timing |= dma_modes[speed - XFER_MW_DMA_0]; - break; - - case XFER_UDMA_5: - case XFER_UDMA_4: - case XFER_UDMA_3: - case XFER_UDMA_2: - case XFER_UDMA_1: - case XFER_UDMA_0: - dma_timing |= dma_modes[2]; - ultra_timing |= ((udma_modes[speed - XFER_UDMA_0]) << (4*unit)); - ultra_enable |= (0x01 << drive->dn); - default: - break; - } + if (speed >= XFER_UDMA_0) { + dma_timing |= dma_modes[2]; + ultra_timing |= (udma_modes[speed - XFER_UDMA_0] << (4 * unit)); + ultra_enable |= (0x01 << drive->dn); + } else if (speed >= XFER_MW_DMA_0) + dma_timing |= dma_modes[speed - XFER_MW_DMA_0]; pci_write_config_byte(dev, drive_pci2[drive->dn], dma_timing); pci_write_config_byte(dev, (0x56|hwif->channel), ultra_timing); diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 7e9dade5648..9e0be7d5498 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -277,21 +277,6 @@ sgiioc4_ide_dma_end(ide_drive_t * drive) return dma_stat; } -static int -sgiioc4_ide_dma_on(ide_drive_t * drive) -{ - drive->using_dma = 1; - - return 0; -} - -static void sgiioc4_dma_off_quietly(ide_drive_t *drive) -{ - drive->using_dma = 0; - - drive->hwif->dma_host_off(drive); -} - static void sgiioc4_set_dma_mode(ide_drive_t *drive, const u8 speed) { } @@ -303,13 +288,10 @@ sgiioc4_ide_dma_test_irq(ide_drive_t * drive) return sgiioc4_checkirq(HWIF(drive)); } -static void sgiioc4_dma_host_on(ide_drive_t * drive) -{ -} - -static void sgiioc4_dma_host_off(ide_drive_t * drive) +static void sgiioc4_dma_host_set(ide_drive_t *drive, int on) { - sgiioc4_clearirq(drive); + if (!on) + sgiioc4_clearirq(drive); } static void @@ -593,14 +575,11 @@ ide_init_sgiioc4(ide_hwif_t * hwif) hwif->mwdma_mask = ATA_MWDMA2_ONLY; + hwif->dma_host_set = &sgiioc4_dma_host_set; hwif->dma_setup = &sgiioc4_ide_dma_setup; hwif->dma_start = &sgiioc4_ide_dma_start; hwif->ide_dma_end = &sgiioc4_ide_dma_end; - hwif->ide_dma_on = &sgiioc4_ide_dma_on; - hwif->dma_off_quietly = &sgiioc4_dma_off_quietly; hwif->ide_dma_test_irq = &sgiioc4_ide_dma_test_irq; - hwif->dma_host_on = &sgiioc4_dma_host_on; - hwif->dma_host_off = &sgiioc4_dma_host_off; hwif->dma_lost_irq = &sgiioc4_dma_lost_irq; hwif->dma_timeout = &ide_dma_timeout; } @@ -614,6 +593,7 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) ide_hwif_t *hwif; int h; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw; /* * Find an empty HWIF; if none available, return -ENOMEM. @@ -653,21 +633,16 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) return -ENOMEM; } - if (hwif->io_ports[IDE_DATA_OFFSET] != cmd_base) { - hw_regs_t hw; - - /* Initialize the IO registers */ - memset(&hw, 0, sizeof(hw)); - sgiioc4_init_hwif_ports(&hw, cmd_base, ctl, irqport); - memcpy(hwif->io_ports, hw.io_ports, sizeof(hwif->io_ports)); - hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; - } + /* Initialize the IO registers */ + memset(&hw, 0, sizeof(hw)); + sgiioc4_init_hwif_ports(&hw, cmd_base, ctl, irqport); + hw.irq = dev->irq; + hw.chipset = ide_pci; + hw.dev = &dev->dev; + ide_init_port_hw(hwif, &hw); - hwif->irq = dev->irq; - hwif->chipset = ide_pci; hwif->pci_dev = dev; hwif->channel = 0; /* Single Channel chip */ - hwif->gendev.parent = &dev->dev;/* setup proper ancestral information */ /* The IOC4 uses MMIO rather than Port IO. */ default_hwif_mmiops(hwif); diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 7b45eaf5afd..908f37b4e0e 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -713,9 +713,6 @@ static int is_dev_seagate_sata(ide_drive_t *drive) const char *s = &drive->id->model[0]; unsigned len; - if (!drive->present) - return 0; - len = strnlen(s, sizeof(drive->id->model)); if ((len > 4) && (!memcmp(s, "ST", 2))) { @@ -730,18 +727,20 @@ static int is_dev_seagate_sata(ide_drive_t *drive) } /** - * siimage_fixup - post probe fixups - * @hwif: interface to fix up + * sil_quirkproc - post probe fixups + * @drive: drive * * Called after drive probe we use this to decide whether the * Seagate fixup must be applied. This used to be in init_iops but * that can occur before we know what drives are present. */ -static void __devinit siimage_fixup(ide_hwif_t *hwif) +static void __devinit sil_quirkproc(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; + /* Try and raise the rqsize */ - if (!is_sata(hwif) || !is_dev_seagate_sata(&hwif->drives[0])) + if (!is_sata(hwif) || !is_dev_seagate_sata(drive)) hwif->rqsize = 128; } @@ -804,6 +803,7 @@ static void __devinit init_hwif_siimage(ide_hwif_t *hwif) hwif->set_pio_mode = &sil_set_pio_mode; hwif->set_dma_mode = &sil_set_dma_mode; + hwif->quirkproc = &sil_quirkproc; if (sata) { static int first = 1; @@ -842,7 +842,6 @@ static void __devinit init_hwif_siimage(ide_hwif_t *hwif) .init_chipset = init_chipset_siimage, \ .init_iops = init_iops_siimage, \ .init_hwif = init_hwif_siimage, \ - .fixup = siimage_fixup, \ .host_flags = IDE_HFLAG_BOOTABLE, \ .pio_mask = ATA_PIO4, \ .mwdma_mask = ATA_MWDMA2, \ diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index 069f104fdce..c7a125b66c2 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -13,6 +13,7 @@ * -- Benjamin Herrenschmidt (01/11/03) benh@kernel.crashing.org * * Copyright (C) 2006-2007 MontaVista Software, Inc. <source@mvista.com> + * Copyright (C) 2007 Bartlomiej Zolnierkiewicz */ #include <linux/types.h> @@ -90,14 +91,8 @@ static void sl82c105_set_pio_mode(ide_drive_t *drive, const u8 pio) drive->drive_data &= 0xffff0000; drive->drive_data |= drv_ctrl; - if (!drive->using_dma) { - /* - * If we are actually using MW DMA, then we can not - * reprogram the interface drive control register. - */ - pci_write_config_word(dev, reg, drv_ctrl); - pci_read_config_word (dev, reg, &drv_ctrl); - } + pci_write_config_word(dev, reg, drv_ctrl); + pci_read_config_word (dev, reg, &drv_ctrl); printk(KERN_DEBUG "%s: selected %s (%dns) (%04X)\n", drive->name, ide_xfer_verbose(pio + XFER_PIO_0), @@ -123,17 +118,6 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed) */ drive->drive_data &= 0x0000ffff; drive->drive_data |= (unsigned long)drv_ctrl << 16; - - /* - * If we are already using DMA, we just reprogram - * the drive control register. - */ - if (drive->using_dma) { - struct pci_dev *dev = HWIF(drive)->pci_dev; - int reg = 0x44 + drive->dn * 4; - - pci_write_config_word(dev, reg, drv_ctrl); - } } /* @@ -201,6 +185,11 @@ static void sl82c105_dma_start(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; + int reg = 0x44 + drive->dn * 4; + + DBG(("%s(drive:%s)\n", __FUNCTION__, drive->name)); + + pci_write_config_word(dev, reg, drive->drive_data >> 16); sl82c105_reset_host(dev); ide_dma_start(drive); @@ -214,64 +203,24 @@ static void sl82c105_dma_timeout(ide_drive_t *drive) ide_dma_timeout(drive); } -static int sl82c105_ide_dma_on(ide_drive_t *drive) -{ - struct pci_dev *dev = HWIF(drive)->pci_dev; - int rc, reg = 0x44 + drive->dn * 4; - - DBG(("sl82c105_ide_dma_on(drive:%s)\n", drive->name)); - - rc = __ide_dma_on(drive); - if (rc == 0) { - pci_write_config_word(dev, reg, drive->drive_data >> 16); - - printk(KERN_INFO "%s: DMA enabled\n", drive->name); - } - return rc; -} - -static void sl82c105_dma_off_quietly(ide_drive_t *drive) +static int sl82c105_dma_end(ide_drive_t *drive) { struct pci_dev *dev = HWIF(drive)->pci_dev; int reg = 0x44 + drive->dn * 4; + int ret; - DBG(("sl82c105_dma_off_quietly(drive:%s)\n", drive->name)); + DBG(("%s(drive:%s)\n", __FUNCTION__, drive->name)); - pci_write_config_word(dev, reg, drive->drive_data); + ret = __ide_dma_end(drive); - ide_dma_off_quietly(drive); -} + pci_write_config_word(dev, reg, drive->drive_data); -/* - * Ok, that is nasty, but we must make sure the DMA timings - * won't be used for a PIO access. The solution here is - * to make sure the 16 bits mode is diabled on the channel - * when DMA is enabled, thus causing the chip to use PIO0 - * timings for those operations. - */ -static void sl82c105_selectproc(ide_drive_t *drive) -{ - ide_hwif_t *hwif = HWIF(drive); - struct pci_dev *dev = hwif->pci_dev; - u32 val, old, mask; - - //DBG(("sl82c105_selectproc(drive:%s)\n", drive->name)); - - mask = hwif->channel ? CTRL_P1F16 : CTRL_P0F16; - old = val = (u32)pci_get_drvdata(dev); - if (drive->using_dma) - val &= ~mask; - else - val |= mask; - if (old != val) { - pci_write_config_dword(dev, 0x40, val); - pci_set_drvdata(dev, (void *)val); - } + return ret; } /* * ATA reset will clear the 16 bits mode in the control - * register, we need to update our cache + * register, we need to reprogram it */ static void sl82c105_resetproc(ide_drive_t *drive) { @@ -281,7 +230,8 @@ static void sl82c105_resetproc(ide_drive_t *drive) DBG(("sl82c105_resetproc(drive:%s)\n", drive->name)); pci_read_config_dword(dev, 0x40, &val); - pci_set_drvdata(dev, (void *)val); + val |= (CTRL_P1F16 | CTRL_P0F16); + pci_write_config_dword(dev, 0x40, val); } /* @@ -334,7 +284,6 @@ static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev, const c pci_read_config_dword(dev, 0x40, &val); val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16; pci_write_config_dword(dev, 0x40, val); - pci_set_drvdata(dev, (void *)val); return dev->irq; } @@ -350,7 +299,6 @@ static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif) hwif->set_pio_mode = &sl82c105_set_pio_mode; hwif->set_dma_mode = &sl82c105_set_dma_mode; - hwif->selectproc = &sl82c105_selectproc; hwif->resetproc = &sl82c105_resetproc; if (!hwif->dma_base) @@ -369,10 +317,9 @@ static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif) hwif->mwdma_mask = ATA_MWDMA2; - hwif->ide_dma_on = &sl82c105_ide_dma_on; - hwif->dma_off_quietly = &sl82c105_dma_off_quietly; hwif->dma_lost_irq = &sl82c105_dma_lost_irq; hwif->dma_start = &sl82c105_dma_start; + hwif->ide_dma_end = &sl82c105_dma_end; hwif->dma_timeout = &sl82c105_dma_timeout; if (hwif->mate) diff --git a/drivers/ide/pci/trm290.c b/drivers/ide/pci/trm290.c index 0151d7fdfb8..04cd893e1ab 100644 --- a/drivers/ide/pci/trm290.c +++ b/drivers/ide/pci/trm290.c @@ -241,11 +241,7 @@ static int trm290_ide_dma_test_irq (ide_drive_t *drive) return (status == 0x00ff); } -static void trm290_dma_host_on(ide_drive_t *drive) -{ -} - -static void trm290_dma_host_off(ide_drive_t *drive) +static void trm290_dma_host_set(ide_drive_t *drive, int on) { } @@ -289,8 +285,7 @@ static void __devinit init_hwif_trm290(ide_hwif_t *hwif) ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 3); - hwif->dma_host_off = &trm290_dma_host_off; - hwif->dma_host_on = &trm290_dma_host_on; + hwif->dma_host_set = &trm290_dma_host_set; hwif->dma_setup = &trm290_dma_setup; hwif->dma_exec_cmd = &trm290_dma_exec_cmd; hwif->dma_start = &trm290_dma_start; diff --git a/drivers/ide/ppc/Makefile b/drivers/ide/ppc/Makefile new file mode 100644 index 00000000000..65af5848b28 --- /dev/null +++ b/drivers/ide/ppc/Makefile @@ -0,0 +1,3 @@ + +obj-$(CONFIG_BLK_DEV_IDE_PMAC) += pmac.o +obj-$(CONFIG_BLK_DEV_MPC8xx_IDE) += mpc8xx.o diff --git a/drivers/ide/ppc/mpc8xx.c b/drivers/ide/ppc/mpc8xx.c index 5f0da35ab5a..3fd5d45b5e0 100644 --- a/drivers/ide/ppc/mpc8xx.c +++ b/drivers/ide/ppc/mpc8xx.c @@ -838,3 +838,21 @@ void m8xx_ide_init(void) ppc_ide_md.default_io_base = m8xx_ide_default_io_base; ppc_ide_md.ide_init_hwif = m8xx_ide_init_hwif_ports; } + +static int __init mpc8xx_ide_probe(void) +{ + u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + +#ifdef IDE0_BASE_OFFSET + idx[0] = 0; +#ifdef IDE1_BASE_OFFSET + idx[1] = 1; +#endif +#endif + + ide_device_add(idx); + + return 0; +} + +module_init(mpc8xx_ide_probe); diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index 3dce80092ff..736d12c8e68 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1012,12 +1012,11 @@ pmac_ide_do_resume(ide_hwif_t *hwif) * rare machines unfortunately, but it's better this way. */ static int -pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) +pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif, hw_regs_t *hw) { struct device_node *np = pmif->node; const int *bidp; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; pmif->cable_80 = 0; pmif->broken_dma = pmif->broken_dma_warn = 0; @@ -1103,11 +1102,9 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) /* Tell common code _not_ to mess with resources */ hwif->mmio = 1; hwif->hwif_data = pmif; - memset(&hw, 0, sizeof(hw)); - pmac_ide_init_hwif_ports(&hw, pmif->regbase, 0, &hwif->irq); - memcpy(hwif->io_ports, hw.io_ports, sizeof(hwif->io_ports)); - hwif->chipset = ide_pmac; - hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET] || pmif->mediabay; + hw->chipset = ide_pmac; + ide_init_port_hw(hwif, hw); + hwif->noprobe = pmif->mediabay; hwif->hold = pmif->mediabay; hwif->cbl = pmif->cable_80 ? ATA_CBL_PATA80 : ATA_CBL_PATA40; hwif->drives[0].unmask = 1; @@ -1136,8 +1133,6 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) hwif->noprobe = 0; #endif /* CONFIG_PMAC_MEDIABAY */ - hwif->sg_max_nents = MAX_DCMDS; - #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC /* has a DBDMA controller channel */ if (pmif->dma_regs) @@ -1163,6 +1158,7 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) ide_hwif_t *hwif; pmac_ide_hwif_t *pmif; int i, rc; + hw_regs_t hw; i = 0; while (i < MAX_HWIFS && (ide_hwifs[i].io_ports[IDE_DATA_OFFSET] != 0 @@ -1205,7 +1201,6 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) regbase = (unsigned long) base; hwif->pci_dev = mdev->bus->pdev; - hwif->gendev.parent = &mdev->ofdev.dev; pmif->mdev = mdev; pmif->node = mdev->ofdev.node; @@ -1223,7 +1218,12 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ dev_set_drvdata(&mdev->ofdev.dev, hwif); - rc = pmac_ide_setup_device(pmif, hwif); + memset(&hw, 0, sizeof(hw)); + pmac_ide_init_hwif_ports(&hw, pmif->regbase, 0, NULL); + hw.irq = irq; + hw.dev = &mdev->ofdev.dev; + + rc = pmac_ide_setup_device(pmif, hwif, &hw); if (rc != 0) { /* The inteface is released to the common IDE layer */ dev_set_drvdata(&mdev->ofdev.dev, NULL); @@ -1282,6 +1282,7 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) void __iomem *base; unsigned long rbase, rlen; int i, rc; + hw_regs_t hw; np = pci_device_to_OF_node(pdev); if (np == NULL) { @@ -1315,7 +1316,6 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) } hwif->pci_dev = pdev; - hwif->gendev.parent = &pdev->dev; pmif->mdev = NULL; pmif->node = np; @@ -1332,7 +1332,12 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, hwif); - rc = pmac_ide_setup_device(pmif, hwif); + memset(&hw, 0, sizeof(hw)); + pmac_ide_init_hwif_ports(&hw, pmif->regbase, 0, NULL); + hw.irq = pdev->irq; + hw.dev = &pdev->dev; + + rc = pmac_ide_setup_device(pmif, hwif, &hw); if (rc != 0) { /* The inteface is released to the common IDE layer */ pci_set_drvdata(pdev, NULL); @@ -1698,11 +1703,7 @@ pmac_ide_dma_test_irq (ide_drive_t *drive) return 1; } -static void pmac_ide_dma_host_off(ide_drive_t *drive) -{ -} - -static void pmac_ide_dma_host_on(ide_drive_t *drive) +static void pmac_ide_dma_host_set(ide_drive_t *drive, int on) { } @@ -1748,15 +1749,14 @@ pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) return; } - hwif->dma_off_quietly = &ide_dma_off_quietly; - hwif->ide_dma_on = &__ide_dma_on; + hwif->sg_max_nents = MAX_DCMDS; + + hwif->dma_host_set = &pmac_ide_dma_host_set; hwif->dma_setup = &pmac_ide_dma_setup; hwif->dma_exec_cmd = &pmac_ide_dma_exec_cmd; hwif->dma_start = &pmac_ide_dma_start; hwif->ide_dma_end = &pmac_ide_dma_end; hwif->ide_dma_test_irq = &pmac_ide_dma_test_irq; - hwif->dma_host_off = &pmac_ide_dma_host_off; - hwif->dma_host_on = &pmac_ide_dma_host_on; hwif->dma_timeout = &ide_dma_timeout; hwif->dma_lost_irq = &pmac_ide_dma_lost_irq; @@ -1786,3 +1786,5 @@ pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) } #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ + +module_init(pmac_ide_probe); diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index d2cd5a3d38f..676c66e7288 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -165,13 +165,17 @@ static unsigned long ide_get_or_set_dma_base(const struct ide_port_info *d, ide_ dma_base = pci_resource_start(dev, baridx); - if (dma_base == 0) + if (dma_base == 0) { printk(KERN_ERR "%s: DMA base is invalid\n", d->name); + return 0; + } } - if ((d->host_flags & IDE_HFLAG_CS5520) == 0 && dma_base) { + if (hwif->channel) + dma_base += 8; + + if ((d->host_flags & IDE_HFLAG_CS5520) == 0) { u8 simplex_stat = 0; - dma_base += hwif->channel ? 8 : 0; switch(dev->device) { case PCI_DEVICE_ID_AL_M5219: @@ -359,6 +363,8 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, const struct ide_port unsigned long ctl = 0, base = 0; ide_hwif_t *hwif; u8 bootable = (d->host_flags & IDE_HFLAG_BOOTABLE) ? 1 : 0; + u8 oldnoprobe = 0; + struct hw_regs_s hw; if ((d->host_flags & IDE_HFLAG_ISA_PORTS) == 0) { /* Possibly we should fail if these checks report true */ @@ -381,26 +387,25 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, const struct ide_port } if ((hwif = ide_match_hwif(base, bootable, d->name)) == NULL) return NULL; /* no room in ide_hwifs[] */ - if (hwif->io_ports[IDE_DATA_OFFSET] != base || - hwif->io_ports[IDE_CONTROL_OFFSET] != (ctl | 2)) { - hw_regs_t hw; - - memset(&hw, 0, sizeof(hw)); -#ifndef CONFIG_IDE_ARCH_OBSOLETE_INIT - ide_std_init_ports(&hw, base, ctl | 2); -#else - ide_init_hwif_ports(&hw, base, ctl | 2, NULL); -#endif - memcpy(hwif->io_ports, hw.io_ports, sizeof(hwif->io_ports)); - hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; - } - hwif->chipset = d->chipset ? d->chipset : ide_pci; + + memset(&hw, 0, sizeof(hw)); + hw.irq = hwif->irq ? hwif->irq : irq; + hw.dev = &dev->dev; + hw.chipset = d->chipset ? d->chipset : ide_pci; + ide_std_init_ports(&hw, base, ctl | 2); + + if (hwif->io_ports[IDE_DATA_OFFSET] == base && + hwif->io_ports[IDE_CONTROL_OFFSET] == (ctl | 2)) + oldnoprobe = hwif->noprobe; + + ide_init_port_hw(hwif, &hw); + + hwif->noprobe = oldnoprobe; + hwif->pci_dev = dev; hwif->cds = d; hwif->channel = port; - if (!hwif->irq) - hwif->irq = irq; if (mate) { hwif->mate = mate; mate->mate = hwif; @@ -535,12 +540,8 @@ void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, int if ((hwif = ide_hwif_configure(dev, d, mate, port, pciirq)) == NULL) continue; - /* setup proper ancestral information */ - hwif->gendev.parent = &dev->dev; - *(idx + port) = hwif->index; - if (d->init_iops) d->init_iops(hwif); @@ -551,8 +552,6 @@ void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, int (d->host_flags & IDE_HFLAG_FORCE_LEGACY_IRQS)) hwif->irq = port ? 15 : 14; - hwif->fixup = d->fixup; - hwif->host_flags = d->host_flags; hwif->pio_mask = d->pio_mask; @@ -699,105 +698,3 @@ out: } EXPORT_SYMBOL_GPL(ide_setup_pci_devices); - -#ifdef CONFIG_IDEPCI_PCIBUS_ORDER -/* - * Module interfaces - */ - -static int pre_init = 1; /* Before first ordered IDE scan */ -static LIST_HEAD(ide_pci_drivers); - -/* - * __ide_pci_register_driver - attach IDE driver - * @driver: pci driver - * @module: owner module of the driver - * - * Registers a driver with the IDE layer. The IDE layer arranges that - * boot time setup is done in the expected device order and then - * hands the controllers off to the core PCI code to do the rest of - * the work. - * - * Returns are the same as for pci_register_driver - */ - -int __ide_pci_register_driver(struct pci_driver *driver, struct module *module, - const char *mod_name) -{ - if (!pre_init) - return __pci_register_driver(driver, module, mod_name); - driver->driver.owner = module; - list_add_tail(&driver->node, &ide_pci_drivers); - return 0; -} -EXPORT_SYMBOL_GPL(__ide_pci_register_driver); - -/** - * ide_scan_pcidev - find an IDE driver for a device - * @dev: PCI device to check - * - * Look for an IDE driver to handle the device we are considering. - * This is only used during boot up to get the ordering correct. After - * boot up the pci layer takes over the job. - */ - -static int __init ide_scan_pcidev(struct pci_dev *dev) -{ - struct list_head *l; - struct pci_driver *d; - - list_for_each(l, &ide_pci_drivers) { - d = list_entry(l, struct pci_driver, node); - if (d->id_table) { - const struct pci_device_id *id = - pci_match_id(d->id_table, dev); - - if (id != NULL && d->probe(dev, id) >= 0) { - dev->driver = d; - pci_dev_get(dev); - return 1; - } - } - } - return 0; -} - -/** - * ide_scan_pcibus - perform the initial IDE driver scan - * @scan_direction: set for reverse order scanning - * - * Perform the initial bus rather than driver ordered scan of the - * PCI drivers. After this all IDE pci handling becomes standard - * module ordering not traditionally ordered. - */ - -void __init ide_scan_pcibus (int scan_direction) -{ - struct pci_dev *dev = NULL; - struct pci_driver *d; - struct list_head *l, *n; - - pre_init = 0; - if (!scan_direction) - while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev))) - ide_scan_pcidev(dev); - else - while ((dev = pci_get_device_reverse(PCI_ANY_ID, PCI_ANY_ID, - dev))) - ide_scan_pcidev(dev); - - /* - * Hand the drivers over to the PCI layer now we - * are post init. - */ - - list_for_each_safe(l, n, &ide_pci_drivers) { - list_del(l); - d = list_entry(l, struct pci_driver, node); - if (__pci_register_driver(d, d->driver.owner, - d->driver.mod_name)) - printk(KERN_ERR "%s: failed to register %s driver\n", - __FUNCTION__, d->driver.mod_name); - } -} -#endif diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c index 48d647abea4..eaba4a9b231 100644 --- a/drivers/macintosh/mediabay.c +++ b/drivers/macintosh/mediabay.c @@ -563,7 +563,8 @@ static void media_bay_step(int i) ide_init_hwif_ports(&hw, (unsigned long) bay->cd_base, (unsigned long) 0, NULL); hw.irq = bay->cd_irq; hw.chipset = ide_pmac; - bay->cd_index = ide_register_hw(&hw, NULL, 0, NULL); + bay->cd_index = + ide_register_hw(&hw, NULL, NULL); pmu_resume(); } if (bay->cd_index == -1) { diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index 5452da1bb1a..b66da74caa5 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -47,12 +47,10 @@ #define LOG_TEMP 0 /* continously log temperature */ -#define I2C_DRIVERID_G4FAN 0x9001 /* fixme */ - static int do_probe( struct i2c_adapter *adapter, int addr, int kind); /* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */ -static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, +static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; @@ -357,7 +355,6 @@ static struct i2c_driver g4fan_driver = { .driver = { .name = "therm_windtunnel", }, - .id = I2C_DRIVERID_G4FAN, .attach_adapter = do_attach, .detach_client = do_detach, }; diff --git a/drivers/media/video/dpc7146.c b/drivers/media/video/dpc7146.c index 255dae30370..566e479e262 100644 --- a/drivers/media/video/dpc7146.c +++ b/drivers/media/video/dpc7146.c @@ -87,11 +87,24 @@ struct dpc int cur_input; /* current input */ }; +static int dpc_check_clients(struct device *dev, void *data) +{ + struct dpc* dpc = data; + struct i2c_client *client = i2c_verify_client(dev); + + if( !client ) + return 0; + + if( I2C_SAA7111A == client->addr ) + dpc->saa7111a = client; + + return 0; +} + /* fixme: add vbi stuff here */ static int dpc_probe(struct saa7146_dev* dev) { struct dpc* dpc = NULL; - struct i2c_client *client; dpc = kzalloc(sizeof(struct dpc), GFP_KERNEL); if( NULL == dpc ) { @@ -115,9 +128,7 @@ static int dpc_probe(struct saa7146_dev* dev) } /* loop through all i2c-devices on the bus and look who is there */ - list_for_each_entry(client, &dpc->i2c_adapter.clients, list) - if( I2C_SAA7111A == client->addr ) - dpc->saa7111a = client; + device_for_each_child(&dpc->i2c_adapter.dev, dpc, dpc_check_clients); /* check if all devices are present */ if( 0 == dpc->saa7111a ) { diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c index b6cd21e6dab..4895540be19 100644 --- a/drivers/media/video/ks0127.c +++ b/drivers/media/video/ks0127.c @@ -764,7 +764,6 @@ static struct i2c_client ks0127_client_tmpl = .addr = 0, .adapter = NULL, .driver = &i2c_driver_ks0127, - .usage_count = 0 }; static int ks0127_found_proc(struct i2c_adapter *adapter, int addr, int kind) diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 98ad3092a07..add6d0d680b 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -149,10 +149,33 @@ struct mxb static struct saa7146_extension extension; +static int mxb_check_clients(struct device *dev, void *data) +{ + struct mxb* mxb = data; + struct i2c_client *client = i2c_verify_client(dev); + + if( !client ) + return 0; + + if( I2C_ADDR_TEA6420_1 == client->addr ) + mxb->tea6420_1 = client; + if( I2C_ADDR_TEA6420_2 == client->addr ) + mxb->tea6420_2 = client; + if( I2C_TEA6415C_2 == client->addr ) + mxb->tea6415c = client; + if( I2C_ADDR_TDA9840 == client->addr ) + mxb->tda9840 = client; + if( I2C_SAA7111 == client->addr ) + mxb->saa7111a = client; + if( 0x60 == client->addr ) + mxb->tuner = client; + + return 0; +} + static int mxb_probe(struct saa7146_dev* dev) { struct mxb* mxb = NULL; - struct i2c_client *client; int result; if ((result = request_module("saa7111")) < 0) { @@ -195,20 +218,7 @@ static int mxb_probe(struct saa7146_dev* dev) } /* loop through all i2c-devices on the bus and look who is there */ - list_for_each_entry(client, &mxb->i2c_adapter.clients, list) { - if( I2C_ADDR_TEA6420_1 == client->addr ) - mxb->tea6420_1 = client; - if( I2C_ADDR_TEA6420_2 == client->addr ) - mxb->tea6420_2 = client; - if( I2C_TEA6415C_2 == client->addr ) - mxb->tea6415c = client; - if( I2C_ADDR_TDA9840 == client->addr ) - mxb->tda9840 = client; - if( I2C_SAA7111 == client->addr ) - mxb->saa7111a = client; - if( 0x60 == client->addr ) - mxb->tuner = client; - } + device_for_each_child(&mxb->i2c_adapter.dev, mxb, mxb_check_clients); /* check if all devices are present */ if( 0 == mxb->tea6420_1 || 0 == mxb->tea6420_2 || 0 == mxb->tea6415c diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index 9a03dc82c6c..5bb75294b5a 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -2589,11 +2589,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) /* First try D1 and then SAA7191 */ if (vino_drvdata->camera.driver && (vino_drvdata->camera.owner == VINO_NO_CHANNEL)) { - if (i2c_use_client(vino_drvdata->camera.driver)) { - ret = -ENODEV; - goto out; - } - + i2c_use_client(vino_drvdata->camera.driver); vino_drvdata->camera.owner = vcs->channel; vcs->input = VINO_INPUT_D1; vcs->data_norm = VINO_DATA_NORM_D1; @@ -2602,11 +2598,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) int input, data_norm; int saa7191_input; - if (i2c_use_client(vino_drvdata->decoder.driver)) { - ret = -ENODEV; - goto out; - } - + i2c_use_client(vino_drvdata->decoder.driver); input = VINO_INPUT_COMPOSITE; saa7191_input = vino_get_saa7191_input(input); @@ -2688,10 +2680,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) } if (vino_drvdata->decoder.owner == VINO_NO_CHANNEL) { - if (i2c_use_client(vino_drvdata->decoder.driver)) { - ret = -ENODEV; - goto out; - } + i2c_use_client(vino_drvdata->decoder.driver); vino_drvdata->decoder.owner = vcs->channel; } @@ -2759,10 +2748,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) } if (vino_drvdata->camera.owner == VINO_NO_CHANNEL) { - if (i2c_use_client(vino_drvdata->camera.driver)) { - ret = -ENODEV; - goto out; - } + i2c_use_client(vino_drvdata->camera.driver); vino_drvdata->camera.owner = vcs->channel; } diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c index b7c8e781386..61aeaf79640 100644 --- a/drivers/mfd/ucb1x00-assabet.c +++ b/drivers/mfd/ucb1x00-assabet.c @@ -20,7 +20,7 @@ #include "ucb1x00.h" #define UCB1X00_ATTR(name,input)\ -static ssize_t name##_show(struct device *dev, struct device_attribute *attr, +static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); \ @@ -38,17 +38,17 @@ UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2); static int ucb1x00_assabet_add(struct ucb1x00_dev *dev) { - device_create_file(&dev->ucb->dev, &device_attr_vbatt); - device_create_file(&dev->ucb->dev, &device_attr_vcharger); - device_create_file(&dev->ucb->dev, &device_attr_batt_temp); + device_create_file(&dev->ucb->dev, &dev_attr_vbatt); + device_create_file(&dev->ucb->dev, &dev_attr_vcharger); + device_create_file(&dev->ucb->dev, &dev_attr_batt_temp); return 0; } static void ucb1x00_assabet_remove(struct ucb1x00_dev *dev) { - device_remove_file(&dev->ucb->cdev, &device_attr_batt_temp); - device_remove_file(&dev->ucb->cdev, &device_attr_vcharger); - device_remove_file(&dev->ucb->cdev, &device_attr_vbatt); + device_remove_file(&dev->ucb->dev, &dev_attr_batt_temp); + device_remove_file(&dev->ucb->dev, &dev_attr_vcharger); + device_remove_file(&dev->ucb->dev, &dev_attr_vbatt); } static struct ucb1x00_driver ucb1x00_assabet_driver = { diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 971e18b91f4..c9dfeb15b48 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -25,6 +25,7 @@ #include <linux/mmc/card.h> #include <linux/clk.h> #include <linux/scatterlist.h> +#include <linux/i2c/tps65010.h> #include <asm/io.h> #include <asm/irq.h> @@ -35,7 +36,6 @@ #include <asm/arch/dma.h> #include <asm/arch/mux.h> #include <asm/arch/fpga.h> -#include <asm/arch/tps65010.h> #define OMAP_MMC_REG_CMD 0x00 #define OMAP_MMC_REG_ARGL 0x04 diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 9af05a2f4af..a6728661c41 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -212,7 +212,7 @@ config MII config MACB tristate "Atmel MACB support" - depends on AVR32 || ARCH_AT91SAM9260 || ARCH_AT91SAM9263 + depends on AVR32 || ARCH_AT91SAM9260 || ARCH_AT91SAM9263 || ARCH_AT91CAP9 select PHYLIB help The Atmel MACB ethernet interface is found on many AT32 and AT91 diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index dfef1637bfb..e0900ca678e 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -16,7 +16,7 @@ #define DRV_VERSION "0.3" /* Addresses to scan: none. This chip cannot be detected. */ -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; /* Insmod parameters */ I2C_CLIENT_INSMOD; diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 1c743641b73..725b0c73c33 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -61,7 +61,7 @@ /* i2c configuration */ #define ISL1208_I2C_ADDR 0xde -static unsigned short normal_i2c[] = { +static const unsigned short normal_i2c[] = { ISL1208_I2C_ADDR>>1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; /* defines addr_data */ diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index a1cd448639c..7683412970c 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -54,7 +54,7 @@ #define MAX6900_I2C_ADDR 0xa0 -static unsigned short normal_i2c[] = { +static const unsigned short normal_i2c[] = { MAX6900_I2C_ADDR >> 1, I2C_CLIENT_END }; diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 0242d803ebe..b3317fcc16c 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -25,7 +25,7 @@ * located at 0x51 will pass the validation routine due to * the way the registers are implemented. */ -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; /* Module parameters */ I2C_CLIENT_INSMOD; diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index 556d0e7da35..c973ba94c42 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -40,7 +40,7 @@ struct pcf8583 { #define CTRL_ALARM 0x02 #define CTRL_TIMER 0x01 -static unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; /* Module parameters */ I2C_CLIENT_INSMOD; diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index b3fae357ca4..b90fb1866ce 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -32,7 +32,7 @@ * unknown chips, the user must explicitly set the probe parameter. */ -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; /* Insmod parameters */ I2C_CLIENT_INSMOD; diff --git a/drivers/s390/block/Makefile b/drivers/s390/block/Makefile index be9f22d52fd..0a89e080b38 100644 --- a/drivers/s390/block/Makefile +++ b/drivers/s390/block/Makefile @@ -2,8 +2,8 @@ # S/390 block devices # -dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_9343_erp.o -dasd_fba_mod-objs := dasd_fba.o dasd_3370_erp.o dasd_9336_erp.o +dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_alias.o +dasd_fba_mod-objs := dasd_fba.o dasd_diag_mod-objs := dasd_diag.o dasd_mod-objs := dasd.o dasd_ioctl.o dasd_proc.o dasd_devmap.o \ dasd_genhd.o dasd_erp.o diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index e6bfce690ca..1db15f3e5d2 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -48,13 +48,15 @@ MODULE_LICENSE("GPL"); /* * SECTION: prototypes for static functions of dasd.c */ -static int dasd_alloc_queue(struct dasd_device * device); -static void dasd_setup_queue(struct dasd_device * device); -static void dasd_free_queue(struct dasd_device * device); -static void dasd_flush_request_queue(struct dasd_device *); -static int dasd_flush_ccw_queue(struct dasd_device *, int); -static void dasd_tasklet(struct dasd_device *); +static int dasd_alloc_queue(struct dasd_block *); +static void dasd_setup_queue(struct dasd_block *); +static void dasd_free_queue(struct dasd_block *); +static void dasd_flush_request_queue(struct dasd_block *); +static int dasd_flush_block_queue(struct dasd_block *); +static void dasd_device_tasklet(struct dasd_device *); +static void dasd_block_tasklet(struct dasd_block *); static void do_kick_device(struct work_struct *); +static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *); /* * SECTION: Operations on the device structure. @@ -65,26 +67,23 @@ static wait_queue_head_t dasd_flush_wq; /* * Allocate memory for a new device structure. */ -struct dasd_device * -dasd_alloc_device(void) +struct dasd_device *dasd_alloc_device(void) { struct dasd_device *device; - device = kzalloc(sizeof (struct dasd_device), GFP_ATOMIC); - if (device == NULL) + device = kzalloc(sizeof(struct dasd_device), GFP_ATOMIC); + if (!device) return ERR_PTR(-ENOMEM); - /* open_count = 0 means device online but not in use */ - atomic_set(&device->open_count, -1); /* Get two pages for normal block device operations. */ device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1); - if (device->ccw_mem == NULL) { + if (!device->ccw_mem) { kfree(device); return ERR_PTR(-ENOMEM); } /* Get one page for error recovery. */ device->erp_mem = (void *) get_zeroed_page(GFP_ATOMIC | GFP_DMA); - if (device->erp_mem == NULL) { + if (!device->erp_mem) { free_pages((unsigned long) device->ccw_mem, 1); kfree(device); return ERR_PTR(-ENOMEM); @@ -93,10 +92,9 @@ dasd_alloc_device(void) dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2); dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE); spin_lock_init(&device->mem_lock); - spin_lock_init(&device->request_queue_lock); - atomic_set (&device->tasklet_scheduled, 0); + atomic_set(&device->tasklet_scheduled, 0); tasklet_init(&device->tasklet, - (void (*)(unsigned long)) dasd_tasklet, + (void (*)(unsigned long)) dasd_device_tasklet, (unsigned long) device); INIT_LIST_HEAD(&device->ccw_queue); init_timer(&device->timer); @@ -110,8 +108,7 @@ dasd_alloc_device(void) /* * Free memory of a device structure. */ -void -dasd_free_device(struct dasd_device *device) +void dasd_free_device(struct dasd_device *device) { kfree(device->private); free_page((unsigned long) device->erp_mem); @@ -120,10 +117,42 @@ dasd_free_device(struct dasd_device *device) } /* + * Allocate memory for a new device structure. + */ +struct dasd_block *dasd_alloc_block(void) +{ + struct dasd_block *block; + + block = kzalloc(sizeof(*block), GFP_ATOMIC); + if (!block) + return ERR_PTR(-ENOMEM); + /* open_count = 0 means device online but not in use */ + atomic_set(&block->open_count, -1); + + spin_lock_init(&block->request_queue_lock); + atomic_set(&block->tasklet_scheduled, 0); + tasklet_init(&block->tasklet, + (void (*)(unsigned long)) dasd_block_tasklet, + (unsigned long) block); + INIT_LIST_HEAD(&block->ccw_queue); + spin_lock_init(&block->queue_lock); + init_timer(&block->timer); + + return block; +} + +/* + * Free memory of a device structure. + */ +void dasd_free_block(struct dasd_block *block) +{ + kfree(block); +} + +/* * Make a new device known to the system. */ -static int -dasd_state_new_to_known(struct dasd_device *device) +static int dasd_state_new_to_known(struct dasd_device *device) { int rc; @@ -133,12 +162,13 @@ dasd_state_new_to_known(struct dasd_device *device) */ dasd_get_device(device); - rc = dasd_alloc_queue(device); - if (rc) { - dasd_put_device(device); - return rc; + if (device->block) { + rc = dasd_alloc_queue(device->block); + if (rc) { + dasd_put_device(device); + return rc; + } } - device->state = DASD_STATE_KNOWN; return 0; } @@ -146,21 +176,24 @@ dasd_state_new_to_known(struct dasd_device *device) /* * Let the system forget about a device. */ -static int -dasd_state_known_to_new(struct dasd_device * device) +static int dasd_state_known_to_new(struct dasd_device *device) { /* Disable extended error reporting for this device. */ dasd_eer_disable(device); /* Forget the discipline information. */ - if (device->discipline) + if (device->discipline) { + if (device->discipline->uncheck_device) + device->discipline->uncheck_device(device); module_put(device->discipline->owner); + } device->discipline = NULL; if (device->base_discipline) module_put(device->base_discipline->owner); device->base_discipline = NULL; device->state = DASD_STATE_NEW; - dasd_free_queue(device); + if (device->block) + dasd_free_queue(device->block); /* Give up reference we took in dasd_state_new_to_known. */ dasd_put_device(device); @@ -170,19 +203,19 @@ dasd_state_known_to_new(struct dasd_device * device) /* * Request the irq line for the device. */ -static int -dasd_state_known_to_basic(struct dasd_device * device) +static int dasd_state_known_to_basic(struct dasd_device *device) { int rc; /* Allocate and register gendisk structure. */ - rc = dasd_gendisk_alloc(device); - if (rc) - return rc; - + if (device->block) { + rc = dasd_gendisk_alloc(device->block); + if (rc) + return rc; + } /* register 'device' debug area, used for all DBF_DEV_XXX calls */ - device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2, - 8 * sizeof (long)); + device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 1, + 8 * sizeof(long)); debug_register_view(device->debug_area, &debug_sprintf_view); debug_set_level(device->debug_area, DBF_WARNING); DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created"); @@ -194,16 +227,17 @@ dasd_state_known_to_basic(struct dasd_device * device) /* * Release the irq line for the device. Terminate any running i/o. */ -static int -dasd_state_basic_to_known(struct dasd_device * device) +static int dasd_state_basic_to_known(struct dasd_device *device) { int rc; - - dasd_gendisk_free(device); - rc = dasd_flush_ccw_queue(device, 1); + if (device->block) { + dasd_gendisk_free(device->block); + dasd_block_clear_timer(device->block); + } + rc = dasd_flush_device_queue(device); if (rc) return rc; - dasd_clear_timer(device); + dasd_device_clear_timer(device); DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device); if (device->debug_area != NULL) { @@ -228,26 +262,32 @@ dasd_state_basic_to_known(struct dasd_device * device) * In case the analysis returns an error, the device setup is stopped * (a fake disk was already added to allow formatting). */ -static int -dasd_state_basic_to_ready(struct dasd_device * device) +static int dasd_state_basic_to_ready(struct dasd_device *device) { int rc; + struct dasd_block *block; rc = 0; - if (device->discipline->do_analysis != NULL) - rc = device->discipline->do_analysis(device); - if (rc) { - if (rc != -EAGAIN) - device->state = DASD_STATE_UNFMT; - return rc; - } + block = device->block; /* make disk known with correct capacity */ - dasd_setup_queue(device); - set_capacity(device->gdp, device->blocks << device->s2b_shift); - device->state = DASD_STATE_READY; - rc = dasd_scan_partitions(device); - if (rc) - device->state = DASD_STATE_BASIC; + if (block) { + if (block->base->discipline->do_analysis != NULL) + rc = block->base->discipline->do_analysis(block); + if (rc) { + if (rc != -EAGAIN) + device->state = DASD_STATE_UNFMT; + return rc; + } + dasd_setup_queue(block); + set_capacity(block->gdp, + block->blocks << block->s2b_shift); + device->state = DASD_STATE_READY; + rc = dasd_scan_partitions(block); + if (rc) + device->state = DASD_STATE_BASIC; + } else { + device->state = DASD_STATE_READY; + } return rc; } @@ -256,28 +296,31 @@ dasd_state_basic_to_ready(struct dasd_device * device) * Forget format information. Check if the target level is basic * and if it is create fake disk for formatting. */ -static int -dasd_state_ready_to_basic(struct dasd_device * device) +static int dasd_state_ready_to_basic(struct dasd_device *device) { int rc; - rc = dasd_flush_ccw_queue(device, 0); - if (rc) - return rc; - dasd_destroy_partitions(device); - dasd_flush_request_queue(device); - device->blocks = 0; - device->bp_block = 0; - device->s2b_shift = 0; device->state = DASD_STATE_BASIC; + if (device->block) { + struct dasd_block *block = device->block; + rc = dasd_flush_block_queue(block); + if (rc) { + device->state = DASD_STATE_READY; + return rc; + } + dasd_destroy_partitions(block); + dasd_flush_request_queue(block); + block->blocks = 0; + block->bp_block = 0; + block->s2b_shift = 0; + } return 0; } /* * Back to basic. */ -static int -dasd_state_unfmt_to_basic(struct dasd_device * device) +static int dasd_state_unfmt_to_basic(struct dasd_device *device) { device->state = DASD_STATE_BASIC; return 0; @@ -291,17 +334,31 @@ dasd_state_unfmt_to_basic(struct dasd_device * device) static int dasd_state_ready_to_online(struct dasd_device * device) { + int rc; + + if (device->discipline->ready_to_online) { + rc = device->discipline->ready_to_online(device); + if (rc) + return rc; + } device->state = DASD_STATE_ONLINE; - dasd_schedule_bh(device); + if (device->block) + dasd_schedule_block_bh(device->block); return 0; } /* * Stop the requeueing of requests again. */ -static int -dasd_state_online_to_ready(struct dasd_device * device) +static int dasd_state_online_to_ready(struct dasd_device *device) { + int rc; + + if (device->discipline->online_to_ready) { + rc = device->discipline->online_to_ready(device); + if (rc) + return rc; + } device->state = DASD_STATE_READY; return 0; } @@ -309,8 +366,7 @@ dasd_state_online_to_ready(struct dasd_device * device) /* * Device startup state changes. */ -static int -dasd_increase_state(struct dasd_device *device) +static int dasd_increase_state(struct dasd_device *device) { int rc; @@ -345,8 +401,7 @@ dasd_increase_state(struct dasd_device *device) /* * Device shutdown state changes. */ -static int -dasd_decrease_state(struct dasd_device *device) +static int dasd_decrease_state(struct dasd_device *device) { int rc; @@ -381,8 +436,7 @@ dasd_decrease_state(struct dasd_device *device) /* * This is the main startup/shutdown routine. */ -static void -dasd_change_state(struct dasd_device *device) +static void dasd_change_state(struct dasd_device *device) { int rc; @@ -409,17 +463,15 @@ dasd_change_state(struct dasd_device *device) * dasd_kick_device will schedule a call do do_kick_device to the kernel * event daemon. */ -static void -do_kick_device(struct work_struct *work) +static void do_kick_device(struct work_struct *work) { struct dasd_device *device = container_of(work, struct dasd_device, kick_work); dasd_change_state(device); - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); dasd_put_device(device); } -void -dasd_kick_device(struct dasd_device *device) +void dasd_kick_device(struct dasd_device *device) { dasd_get_device(device); /* queue call to dasd_kick_device to the kernel event daemon. */ @@ -429,8 +481,7 @@ dasd_kick_device(struct dasd_device *device) /* * Set the target state for a device and starts the state change. */ -void -dasd_set_target_state(struct dasd_device *device, int target) +void dasd_set_target_state(struct dasd_device *device, int target) { /* If we are in probeonly mode stop at DASD_STATE_READY. */ if (dasd_probeonly && target > DASD_STATE_READY) @@ -447,14 +498,12 @@ dasd_set_target_state(struct dasd_device *device, int target) /* * Enable devices with device numbers in [from..to]. */ -static inline int -_wait_for_device(struct dasd_device *device) +static inline int _wait_for_device(struct dasd_device *device) { return (device->state == device->target); } -void -dasd_enable_device(struct dasd_device *device) +void dasd_enable_device(struct dasd_device *device) { dasd_set_target_state(device, DASD_STATE_ONLINE); if (device->state <= DASD_STATE_KNOWN) @@ -475,20 +524,20 @@ unsigned int dasd_profile_level = DASD_PROFILE_OFF; /* * Increments counter in global and local profiling structures. */ -#define dasd_profile_counter(value, counter, device) \ +#define dasd_profile_counter(value, counter, block) \ { \ int index; \ for (index = 0; index < 31 && value >> (2+index); index++); \ dasd_global_profile.counter[index]++; \ - device->profile.counter[index]++; \ + block->profile.counter[index]++; \ } /* * Add profiling information for cqr before execution. */ -static void -dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr, - struct request *req) +static void dasd_profile_start(struct dasd_block *block, + struct dasd_ccw_req *cqr, + struct request *req) { struct list_head *l; unsigned int counter; @@ -498,19 +547,19 @@ dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr, /* count the length of the chanq for statistics */ counter = 0; - list_for_each(l, &device->ccw_queue) + list_for_each(l, &block->ccw_queue) if (++counter >= 31) break; dasd_global_profile.dasd_io_nr_req[counter]++; - device->profile.dasd_io_nr_req[counter]++; + block->profile.dasd_io_nr_req[counter]++; } /* * Add profiling information for cqr after execution. */ -static void -dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, - struct request *req) +static void dasd_profile_end(struct dasd_block *block, + struct dasd_ccw_req *cqr, + struct request *req) { long strtime, irqtime, endtime, tottime; /* in microseconds */ long tottimeps, sectors; @@ -532,27 +581,27 @@ dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, if (!dasd_global_profile.dasd_io_reqs) memset(&dasd_global_profile, 0, - sizeof (struct dasd_profile_info_t)); + sizeof(struct dasd_profile_info_t)); dasd_global_profile.dasd_io_reqs++; dasd_global_profile.dasd_io_sects += sectors; - if (!device->profile.dasd_io_reqs) - memset(&device->profile, 0, - sizeof (struct dasd_profile_info_t)); - device->profile.dasd_io_reqs++; - device->profile.dasd_io_sects += sectors; + if (!block->profile.dasd_io_reqs) + memset(&block->profile, 0, + sizeof(struct dasd_profile_info_t)); + block->profile.dasd_io_reqs++; + block->profile.dasd_io_sects += sectors; - dasd_profile_counter(sectors, dasd_io_secs, device); - dasd_profile_counter(tottime, dasd_io_times, device); - dasd_profile_counter(tottimeps, dasd_io_timps, device); - dasd_profile_counter(strtime, dasd_io_time1, device); - dasd_profile_counter(irqtime, dasd_io_time2, device); - dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, device); - dasd_profile_counter(endtime, dasd_io_time3, device); + dasd_profile_counter(sectors, dasd_io_secs, block); + dasd_profile_counter(tottime, dasd_io_times, block); + dasd_profile_counter(tottimeps, dasd_io_timps, block); + dasd_profile_counter(strtime, dasd_io_time1, block); + dasd_profile_counter(irqtime, dasd_io_time2, block); + dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, block); + dasd_profile_counter(endtime, dasd_io_time3, block); } #else -#define dasd_profile_start(device, cqr, req) do {} while (0) -#define dasd_profile_end(device, cqr, req) do {} while (0) +#define dasd_profile_start(block, cqr, req) do {} while (0) +#define dasd_profile_end(block, cqr, req) do {} while (0) #endif /* CONFIG_DASD_PROFILE */ /* @@ -562,9 +611,9 @@ dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, * memory and 2) dasd_smalloc_request uses the static ccw memory * that gets allocated for each device. */ -struct dasd_ccw_req * -dasd_kmalloc_request(char *magic, int cplength, int datasize, - struct dasd_device * device) +struct dasd_ccw_req *dasd_kmalloc_request(char *magic, int cplength, + int datasize, + struct dasd_device *device) { struct dasd_ccw_req *cqr; @@ -600,9 +649,9 @@ dasd_kmalloc_request(char *magic, int cplength, int datasize, return cqr; } -struct dasd_ccw_req * -dasd_smalloc_request(char *magic, int cplength, int datasize, - struct dasd_device * device) +struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength, + int datasize, + struct dasd_device *device) { unsigned long flags; struct dasd_ccw_req *cqr; @@ -649,8 +698,7 @@ dasd_smalloc_request(char *magic, int cplength, int datasize, * idal lists that might have been created by dasd_set_cda and the * struct dasd_ccw_req itself. */ -void -dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) +void dasd_kfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device) { #ifdef CONFIG_64BIT struct ccw1 *ccw; @@ -667,8 +715,7 @@ dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) dasd_put_device(device); } -void -dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) +void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device) { unsigned long flags; @@ -681,14 +728,13 @@ dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) /* * Check discipline magic in cqr. */ -static inline int -dasd_check_cqr(struct dasd_ccw_req *cqr) +static inline int dasd_check_cqr(struct dasd_ccw_req *cqr) { struct dasd_device *device; if (cqr == NULL) return -EINVAL; - device = cqr->device; + device = cqr->startdev; if (strncmp((char *) &cqr->magic, device->discipline->ebcname, 4)) { DEV_MESSAGE(KERN_WARNING, device, " dasd_ccw_req 0x%08x magic doesn't match" @@ -706,8 +752,7 @@ dasd_check_cqr(struct dasd_ccw_req *cqr) * ccw_device_clear can fail if the i/o subsystem * is in a bad mood. */ -int -dasd_term_IO(struct dasd_ccw_req * cqr) +int dasd_term_IO(struct dasd_ccw_req *cqr) { struct dasd_device *device; int retries, rc; @@ -717,13 +762,13 @@ dasd_term_IO(struct dasd_ccw_req * cqr) if (rc) return rc; retries = 0; - device = (struct dasd_device *) cqr->device; + device = (struct dasd_device *) cqr->startdev; while ((retries < 5) && (cqr->status == DASD_CQR_IN_IO)) { rc = ccw_device_clear(device->cdev, (long) cqr); switch (rc) { case 0: /* termination successful */ cqr->retries--; - cqr->status = DASD_CQR_CLEAR; + cqr->status = DASD_CQR_CLEAR_PENDING; cqr->stopclk = get_clock(); cqr->starttime = 0; DBF_DEV_EVENT(DBF_DEBUG, device, @@ -753,7 +798,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr) } retries++; } - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); return rc; } @@ -761,8 +806,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr) * Start the i/o. This start_IO can fail if the channel is really busy. * In that case set up a timer to start the request later. */ -int -dasd_start_IO(struct dasd_ccw_req * cqr) +int dasd_start_IO(struct dasd_ccw_req *cqr) { struct dasd_device *device; int rc; @@ -771,12 +815,12 @@ dasd_start_IO(struct dasd_ccw_req * cqr) rc = dasd_check_cqr(cqr); if (rc) return rc; - device = (struct dasd_device *) cqr->device; + device = (struct dasd_device *) cqr->startdev; if (cqr->retries < 0) { DEV_MESSAGE(KERN_DEBUG, device, "start_IO: request %p (%02x/%i) - no retry left.", cqr, cqr->status, cqr->retries); - cqr->status = DASD_CQR_FAILED; + cqr->status = DASD_CQR_ERROR; return -EIO; } cqr->startclk = get_clock(); @@ -833,8 +877,7 @@ dasd_start_IO(struct dasd_ccw_req * cqr) * The head of the ccw queue will have status DASD_CQR_IN_IO for 1), * DASD_CQR_QUEUED for 2) and 3). */ -static void -dasd_timeout_device(unsigned long ptr) +static void dasd_device_timeout(unsigned long ptr) { unsigned long flags; struct dasd_device *device; @@ -844,14 +887,13 @@ dasd_timeout_device(unsigned long ptr) /* re-activate request queue */ device->stopped &= ~DASD_STOPPED_PENDING; spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); } /* * Setup timeout for a device in jiffies. */ -void -dasd_set_timer(struct dasd_device *device, int expires) +void dasd_device_set_timer(struct dasd_device *device, int expires) { if (expires == 0) { if (timer_pending(&device->timer)) @@ -862,7 +904,7 @@ dasd_set_timer(struct dasd_device *device, int expires) if (mod_timer(&device->timer, jiffies + expires)) return; } - device->timer.function = dasd_timeout_device; + device->timer.function = dasd_device_timeout; device->timer.data = (unsigned long) device; device->timer.expires = jiffies + expires; add_timer(&device->timer); @@ -871,15 +913,14 @@ dasd_set_timer(struct dasd_device *device, int expires) /* * Clear timeout for a device. */ -void -dasd_clear_timer(struct dasd_device *device) +void dasd_device_clear_timer(struct dasd_device *device) { if (timer_pending(&device->timer)) del_timer(&device->timer); } -static void -dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) +static void dasd_handle_killed_request(struct ccw_device *cdev, + unsigned long intparm) { struct dasd_ccw_req *cqr; struct dasd_device *device; @@ -893,7 +934,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) return; } - device = (struct dasd_device *) cqr->device; + device = (struct dasd_device *) cqr->startdev; if (device == NULL || device != dasd_device_from_cdev_locked(cdev) || strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { @@ -905,46 +946,32 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) /* Schedule request to be retried. */ cqr->status = DASD_CQR_QUEUED; - dasd_clear_timer(device); - dasd_schedule_bh(device); + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); dasd_put_device(device); } -static void -dasd_handle_state_change_pending(struct dasd_device *device) +void dasd_generic_handle_state_change(struct dasd_device *device) { - struct dasd_ccw_req *cqr; - struct list_head *l, *n; - /* First of all start sense subsystem status request. */ dasd_eer_snss(device); device->stopped &= ~DASD_STOPPED_PENDING; - - /* restart all 'running' IO on queue */ - list_for_each_safe(l, n, &device->ccw_queue) { - cqr = list_entry(l, struct dasd_ccw_req, list); - if (cqr->status == DASD_CQR_IN_IO) { - cqr->status = DASD_CQR_QUEUED; - } - } - dasd_clear_timer(device); - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); + if (device->block) + dasd_schedule_block_bh(device->block); } /* * Interrupt handler for "normal" ssch-io based dasd devices. */ -void -dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, - struct irb *irb) +void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, + struct irb *irb) { struct dasd_ccw_req *cqr, *next; struct dasd_device *device; unsigned long long now; int expires; - dasd_era_t era; - char mask; if (IS_ERR(irb)) { switch (PTR_ERR(irb)) { @@ -969,29 +996,25 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, cdev->dev.bus_id, ((irb->scsw.cstat<<8)|irb->scsw.dstat), (unsigned int) intparm); - /* first of all check for state change pending interrupt */ - mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; - if ((irb->scsw.dstat & mask) == mask) { + /* check for unsolicited interrupts */ + cqr = (struct dasd_ccw_req *) intparm; + if (!cqr || ((irb->scsw.cc == 1) && + (irb->scsw.fctl & SCSW_FCTL_START_FUNC) && + (irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) ) { + if (cqr && cqr->status == DASD_CQR_IN_IO) + cqr->status = DASD_CQR_QUEUED; device = dasd_device_from_cdev_locked(cdev); if (!IS_ERR(device)) { - dasd_handle_state_change_pending(device); + dasd_device_clear_timer(device); + device->discipline->handle_unsolicited_interrupt(device, + irb); dasd_put_device(device); } return; } - cqr = (struct dasd_ccw_req *) intparm; - - /* check for unsolicited interrupts */ - if (cqr == NULL) { - MESSAGE(KERN_DEBUG, - "unsolicited interrupt received: bus_id %s", - cdev->dev.bus_id); - return; - } - - device = (struct dasd_device *) cqr->device; - if (device == NULL || + device = (struct dasd_device *) cqr->startdev; + if (!device || strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s", cdev->dev.bus_id); @@ -999,12 +1022,12 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, } /* Check for clear pending */ - if (cqr->status == DASD_CQR_CLEAR && + if (cqr->status == DASD_CQR_CLEAR_PENDING && irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) { - cqr->status = DASD_CQR_QUEUED; - dasd_clear_timer(device); + cqr->status = DASD_CQR_CLEARED; + dasd_device_clear_timer(device); wake_up(&dasd_flush_wq); - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); return; } @@ -1017,277 +1040,170 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, } DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x for cqr %p", ((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr); - - /* Find out the appropriate era_action. */ - if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) - era = dasd_era_fatal; - else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && - irb->scsw.cstat == 0 && - !irb->esw.esw0.erw.cons) - era = dasd_era_none; - else if (irb->esw.esw0.erw.cons) - era = device->discipline->examine_error(cqr, irb); - else - era = dasd_era_recover; - - DBF_DEV_EVENT(DBF_DEBUG, device, "era_code %d", era); + next = NULL; expires = 0; - if (era == dasd_era_none) { - cqr->status = DASD_CQR_DONE; + if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && + irb->scsw.cstat == 0 && !irb->esw.esw0.erw.cons) { + /* request was completed successfully */ + cqr->status = DASD_CQR_SUCCESS; cqr->stopclk = now; /* Start first request on queue if possible -> fast_io. */ - if (cqr->list.next != &device->ccw_queue) { - next = list_entry(cqr->list.next, - struct dasd_ccw_req, list); - if ((next->status == DASD_CQR_QUEUED) && - (!device->stopped)) { - if (device->discipline->start_IO(next) == 0) - expires = next->expires; - else - DEV_MESSAGE(KERN_DEBUG, device, "%s", - "Interrupt fastpath " - "failed!"); - } + if (cqr->devlist.next != &device->ccw_queue) { + next = list_entry(cqr->devlist.next, + struct dasd_ccw_req, devlist); } - } else { /* error */ - memcpy(&cqr->irb, irb, sizeof (struct irb)); + } else { /* error */ + memcpy(&cqr->irb, irb, sizeof(struct irb)); if (device->features & DASD_FEATURE_ERPLOG) { - /* dump sense data */ dasd_log_sense(cqr, irb); } - switch (era) { - case dasd_era_fatal: - cqr->status = DASD_CQR_FAILED; - cqr->stopclk = now; - break; - case dasd_era_recover: + /* If we have no sense data, or we just don't want complex ERP + * for this request, but if we have retries left, then just + * reset this request and retry it in the fastpath + */ + if (!(cqr->irb.esw.esw0.erw.cons && + test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) && + cqr->retries > 0) { + DEV_MESSAGE(KERN_DEBUG, device, + "default ERP in fastpath (%i retries left)", + cqr->retries); + cqr->lpm = LPM_ANYPATH; + cqr->status = DASD_CQR_QUEUED; + next = cqr; + } else cqr->status = DASD_CQR_ERROR; - break; - default: - BUG(); - } + } + if (next && (next->status == DASD_CQR_QUEUED) && + (!device->stopped)) { + if (device->discipline->start_IO(next) == 0) + expires = next->expires; + else + DEV_MESSAGE(KERN_DEBUG, device, "%s", + "Interrupt fastpath " + "failed!"); } if (expires != 0) - dasd_set_timer(device, expires); + dasd_device_set_timer(device, expires); else - dasd_clear_timer(device); - dasd_schedule_bh(device); + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); } /* - * posts the buffer_cache about a finalized request + * If we have an error on a dasd_block layer request then we cancel + * and return all further requests from the same dasd_block as well. */ -static inline void -dasd_end_request(struct request *req, int uptodate) +static void __dasd_device_recovery(struct dasd_device *device, + struct dasd_ccw_req *ref_cqr) { - if (end_that_request_first(req, uptodate, req->hard_nr_sectors)) - BUG(); - add_disk_randomness(req->rq_disk); - end_that_request_last(req, uptodate); -} + struct list_head *l, *n; + struct dasd_ccw_req *cqr; -/* - * Process finished error recovery ccw. - */ -static inline void -__dasd_process_erp(struct dasd_device *device, struct dasd_ccw_req *cqr) -{ - dasd_erp_fn_t erp_fn; + /* + * only requeue request that came from the dasd_block layer + */ + if (!ref_cqr->block) + return; - if (cqr->status == DASD_CQR_DONE) - DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful"); - else - DEV_MESSAGE(KERN_ERR, device, "%s", "ERP unsuccessful"); - erp_fn = device->discipline->erp_postaction(cqr); - erp_fn(cqr); -} + list_for_each_safe(l, n, &device->ccw_queue) { + cqr = list_entry(l, struct dasd_ccw_req, devlist); + if (cqr->status == DASD_CQR_QUEUED && + ref_cqr->block == cqr->block) { + cqr->status = DASD_CQR_CLEARED; + } + } +}; /* - * Process ccw request queue. + * Remove those ccw requests from the queue that need to be returned + * to the upper layer. */ -static void -__dasd_process_ccw_queue(struct dasd_device * device, - struct list_head *final_queue) +static void __dasd_device_process_ccw_queue(struct dasd_device *device, + struct list_head *final_queue) { struct list_head *l, *n; struct dasd_ccw_req *cqr; - dasd_erp_fn_t erp_fn; -restart: /* Process request with final status. */ list_for_each_safe(l, n, &device->ccw_queue) { - cqr = list_entry(l, struct dasd_ccw_req, list); + cqr = list_entry(l, struct dasd_ccw_req, devlist); + /* Stop list processing at the first non-final request. */ - if (cqr->status != DASD_CQR_DONE && - cqr->status != DASD_CQR_FAILED && - cqr->status != DASD_CQR_ERROR) + if (cqr->status == DASD_CQR_QUEUED || + cqr->status == DASD_CQR_IN_IO || + cqr->status == DASD_CQR_CLEAR_PENDING) break; - /* Process requests with DASD_CQR_ERROR */ if (cqr->status == DASD_CQR_ERROR) { - if (cqr->irb.scsw.fctl & SCSW_FCTL_HALT_FUNC) { - cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock(); - } else { - if (cqr->irb.esw.esw0.erw.cons && - test_bit(DASD_CQR_FLAGS_USE_ERP, - &cqr->flags)) { - erp_fn = device->discipline-> - erp_action(cqr); - erp_fn(cqr); - } else - dasd_default_erp_action(cqr); - } - goto restart; - } - - /* First of all call extended error reporting. */ - if (dasd_eer_enabled(device) && - cqr->status == DASD_CQR_FAILED) { - dasd_eer_write(device, cqr, DASD_EER_FATALERROR); - - /* restart request */ - cqr->status = DASD_CQR_QUEUED; - cqr->retries = 255; - device->stopped |= DASD_STOPPED_QUIESCE; - goto restart; + __dasd_device_recovery(device, cqr); } - - /* Process finished ERP request. */ - if (cqr->refers) { - __dasd_process_erp(device, cqr); - goto restart; - } - /* Rechain finished requests to final queue */ - cqr->endclk = get_clock(); - list_move_tail(&cqr->list, final_queue); + list_move_tail(&cqr->devlist, final_queue); } } -static void -dasd_end_request_cb(struct dasd_ccw_req * cqr, void *data) -{ - struct request *req; - struct dasd_device *device; - int status; - - req = (struct request *) data; - device = cqr->device; - dasd_profile_end(device, cqr, req); - status = cqr->device->discipline->free_cp(cqr,req); - spin_lock_irq(&device->request_queue_lock); - dasd_end_request(req, status); - spin_unlock_irq(&device->request_queue_lock); -} - - /* - * Fetch requests from the block device queue. + * the cqrs from the final queue are returned to the upper layer + * by setting a dasd_block state and calling the callback function */ -static void -__dasd_process_blk_queue(struct dasd_device * device) +static void __dasd_device_process_final_queue(struct dasd_device *device, + struct list_head *final_queue) { - struct request_queue *queue; - struct request *req; + struct list_head *l, *n; struct dasd_ccw_req *cqr; - int nr_queued; - - queue = device->request_queue; - /* No queue ? Then there is nothing to do. */ - if (queue == NULL) - return; - - /* - * We requeue request from the block device queue to the ccw - * queue only in two states. In state DASD_STATE_READY the - * partition detection is done and we need to requeue requests - * for that. State DASD_STATE_ONLINE is normal block device - * operation. - */ - if (device->state != DASD_STATE_READY && - device->state != DASD_STATE_ONLINE) - return; - nr_queued = 0; - /* Now we try to fetch requests from the request queue */ - list_for_each_entry(cqr, &device->ccw_queue, list) - if (cqr->status == DASD_CQR_QUEUED) - nr_queued++; - while (!blk_queue_plugged(queue) && - elv_next_request(queue) && - nr_queued < DASD_CHANQ_MAX_SIZE) { - req = elv_next_request(queue); - if (device->features & DASD_FEATURE_READONLY && - rq_data_dir(req) == WRITE) { - DBF_DEV_EVENT(DBF_ERR, device, - "Rejecting write request %p", - req); - blkdev_dequeue_request(req); - dasd_end_request(req, 0); - continue; - } - if (device->stopped & DASD_STOPPED_DC_EIO) { - blkdev_dequeue_request(req); - dasd_end_request(req, 0); - continue; - } - cqr = device->discipline->build_cp(device, req); - if (IS_ERR(cqr)) { - if (PTR_ERR(cqr) == -ENOMEM) - break; /* terminate request queue loop */ - if (PTR_ERR(cqr) == -EAGAIN) { - /* - * The current request cannot be build right - * now, we have to try later. If this request - * is the head-of-queue we stop the device - * for 1/2 second. - */ - if (!list_empty(&device->ccw_queue)) - break; - device->stopped |= DASD_STOPPED_PENDING; - dasd_set_timer(device, HZ/2); - break; - } - DBF_DEV_EVENT(DBF_ERR, device, - "CCW creation failed (rc=%ld) " - "on request %p", - PTR_ERR(cqr), req); - blkdev_dequeue_request(req); - dasd_end_request(req, 0); - continue; + list_for_each_safe(l, n, final_queue) { + cqr = list_entry(l, struct dasd_ccw_req, devlist); + list_del_init(&cqr->devlist); + if (cqr->block) + spin_lock_bh(&cqr->block->queue_lock); + switch (cqr->status) { + case DASD_CQR_SUCCESS: + cqr->status = DASD_CQR_DONE; + break; + case DASD_CQR_ERROR: + cqr->status = DASD_CQR_NEED_ERP; + break; + case DASD_CQR_CLEARED: + cqr->status = DASD_CQR_TERMINATED; + break; + default: + DEV_MESSAGE(KERN_ERR, device, + "wrong cqr status in __dasd_process_final_queue " + "for cqr %p, status %x", + cqr, cqr->status); + BUG(); } - cqr->callback = dasd_end_request_cb; - cqr->callback_data = (void *) req; - cqr->status = DASD_CQR_QUEUED; - blkdev_dequeue_request(req); - list_add_tail(&cqr->list, &device->ccw_queue); - dasd_profile_start(device, cqr, req); - nr_queued++; + if (cqr->block) + spin_unlock_bh(&cqr->block->queue_lock); + if (cqr->callback != NULL) + (cqr->callback)(cqr, cqr->callback_data); } } + + /* * Take a look at the first request on the ccw queue and check * if it reached its expire time. If so, terminate the IO. */ -static void -__dasd_check_expire(struct dasd_device * device) +static void __dasd_device_check_expire(struct dasd_device *device) { struct dasd_ccw_req *cqr; if (list_empty(&device->ccw_queue)) return; - cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); + cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); if ((cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) && (time_after_eq(jiffies, cqr->expires + cqr->starttime))) { if (device->discipline->term_IO(cqr) != 0) { /* Hmpf, try again in 5 sec */ - dasd_set_timer(device, 5*HZ); DEV_MESSAGE(KERN_ERR, device, "internal error - timeout (%is) expired " "for cqr %p, termination failed, " "retrying in 5s", (cqr->expires/HZ), cqr); + cqr->expires += 5*HZ; + dasd_device_set_timer(device, 5*HZ); } else { DEV_MESSAGE(KERN_ERR, device, "internal error - timeout (%is) expired " @@ -1301,77 +1217,53 @@ __dasd_check_expire(struct dasd_device * device) * Take a look at the first request on the ccw queue and check * if it needs to be started. */ -static void -__dasd_start_head(struct dasd_device * device) +static void __dasd_device_start_head(struct dasd_device *device) { struct dasd_ccw_req *cqr; int rc; if (list_empty(&device->ccw_queue)) return; - cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); + cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); if (cqr->status != DASD_CQR_QUEUED) return; - /* Non-temporary stop condition will trigger fail fast */ - if (device->stopped & ~DASD_STOPPED_PENDING && - test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && - (!dasd_eer_enabled(device))) { - cqr->status = DASD_CQR_FAILED; - dasd_schedule_bh(device); + /* when device is stopped, return request to previous layer */ + if (device->stopped) { + cqr->status = DASD_CQR_CLEARED; + dasd_schedule_device_bh(device); return; } - /* Don't try to start requests if device is stopped */ - if (device->stopped) - return; rc = device->discipline->start_IO(cqr); if (rc == 0) - dasd_set_timer(device, cqr->expires); + dasd_device_set_timer(device, cqr->expires); else if (rc == -EACCES) { - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); } else /* Hmpf, try again in 1/2 sec */ - dasd_set_timer(device, 50); -} - -static inline int -_wait_for_clear(struct dasd_ccw_req *cqr) -{ - return (cqr->status == DASD_CQR_QUEUED); + dasd_device_set_timer(device, 50); } /* - * Remove all requests from the ccw queue (all = '1') or only block device - * requests in case all = '0'. - * Take care of the erp-chain (chained via cqr->refers) and remove either - * the whole erp-chain or none of the erp-requests. - * If a request is currently running, term_IO is called and the request - * is re-queued. Prior to removing the terminated request we need to wait - * for the clear-interrupt. - * In case termination is not possible we stop processing and just finishing - * the already moved requests. + * Go through all request on the dasd_device request queue, + * terminate them on the cdev if necessary, and return them to the + * submitting layer via callback. + * Note: + * Make sure that all 'submitting layers' still exist when + * this function is called!. In other words, when 'device' is a base + * device then all block layer requests must have been removed before + * via dasd_flush_block_queue. */ -static int -dasd_flush_ccw_queue(struct dasd_device * device, int all) +int dasd_flush_device_queue(struct dasd_device *device) { - struct dasd_ccw_req *cqr, *orig, *n; - int rc, i; - + struct dasd_ccw_req *cqr, *n; + int rc; struct list_head flush_queue; INIT_LIST_HEAD(&flush_queue); spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = 0; -restart: - list_for_each_entry_safe(cqr, n, &device->ccw_queue, list) { - /* get original request of erp request-chain */ - for (orig = cqr; orig->refers != NULL; orig = orig->refers); - - /* Flush all request or only block device requests? */ - if (all == 0 && cqr->callback != dasd_end_request_cb && - orig->callback != dasd_end_request_cb) { - continue; - } + list_for_each_entry_safe(cqr, n, &device->ccw_queue, devlist) { /* Check status and move request to flush_queue */ switch (cqr->status) { case DASD_CQR_IN_IO: @@ -1387,90 +1279,60 @@ restart: } break; case DASD_CQR_QUEUED: - case DASD_CQR_ERROR: - /* set request to FAILED */ cqr->stopclk = get_clock(); - cqr->status = DASD_CQR_FAILED; + cqr->status = DASD_CQR_CLEARED; break; - default: /* do not touch the others */ + default: /* no need to modify the others */ break; } - /* Rechain request (including erp chain) */ - for (i = 0; cqr != NULL; cqr = cqr->refers, i++) { - cqr->endclk = get_clock(); - list_move_tail(&cqr->list, &flush_queue); - } - if (i > 1) - /* moved more than one request - need to restart */ - goto restart; + list_move_tail(&cqr->devlist, &flush_queue); } - finished: spin_unlock_irq(get_ccwdev_lock(device->cdev)); - /* Now call the callback function of flushed requests */ -restart_cb: - list_for_each_entry_safe(cqr, n, &flush_queue, list) { - if (cqr->status == DASD_CQR_CLEAR) { - /* wait for clear interrupt! */ - wait_event(dasd_flush_wq, _wait_for_clear(cqr)); - cqr->status = DASD_CQR_FAILED; - } - /* Process finished ERP request. */ - if (cqr->refers) { - __dasd_process_erp(device, cqr); - /* restart list_for_xx loop since dasd_process_erp - * might remove multiple elements */ - goto restart_cb; - } - /* call the callback function */ - cqr->endclk = get_clock(); - if (cqr->callback != NULL) - (cqr->callback)(cqr, cqr->callback_data); - } + /* + * After this point all requests must be in state CLEAR_PENDING, + * CLEARED, SUCCESS or ERROR. Now wait for CLEAR_PENDING to become + * one of the others. + */ + list_for_each_entry_safe(cqr, n, &flush_queue, devlist) + wait_event(dasd_flush_wq, + (cqr->status != DASD_CQR_CLEAR_PENDING)); + /* + * Now set each request back to TERMINATED, DONE or NEED_ERP + * and call the callback function of flushed requests + */ + __dasd_device_process_final_queue(device, &flush_queue); return rc; } /* * Acquire the device lock and process queues for the device. */ -static void -dasd_tasklet(struct dasd_device * device) +static void dasd_device_tasklet(struct dasd_device *device) { struct list_head final_queue; - struct list_head *l, *n; - struct dasd_ccw_req *cqr; atomic_set (&device->tasklet_scheduled, 0); INIT_LIST_HEAD(&final_queue); spin_lock_irq(get_ccwdev_lock(device->cdev)); /* Check expire time of first request on the ccw queue. */ - __dasd_check_expire(device); - /* Finish off requests on ccw queue */ - __dasd_process_ccw_queue(device, &final_queue); + __dasd_device_check_expire(device); + /* find final requests on ccw queue */ + __dasd_device_process_ccw_queue(device, &final_queue); spin_unlock_irq(get_ccwdev_lock(device->cdev)); /* Now call the callback function of requests with final status */ - list_for_each_safe(l, n, &final_queue) { - cqr = list_entry(l, struct dasd_ccw_req, list); - list_del_init(&cqr->list); - if (cqr->callback != NULL) - (cqr->callback)(cqr, cqr->callback_data); - } - spin_lock_irq(&device->request_queue_lock); - spin_lock(get_ccwdev_lock(device->cdev)); - /* Get new request from the block device request queue */ - __dasd_process_blk_queue(device); + __dasd_device_process_final_queue(device, &final_queue); + spin_lock_irq(get_ccwdev_lock(device->cdev)); /* Now check if the head of the ccw queue needs to be started. */ - __dasd_start_head(device); - spin_unlock(get_ccwdev_lock(device->cdev)); - spin_unlock_irq(&device->request_queue_lock); + __dasd_device_start_head(device); + spin_unlock_irq(get_ccwdev_lock(device->cdev)); dasd_put_device(device); } /* * Schedules a call to dasd_tasklet over the device tasklet. */ -void -dasd_schedule_bh(struct dasd_device * device) +void dasd_schedule_device_bh(struct dasd_device *device) { /* Protect against rescheduling. */ if (atomic_cmpxchg (&device->tasklet_scheduled, 0, 1) != 0) @@ -1480,160 +1342,109 @@ dasd_schedule_bh(struct dasd_device * device) } /* - * Queue a request to the head of the ccw_queue. Start the I/O if - * possible. + * Queue a request to the head of the device ccw_queue. + * Start the I/O if possible. */ -void -dasd_add_request_head(struct dasd_ccw_req *req) +void dasd_add_request_head(struct dasd_ccw_req *cqr) { struct dasd_device *device; unsigned long flags; - device = req->device; + device = cqr->startdev; spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - req->status = DASD_CQR_QUEUED; - req->device = device; - list_add(&req->list, &device->ccw_queue); + cqr->status = DASD_CQR_QUEUED; + list_add(&cqr->devlist, &device->ccw_queue); /* let the bh start the request to keep them in order */ - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } /* - * Queue a request to the tail of the ccw_queue. Start the I/O if - * possible. + * Queue a request to the tail of the device ccw_queue. + * Start the I/O if possible. */ -void -dasd_add_request_tail(struct dasd_ccw_req *req) +void dasd_add_request_tail(struct dasd_ccw_req *cqr) { struct dasd_device *device; unsigned long flags; - device = req->device; + device = cqr->startdev; spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - req->status = DASD_CQR_QUEUED; - req->device = device; - list_add_tail(&req->list, &device->ccw_queue); + cqr->status = DASD_CQR_QUEUED; + list_add_tail(&cqr->devlist, &device->ccw_queue); /* let the bh start the request to keep them in order */ - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } /* - * Wakeup callback. + * Wakeup helper for the 'sleep_on' functions. */ -static void -dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data) +static void dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data) { wake_up((wait_queue_head_t *) data); } -static inline int -_wait_for_wakeup(struct dasd_ccw_req *cqr) +static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr) { struct dasd_device *device; int rc; - device = cqr->device; + device = cqr->startdev; spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = ((cqr->status == DASD_CQR_DONE || - cqr->status == DASD_CQR_FAILED) && - list_empty(&cqr->list)); + cqr->status == DASD_CQR_NEED_ERP || + cqr->status == DASD_CQR_TERMINATED) && + list_empty(&cqr->devlist)); spin_unlock_irq(get_ccwdev_lock(device->cdev)); return rc; } /* - * Attempts to start a special ccw queue and waits for its completion. + * Queue a request to the tail of the device ccw_queue and wait for + * it's completion. */ -int -dasd_sleep_on(struct dasd_ccw_req * cqr) +int dasd_sleep_on(struct dasd_ccw_req *cqr) { wait_queue_head_t wait_q; struct dasd_device *device; int rc; - device = cqr->device; - spin_lock_irq(get_ccwdev_lock(device->cdev)); + device = cqr->startdev; init_waitqueue_head (&wait_q); cqr->callback = dasd_wakeup_cb; cqr->callback_data = (void *) &wait_q; - cqr->status = DASD_CQR_QUEUED; - list_add_tail(&cqr->list, &device->ccw_queue); - - /* let the bh start the request to keep them in order */ - dasd_schedule_bh(device); - - spin_unlock_irq(get_ccwdev_lock(device->cdev)); - + dasd_add_request_tail(cqr); wait_event(wait_q, _wait_for_wakeup(cqr)); /* Request status is either done or failed. */ - rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; + rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; return rc; } /* - * Attempts to start a special ccw queue and wait interruptible - * for its completion. + * Queue a request to the tail of the device ccw_queue and wait + * interruptible for it's completion. */ -int -dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr) +int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr) { wait_queue_head_t wait_q; struct dasd_device *device; - int rc, finished; - - device = cqr->device; - spin_lock_irq(get_ccwdev_lock(device->cdev)); + int rc; + device = cqr->startdev; init_waitqueue_head (&wait_q); cqr->callback = dasd_wakeup_cb; cqr->callback_data = (void *) &wait_q; - cqr->status = DASD_CQR_QUEUED; - list_add_tail(&cqr->list, &device->ccw_queue); - - /* let the bh start the request to keep them in order */ - dasd_schedule_bh(device); - spin_unlock_irq(get_ccwdev_lock(device->cdev)); - - finished = 0; - while (!finished) { - rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr)); - if (rc != -ERESTARTSYS) { - /* Request is final (done or failed) */ - rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; - break; - } - spin_lock_irq(get_ccwdev_lock(device->cdev)); - switch (cqr->status) { - case DASD_CQR_IN_IO: - /* terminate runnig cqr */ - if (device->discipline->term_IO) { - cqr->retries = -1; - device->discipline->term_IO(cqr); - /* wait (non-interruptible) for final status - * because signal ist still pending */ - spin_unlock_irq(get_ccwdev_lock(device->cdev)); - wait_event(wait_q, _wait_for_wakeup(cqr)); - spin_lock_irq(get_ccwdev_lock(device->cdev)); - rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; - finished = 1; - } - break; - case DASD_CQR_QUEUED: - /* request */ - list_del_init(&cqr->list); - rc = -EIO; - finished = 1; - break; - default: - /* cqr with 'non-interruptable' status - just wait */ - break; - } - spin_unlock_irq(get_ccwdev_lock(device->cdev)); + dasd_add_request_tail(cqr); + rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr)); + if (rc == -ERESTARTSYS) { + dasd_cancel_req(cqr); + /* wait (non-interruptible) for final status */ + wait_event(wait_q, _wait_for_wakeup(cqr)); } + rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; return rc; } @@ -1643,25 +1454,23 @@ dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr) * and be put back to status queued, before the special request is added * to the head of the queue. Then the special request is waited on normally. */ -static inline int -_dasd_term_running_cqr(struct dasd_device *device) +static inline int _dasd_term_running_cqr(struct dasd_device *device) { struct dasd_ccw_req *cqr; if (list_empty(&device->ccw_queue)) return 0; - cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); + cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); return device->discipline->term_IO(cqr); } -int -dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) +int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr) { wait_queue_head_t wait_q; struct dasd_device *device; int rc; - device = cqr->device; + device = cqr->startdev; spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = _dasd_term_running_cqr(device); if (rc) { @@ -1673,17 +1482,17 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) cqr->callback = dasd_wakeup_cb; cqr->callback_data = (void *) &wait_q; cqr->status = DASD_CQR_QUEUED; - list_add(&cqr->list, &device->ccw_queue); + list_add(&cqr->devlist, &device->ccw_queue); /* let the bh start the request to keep them in order */ - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); spin_unlock_irq(get_ccwdev_lock(device->cdev)); wait_event(wait_q, _wait_for_wakeup(cqr)); /* Request status is either done or failed. */ - rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; + rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; return rc; } @@ -1692,11 +1501,14 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) * This is useful to timeout requests. The request will be * terminated if it is currently in i/o. * Returns 1 if the request has been terminated. + * 0 if there was no need to terminate the request (not started yet) + * negative error code if termination failed + * Cancellation of a request is an asynchronous operation! The calling + * function has to wait until the request is properly returned via callback. */ -int -dasd_cancel_req(struct dasd_ccw_req *cqr) +int dasd_cancel_req(struct dasd_ccw_req *cqr) { - struct dasd_device *device = cqr->device; + struct dasd_device *device = cqr->startdev; unsigned long flags; int rc; @@ -1704,74 +1516,453 @@ dasd_cancel_req(struct dasd_ccw_req *cqr) spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); switch (cqr->status) { case DASD_CQR_QUEUED: - /* request was not started - just set to failed */ - cqr->status = DASD_CQR_FAILED; + /* request was not started - just set to cleared */ + cqr->status = DASD_CQR_CLEARED; break; case DASD_CQR_IN_IO: /* request in IO - terminate IO and release again */ - if (device->discipline->term_IO(cqr) != 0) - /* what to do if unable to terminate ?????? - e.g. not _IN_IO */ - cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock(); - rc = 1; + rc = device->discipline->term_IO(cqr); + if (rc) { + DEV_MESSAGE(KERN_ERR, device, + "dasd_cancel_req is unable " + " to terminate request %p, rc = %d", + cqr, rc); + } else { + cqr->stopclk = get_clock(); + rc = 1; + } break; - case DASD_CQR_DONE: - case DASD_CQR_FAILED: - /* already finished - do nothing */ + default: /* already finished or clear pending - do nothing */ break; - default: - DEV_MESSAGE(KERN_ALERT, device, - "invalid status %02x in request", - cqr->status); + } + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + dasd_schedule_device_bh(device); + return rc; +} + + +/* + * SECTION: Operations of the dasd_block layer. + */ + +/* + * Timeout function for dasd_block. This is used when the block layer + * is waiting for something that may not come reliably, (e.g. a state + * change interrupt) + */ +static void dasd_block_timeout(unsigned long ptr) +{ + unsigned long flags; + struct dasd_block *block; + + block = (struct dasd_block *) ptr; + spin_lock_irqsave(get_ccwdev_lock(block->base->cdev), flags); + /* re-activate request queue */ + block->base->stopped &= ~DASD_STOPPED_PENDING; + spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags); + dasd_schedule_block_bh(block); +} + +/* + * Setup timeout for a dasd_block in jiffies. + */ +void dasd_block_set_timer(struct dasd_block *block, int expires) +{ + if (expires == 0) { + if (timer_pending(&block->timer)) + del_timer(&block->timer); + return; + } + if (timer_pending(&block->timer)) { + if (mod_timer(&block->timer, jiffies + expires)) + return; + } + block->timer.function = dasd_block_timeout; + block->timer.data = (unsigned long) block; + block->timer.expires = jiffies + expires; + add_timer(&block->timer); +} + +/* + * Clear timeout for a dasd_block. + */ +void dasd_block_clear_timer(struct dasd_block *block) +{ + if (timer_pending(&block->timer)) + del_timer(&block->timer); +} + +/* + * posts the buffer_cache about a finalized request + */ +static inline void dasd_end_request(struct request *req, int uptodate) +{ + if (end_that_request_first(req, uptodate, req->hard_nr_sectors)) BUG(); + add_disk_randomness(req->rq_disk); + end_that_request_last(req, uptodate); +} + +/* + * Process finished error recovery ccw. + */ +static inline void __dasd_block_process_erp(struct dasd_block *block, + struct dasd_ccw_req *cqr) +{ + dasd_erp_fn_t erp_fn; + struct dasd_device *device = block->base; + if (cqr->status == DASD_CQR_DONE) + DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful"); + else + DEV_MESSAGE(KERN_ERR, device, "%s", "ERP unsuccessful"); + erp_fn = device->discipline->erp_postaction(cqr); + erp_fn(cqr); +} + +/* + * Fetch requests from the block device queue. + */ +static void __dasd_process_request_queue(struct dasd_block *block) +{ + struct request_queue *queue; + struct request *req; + struct dasd_ccw_req *cqr; + struct dasd_device *basedev; + unsigned long flags; + queue = block->request_queue; + basedev = block->base; + /* No queue ? Then there is nothing to do. */ + if (queue == NULL) + return; + + /* + * We requeue request from the block device queue to the ccw + * queue only in two states. In state DASD_STATE_READY the + * partition detection is done and we need to requeue requests + * for that. State DASD_STATE_ONLINE is normal block device + * operation. + */ + if (basedev->state < DASD_STATE_READY) + return; + /* Now we try to fetch requests from the request queue */ + while (!blk_queue_plugged(queue) && + elv_next_request(queue)) { + + req = elv_next_request(queue); + + if (basedev->features & DASD_FEATURE_READONLY && + rq_data_dir(req) == WRITE) { + DBF_DEV_EVENT(DBF_ERR, basedev, + "Rejecting write request %p", + req); + blkdev_dequeue_request(req); + dasd_end_request(req, 0); + continue; + } + cqr = basedev->discipline->build_cp(basedev, block, req); + if (IS_ERR(cqr)) { + if (PTR_ERR(cqr) == -EBUSY) + break; /* normal end condition */ + if (PTR_ERR(cqr) == -ENOMEM) + break; /* terminate request queue loop */ + if (PTR_ERR(cqr) == -EAGAIN) { + /* + * The current request cannot be build right + * now, we have to try later. If this request + * is the head-of-queue we stop the device + * for 1/2 second. + */ + if (!list_empty(&block->ccw_queue)) + break; + spin_lock_irqsave(get_ccwdev_lock(basedev->cdev), flags); + basedev->stopped |= DASD_STOPPED_PENDING; + spin_unlock_irqrestore(get_ccwdev_lock(basedev->cdev), flags); + dasd_block_set_timer(block, HZ/2); + break; + } + DBF_DEV_EVENT(DBF_ERR, basedev, + "CCW creation failed (rc=%ld) " + "on request %p", + PTR_ERR(cqr), req); + blkdev_dequeue_request(req); + dasd_end_request(req, 0); + continue; + } + /* + * Note: callback is set to dasd_return_cqr_cb in + * __dasd_block_start_head to cover erp requests as well + */ + cqr->callback_data = (void *) req; + cqr->status = DASD_CQR_FILLED; + blkdev_dequeue_request(req); + list_add_tail(&cqr->blocklist, &block->ccw_queue); + dasd_profile_start(block, cqr, req); + } +} + +static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr) +{ + struct request *req; + int status; + + req = (struct request *) cqr->callback_data; + dasd_profile_end(cqr->block, cqr, req); + status = cqr->memdev->discipline->free_cp(cqr, req); + dasd_end_request(req, status); +} + +/* + * Process ccw request queue. + */ +static void __dasd_process_block_ccw_queue(struct dasd_block *block, + struct list_head *final_queue) +{ + struct list_head *l, *n; + struct dasd_ccw_req *cqr; + dasd_erp_fn_t erp_fn; + unsigned long flags; + struct dasd_device *base = block->base; + +restart: + /* Process request with final status. */ + list_for_each_safe(l, n, &block->ccw_queue) { + cqr = list_entry(l, struct dasd_ccw_req, blocklist); + if (cqr->status != DASD_CQR_DONE && + cqr->status != DASD_CQR_FAILED && + cqr->status != DASD_CQR_NEED_ERP && + cqr->status != DASD_CQR_TERMINATED) + continue; + + if (cqr->status == DASD_CQR_TERMINATED) { + base->discipline->handle_terminated_request(cqr); + goto restart; + } + + /* Process requests that may be recovered */ + if (cqr->status == DASD_CQR_NEED_ERP) { + if (cqr->irb.esw.esw0.erw.cons && + test_bit(DASD_CQR_FLAGS_USE_ERP, + &cqr->flags)) { + erp_fn = base->discipline->erp_action(cqr); + erp_fn(cqr); + } + goto restart; + } + + /* First of all call extended error reporting. */ + if (dasd_eer_enabled(base) && + cqr->status == DASD_CQR_FAILED) { + dasd_eer_write(base, cqr, DASD_EER_FATALERROR); + + /* restart request */ + cqr->status = DASD_CQR_FILLED; + cqr->retries = 255; + spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); + base->stopped |= DASD_STOPPED_QUIESCE; + spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), + flags); + goto restart; + } + + /* Process finished ERP request. */ + if (cqr->refers) { + __dasd_block_process_erp(block, cqr); + goto restart; + } + + /* Rechain finished requests to final queue */ + cqr->endclk = get_clock(); + list_move_tail(&cqr->blocklist, final_queue); + } +} + +static void dasd_return_cqr_cb(struct dasd_ccw_req *cqr, void *data) +{ + dasd_schedule_block_bh(cqr->block); +} + +static void __dasd_block_start_head(struct dasd_block *block) +{ + struct dasd_ccw_req *cqr; + + if (list_empty(&block->ccw_queue)) + return; + /* We allways begin with the first requests on the queue, as some + * of previously started requests have to be enqueued on a + * dasd_device again for error recovery. + */ + list_for_each_entry(cqr, &block->ccw_queue, blocklist) { + if (cqr->status != DASD_CQR_FILLED) + continue; + /* Non-temporary stop condition will trigger fail fast */ + if (block->base->stopped & ~DASD_STOPPED_PENDING && + test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && + (!dasd_eer_enabled(block->base))) { + cqr->status = DASD_CQR_FAILED; + dasd_schedule_block_bh(block); + continue; + } + /* Don't try to start requests if device is stopped */ + if (block->base->stopped) + return; + + /* just a fail safe check, should not happen */ + if (!cqr->startdev) + cqr->startdev = block->base; + + /* make sure that the requests we submit find their way back */ + cqr->callback = dasd_return_cqr_cb; + + dasd_add_request_tail(cqr); + } +} + +/* + * Central dasd_block layer routine. Takes requests from the generic + * block layer request queue, creates ccw requests, enqueues them on + * a dasd_device and processes ccw requests that have been returned. + */ +static void dasd_block_tasklet(struct dasd_block *block) +{ + struct list_head final_queue; + struct list_head *l, *n; + struct dasd_ccw_req *cqr; + + atomic_set(&block->tasklet_scheduled, 0); + INIT_LIST_HEAD(&final_queue); + spin_lock(&block->queue_lock); + /* Finish off requests on ccw queue */ + __dasd_process_block_ccw_queue(block, &final_queue); + spin_unlock(&block->queue_lock); + /* Now call the callback function of requests with final status */ + spin_lock_irq(&block->request_queue_lock); + list_for_each_safe(l, n, &final_queue) { + cqr = list_entry(l, struct dasd_ccw_req, blocklist); + list_del_init(&cqr->blocklist); + __dasd_cleanup_cqr(cqr); + } + spin_lock(&block->queue_lock); + /* Get new request from the block device request queue */ + __dasd_process_request_queue(block); + /* Now check if the head of the ccw queue needs to be started. */ + __dasd_block_start_head(block); + spin_unlock(&block->queue_lock); + spin_unlock_irq(&block->request_queue_lock); + dasd_put_device(block->base); +} + +static void _dasd_wake_block_flush_cb(struct dasd_ccw_req *cqr, void *data) +{ + wake_up(&dasd_flush_wq); +} + +/* + * Go through all request on the dasd_block request queue, cancel them + * on the respective dasd_device, and return them to the generic + * block layer. + */ +static int dasd_flush_block_queue(struct dasd_block *block) +{ + struct dasd_ccw_req *cqr, *n; + int rc, i; + struct list_head flush_queue; + + INIT_LIST_HEAD(&flush_queue); + spin_lock_bh(&block->queue_lock); + rc = 0; +restart: + list_for_each_entry_safe(cqr, n, &block->ccw_queue, blocklist) { + /* if this request currently owned by a dasd_device cancel it */ + if (cqr->status >= DASD_CQR_QUEUED) + rc = dasd_cancel_req(cqr); + if (rc < 0) + break; + /* Rechain request (including erp chain) so it won't be + * touched by the dasd_block_tasklet anymore. + * Replace the callback so we notice when the request + * is returned from the dasd_device layer. + */ + cqr->callback = _dasd_wake_block_flush_cb; + for (i = 0; cqr != NULL; cqr = cqr->refers, i++) + list_move_tail(&cqr->blocklist, &flush_queue); + if (i > 1) + /* moved more than one request - need to restart */ + goto restart; + } + spin_unlock_bh(&block->queue_lock); + /* Now call the callback function of flushed requests */ +restart_cb: + list_for_each_entry_safe(cqr, n, &flush_queue, blocklist) { + wait_event(dasd_flush_wq, (cqr->status < DASD_CQR_QUEUED)); + /* Process finished ERP request. */ + if (cqr->refers) { + __dasd_block_process_erp(block, cqr); + /* restart list_for_xx loop since dasd_process_erp + * might remove multiple elements */ + goto restart_cb; + } + /* call the callback function */ + cqr->endclk = get_clock(); + list_del_init(&cqr->blocklist); + __dasd_cleanup_cqr(cqr); } - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); - dasd_schedule_bh(device); return rc; } /* - * SECTION: Block device operations (request queue, partitions, open, release). + * Schedules a call to dasd_tasklet over the device tasklet. + */ +void dasd_schedule_block_bh(struct dasd_block *block) +{ + /* Protect against rescheduling. */ + if (atomic_cmpxchg(&block->tasklet_scheduled, 0, 1) != 0) + return; + /* life cycle of block is bound to it's base device */ + dasd_get_device(block->base); + tasklet_hi_schedule(&block->tasklet); +} + + +/* + * SECTION: external block device operations + * (request queue handling, open, release, etc.) */ /* * Dasd request queue function. Called from ll_rw_blk.c */ -static void -do_dasd_request(struct request_queue * queue) +static void do_dasd_request(struct request_queue *queue) { - struct dasd_device *device; + struct dasd_block *block; - device = (struct dasd_device *) queue->queuedata; - spin_lock(get_ccwdev_lock(device->cdev)); + block = queue->queuedata; + spin_lock(&block->queue_lock); /* Get new request from the block device request queue */ - __dasd_process_blk_queue(device); + __dasd_process_request_queue(block); /* Now check if the head of the ccw queue needs to be started. */ - __dasd_start_head(device); - spin_unlock(get_ccwdev_lock(device->cdev)); + __dasd_block_start_head(block); + spin_unlock(&block->queue_lock); } /* * Allocate and initialize request queue and default I/O scheduler. */ -static int -dasd_alloc_queue(struct dasd_device * device) +static int dasd_alloc_queue(struct dasd_block *block) { int rc; - device->request_queue = blk_init_queue(do_dasd_request, - &device->request_queue_lock); - if (device->request_queue == NULL) + block->request_queue = blk_init_queue(do_dasd_request, + &block->request_queue_lock); + if (block->request_queue == NULL) return -ENOMEM; - device->request_queue->queuedata = device; + block->request_queue->queuedata = block; - elevator_exit(device->request_queue->elevator); - rc = elevator_init(device->request_queue, "deadline"); + elevator_exit(block->request_queue->elevator); + rc = elevator_init(block->request_queue, "deadline"); if (rc) { - blk_cleanup_queue(device->request_queue); + blk_cleanup_queue(block->request_queue); return rc; } return 0; @@ -1780,79 +1971,76 @@ dasd_alloc_queue(struct dasd_device * device) /* * Allocate and initialize request queue. */ -static void -dasd_setup_queue(struct dasd_device * device) +static void dasd_setup_queue(struct dasd_block *block) { int max; - blk_queue_hardsect_size(device->request_queue, device->bp_block); - max = device->discipline->max_blocks << device->s2b_shift; - blk_queue_max_sectors(device->request_queue, max); - blk_queue_max_phys_segments(device->request_queue, -1L); - blk_queue_max_hw_segments(device->request_queue, -1L); - blk_queue_max_segment_size(device->request_queue, -1L); - blk_queue_segment_boundary(device->request_queue, -1L); - blk_queue_ordered(device->request_queue, QUEUE_ORDERED_TAG, NULL); + blk_queue_hardsect_size(block->request_queue, block->bp_block); + max = block->base->discipline->max_blocks << block->s2b_shift; + blk_queue_max_sectors(block->request_queue, max); + blk_queue_max_phys_segments(block->request_queue, -1L); + blk_queue_max_hw_segments(block->request_queue, -1L); + blk_queue_max_segment_size(block->request_queue, -1L); + blk_queue_segment_boundary(block->request_queue, -1L); + blk_queue_ordered(block->request_queue, QUEUE_ORDERED_DRAIN, NULL); } /* * Deactivate and free request queue. */ -static void -dasd_free_queue(struct dasd_device * device) +static void dasd_free_queue(struct dasd_block *block) { - if (device->request_queue) { - blk_cleanup_queue(device->request_queue); - device->request_queue = NULL; + if (block->request_queue) { + blk_cleanup_queue(block->request_queue); + block->request_queue = NULL; } } /* * Flush request on the request queue. */ -static void -dasd_flush_request_queue(struct dasd_device * device) +static void dasd_flush_request_queue(struct dasd_block *block) { struct request *req; - if (!device->request_queue) + if (!block->request_queue) return; - spin_lock_irq(&device->request_queue_lock); - while ((req = elv_next_request(device->request_queue))) { + spin_lock_irq(&block->request_queue_lock); + while ((req = elv_next_request(block->request_queue))) { blkdev_dequeue_request(req); dasd_end_request(req, 0); } - spin_unlock_irq(&device->request_queue_lock); + spin_unlock_irq(&block->request_queue_lock); } -static int -dasd_open(struct inode *inp, struct file *filp) +static int dasd_open(struct inode *inp, struct file *filp) { struct gendisk *disk = inp->i_bdev->bd_disk; - struct dasd_device *device = disk->private_data; + struct dasd_block *block = disk->private_data; + struct dasd_device *base = block->base; int rc; - atomic_inc(&device->open_count); - if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) { + atomic_inc(&block->open_count); + if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) { rc = -ENODEV; goto unlock; } - if (!try_module_get(device->discipline->owner)) { + if (!try_module_get(base->discipline->owner)) { rc = -EINVAL; goto unlock; } if (dasd_probeonly) { - DEV_MESSAGE(KERN_INFO, device, "%s", + DEV_MESSAGE(KERN_INFO, base, "%s", "No access to device due to probeonly mode"); rc = -EPERM; goto out; } - if (device->state <= DASD_STATE_BASIC) { - DBF_DEV_EVENT(DBF_ERR, device, " %s", + if (base->state <= DASD_STATE_BASIC) { + DBF_DEV_EVENT(DBF_ERR, base, " %s", " Cannot open unrecognized device"); rc = -ENODEV; goto out; @@ -1861,41 +2049,41 @@ dasd_open(struct inode *inp, struct file *filp) return 0; out: - module_put(device->discipline->owner); + module_put(base->discipline->owner); unlock: - atomic_dec(&device->open_count); + atomic_dec(&block->open_count); return rc; } -static int -dasd_release(struct inode *inp, struct file *filp) +static int dasd_release(struct inode *inp, struct file *filp) { struct gendisk *disk = inp->i_bdev->bd_disk; - struct dasd_device *device = disk->private_data; + struct dasd_block *block = disk->private_data; - atomic_dec(&device->open_count); - module_put(device->discipline->owner); + atomic_dec(&block->open_count); + module_put(block->base->discipline->owner); return 0; } /* * Return disk geometry. */ -static int -dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo) +static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo) { - struct dasd_device *device; + struct dasd_block *block; + struct dasd_device *base; - device = bdev->bd_disk->private_data; - if (!device) + block = bdev->bd_disk->private_data; + base = block->base; + if (!block) return -ENODEV; - if (!device->discipline || - !device->discipline->fill_geometry) + if (!base->discipline || + !base->discipline->fill_geometry) return -EINVAL; - device->discipline->fill_geometry(device, geo); - geo->start = get_start_sect(bdev) >> device->s2b_shift; + base->discipline->fill_geometry(block, geo); + geo->start = get_start_sect(bdev) >> block->s2b_shift; return 0; } @@ -1909,6 +2097,9 @@ dasd_device_operations = { .getgeo = dasd_getgeo, }; +/******************************************************************************* + * end of block device operations + */ static void dasd_exit(void) @@ -1937,9 +2128,8 @@ dasd_exit(void) * Initial attempt at a probe function. this can be simplified once * the other detection code is gone. */ -int -dasd_generic_probe (struct ccw_device *cdev, - struct dasd_discipline *discipline) +int dasd_generic_probe(struct ccw_device *cdev, + struct dasd_discipline *discipline) { int ret; @@ -1969,19 +2159,20 @@ dasd_generic_probe (struct ccw_device *cdev, ret = ccw_device_set_online(cdev); if (ret) printk(KERN_WARNING - "dasd_generic_probe: could not initially online " - "ccw-device %s\n", cdev->dev.bus_id); - return ret; + "dasd_generic_probe: could not initially " + "online ccw-device %s; return code: %d\n", + cdev->dev.bus_id, ret); + return 0; } /* * This will one day be called from a global not_oper handler. * It is also used by driver_unregister during module unload. */ -void -dasd_generic_remove (struct ccw_device *cdev) +void dasd_generic_remove(struct ccw_device *cdev) { struct dasd_device *device; + struct dasd_block *block; cdev->handler = NULL; @@ -2001,7 +2192,15 @@ dasd_generic_remove (struct ccw_device *cdev) */ dasd_set_target_state(device, DASD_STATE_NEW); /* dasd_delete_device destroys the device reference. */ + block = device->block; + device->block = NULL; dasd_delete_device(device); + /* + * life cycle of block is bound to device, so delete it after + * device was safely removed + */ + if (block) + dasd_free_block(block); } /* @@ -2009,10 +2208,8 @@ dasd_generic_remove (struct ccw_device *cdev) * the device is detected for the first time and is supposed to be used * or the user has started activation through sysfs. */ -int -dasd_generic_set_online (struct ccw_device *cdev, - struct dasd_discipline *base_discipline) - +int dasd_generic_set_online(struct ccw_device *cdev, + struct dasd_discipline *base_discipline) { struct dasd_discipline *discipline; struct dasd_device *device; @@ -2048,6 +2245,7 @@ dasd_generic_set_online (struct ccw_device *cdev, device->base_discipline = base_discipline; device->discipline = discipline; + /* check_device will allocate block device if necessary */ rc = discipline->check_device(device); if (rc) { printk (KERN_WARNING @@ -2067,6 +2265,8 @@ dasd_generic_set_online (struct ccw_device *cdev, cdev->dev.bus_id); rc = -ENODEV; dasd_set_target_state(device, DASD_STATE_NEW); + if (device->block) + dasd_free_block(device->block); dasd_delete_device(device); } else pr_debug("dasd_generic device %s found\n", @@ -2081,10 +2281,10 @@ dasd_generic_set_online (struct ccw_device *cdev, return rc; } -int -dasd_generic_set_offline (struct ccw_device *cdev) +int dasd_generic_set_offline(struct ccw_device *cdev) { struct dasd_device *device; + struct dasd_block *block; int max_count, open_count; device = dasd_device_from_cdev(cdev); @@ -2101,30 +2301,39 @@ dasd_generic_set_offline (struct ccw_device *cdev) * the blkdev_get in dasd_scan_partitions. We are only interested * in the other openers. */ - max_count = device->bdev ? 0 : -1; - open_count = (int) atomic_read(&device->open_count); - if (open_count > max_count) { - if (open_count > 0) - printk (KERN_WARNING "Can't offline dasd device with " - "open count = %i.\n", - open_count); - else - printk (KERN_WARNING "%s", - "Can't offline dasd device due to internal " - "use\n"); - clear_bit(DASD_FLAG_OFFLINE, &device->flags); - dasd_put_device(device); - return -EBUSY; + if (device->block) { + struct dasd_block *block = device->block; + max_count = block->bdev ? 0 : -1; + open_count = (int) atomic_read(&block->open_count); + if (open_count > max_count) { + if (open_count > 0) + printk(KERN_WARNING "Can't offline dasd " + "device with open count = %i.\n", + open_count); + else + printk(KERN_WARNING "%s", + "Can't offline dasd device due " + "to internal use\n"); + clear_bit(DASD_FLAG_OFFLINE, &device->flags); + dasd_put_device(device); + return -EBUSY; + } } dasd_set_target_state(device, DASD_STATE_NEW); /* dasd_delete_device destroys the device reference. */ + block = device->block; + device->block = NULL; dasd_delete_device(device); - + /* + * life cycle of block is bound to device, so delete it after + * device was safely removed + */ + if (block) + dasd_free_block(block); return 0; } -int -dasd_generic_notify(struct ccw_device *cdev, int event) +int dasd_generic_notify(struct ccw_device *cdev, int event) { struct dasd_device *device; struct dasd_ccw_req *cqr; @@ -2145,27 +2354,22 @@ dasd_generic_notify(struct ccw_device *cdev, int event) if (device->state < DASD_STATE_BASIC) break; /* Device is active. We want to keep it. */ - if (test_bit(DASD_FLAG_DSC_ERROR, &device->flags)) { - list_for_each_entry(cqr, &device->ccw_queue, list) - if (cqr->status == DASD_CQR_IN_IO) - cqr->status = DASD_CQR_FAILED; - device->stopped |= DASD_STOPPED_DC_EIO; - } else { - list_for_each_entry(cqr, &device->ccw_queue, list) - if (cqr->status == DASD_CQR_IN_IO) { - cqr->status = DASD_CQR_QUEUED; - cqr->retries++; - } - device->stopped |= DASD_STOPPED_DC_WAIT; - dasd_set_timer(device, 0); - } - dasd_schedule_bh(device); + list_for_each_entry(cqr, &device->ccw_queue, devlist) + if (cqr->status == DASD_CQR_IN_IO) { + cqr->status = DASD_CQR_QUEUED; + cqr->retries++; + } + device->stopped |= DASD_STOPPED_DC_WAIT; + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); ret = 1; break; case CIO_OPER: /* FIXME: add a sanity check. */ - device->stopped &= ~(DASD_STOPPED_DC_WAIT|DASD_STOPPED_DC_EIO); - dasd_schedule_bh(device); + device->stopped &= ~DASD_STOPPED_DC_WAIT; + dasd_schedule_device_bh(device); + if (device->block) + dasd_schedule_block_bh(device->block); ret = 1; break; } @@ -2195,7 +2399,8 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, ccw->cda = (__u32)(addr_t)rdc_buffer; ccw->count = rdc_buffer_size; - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; cqr->expires = 10*HZ; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); cqr->retries = 2; @@ -2217,13 +2422,12 @@ int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic, return PTR_ERR(cqr); ret = dasd_sleep_on(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return ret; } EXPORT_SYMBOL_GPL(dasd_generic_read_dev_chars); -static int __init -dasd_init(void) +static int __init dasd_init(void) { int rc; @@ -2231,7 +2435,7 @@ dasd_init(void) init_waitqueue_head(&dasd_flush_wq); /* register 'common' DASD debug area, used for all DBF_XXX calls */ - dasd_debug_area = debug_register("dasd", 1, 2, 8 * sizeof (long)); + dasd_debug_area = debug_register("dasd", 1, 1, 8 * sizeof(long)); if (dasd_debug_area == NULL) { rc = -ENOMEM; goto failed; @@ -2277,15 +2481,18 @@ EXPORT_SYMBOL(dasd_diag_discipline_pointer); EXPORT_SYMBOL(dasd_add_request_head); EXPORT_SYMBOL(dasd_add_request_tail); EXPORT_SYMBOL(dasd_cancel_req); -EXPORT_SYMBOL(dasd_clear_timer); +EXPORT_SYMBOL(dasd_device_clear_timer); +EXPORT_SYMBOL(dasd_block_clear_timer); EXPORT_SYMBOL(dasd_enable_device); EXPORT_SYMBOL(dasd_int_handler); EXPORT_SYMBOL(dasd_kfree_request); EXPORT_SYMBOL(dasd_kick_device); EXPORT_SYMBOL(dasd_kmalloc_request); -EXPORT_SYMBOL(dasd_schedule_bh); +EXPORT_SYMBOL(dasd_schedule_device_bh); +EXPORT_SYMBOL(dasd_schedule_block_bh); EXPORT_SYMBOL(dasd_set_target_state); -EXPORT_SYMBOL(dasd_set_timer); +EXPORT_SYMBOL(dasd_device_set_timer); +EXPORT_SYMBOL(dasd_block_set_timer); EXPORT_SYMBOL(dasd_sfree_request); EXPORT_SYMBOL(dasd_sleep_on); EXPORT_SYMBOL(dasd_sleep_on_immediatly); @@ -2299,4 +2506,7 @@ EXPORT_SYMBOL_GPL(dasd_generic_remove); EXPORT_SYMBOL_GPL(dasd_generic_notify); EXPORT_SYMBOL_GPL(dasd_generic_set_online); EXPORT_SYMBOL_GPL(dasd_generic_set_offline); - +EXPORT_SYMBOL_GPL(dasd_generic_handle_state_change); +EXPORT_SYMBOL_GPL(dasd_flush_device_queue); +EXPORT_SYMBOL_GPL(dasd_alloc_block); +EXPORT_SYMBOL_GPL(dasd_free_block); diff --git a/drivers/s390/block/dasd_3370_erp.c b/drivers/s390/block/dasd_3370_erp.c deleted file mode 100644 index 1ddab8991d9..00000000000 --- a/drivers/s390/block/dasd_3370_erp.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * File...........: linux/drivers/s390/block/dasd_3370_erp.c - * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> - * Bugreports.to..: <Linux390@de.ibm.com> - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000 - * - */ - -#define PRINTK_HEADER "dasd_erp(3370)" - -#include "dasd_int.h" - - -/* - * DASD_3370_ERP_EXAMINE - * - * DESCRIPTION - * Checks only for fatal/no/recover error. - * A detailed examination of the sense data is done later outside - * the interrupt handler. - * - * The logic is based on the 'IBM 3880 Storage Control Reference' manual - * 'Chapter 7. 3370 Sense Data'. - * - * RETURN VALUES - * dasd_era_none no error - * dasd_era_fatal for all fatal (unrecoverable errors) - * dasd_era_recover for all others. - */ -dasd_era_t -dasd_3370_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) -{ - char *sense = irb->ecw; - - /* check for successful execution first */ - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - if (sense[0] & 0x80) { /* CMD reject */ - return dasd_era_fatal; - } - if (sense[0] & 0x40) { /* Drive offline */ - return dasd_era_recover; - } - if (sense[0] & 0x20) { /* Bus out parity */ - return dasd_era_recover; - } - if (sense[0] & 0x10) { /* equipment check */ - if (sense[1] & 0x80) { - return dasd_era_fatal; - } - return dasd_era_recover; - } - if (sense[0] & 0x08) { /* data check */ - if (sense[1] & 0x80) { - return dasd_era_fatal; - } - return dasd_era_recover; - } - if (sense[0] & 0x04) { /* overrun */ - if (sense[1] & 0x80) { - return dasd_era_fatal; - } - return dasd_era_recover; - } - if (sense[1] & 0x40) { /* invalid blocksize */ - return dasd_era_fatal; - } - if (sense[1] & 0x04) { /* file protected */ - return dasd_era_recover; - } - if (sense[1] & 0x01) { /* operation incomplete */ - return dasd_era_recover; - } - if (sense[2] & 0x80) { /* check data erroor */ - return dasd_era_recover; - } - if (sense[2] & 0x10) { /* Env. data present */ - return dasd_era_recover; - } - /* examine the 24 byte sense data */ - return dasd_era_recover; - -} /* END dasd_3370_erp_examine */ diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 5b7385e430e..c361ab69ec0 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -26,158 +26,6 @@ struct DCTL_data { /* ***************************************************************************** - * SECTION ERP EXAMINATION - ***************************************************************************** - */ - -/* - * DASD_3990_ERP_EXAMINE_24 - * - * DESCRIPTION - * Checks only for fatal (unrecoverable) error. - * A detailed examination of the sense data is done later outside - * the interrupt handler. - * - * Each bit configuration leading to an action code 2 (Exit with - * programming error or unusual condition indication) - * are handled as fatal errors. - * - * All other configurations are handled as recoverable errors. - * - * RETURN VALUES - * dasd_era_fatal for all fatal (unrecoverable errors) - * dasd_era_recover for all others. - */ -static dasd_era_t -dasd_3990_erp_examine_24(struct dasd_ccw_req * cqr, char *sense) -{ - - struct dasd_device *device = cqr->device; - - /* check for 'Command Reject' */ - if ((sense[0] & SNS0_CMD_REJECT) && - (!(sense[2] & SNS2_ENV_DATA_PRESENT))) { - - DEV_MESSAGE(KERN_ERR, device, "%s", - "EXAMINE 24: Command Reject detected - " - "fatal error"); - - return dasd_era_fatal; - } - - /* check for 'Invalid Track Format' */ - if ((sense[1] & SNS1_INV_TRACK_FORMAT) && - (!(sense[2] & SNS2_ENV_DATA_PRESENT))) { - - DEV_MESSAGE(KERN_ERR, device, "%s", - "EXAMINE 24: Invalid Track Format detected " - "- fatal error"); - - return dasd_era_fatal; - } - - /* check for 'No Record Found' */ - if (sense[1] & SNS1_NO_REC_FOUND) { - - /* FIXME: fatal error ?!? */ - DEV_MESSAGE(KERN_ERR, device, - "EXAMINE 24: No Record Found detected %s", - device->state <= DASD_STATE_BASIC ? - " " : "- fatal error"); - - return dasd_era_fatal; - } - - /* return recoverable for all others */ - return dasd_era_recover; -} /* END dasd_3990_erp_examine_24 */ - -/* - * DASD_3990_ERP_EXAMINE_32 - * - * DESCRIPTION - * Checks only for fatal/no/recoverable error. - * A detailed examination of the sense data is done later outside - * the interrupt handler. - * - * RETURN VALUES - * dasd_era_none no error - * dasd_era_fatal for all fatal (unrecoverable errors) - * dasd_era_recover for recoverable others. - */ -static dasd_era_t -dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense) -{ - - struct dasd_device *device = cqr->device; - - switch (sense[25]) { - case 0x00: - return dasd_era_none; - - case 0x01: - DEV_MESSAGE(KERN_ERR, device, "%s", "EXAMINE 32: fatal error"); - - return dasd_era_fatal; - - default: - - return dasd_era_recover; - } - -} /* end dasd_3990_erp_examine_32 */ - -/* - * DASD_3990_ERP_EXAMINE - * - * DESCRIPTION - * Checks only for fatal/no/recover error. - * A detailed examination of the sense data is done later outside - * the interrupt handler. - * - * The logic is based on the 'IBM 3990 Storage Control Reference' manual - * 'Chapter 7. Error Recovery Procedures'. - * - * RETURN VALUES - * dasd_era_none no error - * dasd_era_fatal for all fatal (unrecoverable errors) - * dasd_era_recover for all others. - */ -dasd_era_t -dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) -{ - - char *sense = irb->ecw; - dasd_era_t era = dasd_era_recover; - struct dasd_device *device = cqr->device; - - /* check for successful execution first */ - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - - /* distinguish between 24 and 32 byte sense data */ - if (sense[27] & DASD_SENSE_BIT_0) { - - era = dasd_3990_erp_examine_24(cqr, sense); - - } else { - - era = dasd_3990_erp_examine_32(cqr, sense); - - } - - /* log the erp chain if fatal error occurred */ - if ((era == dasd_era_fatal) && (device->state >= DASD_STATE_READY)) { - dasd_log_sense(cqr, irb); - } - - return era; - -} /* END dasd_3990_erp_examine */ - -/* - ***************************************************************************** * SECTION ERP HANDLING ***************************************************************************** */ @@ -206,7 +54,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) { struct dasd_ccw_req *cqr = erp->refers; - dasd_free_erp_request(erp, erp->device); + dasd_free_erp_request(erp, erp->memdev); cqr->status = final_status; return cqr; @@ -224,15 +72,17 @@ static void dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; + unsigned long flags; DEV_MESSAGE(KERN_INFO, device, "blocking request queue for %is", expires/HZ); + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); device->stopped |= DASD_STOPPED_PENDING; - erp->status = DASD_CQR_QUEUED; - - dasd_set_timer(device, expires); + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + erp->status = DASD_CQR_FILLED; + dasd_block_set_timer(device->block, expires); } /* @@ -251,7 +101,7 @@ static struct dasd_ccw_req * dasd_3990_erp_int_req(struct dasd_ccw_req * erp) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; /* first time set initial retry counter and erp_function */ /* and retry once without blocking queue */ @@ -292,11 +142,14 @@ dasd_3990_erp_int_req(struct dasd_ccw_req * erp) static void dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; __u8 opm; + unsigned long flags; /* try alternate valid path */ + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); opm = ccw_device_get_path_mask(device->cdev); + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); //FIXME: start with get_opm ? if (erp->lpm == 0) erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum); @@ -309,9 +162,8 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) "try alternate lpm=%x (lpum=%x / opm=%x)", erp->lpm, erp->irb.esw.esw0.sublog.lpum, opm); - /* reset status to queued to handle the request again... */ - if (erp->status > DASD_CQR_QUEUED) - erp->status = DASD_CQR_QUEUED; + /* reset status to submit the request again... */ + erp->status = DASD_CQR_FILLED; erp->retries = 1; } else { DEV_MESSAGE(KERN_ERR, device, @@ -320,8 +172,7 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) erp->irb.esw.esw0.sublog.lpum, opm); /* post request with permanent error */ - if (erp->status > DASD_CQR_QUEUED) - erp->status = DASD_CQR_FAILED; + erp->status = DASD_CQR_FAILED; } } /* end dasd_3990_erp_alternate_path */ @@ -344,14 +195,14 @@ static struct dasd_ccw_req * dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; struct DCTL_data *DCTL_data; struct ccw1 *ccw; struct dasd_ccw_req *dctl_cqr; dctl_cqr = dasd_alloc_erp_request((char *) &erp->magic, 1, - sizeof (struct DCTL_data), - erp->device); + sizeof(struct DCTL_data), + device); if (IS_ERR(dctl_cqr)) { DEV_MESSAGE(KERN_ERR, device, "%s", "Unable to allocate DCTL-CQR"); @@ -365,13 +216,14 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) DCTL_data->modifier = modifier; ccw = dctl_cqr->cpaddr; - memset(ccw, 0, sizeof (struct ccw1)); + memset(ccw, 0, sizeof(struct ccw1)); ccw->cmd_code = CCW_CMD_DCTL; ccw->count = 4; ccw->cda = (__u32)(addr_t) DCTL_data; dctl_cqr->function = dasd_3990_erp_DCTL; dctl_cqr->refers = erp; - dctl_cqr->device = erp->device; + dctl_cqr->startdev = device; + dctl_cqr->memdev = device; dctl_cqr->magic = erp->magic; dctl_cqr->expires = 5 * 60 * HZ; dctl_cqr->retries = 2; @@ -435,7 +287,7 @@ static struct dasd_ccw_req * dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; /* first time set initial retry counter and erp_function */ /* and retry once without waiting for state change pending */ @@ -472,7 +324,7 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) "redriving request immediately, " "%d retries left", erp->retries); - erp->status = DASD_CQR_QUEUED; + erp->status = DASD_CQR_FILLED; } } @@ -530,7 +382,7 @@ static void dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; char msg_format = (sense[7] & 0xF0); char msg_no = (sense[7] & 0x0F); @@ -1157,7 +1009,7 @@ static struct dasd_ccw_req * dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_com_rej; @@ -1198,7 +1050,7 @@ static struct dasd_ccw_req * dasd_3990_erp_bus_out(struct dasd_ccw_req * erp) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; /* first time set initial retry counter and erp_function */ /* and retry once without blocking queue */ @@ -1237,7 +1089,7 @@ static struct dasd_ccw_req * dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_equip_check; @@ -1279,7 +1131,6 @@ dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense) erp = dasd_3990_erp_action_5(erp); } - return erp; } /* end dasd_3990_erp_equip_check */ @@ -1299,7 +1150,7 @@ static struct dasd_ccw_req * dasd_3990_erp_data_check(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_data_check; @@ -1358,7 +1209,7 @@ static struct dasd_ccw_req * dasd_3990_erp_overrun(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_overrun; @@ -1387,7 +1238,7 @@ static struct dasd_ccw_req * dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_inv_format; @@ -1403,8 +1254,7 @@ dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense) } else { DEV_MESSAGE(KERN_ERR, device, "%s", - "Invalid Track Format - Fatal error should have " - "been handled within the interrupt handler"); + "Invalid Track Format - Fatal error"); erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); } @@ -1428,7 +1278,7 @@ static struct dasd_ccw_req * dasd_3990_erp_EOC(struct dasd_ccw_req * default_erp, char *sense) { - struct dasd_device *device = default_erp->device; + struct dasd_device *device = default_erp->startdev; DEV_MESSAGE(KERN_ERR, device, "%s", "End-of-Cylinder - must never happen"); @@ -1453,7 +1303,7 @@ static struct dasd_ccw_req * dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_env_data; @@ -1463,11 +1313,9 @@ dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense) /* don't retry on disabled interface */ if (sense[7] != 0x0F) { - erp = dasd_3990_erp_action_4(erp, sense); } else { - - erp = dasd_3990_erp_cleanup(erp, DASD_CQR_IN_IO); + erp->status = DASD_CQR_FILLED; } return erp; @@ -1490,11 +1338,10 @@ static struct dasd_ccw_req * dasd_3990_erp_no_rec(struct dasd_ccw_req * default_erp, char *sense) { - struct dasd_device *device = default_erp->device; + struct dasd_device *device = default_erp->startdev; DEV_MESSAGE(KERN_ERR, device, "%s", - "No Record Found - Fatal error should " - "have been handled within the interrupt handler"); + "No Record Found - Fatal error "); return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED); @@ -1517,7 +1364,7 @@ static struct dasd_ccw_req * dasd_3990_erp_file_prot(struct dasd_ccw_req * erp) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; DEV_MESSAGE(KERN_ERR, device, "%s", "File Protected"); @@ -1526,6 +1373,43 @@ dasd_3990_erp_file_prot(struct dasd_ccw_req * erp) } /* end dasd_3990_erp_file_prot */ /* + * DASD_3990_ERP_INSPECT_ALIAS + * + * DESCRIPTION + * Checks if the original request was started on an alias device. + * If yes, it modifies the original and the erp request so that + * the erp request can be started on a base device. + * + * PARAMETER + * erp pointer to the currently created default ERP + * + * RETURN VALUES + * erp pointer to the modified ERP, or NULL + */ + +static struct dasd_ccw_req *dasd_3990_erp_inspect_alias( + struct dasd_ccw_req *erp) +{ + struct dasd_ccw_req *cqr = erp->refers; + + if (cqr->block && + (cqr->block->base != cqr->startdev)) { + if (cqr->startdev->features & DASD_FEATURE_ERPLOG) { + DEV_MESSAGE(KERN_ERR, cqr->startdev, + "ERP on alias device for request %p," + " recover on base device %s", cqr, + cqr->block->base->cdev->dev.bus_id); + } + dasd_eckd_reset_ccw_to_base_io(cqr); + erp->startdev = cqr->block->base; + erp->function = dasd_3990_erp_inspect_alias; + return erp; + } else + return NULL; +} + + +/* * DASD_3990_ERP_INSPECT_24 * * DESCRIPTION @@ -1623,7 +1507,7 @@ static struct dasd_ccw_req * dasd_3990_erp_action_10_32(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->retries = 256; erp->function = dasd_3990_erp_action_10_32; @@ -1657,13 +1541,14 @@ static struct dasd_ccw_req * dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) { - struct dasd_device *device = default_erp->device; + struct dasd_device *device = default_erp->startdev; __u32 cpa = 0; struct dasd_ccw_req *cqr; struct dasd_ccw_req *erp; struct DE_eckd_data *DE_data; + struct PFX_eckd_data *PFX_data; char *LO_data; /* LO_eckd_data_t */ - struct ccw1 *ccw; + struct ccw1 *ccw, *oldccw; DEV_MESSAGE(KERN_DEBUG, device, "%s", "Write not finished because of unexpected condition"); @@ -1702,8 +1587,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* Build new ERP request including DE/LO */ erp = dasd_alloc_erp_request((char *) &cqr->magic, 2 + 1,/* DE/LO + TIC */ - sizeof (struct DE_eckd_data) + - sizeof (struct LO_eckd_data), device); + sizeof(struct DE_eckd_data) + + sizeof(struct LO_eckd_data), device); if (IS_ERR(erp)) { DEV_MESSAGE(KERN_ERR, device, "%s", "Unable to allocate ERP"); @@ -1712,10 +1597,16 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* use original DE */ DE_data = erp->data; - memcpy(DE_data, cqr->data, sizeof (struct DE_eckd_data)); + oldccw = cqr->cpaddr; + if (oldccw->cmd_code == DASD_ECKD_CCW_PFX) { + PFX_data = cqr->data; + memcpy(DE_data, &PFX_data->define_extend, + sizeof(struct DE_eckd_data)); + } else + memcpy(DE_data, cqr->data, sizeof(struct DE_eckd_data)); /* create LO */ - LO_data = erp->data + sizeof (struct DE_eckd_data); + LO_data = erp->data + sizeof(struct DE_eckd_data); if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) { @@ -1748,7 +1639,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* create DE ccw */ ccw = erp->cpaddr; - memset(ccw, 0, sizeof (struct ccw1)); + memset(ccw, 0, sizeof(struct ccw1)); ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT; ccw->flags = CCW_FLAG_CC; ccw->count = 16; @@ -1756,7 +1647,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* create LO ccw */ ccw++; - memset(ccw, 0, sizeof (struct ccw1)); + memset(ccw, 0, sizeof(struct ccw1)); ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD; ccw->flags = CCW_FLAG_CC; ccw->count = 16; @@ -1770,7 +1661,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* fill erp related fields */ erp->function = dasd_3990_erp_action_1B_32; erp->refers = default_erp->refers; - erp->device = device; + erp->startdev = device; + erp->memdev = device; erp->magic = default_erp->magic; erp->expires = 0; erp->retries = 256; @@ -1803,7 +1695,7 @@ static struct dasd_ccw_req * dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) { - struct dasd_device *device = previous_erp->device; + struct dasd_device *device = previous_erp->startdev; __u32 cpa = 0; struct dasd_ccw_req *cqr; struct dasd_ccw_req *erp; @@ -1827,7 +1719,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) DEV_MESSAGE(KERN_DEBUG, device, "%s", "Imprecise ending is set - just retry"); - previous_erp->status = DASD_CQR_QUEUED; + previous_erp->status = DASD_CQR_FILLED; return previous_erp; } @@ -1850,7 +1742,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) erp = previous_erp; /* update the LO with the new returned sense data */ - LO_data = erp->data + sizeof (struct DE_eckd_data); + LO_data = erp->data + sizeof(struct DE_eckd_data); if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) { @@ -1889,7 +1781,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) ccw++; /* addr of TIC ccw */ ccw->cda = cpa; - erp->status = DASD_CQR_QUEUED; + erp->status = DASD_CQR_FILLED; return erp; @@ -1968,9 +1860,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense) * try further actions. */ erp->lpm = 0; - - erp->status = DASD_CQR_ERROR; - + erp->status = DASD_CQR_NEED_ERP; } } @@ -2047,7 +1937,7 @@ dasd_3990_erp_compound_config(struct dasd_ccw_req * erp, char *sense) if ((sense[25] & DASD_SENSE_BIT_1) && (sense[26] & DASD_SENSE_BIT_2)) { /* set to suspended duplex state then restart */ - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; DEV_MESSAGE(KERN_ERR, device, "%s", "Set device to suspended duplex state should be " @@ -2081,28 +1971,26 @@ dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense) { if ((erp->function == dasd_3990_erp_compound_retry) && - (erp->status == DASD_CQR_ERROR)) { + (erp->status == DASD_CQR_NEED_ERP)) { dasd_3990_erp_compound_path(erp, sense); } if ((erp->function == dasd_3990_erp_compound_path) && - (erp->status == DASD_CQR_ERROR)) { + (erp->status == DASD_CQR_NEED_ERP)) { erp = dasd_3990_erp_compound_code(erp, sense); } if ((erp->function == dasd_3990_erp_compound_code) && - (erp->status == DASD_CQR_ERROR)) { + (erp->status == DASD_CQR_NEED_ERP)) { dasd_3990_erp_compound_config(erp, sense); } /* if no compound action ERP specified, the request failed */ - if (erp->status == DASD_CQR_ERROR) { - + if (erp->status == DASD_CQR_NEED_ERP) erp->status = DASD_CQR_FAILED; - } return erp; @@ -2127,7 +2015,7 @@ static struct dasd_ccw_req * dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_inspect_32; @@ -2149,8 +2037,7 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) case 0x01: /* fatal error */ DEV_MESSAGE(KERN_ERR, device, "%s", - "Fatal error should have been " - "handled within the interrupt handler"); + "Retry not recommended - Fatal error"); erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); break; @@ -2253,6 +2140,11 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp) /* already set up new ERP ! */ char *sense = erp->refers->irb.ecw; + /* if this problem occured on an alias retry on base */ + erp_new = dasd_3990_erp_inspect_alias(erp); + if (erp_new) + return erp_new; + /* distinguish between 24 and 32 byte sense data */ if (sense[27] & DASD_SENSE_BIT_0) { @@ -2287,13 +2179,13 @@ static struct dasd_ccw_req * dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) { - struct dasd_device *device = cqr->device; + struct dasd_device *device = cqr->startdev; struct ccw1 *ccw; /* allocate additional request block */ struct dasd_ccw_req *erp; - erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, cqr->device); + erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, device); if (IS_ERR(erp)) { if (cqr->retries <= 0) { DEV_MESSAGE(KERN_ERR, device, "%s", @@ -2305,7 +2197,7 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) "Unable to allocate ERP request " "(%i retries left)", cqr->retries); - dasd_set_timer(device, (HZ << 3)); + dasd_block_set_timer(device->block, (HZ << 3)); } return cqr; } @@ -2319,7 +2211,9 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) ccw->cda = (long)(cqr->cpaddr); erp->function = dasd_3990_erp_add_erp; erp->refers = cqr; - erp->device = cqr->device; + erp->startdev = device; + erp->memdev = device; + erp->block = cqr->block; erp->magic = cqr->magic; erp->expires = 0; erp->retries = 256; @@ -2466,7 +2360,7 @@ static struct dasd_ccw_req * dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; char *sense = erp->irb.ecw; /* check for 24 byte sense ERP */ @@ -2557,7 +2451,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, struct dasd_ccw_req *erp) { - struct dasd_device *device = erp_head->device; + struct dasd_device *device = erp_head->startdev; struct dasd_ccw_req *erp_done = erp_head; /* finished req */ struct dasd_ccw_req *erp_free = NULL; /* req to be freed */ @@ -2569,13 +2463,13 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, "original request was lost\n"); /* remove the request from the device queue */ - list_del(&erp_done->list); + list_del(&erp_done->blocklist); erp_free = erp_done; erp_done = erp_done->refers; /* free the finished erp request */ - dasd_free_erp_request(erp_free, erp_free->device); + dasd_free_erp_request(erp_free, erp_free->memdev); } /* end while */ @@ -2603,7 +2497,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, erp->retries, erp); /* handle the request again... */ - erp->status = DASD_CQR_QUEUED; + erp->status = DASD_CQR_FILLED; } } else { @@ -2620,7 +2514,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, * DASD_3990_ERP_ACTION * * DESCRIPTION - * controll routine for 3990 erp actions. + * control routine for 3990 erp actions. * Has to be called with the queue lock (namely the s390_irq_lock) acquired. * * PARAMETER @@ -2636,9 +2530,8 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, struct dasd_ccw_req * dasd_3990_erp_action(struct dasd_ccw_req * cqr) { - struct dasd_ccw_req *erp = NULL; - struct dasd_device *device = cqr->device; + struct dasd_device *device = cqr->startdev; struct dasd_ccw_req *temp_erp = NULL; if (device->features & DASD_FEATURE_ERPLOG) { @@ -2704,10 +2597,11 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) } } - /* enqueue added ERP request */ - if (erp->status == DASD_CQR_FILLED) { - erp->status = DASD_CQR_QUEUED; - list_add(&erp->list, &device->ccw_queue); + /* enqueue ERP request if it's a new one */ + if (list_empty(&erp->blocklist)) { + cqr->status = DASD_CQR_IN_ERP; + /* add erp request before the cqr */ + list_add_tail(&erp->blocklist, &cqr->blocklist); } return erp; diff --git a/drivers/s390/block/dasd_9336_erp.c b/drivers/s390/block/dasd_9336_erp.c deleted file mode 100644 index 6e082688475..00000000000 --- a/drivers/s390/block/dasd_9336_erp.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * File...........: linux/drivers/s390/block/dasd_9336_erp.c - * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> - * Bugreports.to..: <Linux390@de.ibm.com> - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000 - * - */ - -#define PRINTK_HEADER "dasd_erp(9336)" - -#include "dasd_int.h" - - -/* - * DASD_9336_ERP_EXAMINE - * - * DESCRIPTION - * Checks only for fatal/no/recover error. - * A detailed examination of the sense data is done later outside - * the interrupt handler. - * - * The logic is based on the 'IBM 3880 Storage Control Reference' manual - * 'Chapter 7. 9336 Sense Data'. - * - * RETURN VALUES - * dasd_era_none no error - * dasd_era_fatal for all fatal (unrecoverable errors) - * dasd_era_recover for all others. - */ -dasd_era_t -dasd_9336_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) -{ - /* check for successful execution first */ - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - - /* examine the 24 byte sense data */ - return dasd_era_recover; - -} /* END dasd_9336_erp_examine */ diff --git a/drivers/s390/block/dasd_9343_erp.c b/drivers/s390/block/dasd_9343_erp.c deleted file mode 100644 index ddecb9808ed..00000000000 --- a/drivers/s390/block/dasd_9343_erp.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * File...........: linux/drivers/s390/block/dasd_9345_erp.c - * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> - * Bugreports.to..: <Linux390@de.ibm.com> - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000 - * - */ - -#define PRINTK_HEADER "dasd_erp(9343)" - -#include "dasd_int.h" - -dasd_era_t -dasd_9343_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) -{ - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - - return dasd_era_recover; -} diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c new file mode 100644 index 00000000000..3a40bee9d35 --- /dev/null +++ b/drivers/s390/block/dasd_alias.c @@ -0,0 +1,903 @@ +/* + * PAV alias management for the DASD ECKD discipline + * + * Copyright IBM Corporation, 2007 + * Author(s): Stefan Weinhuber <wein@de.ibm.com> + */ + +#include <linux/list.h> +#include <asm/ebcdic.h> +#include "dasd_int.h" +#include "dasd_eckd.h" + +#ifdef PRINTK_HEADER +#undef PRINTK_HEADER +#endif /* PRINTK_HEADER */ +#define PRINTK_HEADER "dasd(eckd):" + + +/* + * General concept of alias management: + * - PAV and DASD alias management is specific to the eckd discipline. + * - A device is connected to an lcu as long as the device exists. + * dasd_alias_make_device_known_to_lcu will be called wenn the + * device is checked by the eckd discipline and + * dasd_alias_disconnect_device_from_lcu will be called + * before the device is deleted. + * - The dasd_alias_add_device / dasd_alias_remove_device + * functions mark the point when a device is 'ready for service'. + * - A summary unit check is a rare occasion, but it is mandatory to + * support it. It requires some complex recovery actions before the + * devices can be used again (see dasd_alias_handle_summary_unit_check). + * - dasd_alias_get_start_dev will find an alias device that can be used + * instead of the base device and does some (very simple) load balancing. + * This is the function that gets called for each I/O, so when improving + * something, this function should get faster or better, the rest has just + * to be correct. + */ + + +static void summary_unit_check_handling_work(struct work_struct *); +static void lcu_update_work(struct work_struct *); +static int _schedule_lcu_update(struct alias_lcu *, struct dasd_device *); + +static struct alias_root aliastree = { + .serverlist = LIST_HEAD_INIT(aliastree.serverlist), + .lock = __SPIN_LOCK_UNLOCKED(aliastree.lock), +}; + +static struct alias_server *_find_server(struct dasd_uid *uid) +{ + struct alias_server *pos; + list_for_each_entry(pos, &aliastree.serverlist, server) { + if (!strncmp(pos->uid.vendor, uid->vendor, + sizeof(uid->vendor)) + && !strncmp(pos->uid.serial, uid->serial, + sizeof(uid->serial))) + return pos; + }; + return NULL; +} + +static struct alias_lcu *_find_lcu(struct alias_server *server, + struct dasd_uid *uid) +{ + struct alias_lcu *pos; + list_for_each_entry(pos, &server->lculist, lcu) { + if (pos->uid.ssid == uid->ssid) + return pos; + }; + return NULL; +} + +static struct alias_pav_group *_find_group(struct alias_lcu *lcu, + struct dasd_uid *uid) +{ + struct alias_pav_group *pos; + __u8 search_unit_addr; + + /* for hyper pav there is only one group */ + if (lcu->pav == HYPER_PAV) { + if (list_empty(&lcu->grouplist)) + return NULL; + else + return list_first_entry(&lcu->grouplist, + struct alias_pav_group, group); + } + + /* for base pav we have to find the group that matches the base */ + if (uid->type == UA_BASE_DEVICE) + search_unit_addr = uid->real_unit_addr; + else + search_unit_addr = uid->base_unit_addr; + list_for_each_entry(pos, &lcu->grouplist, group) { + if (pos->uid.base_unit_addr == search_unit_addr) + return pos; + }; + return NULL; +} + +static struct alias_server *_allocate_server(struct dasd_uid *uid) +{ + struct alias_server *server; + + server = kzalloc(sizeof(*server), GFP_KERNEL); + if (!server) + return ERR_PTR(-ENOMEM); + memcpy(server->uid.vendor, uid->vendor, sizeof(uid->vendor)); + memcpy(server->uid.serial, uid->serial, sizeof(uid->serial)); + INIT_LIST_HEAD(&server->server); + INIT_LIST_HEAD(&server->lculist); + return server; +} + +static void _free_server(struct alias_server *server) +{ + kfree(server); +} + +static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid) +{ + struct alias_lcu *lcu; + + lcu = kzalloc(sizeof(*lcu), GFP_KERNEL); + if (!lcu) + return ERR_PTR(-ENOMEM); + lcu->uac = kzalloc(sizeof(*(lcu->uac)), GFP_KERNEL | GFP_DMA); + if (!lcu->uac) + goto out_err1; + lcu->rsu_cqr = kzalloc(sizeof(*lcu->rsu_cqr), GFP_KERNEL | GFP_DMA); + if (!lcu->rsu_cqr) + goto out_err2; + lcu->rsu_cqr->cpaddr = kzalloc(sizeof(struct ccw1), + GFP_KERNEL | GFP_DMA); + if (!lcu->rsu_cqr->cpaddr) + goto out_err3; + lcu->rsu_cqr->data = kzalloc(16, GFP_KERNEL | GFP_DMA); + if (!lcu->rsu_cqr->data) + goto out_err4; + + memcpy(lcu->uid.vendor, uid->vendor, sizeof(uid->vendor)); + memcpy(lcu->uid.serial, uid->serial, sizeof(uid->serial)); + lcu->uid.ssid = uid->ssid; + lcu->pav = NO_PAV; + lcu->flags = NEED_UAC_UPDATE | UPDATE_PENDING; + INIT_LIST_HEAD(&lcu->lcu); + INIT_LIST_HEAD(&lcu->inactive_devices); + INIT_LIST_HEAD(&lcu->active_devices); + INIT_LIST_HEAD(&lcu->grouplist); + INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work); + INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work); + spin_lock_init(&lcu->lock); + return lcu; + +out_err4: + kfree(lcu->rsu_cqr->cpaddr); +out_err3: + kfree(lcu->rsu_cqr); +out_err2: + kfree(lcu->uac); +out_err1: + kfree(lcu); + return ERR_PTR(-ENOMEM); +} + +static void _free_lcu(struct alias_lcu *lcu) +{ + kfree(lcu->rsu_cqr->data); + kfree(lcu->rsu_cqr->cpaddr); + kfree(lcu->rsu_cqr); + kfree(lcu->uac); + kfree(lcu); +} + +/* + * This is the function that will allocate all the server and lcu data, + * so this function must be called first for a new device. + * If the return value is 1, the lcu was already known before, if it + * is 0, this is a new lcu. + * Negative return code indicates that something went wrong (e.g. -ENOMEM) + */ +int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + unsigned long flags; + struct alias_server *server, *newserver; + struct alias_lcu *lcu, *newlcu; + int is_lcu_known; + struct dasd_uid *uid; + + private = (struct dasd_eckd_private *) device->private; + uid = &private->uid; + spin_lock_irqsave(&aliastree.lock, flags); + is_lcu_known = 1; + server = _find_server(uid); + if (!server) { + spin_unlock_irqrestore(&aliastree.lock, flags); + newserver = _allocate_server(uid); + if (IS_ERR(newserver)) + return PTR_ERR(newserver); + spin_lock_irqsave(&aliastree.lock, flags); + server = _find_server(uid); + if (!server) { + list_add(&newserver->server, &aliastree.serverlist); + server = newserver; + is_lcu_known = 0; + } else { + /* someone was faster */ + _free_server(newserver); + } + } + + lcu = _find_lcu(server, uid); + if (!lcu) { + spin_unlock_irqrestore(&aliastree.lock, flags); + newlcu = _allocate_lcu(uid); + if (IS_ERR(newlcu)) + return PTR_ERR(lcu); + spin_lock_irqsave(&aliastree.lock, flags); + lcu = _find_lcu(server, uid); + if (!lcu) { + list_add(&newlcu->lcu, &server->lculist); + lcu = newlcu; + is_lcu_known = 0; + } else { + /* someone was faster */ + _free_lcu(newlcu); + } + is_lcu_known = 0; + } + spin_lock(&lcu->lock); + list_add(&device->alias_list, &lcu->inactive_devices); + private->lcu = lcu; + spin_unlock(&lcu->lock); + spin_unlock_irqrestore(&aliastree.lock, flags); + + return is_lcu_known; +} + +/* + * This function removes a device from the scope of alias management. + * The complicated part is to make sure that it is not in use by + * any of the workers. If necessary cancel the work. + */ +void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + unsigned long flags; + struct alias_lcu *lcu; + struct alias_server *server; + int was_pending; + + private = (struct dasd_eckd_private *) device->private; + lcu = private->lcu; + spin_lock_irqsave(&lcu->lock, flags); + list_del_init(&device->alias_list); + /* make sure that the workers don't use this device */ + if (device == lcu->suc_data.device) { + spin_unlock_irqrestore(&lcu->lock, flags); + cancel_work_sync(&lcu->suc_data.worker); + spin_lock_irqsave(&lcu->lock, flags); + if (device == lcu->suc_data.device) + lcu->suc_data.device = NULL; + } + was_pending = 0; + if (device == lcu->ruac_data.device) { + spin_unlock_irqrestore(&lcu->lock, flags); + was_pending = 1; + cancel_delayed_work_sync(&lcu->ruac_data.dwork); + spin_lock_irqsave(&lcu->lock, flags); + if (device == lcu->ruac_data.device) + lcu->ruac_data.device = NULL; + } + private->lcu = NULL; + spin_unlock_irqrestore(&lcu->lock, flags); + + spin_lock_irqsave(&aliastree.lock, flags); + spin_lock(&lcu->lock); + if (list_empty(&lcu->grouplist) && + list_empty(&lcu->active_devices) && + list_empty(&lcu->inactive_devices)) { + list_del(&lcu->lcu); + spin_unlock(&lcu->lock); + _free_lcu(lcu); + lcu = NULL; + } else { + if (was_pending) + _schedule_lcu_update(lcu, NULL); + spin_unlock(&lcu->lock); + } + server = _find_server(&private->uid); + if (server && list_empty(&server->lculist)) { + list_del(&server->server); + _free_server(server); + } + spin_unlock_irqrestore(&aliastree.lock, flags); +} + +/* + * This function assumes that the unit address configuration stored + * in the lcu is up to date and will update the device uid before + * adding it to a pav group. + */ +static int _add_device_to_lcu(struct alias_lcu *lcu, + struct dasd_device *device) +{ + + struct dasd_eckd_private *private; + struct alias_pav_group *group; + struct dasd_uid *uid; + + private = (struct dasd_eckd_private *) device->private; + uid = &private->uid; + uid->type = lcu->uac->unit[uid->real_unit_addr].ua_type; + uid->base_unit_addr = lcu->uac->unit[uid->real_unit_addr].base_ua; + dasd_set_uid(device->cdev, &private->uid); + + /* if we have no PAV anyway, we don't need to bother with PAV groups */ + if (lcu->pav == NO_PAV) { + list_move(&device->alias_list, &lcu->active_devices); + return 0; + } + + group = _find_group(lcu, uid); + if (!group) { + group = kzalloc(sizeof(*group), GFP_ATOMIC); + if (!group) + return -ENOMEM; + memcpy(group->uid.vendor, uid->vendor, sizeof(uid->vendor)); + memcpy(group->uid.serial, uid->serial, sizeof(uid->serial)); + group->uid.ssid = uid->ssid; + if (uid->type == UA_BASE_DEVICE) + group->uid.base_unit_addr = uid->real_unit_addr; + else + group->uid.base_unit_addr = uid->base_unit_addr; + INIT_LIST_HEAD(&group->group); + INIT_LIST_HEAD(&group->baselist); + INIT_LIST_HEAD(&group->aliaslist); + list_add(&group->group, &lcu->grouplist); + } + if (uid->type == UA_BASE_DEVICE) + list_move(&device->alias_list, &group->baselist); + else + list_move(&device->alias_list, &group->aliaslist); + private->pavgroup = group; + return 0; +}; + +static void _remove_device_from_lcu(struct alias_lcu *lcu, + struct dasd_device *device) +{ + struct dasd_eckd_private *private; + struct alias_pav_group *group; + + private = (struct dasd_eckd_private *) device->private; + list_move(&device->alias_list, &lcu->inactive_devices); + group = private->pavgroup; + if (!group) + return; + private->pavgroup = NULL; + if (list_empty(&group->baselist) && list_empty(&group->aliaslist)) { + list_del(&group->group); + kfree(group); + return; + } + if (group->next == device) + group->next = NULL; +}; + +static int read_unit_address_configuration(struct dasd_device *device, + struct alias_lcu *lcu) +{ + struct dasd_psf_prssd_data *prssdp; + struct dasd_ccw_req *cqr; + struct ccw1 *ccw; + int rc; + unsigned long flags; + + cqr = dasd_kmalloc_request("ECKD", + 1 /* PSF */ + 1 /* RSSD */ , + (sizeof(struct dasd_psf_prssd_data)), + device); + if (IS_ERR(cqr)) + return PTR_ERR(cqr); + cqr->startdev = device; + cqr->memdev = device; + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + cqr->retries = 10; + cqr->expires = 20 * HZ; + + /* Prepare for Read Subsystem Data */ + prssdp = (struct dasd_psf_prssd_data *) cqr->data; + memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); + prssdp->order = PSF_ORDER_PRSSD; + prssdp->suborder = 0x0e; /* Read unit address configuration */ + /* all other bytes of prssdp must be zero */ + + ccw = cqr->cpaddr; + ccw->cmd_code = DASD_ECKD_CCW_PSF; + ccw->count = sizeof(struct dasd_psf_prssd_data); + ccw->flags |= CCW_FLAG_CC; + ccw->cda = (__u32)(addr_t) prssdp; + + /* Read Subsystem Data - feature codes */ + memset(lcu->uac, 0, sizeof(*(lcu->uac))); + + ccw++; + ccw->cmd_code = DASD_ECKD_CCW_RSSD; + ccw->count = sizeof(*(lcu->uac)); + ccw->cda = (__u32)(addr_t) lcu->uac; + + cqr->buildclk = get_clock(); + cqr->status = DASD_CQR_FILLED; + + /* need to unset flag here to detect race with summary unit check */ + spin_lock_irqsave(&lcu->lock, flags); + lcu->flags &= ~NEED_UAC_UPDATE; + spin_unlock_irqrestore(&lcu->lock, flags); + + do { + rc = dasd_sleep_on(cqr); + } while (rc && (cqr->retries > 0)); + if (rc) { + spin_lock_irqsave(&lcu->lock, flags); + lcu->flags |= NEED_UAC_UPDATE; + spin_unlock_irqrestore(&lcu->lock, flags); + } + dasd_kfree_request(cqr, cqr->memdev); + return rc; +} + +static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu) +{ + unsigned long flags; + struct alias_pav_group *pavgroup, *tempgroup; + struct dasd_device *device, *tempdev; + int i, rc; + struct dasd_eckd_private *private; + + spin_lock_irqsave(&lcu->lock, flags); + list_for_each_entry_safe(pavgroup, tempgroup, &lcu->grouplist, group) { + list_for_each_entry_safe(device, tempdev, &pavgroup->baselist, + alias_list) { + list_move(&device->alias_list, &lcu->active_devices); + private = (struct dasd_eckd_private *) device->private; + private->pavgroup = NULL; + } + list_for_each_entry_safe(device, tempdev, &pavgroup->aliaslist, + alias_list) { + list_move(&device->alias_list, &lcu->active_devices); + private = (struct dasd_eckd_private *) device->private; + private->pavgroup = NULL; + } + list_del(&pavgroup->group); + kfree(pavgroup); + } + spin_unlock_irqrestore(&lcu->lock, flags); + + rc = read_unit_address_configuration(refdev, lcu); + if (rc) + return rc; + + spin_lock_irqsave(&lcu->lock, flags); + lcu->pav = NO_PAV; + for (i = 0; i < MAX_DEVICES_PER_LCU; ++i) { + switch (lcu->uac->unit[i].ua_type) { + case UA_BASE_PAV_ALIAS: + lcu->pav = BASE_PAV; + break; + case UA_HYPER_PAV_ALIAS: + lcu->pav = HYPER_PAV; + break; + } + if (lcu->pav != NO_PAV) + break; + } + + list_for_each_entry_safe(device, tempdev, &lcu->active_devices, + alias_list) { + _add_device_to_lcu(lcu, device); + } + spin_unlock_irqrestore(&lcu->lock, flags); + return 0; +} + +static void lcu_update_work(struct work_struct *work) +{ + struct alias_lcu *lcu; + struct read_uac_work_data *ruac_data; + struct dasd_device *device; + unsigned long flags; + int rc; + + ruac_data = container_of(work, struct read_uac_work_data, dwork.work); + lcu = container_of(ruac_data, struct alias_lcu, ruac_data); + device = ruac_data->device; + rc = _lcu_update(device, lcu); + /* + * Need to check flags again, as there could have been another + * prepare_update or a new device a new device while we were still + * processing the data + */ + spin_lock_irqsave(&lcu->lock, flags); + if (rc || (lcu->flags & NEED_UAC_UPDATE)) { + DEV_MESSAGE(KERN_WARNING, device, "could not update" + " alias data in lcu (rc = %d), retry later", rc); + schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ); + } else { + lcu->ruac_data.device = NULL; + lcu->flags &= ~UPDATE_PENDING; + } + spin_unlock_irqrestore(&lcu->lock, flags); +} + +static int _schedule_lcu_update(struct alias_lcu *lcu, + struct dasd_device *device) +{ + struct dasd_device *usedev = NULL; + struct alias_pav_group *group; + + lcu->flags |= NEED_UAC_UPDATE; + if (lcu->ruac_data.device) { + /* already scheduled or running */ + return 0; + } + if (device && !list_empty(&device->alias_list)) + usedev = device; + + if (!usedev && !list_empty(&lcu->grouplist)) { + group = list_first_entry(&lcu->grouplist, + struct alias_pav_group, group); + if (!list_empty(&group->baselist)) + usedev = list_first_entry(&group->baselist, + struct dasd_device, + alias_list); + else if (!list_empty(&group->aliaslist)) + usedev = list_first_entry(&group->aliaslist, + struct dasd_device, + alias_list); + } + if (!usedev && !list_empty(&lcu->active_devices)) { + usedev = list_first_entry(&lcu->active_devices, + struct dasd_device, alias_list); + } + /* + * if we haven't found a proper device yet, give up for now, the next + * device that will be set active will trigger an lcu update + */ + if (!usedev) + return -EINVAL; + lcu->ruac_data.device = usedev; + schedule_delayed_work(&lcu->ruac_data.dwork, 0); + return 0; +} + +int dasd_alias_add_device(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + struct alias_lcu *lcu; + unsigned long flags; + int rc; + + private = (struct dasd_eckd_private *) device->private; + lcu = private->lcu; + rc = 0; + spin_lock_irqsave(&lcu->lock, flags); + if (!(lcu->flags & UPDATE_PENDING)) { + rc = _add_device_to_lcu(lcu, device); + if (rc) + lcu->flags |= UPDATE_PENDING; + } + if (lcu->flags & UPDATE_PENDING) { + list_move(&device->alias_list, &lcu->active_devices); + _schedule_lcu_update(lcu, device); + } + spin_unlock_irqrestore(&lcu->lock, flags); + return rc; +} + +int dasd_alias_remove_device(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + struct alias_lcu *lcu; + unsigned long flags; + + private = (struct dasd_eckd_private *) device->private; + lcu = private->lcu; + spin_lock_irqsave(&lcu->lock, flags); + _remove_device_from_lcu(lcu, device); + spin_unlock_irqrestore(&lcu->lock, flags); + return 0; +} + +struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device) +{ + + struct dasd_device *alias_device; + struct alias_pav_group *group; + struct alias_lcu *lcu; + struct dasd_eckd_private *private, *alias_priv; + unsigned long flags; + + private = (struct dasd_eckd_private *) base_device->private; + group = private->pavgroup; + lcu = private->lcu; + if (!group || !lcu) + return NULL; + if (lcu->pav == NO_PAV || + lcu->flags & (NEED_UAC_UPDATE | UPDATE_PENDING)) + return NULL; + + spin_lock_irqsave(&lcu->lock, flags); + alias_device = group->next; + if (!alias_device) { + if (list_empty(&group->aliaslist)) { + spin_unlock_irqrestore(&lcu->lock, flags); + return NULL; + } else { + alias_device = list_first_entry(&group->aliaslist, + struct dasd_device, + alias_list); + } + } + if (list_is_last(&alias_device->alias_list, &group->aliaslist)) + group->next = list_first_entry(&group->aliaslist, + struct dasd_device, alias_list); + else + group->next = list_first_entry(&alias_device->alias_list, + struct dasd_device, alias_list); + spin_unlock_irqrestore(&lcu->lock, flags); + alias_priv = (struct dasd_eckd_private *) alias_device->private; + if ((alias_priv->count < private->count) && !alias_device->stopped) + return alias_device; + else + return NULL; +} + +/* + * Summary unit check handling depends on the way alias devices + * are handled so it is done here rather then in dasd_eckd.c + */ +static int reset_summary_unit_check(struct alias_lcu *lcu, + struct dasd_device *device, + char reason) +{ + struct dasd_ccw_req *cqr; + int rc = 0; + + cqr = lcu->rsu_cqr; + strncpy((char *) &cqr->magic, "ECKD", 4); + ASCEBC((char *) &cqr->magic, 4); + cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RSCK; + cqr->cpaddr->flags = 0 ; + cqr->cpaddr->count = 16; + cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; + ((char *)cqr->data)[0] = reason; + + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + cqr->retries = 255; /* set retry counter to enable basic ERP */ + cqr->startdev = device; + cqr->memdev = device; + cqr->block = NULL; + cqr->expires = 5 * HZ; + cqr->buildclk = get_clock(); + cqr->status = DASD_CQR_FILLED; + + rc = dasd_sleep_on_immediatly(cqr); + return rc; +} + +static void _restart_all_base_devices_on_lcu(struct alias_lcu *lcu) +{ + struct alias_pav_group *pavgroup; + struct dasd_device *device; + struct dasd_eckd_private *private; + + /* active and inactive list can contain alias as well as base devices */ + list_for_each_entry(device, &lcu->active_devices, alias_list) { + private = (struct dasd_eckd_private *) device->private; + if (private->uid.type != UA_BASE_DEVICE) + continue; + dasd_schedule_block_bh(device->block); + dasd_schedule_device_bh(device); + } + list_for_each_entry(device, &lcu->inactive_devices, alias_list) { + private = (struct dasd_eckd_private *) device->private; + if (private->uid.type != UA_BASE_DEVICE) + continue; + dasd_schedule_block_bh(device->block); + dasd_schedule_device_bh(device); + } + list_for_each_entry(pavgroup, &lcu->grouplist, group) { + list_for_each_entry(device, &pavgroup->baselist, alias_list) { + dasd_schedule_block_bh(device->block); + dasd_schedule_device_bh(device); + } + } +} + +static void flush_all_alias_devices_on_lcu(struct alias_lcu *lcu) +{ + struct alias_pav_group *pavgroup; + struct dasd_device *device, *temp; + struct dasd_eckd_private *private; + int rc; + unsigned long flags; + LIST_HEAD(active); + + /* + * Problem here ist that dasd_flush_device_queue may wait + * for termination of a request to complete. We can't keep + * the lcu lock during that time, so we must assume that + * the lists may have changed. + * Idea: first gather all active alias devices in a separate list, + * then flush the first element of this list unlocked, and afterwards + * check if it is still on the list before moving it to the + * active_devices list. + */ + + spin_lock_irqsave(&lcu->lock, flags); + list_for_each_entry_safe(device, temp, &lcu->active_devices, + alias_list) { + private = (struct dasd_eckd_private *) device->private; + if (private->uid.type == UA_BASE_DEVICE) + continue; + list_move(&device->alias_list, &active); + } + + list_for_each_entry(pavgroup, &lcu->grouplist, group) { + list_splice_init(&pavgroup->aliaslist, &active); + } + while (!list_empty(&active)) { + device = list_first_entry(&active, struct dasd_device, + alias_list); + spin_unlock_irqrestore(&lcu->lock, flags); + rc = dasd_flush_device_queue(device); + spin_lock_irqsave(&lcu->lock, flags); + /* + * only move device around if it wasn't moved away while we + * were waiting for the flush + */ + if (device == list_first_entry(&active, + struct dasd_device, alias_list)) + list_move(&device->alias_list, &lcu->active_devices); + } + spin_unlock_irqrestore(&lcu->lock, flags); +} + +/* + * This function is called in interrupt context, so the + * cdev lock for device is already locked! + */ +static void _stop_all_devices_on_lcu(struct alias_lcu *lcu, + struct dasd_device *device) +{ + struct alias_pav_group *pavgroup; + struct dasd_device *pos; + + list_for_each_entry(pos, &lcu->active_devices, alias_list) { + if (pos != device) + spin_lock(get_ccwdev_lock(pos->cdev)); + pos->stopped |= DASD_STOPPED_SU; + if (pos != device) + spin_unlock(get_ccwdev_lock(pos->cdev)); + } + list_for_each_entry(pos, &lcu->inactive_devices, alias_list) { + if (pos != device) + spin_lock(get_ccwdev_lock(pos->cdev)); + pos->stopped |= DASD_STOPPED_SU; + if (pos != device) + spin_unlock(get_ccwdev_lock(pos->cdev)); + } + list_for_each_entry(pavgroup, &lcu->grouplist, group) { + list_for_each_entry(pos, &pavgroup->baselist, alias_list) { + if (pos != device) + spin_lock(get_ccwdev_lock(pos->cdev)); + pos->stopped |= DASD_STOPPED_SU; + if (pos != device) + spin_unlock(get_ccwdev_lock(pos->cdev)); + } + list_for_each_entry(pos, &pavgroup->aliaslist, alias_list) { + if (pos != device) + spin_lock(get_ccwdev_lock(pos->cdev)); + pos->stopped |= DASD_STOPPED_SU; + if (pos != device) + spin_unlock(get_ccwdev_lock(pos->cdev)); + } + } +} + +static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu) +{ + struct alias_pav_group *pavgroup; + struct dasd_device *device; + unsigned long flags; + + list_for_each_entry(device, &lcu->active_devices, alias_list) { + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + device->stopped &= ~DASD_STOPPED_SU; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + } + + list_for_each_entry(device, &lcu->inactive_devices, alias_list) { + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + device->stopped &= ~DASD_STOPPED_SU; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + } + + list_for_each_entry(pavgroup, &lcu->grouplist, group) { + list_for_each_entry(device, &pavgroup->baselist, alias_list) { + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + device->stopped &= ~DASD_STOPPED_SU; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), + flags); + } + list_for_each_entry(device, &pavgroup->aliaslist, alias_list) { + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + device->stopped &= ~DASD_STOPPED_SU; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), + flags); + } + } +} + +static void summary_unit_check_handling_work(struct work_struct *work) +{ + struct alias_lcu *lcu; + struct summary_unit_check_work_data *suc_data; + unsigned long flags; + struct dasd_device *device; + + suc_data = container_of(work, struct summary_unit_check_work_data, + worker); + lcu = container_of(suc_data, struct alias_lcu, suc_data); + device = suc_data->device; + + /* 1. flush alias devices */ + flush_all_alias_devices_on_lcu(lcu); + + /* 2. reset summary unit check */ + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + device->stopped &= ~(DASD_STOPPED_SU | DASD_STOPPED_PENDING); + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + reset_summary_unit_check(lcu, device, suc_data->reason); + + spin_lock_irqsave(&lcu->lock, flags); + _unstop_all_devices_on_lcu(lcu); + _restart_all_base_devices_on_lcu(lcu); + /* 3. read new alias configuration */ + _schedule_lcu_update(lcu, device); + lcu->suc_data.device = NULL; + spin_unlock_irqrestore(&lcu->lock, flags); +} + +/* + * note: this will be called from int handler context (cdev locked) + */ +void dasd_alias_handle_summary_unit_check(struct dasd_device *device, + struct irb *irb) +{ + struct alias_lcu *lcu; + char reason; + struct dasd_eckd_private *private; + + private = (struct dasd_eckd_private *) device->private; + + reason = irb->ecw[8]; + DEV_MESSAGE(KERN_WARNING, device, "%s %x", + "eckd handle summary unit check: reason", reason); + + lcu = private->lcu; + if (!lcu) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "device not ready to handle summary" + " unit check (no lcu structure)"); + return; + } + spin_lock(&lcu->lock); + _stop_all_devices_on_lcu(lcu, device); + /* prepare for lcu_update */ + private->lcu->flags |= NEED_UAC_UPDATE | UPDATE_PENDING; + /* If this device is about to be removed just return and wait for + * the next interrupt on a different device + */ + if (list_empty(&device->alias_list)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "device is in offline processing," + " don't do summary unit check handling"); + spin_unlock(&lcu->lock); + return; + } + if (lcu->suc_data.device) { + /* already scheduled or running */ + DEV_MESSAGE(KERN_WARNING, device, "%s", + "previous instance of summary unit check worker" + " still pending"); + spin_unlock(&lcu->lock); + return ; + } + lcu->suc_data.reason = reason; + lcu->suc_data.device = device; + spin_unlock(&lcu->lock); + schedule_work(&lcu->suc_data.worker); +}; diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 0c67258fb9e..f4fb4025734 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -49,22 +49,6 @@ struct dasd_devmap { }; /* - * dasd_server_ssid_map contains a globally unique storage server subsystem ID. - * dasd_server_ssid_list contains the list of all subsystem IDs accessed by - * the DASD device driver. - */ -struct dasd_server_ssid_map { - struct list_head list; - struct system_id { - char vendor[4]; - char serial[15]; - __u16 ssid; - } sid; -}; - -static struct list_head dasd_server_ssid_list; - -/* * Parameter parsing functions for dasd= parameter. The syntax is: * <devno> : (0x)?[0-9a-fA-F]+ * <busid> : [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+ @@ -721,8 +705,9 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr, devmap->features &= ~DASD_FEATURE_READONLY; if (devmap->device) devmap->device->features = devmap->features; - if (devmap->device && devmap->device->gdp) - set_disk_ro(devmap->device->gdp, val); + if (devmap->device && devmap->device->block + && devmap->device->block->gdp) + set_disk_ro(devmap->device->block->gdp, val); spin_unlock(&dasd_devmap_lock); return count; } @@ -893,12 +878,16 @@ dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf) devmap = dasd_find_busid(dev->bus_id); spin_lock(&dasd_devmap_lock); - if (!IS_ERR(devmap)) - alias = devmap->uid.alias; + if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) { + spin_unlock(&dasd_devmap_lock); + return sprintf(buf, "0\n"); + } + if (devmap->uid.type == UA_BASE_PAV_ALIAS || + devmap->uid.type == UA_HYPER_PAV_ALIAS) + alias = 1; else alias = 0; spin_unlock(&dasd_devmap_lock); - return sprintf(buf, alias ? "1\n" : "0\n"); } @@ -930,19 +919,36 @@ static ssize_t dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dasd_devmap *devmap; - char uid[UID_STRLEN]; + char uid_string[UID_STRLEN]; + char ua_string[3]; + struct dasd_uid *uid; devmap = dasd_find_busid(dev->bus_id); spin_lock(&dasd_devmap_lock); - if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0) - snprintf(uid, sizeof(uid), "%s.%s.%04x.%02x", - devmap->uid.vendor, devmap->uid.serial, - devmap->uid.ssid, devmap->uid.unit_addr); - else - uid[0] = 0; + if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) { + spin_unlock(&dasd_devmap_lock); + return sprintf(buf, "\n"); + } + uid = &devmap->uid; + switch (uid->type) { + case UA_BASE_DEVICE: + sprintf(ua_string, "%02x", uid->real_unit_addr); + break; + case UA_BASE_PAV_ALIAS: + sprintf(ua_string, "%02x", uid->base_unit_addr); + break; + case UA_HYPER_PAV_ALIAS: + sprintf(ua_string, "xx"); + break; + default: + /* should not happen, treat like base device */ + sprintf(ua_string, "%02x", uid->real_unit_addr); + break; + } + snprintf(uid_string, sizeof(uid_string), "%s.%s.%04x.%s", + uid->vendor, uid->serial, uid->ssid, ua_string); spin_unlock(&dasd_devmap_lock); - - return snprintf(buf, PAGE_SIZE, "%s\n", uid); + return snprintf(buf, PAGE_SIZE, "%s\n", uid_string); } static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL); @@ -1040,39 +1046,16 @@ int dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid) { struct dasd_devmap *devmap; - struct dasd_server_ssid_map *srv, *tmp; devmap = dasd_find_busid(cdev->dev.bus_id); if (IS_ERR(devmap)) return PTR_ERR(devmap); - /* generate entry for server_ssid_map */ - srv = (struct dasd_server_ssid_map *) - kzalloc(sizeof(struct dasd_server_ssid_map), GFP_KERNEL); - if (!srv) - return -ENOMEM; - strncpy(srv->sid.vendor, uid->vendor, sizeof(srv->sid.vendor) - 1); - strncpy(srv->sid.serial, uid->serial, sizeof(srv->sid.serial) - 1); - srv->sid.ssid = uid->ssid; - - /* server is already contained ? */ spin_lock(&dasd_devmap_lock); devmap->uid = *uid; - list_for_each_entry(tmp, &dasd_server_ssid_list, list) { - if (!memcmp(&srv->sid, &tmp->sid, - sizeof(struct system_id))) { - kfree(srv); - srv = NULL; - break; - } - } - - /* add servermap to serverlist */ - if (srv) - list_add(&srv->list, &dasd_server_ssid_list); spin_unlock(&dasd_devmap_lock); - return (srv ? 1 : 0); + return 0; } EXPORT_SYMBOL_GPL(dasd_set_uid); @@ -1138,9 +1121,6 @@ dasd_devmap_init(void) dasd_max_devindex = 0; for (i = 0; i < 256; i++) INIT_LIST_HEAD(&dasd_hashlists[i]); - - /* Initialize servermap structure. */ - INIT_LIST_HEAD(&dasd_server_ssid_list); return 0; } diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 571320ab9e1..d91df38ee4f 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -142,7 +142,7 @@ dasd_diag_erp(struct dasd_device *device) int rc; mdsk_term_io(device); - rc = mdsk_init_io(device, device->bp_block, 0, NULL); + rc = mdsk_init_io(device, device->block->bp_block, 0, NULL); if (rc) DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, " "rc=%d", rc); @@ -158,11 +158,11 @@ dasd_start_diag(struct dasd_ccw_req * cqr) struct dasd_diag_req *dreq; int rc; - device = cqr->device; + device = cqr->startdev; if (cqr->retries < 0) { DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p " "- no retry left)", cqr); - cqr->status = DASD_CQR_FAILED; + cqr->status = DASD_CQR_ERROR; return -EIO; } private = (struct dasd_diag_private *) device->private; @@ -184,7 +184,7 @@ dasd_start_diag(struct dasd_ccw_req * cqr) switch (rc) { case 0: /* Synchronous I/O finished successfully */ cqr->stopclk = get_clock(); - cqr->status = DASD_CQR_DONE; + cqr->status = DASD_CQR_SUCCESS; /* Indicate to calling function that only a dasd_schedule_bh() and no timer is needed */ rc = -EACCES; @@ -209,12 +209,12 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr) { struct dasd_device *device; - device = cqr->device; + device = cqr->startdev; mdsk_term_io(device); - mdsk_init_io(device, device->bp_block, 0, NULL); - cqr->status = DASD_CQR_CLEAR; + mdsk_init_io(device, device->block->bp_block, 0, NULL); + cqr->status = DASD_CQR_CLEAR_PENDING; cqr->stopclk = get_clock(); - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); return 0; } @@ -247,7 +247,7 @@ dasd_ext_handler(__u16 code) return; } cqr = (struct dasd_ccw_req *) ip; - device = (struct dasd_device *) cqr->device; + device = (struct dasd_device *) cqr->startdev; if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { DEV_MESSAGE(KERN_WARNING, device, " magic number of dasd_ccw_req 0x%08X doesn't" @@ -260,10 +260,10 @@ dasd_ext_handler(__u16 code) spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); /* Check for a pending clear operation */ - if (cqr->status == DASD_CQR_CLEAR) { - cqr->status = DASD_CQR_QUEUED; - dasd_clear_timer(device); - dasd_schedule_bh(device); + if (cqr->status == DASD_CQR_CLEAR_PENDING) { + cqr->status = DASD_CQR_CLEARED; + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); return; } @@ -272,11 +272,11 @@ dasd_ext_handler(__u16 code) expires = 0; if (status == 0) { - cqr->status = DASD_CQR_DONE; + cqr->status = DASD_CQR_SUCCESS; /* Start first request on queue if possible -> fast_io. */ if (!list_empty(&device->ccw_queue)) { next = list_entry(device->ccw_queue.next, - struct dasd_ccw_req, list); + struct dasd_ccw_req, devlist); if (next->status == DASD_CQR_QUEUED) { rc = dasd_start_diag(next); if (rc == 0) @@ -296,10 +296,10 @@ dasd_ext_handler(__u16 code) } if (expires != 0) - dasd_set_timer(device, expires); + dasd_device_set_timer(device, expires); else - dasd_clear_timer(device); - dasd_schedule_bh(device); + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } @@ -309,6 +309,7 @@ dasd_ext_handler(__u16 code) static int dasd_diag_check_device(struct dasd_device *device) { + struct dasd_block *block; struct dasd_diag_private *private; struct dasd_diag_characteristics *rdc_data; struct dasd_diag_bio bio; @@ -328,6 +329,16 @@ dasd_diag_check_device(struct dasd_device *device) ccw_device_get_id(device->cdev, &private->dev_id); device->private = (void *) private; } + block = dasd_alloc_block(); + if (IS_ERR(block)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "could not allocate dasd block structure"); + kfree(device->private); + return PTR_ERR(block); + } + device->block = block; + block->base = device; + /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); rdc_data->dev_nr = private->dev_id.devno; @@ -409,14 +420,14 @@ dasd_diag_check_device(struct dasd_device *device) sizeof(DASD_DIAG_CMS1)) == 0) { /* get formatted blocksize from label block */ bsize = (unsigned int) label->block_size; - device->blocks = (unsigned long) label->block_count; + block->blocks = (unsigned long) label->block_count; } else - device->blocks = end_block; - device->bp_block = bsize; - device->s2b_shift = 0; /* bits to shift 512 to get a block */ + block->blocks = end_block; + block->bp_block = bsize; + block->s2b_shift = 0; /* bits to shift 512 to get a block */ for (sb = 512; sb < bsize; sb = sb << 1) - device->s2b_shift++; - rc = mdsk_init_io(device, device->bp_block, 0, NULL); + block->s2b_shift++; + rc = mdsk_init_io(device, block->bp_block, 0, NULL); if (rc) { DEV_MESSAGE(KERN_WARNING, device, "DIAG initialization " "failed (rc=%d)", rc); @@ -424,9 +435,9 @@ dasd_diag_check_device(struct dasd_device *device) } else { DEV_MESSAGE(KERN_INFO, device, "(%ld B/blk): %ldkB", - (unsigned long) device->bp_block, - (unsigned long) (device->blocks << - device->s2b_shift) >> 1); + (unsigned long) block->bp_block, + (unsigned long) (block->blocks << + block->s2b_shift) >> 1); } out: free_page((long) label); @@ -436,22 +447,16 @@ out: /* Fill in virtual disk geometry for device. Return zero on success, non-zero * otherwise. */ static int -dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) +dasd_diag_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) { - if (dasd_check_blocksize(device->bp_block) != 0) + if (dasd_check_blocksize(block->bp_block) != 0) return -EINVAL; - geo->cylinders = (device->blocks << device->s2b_shift) >> 10; + geo->cylinders = (block->blocks << block->s2b_shift) >> 10; geo->heads = 16; - geo->sectors = 128 >> device->s2b_shift; + geo->sectors = 128 >> block->s2b_shift; return 0; } -static dasd_era_t -dasd_diag_examine_error(struct dasd_ccw_req * cqr, struct irb * stat) -{ - return dasd_era_fatal; -} - static dasd_erp_fn_t dasd_diag_erp_action(struct dasd_ccw_req * cqr) { @@ -466,8 +471,9 @@ dasd_diag_erp_postaction(struct dasd_ccw_req * cqr) /* Create DASD request from block device request. Return pointer to new * request on success, ERR_PTR otherwise. */ -static struct dasd_ccw_req * -dasd_diag_build_cp(struct dasd_device * device, struct request *req) +static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, + struct dasd_block *block, + struct request *req) { struct dasd_ccw_req *cqr; struct dasd_diag_req *dreq; @@ -486,17 +492,17 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) rw_cmd = MDSK_WRITE_REQ; else return ERR_PTR(-EINVAL); - blksize = device->bp_block; + blksize = block->bp_block; /* Calculate record id of first and last block. */ - first_rec = req->sector >> device->s2b_shift; - last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift; + first_rec = req->sector >> block->s2b_shift; + last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift; /* Check struct bio and count the number of blocks for the request. */ count = 0; rq_for_each_segment(bv, req, iter) { if (bv->bv_len & (blksize - 1)) /* Fba can only do full blocks. */ return ERR_PTR(-EINVAL); - count += bv->bv_len >> (device->s2b_shift + 9); + count += bv->bv_len >> (block->s2b_shift + 9); } /* Paranoia. */ if (count != last_rec - first_rec + 1) @@ -505,7 +511,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) datasize = sizeof(struct dasd_diag_req) + count*sizeof(struct dasd_diag_bio); cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0, - datasize, device); + datasize, memdev); if (IS_ERR(cqr)) return cqr; @@ -529,7 +535,9 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) cqr->buildclk = get_clock(); if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); - cqr->device = device; + cqr->startdev = memdev; + cqr->memdev = memdev; + cqr->block = block; cqr->expires = DIAG_TIMEOUT; cqr->status = DASD_CQR_FILLED; return cqr; @@ -543,10 +551,15 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) int status; status = cqr->status == DASD_CQR_DONE; - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return status; } +static void dasd_diag_handle_terminated_request(struct dasd_ccw_req *cqr) +{ + cqr->status = DASD_CQR_FILLED; +}; + /* Fill in IOCTL data for device. */ static int dasd_diag_fill_info(struct dasd_device * device, @@ -583,7 +596,7 @@ static struct dasd_discipline dasd_diag_discipline = { .fill_geometry = dasd_diag_fill_geometry, .start_IO = dasd_start_diag, .term_IO = dasd_diag_term_IO, - .examine_error = dasd_diag_examine_error, + .handle_terminated_request = dasd_diag_handle_terminated_request, .erp_action = dasd_diag_erp_action, .erp_postaction = dasd_diag_erp_postaction, .build_cp = dasd_diag_build_cp, diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 44adf8496bd..61f16937c1e 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -52,16 +52,6 @@ MODULE_LICENSE("GPL"); static struct dasd_discipline dasd_eckd_discipline; -struct dasd_eckd_private { - struct dasd_eckd_characteristics rdc_data; - struct dasd_eckd_confdata conf_data; - struct dasd_eckd_path path_data; - struct eckd_count count_area[5]; - int init_cqr_status; - int uses_cdl; - struct attrib_data_t attrib; /* e.g. cache operations */ -}; - /* The ccw bus type uses this table to find devices that it sends to * dasd_eckd_probe */ static struct ccw_device_id dasd_eckd_ids[] = { @@ -188,7 +178,7 @@ check_XRC (struct ccw1 *de_ccw, if (rc == -ENOSYS || rc == -EACCES) rc = 0; - de_ccw->count = sizeof (struct DE_eckd_data); + de_ccw->count = sizeof(struct DE_eckd_data); de_ccw->flags |= CCW_FLAG_SLI; return rc; } @@ -208,7 +198,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, ccw->count = 16; ccw->cda = (__u32) __pa(data); - memset(data, 0, sizeof (struct DE_eckd_data)); + memset(data, 0, sizeof(struct DE_eckd_data)); switch (cmd) { case DASD_ECKD_CCW_READ_HOME_ADDRESS: case DASD_ECKD_CCW_READ_RECORD_ZERO: @@ -280,6 +270,132 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, return rc; } +static int check_XRC_on_prefix(struct PFX_eckd_data *pfxdata, + struct dasd_device *device) +{ + struct dasd_eckd_private *private; + int rc; + + private = (struct dasd_eckd_private *) device->private; + if (!private->rdc_data.facilities.XRC_supported) + return 0; + + /* switch on System Time Stamp - needed for XRC Support */ + pfxdata->define_extend.ga_extended |= 0x08; /* 'Time Stamp Valid' */ + pfxdata->define_extend.ga_extended |= 0x02; /* 'Extended Parameter' */ + pfxdata->validity.time_stamp = 1; /* 'Time Stamp Valid' */ + + rc = get_sync_clock(&pfxdata->define_extend.ep_sys_time); + /* Ignore return code if sync clock is switched off. */ + if (rc == -ENOSYS || rc == -EACCES) + rc = 0; + return rc; +} + +static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk, + int totrk, int cmd, struct dasd_device *basedev, + struct dasd_device *startdev) +{ + struct dasd_eckd_private *basepriv, *startpriv; + struct DE_eckd_data *data; + struct ch_t geo, beg, end; + int rc = 0; + + basepriv = (struct dasd_eckd_private *) basedev->private; + startpriv = (struct dasd_eckd_private *) startdev->private; + data = &pfxdata->define_extend; + + ccw->cmd_code = DASD_ECKD_CCW_PFX; + ccw->flags = 0; + ccw->count = sizeof(*pfxdata); + ccw->cda = (__u32) __pa(pfxdata); + + memset(pfxdata, 0, sizeof(*pfxdata)); + /* prefix data */ + pfxdata->format = 0; + pfxdata->base_address = basepriv->conf_data.ned1.unit_addr; + pfxdata->base_lss = basepriv->conf_data.ned1.ID; + pfxdata->validity.define_extend = 1; + + /* private uid is kept up to date, conf_data may be outdated */ + if (startpriv->uid.type != UA_BASE_DEVICE) { + pfxdata->validity.verify_base = 1; + if (startpriv->uid.type == UA_HYPER_PAV_ALIAS) + pfxdata->validity.hyper_pav = 1; + } + + /* define extend data (mostly)*/ + switch (cmd) { + case DASD_ECKD_CCW_READ_HOME_ADDRESS: + case DASD_ECKD_CCW_READ_RECORD_ZERO: + case DASD_ECKD_CCW_READ: + case DASD_ECKD_CCW_READ_MT: + case DASD_ECKD_CCW_READ_CKD: + case DASD_ECKD_CCW_READ_CKD_MT: + case DASD_ECKD_CCW_READ_KD: + case DASD_ECKD_CCW_READ_KD_MT: + case DASD_ECKD_CCW_READ_COUNT: + data->mask.perm = 0x1; + data->attributes.operation = basepriv->attrib.operation; + break; + case DASD_ECKD_CCW_WRITE: + case DASD_ECKD_CCW_WRITE_MT: + case DASD_ECKD_CCW_WRITE_KD: + case DASD_ECKD_CCW_WRITE_KD_MT: + data->mask.perm = 0x02; + data->attributes.operation = basepriv->attrib.operation; + rc = check_XRC_on_prefix(pfxdata, basedev); + break; + case DASD_ECKD_CCW_WRITE_CKD: + case DASD_ECKD_CCW_WRITE_CKD_MT: + data->attributes.operation = DASD_BYPASS_CACHE; + rc = check_XRC_on_prefix(pfxdata, basedev); + break; + case DASD_ECKD_CCW_ERASE: + case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: + case DASD_ECKD_CCW_WRITE_RECORD_ZERO: + data->mask.perm = 0x3; + data->mask.auth = 0x1; + data->attributes.operation = DASD_BYPASS_CACHE; + rc = check_XRC_on_prefix(pfxdata, basedev); + break; + default: + DEV_MESSAGE(KERN_ERR, basedev, "unknown opcode 0x%x", cmd); + break; + } + + data->attributes.mode = 0x3; /* ECKD */ + + if ((basepriv->rdc_data.cu_type == 0x2105 || + basepriv->rdc_data.cu_type == 0x2107 || + basepriv->rdc_data.cu_type == 0x1750) + && !(basepriv->uses_cdl && trk < 2)) + data->ga_extended |= 0x40; /* Regular Data Format Mode */ + + geo.cyl = basepriv->rdc_data.no_cyl; + geo.head = basepriv->rdc_data.trk_per_cyl; + beg.cyl = trk / geo.head; + beg.head = trk % geo.head; + end.cyl = totrk / geo.head; + end.head = totrk % geo.head; + + /* check for sequential prestage - enhance cylinder range */ + if (data->attributes.operation == DASD_SEQ_PRESTAGE || + data->attributes.operation == DASD_SEQ_ACCESS) { + + if (end.cyl + basepriv->attrib.nr_cyl < geo.cyl) + end.cyl += basepriv->attrib.nr_cyl; + else + end.cyl = (geo.cyl - 1); + } + + data->beg_ext.cyl = beg.cyl; + data->beg_ext.head = beg.head; + data->end_ext.cyl = end.cyl; + data->end_ext.head = end.head; + return rc; +} + static void locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, int rec_on_trk, int no_rec, int cmd, @@ -300,7 +416,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, ccw->count = 16; ccw->cda = (__u32) __pa(data); - memset(data, 0, sizeof (struct LO_eckd_data)); + memset(data, 0, sizeof(struct LO_eckd_data)); sector = 0; if (rec_on_trk) { switch (private->rdc_data.dev_type) { @@ -441,12 +557,15 @@ dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid) sizeof(uid->serial) - 1); EBCASC(uid->serial, sizeof(uid->serial) - 1); uid->ssid = confdata->neq.subsystemID; - if (confdata->ned2.sneq.flags == 0x40) { - uid->alias = 1; - uid->unit_addr = confdata->ned2.sneq.base_unit_addr; - } else - uid->unit_addr = confdata->ned1.unit_addr; - + uid->real_unit_addr = confdata->ned1.unit_addr; + if (confdata->ned2.sneq.flags == 0x40 && + confdata->ned2.sneq.format == 0x0001) { + uid->type = confdata->ned2.sneq.sua_flags; + if (uid->type == UA_BASE_PAV_ALIAS) + uid->base_unit_addr = confdata->ned2.sneq.base_unit_addr; + } else { + uid->type = UA_BASE_DEVICE; + } return 0; } @@ -470,7 +589,9 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, ccw->cda = (__u32)(addr_t)rcd_buffer; ccw->count = ciw->count; - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; + cqr->block = NULL; cqr->expires = 10*HZ; cqr->lpm = lpm; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); @@ -511,7 +632,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, /* * on success we update the user input parms */ - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); if (ret) goto out_error; @@ -557,19 +678,19 @@ dasd_eckd_read_conf(struct dasd_device *device) "data retrieved"); continue; /* no error */ } - if (conf_len != sizeof (struct dasd_eckd_confdata)) { + if (conf_len != sizeof(struct dasd_eckd_confdata)) { MESSAGE(KERN_WARNING, "sizes of configuration data mismatch" "%d (read) vs %ld (expected)", conf_len, - sizeof (struct dasd_eckd_confdata)); + sizeof(struct dasd_eckd_confdata)); kfree(conf_data); continue; /* no error */ } /* save first valid configuration data */ if (!conf_data_saved){ memcpy(&private->conf_data, conf_data, - sizeof (struct dasd_eckd_confdata)); + sizeof(struct dasd_eckd_confdata)); conf_data_saved++; } switch (((char *)conf_data)[242] & 0x07){ @@ -586,39 +707,104 @@ dasd_eckd_read_conf(struct dasd_device *device) return 0; } +static int dasd_eckd_read_features(struct dasd_device *device) +{ + struct dasd_psf_prssd_data *prssdp; + struct dasd_rssd_features *features; + struct dasd_ccw_req *cqr; + struct ccw1 *ccw; + int rc; + struct dasd_eckd_private *private; + + private = (struct dasd_eckd_private *) device->private; + cqr = dasd_smalloc_request(dasd_eckd_discipline.name, + 1 /* PSF */ + 1 /* RSSD */ , + (sizeof(struct dasd_psf_prssd_data) + + sizeof(struct dasd_rssd_features)), + device); + if (IS_ERR(cqr)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "Could not allocate initialization request"); + return PTR_ERR(cqr); + } + cqr->startdev = device; + cqr->memdev = device; + cqr->block = NULL; + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + cqr->retries = 5; + cqr->expires = 10 * HZ; + + /* Prepare for Read Subsystem Data */ + prssdp = (struct dasd_psf_prssd_data *) cqr->data; + memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); + prssdp->order = PSF_ORDER_PRSSD; + prssdp->suborder = 0x41; /* Read Feature Codes */ + /* all other bytes of prssdp must be zero */ + + ccw = cqr->cpaddr; + ccw->cmd_code = DASD_ECKD_CCW_PSF; + ccw->count = sizeof(struct dasd_psf_prssd_data); + ccw->flags |= CCW_FLAG_CC; + ccw->cda = (__u32)(addr_t) prssdp; + + /* Read Subsystem Data - feature codes */ + features = (struct dasd_rssd_features *) (prssdp + 1); + memset(features, 0, sizeof(struct dasd_rssd_features)); + + ccw++; + ccw->cmd_code = DASD_ECKD_CCW_RSSD; + ccw->count = sizeof(struct dasd_rssd_features); + ccw->cda = (__u32)(addr_t) features; + + cqr->buildclk = get_clock(); + cqr->status = DASD_CQR_FILLED; + rc = dasd_sleep_on(cqr); + if (rc == 0) { + prssdp = (struct dasd_psf_prssd_data *) cqr->data; + features = (struct dasd_rssd_features *) (prssdp + 1); + memcpy(&private->features, features, + sizeof(struct dasd_rssd_features)); + } + dasd_sfree_request(cqr, cqr->memdev); + return rc; +} + + /* * Build CP for Perform Subsystem Function - SSC. */ -static struct dasd_ccw_req * -dasd_eckd_build_psf_ssc(struct dasd_device *device) +static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device) { - struct dasd_ccw_req *cqr; - struct dasd_psf_ssc_data *psf_ssc_data; - struct ccw1 *ccw; + struct dasd_ccw_req *cqr; + struct dasd_psf_ssc_data *psf_ssc_data; + struct ccw1 *ccw; - cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ , + cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ , sizeof(struct dasd_psf_ssc_data), device); - if (IS_ERR(cqr)) { - DEV_MESSAGE(KERN_WARNING, device, "%s", + if (IS_ERR(cqr)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", "Could not allocate PSF-SSC request"); - return cqr; - } - psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; - psf_ssc_data->order = PSF_ORDER_SSC; - psf_ssc_data->suborder = 0x08; - - ccw = cqr->cpaddr; - ccw->cmd_code = DASD_ECKD_CCW_PSF; - ccw->cda = (__u32)(addr_t)psf_ssc_data; - ccw->count = 66; - - cqr->device = device; - cqr->expires = 10*HZ; - cqr->buildclk = get_clock(); - cqr->status = DASD_CQR_FILLED; - return cqr; + return cqr; + } + psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; + psf_ssc_data->order = PSF_ORDER_SSC; + psf_ssc_data->suborder = 0x88; + psf_ssc_data->reserved[0] = 0x88; + + ccw = cqr->cpaddr; + ccw->cmd_code = DASD_ECKD_CCW_PSF; + ccw->cda = (__u32)(addr_t)psf_ssc_data; + ccw->count = 66; + + cqr->startdev = device; + cqr->memdev = device; + cqr->block = NULL; + cqr->expires = 10*HZ; + cqr->buildclk = get_clock(); + cqr->status = DASD_CQR_FILLED; + return cqr; } /* @@ -629,28 +815,28 @@ dasd_eckd_build_psf_ssc(struct dasd_device *device) static int dasd_eckd_psf_ssc(struct dasd_device *device) { - struct dasd_ccw_req *cqr; - int rc; - - cqr = dasd_eckd_build_psf_ssc(device); - if (IS_ERR(cqr)) - return PTR_ERR(cqr); - - rc = dasd_sleep_on(cqr); - if (!rc) - /* trigger CIO to reprobe devices */ - css_schedule_reprobe(); - dasd_sfree_request(cqr, cqr->device); - return rc; + struct dasd_ccw_req *cqr; + int rc; + + cqr = dasd_eckd_build_psf_ssc(device); + if (IS_ERR(cqr)) + return PTR_ERR(cqr); + + rc = dasd_sleep_on(cqr); + if (!rc) + /* trigger CIO to reprobe devices */ + css_schedule_reprobe(); + dasd_sfree_request(cqr, cqr->memdev); + return rc; } /* * Valide storage server of current device. */ -static int -dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid) +static int dasd_eckd_validate_server(struct dasd_device *device) { int rc; + struct dasd_eckd_private *private; /* Currently PAV is the only reason to 'validate' server on LPAR */ if (dasd_nopav || MACHINE_IS_VM) @@ -659,9 +845,11 @@ dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid) rc = dasd_eckd_psf_ssc(device); /* may be requested feature is not available on server, * therefore just report error and go ahead */ + private = (struct dasd_eckd_private *) device->private; DEV_MESSAGE(KERN_INFO, device, "PSF-SSC on storage subsystem %s.%s.%04x returned rc=%d", - uid->vendor, uid->serial, uid->ssid, rc); + private->uid.vendor, private->uid.serial, + private->uid.ssid, rc); /* RE-Read Configuration Data */ return dasd_eckd_read_conf(device); } @@ -674,9 +862,9 @@ static int dasd_eckd_check_characteristics(struct dasd_device *device) { struct dasd_eckd_private *private; - struct dasd_uid uid; + struct dasd_block *block; void *rdc_data; - int rc; + int is_known, rc; private = (struct dasd_eckd_private *) device->private; if (private == NULL) { @@ -699,27 +887,54 @@ dasd_eckd_check_characteristics(struct dasd_device *device) /* Read Configuration Data */ rc = dasd_eckd_read_conf(device); if (rc) - return rc; + goto out_err1; /* Generate device unique id and register in devmap */ - rc = dasd_eckd_generate_uid(device, &uid); + rc = dasd_eckd_generate_uid(device, &private->uid); if (rc) - return rc; - rc = dasd_set_uid(device->cdev, &uid); - if (rc == 1) /* new server found */ - rc = dasd_eckd_validate_server(device, &uid); + goto out_err1; + dasd_set_uid(device->cdev, &private->uid); + + if (private->uid.type == UA_BASE_DEVICE) { + block = dasd_alloc_block(); + if (IS_ERR(block)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "could not allocate dasd block structure"); + rc = PTR_ERR(block); + goto out_err1; + } + device->block = block; + block->base = device; + } + + /* register lcu with alias handling, enable PAV if this is a new lcu */ + is_known = dasd_alias_make_device_known_to_lcu(device); + if (is_known < 0) { + rc = is_known; + goto out_err2; + } + if (!is_known) { + /* new lcu found */ + rc = dasd_eckd_validate_server(device); /* will switch pav on */ + if (rc) + goto out_err3; + } + + /* Read Feature Codes */ + rc = dasd_eckd_read_features(device); if (rc) - return rc; + goto out_err3; /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); memset(rdc_data, 0, sizeof(rdc_data)); rc = dasd_generic_read_dev_chars(device, "ECKD", &rdc_data, 64); - if (rc) + if (rc) { DEV_MESSAGE(KERN_WARNING, device, "Read device characteristics returned " "rc=%d", rc); - + goto out_err3; + } DEV_MESSAGE(KERN_INFO, device, "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", private->rdc_data.dev_type, @@ -729,9 +944,24 @@ dasd_eckd_check_characteristics(struct dasd_device *device) private->rdc_data.no_cyl, private->rdc_data.trk_per_cyl, private->rdc_data.sec_per_trk); + return 0; + +out_err3: + dasd_alias_disconnect_device_from_lcu(device); +out_err2: + dasd_free_block(device->block); + device->block = NULL; +out_err1: + kfree(device->private); + device->private = NULL; return rc; } +static void dasd_eckd_uncheck_device(struct dasd_device *device) +{ + dasd_alias_disconnect_device_from_lcu(device); +} + static struct dasd_ccw_req * dasd_eckd_analysis_ccw(struct dasd_device *device) { @@ -755,7 +985,7 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) /* Define extent for the first 3 tracks. */ define_extent(ccw++, cqr->data, 0, 2, DASD_ECKD_CCW_READ_COUNT, device); - LO_data = cqr->data + sizeof (struct DE_eckd_data); + LO_data = cqr->data + sizeof(struct DE_eckd_data); /* Locate record for the first 4 records on track 0. */ ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, LO_data++, 0, 0, 4, @@ -783,7 +1013,9 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) ccw->count = 8; ccw->cda = (__u32)(addr_t) count_data; - cqr->device = device; + cqr->block = NULL; + cqr->startdev = device; + cqr->memdev = device; cqr->retries = 0; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -803,7 +1035,7 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) struct dasd_eckd_private *private; struct dasd_device *device; - device = init_cqr->device; + device = init_cqr->startdev; private = (struct dasd_eckd_private *) device->private; private->init_cqr_status = init_cqr->status; dasd_sfree_request(init_cqr, device); @@ -811,13 +1043,13 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) } static int -dasd_eckd_start_analysis(struct dasd_device *device) +dasd_eckd_start_analysis(struct dasd_block *block) { struct dasd_eckd_private *private; struct dasd_ccw_req *init_cqr; - private = (struct dasd_eckd_private *) device->private; - init_cqr = dasd_eckd_analysis_ccw(device); + private = (struct dasd_eckd_private *) block->base->private; + init_cqr = dasd_eckd_analysis_ccw(block->base); if (IS_ERR(init_cqr)) return PTR_ERR(init_cqr); init_cqr->callback = dasd_eckd_analysis_callback; @@ -828,13 +1060,15 @@ dasd_eckd_start_analysis(struct dasd_device *device) } static int -dasd_eckd_end_analysis(struct dasd_device *device) +dasd_eckd_end_analysis(struct dasd_block *block) { + struct dasd_device *device; struct dasd_eckd_private *private; struct eckd_count *count_area; unsigned int sb, blk_per_trk; int status, i; + device = block->base; private = (struct dasd_eckd_private *) device->private; status = private->init_cqr_status; private->init_cqr_status = -1; @@ -846,7 +1080,7 @@ dasd_eckd_end_analysis(struct dasd_device *device) private->uses_cdl = 1; /* Calculate number of blocks/records per track. */ - blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block); + blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); /* Check Track 0 for Compatible Disk Layout */ count_area = NULL; for (i = 0; i < 3; i++) { @@ -876,56 +1110,65 @@ dasd_eckd_end_analysis(struct dasd_device *device) if (count_area != NULL && count_area->kl == 0) { /* we found notthing violating our disk layout */ if (dasd_check_blocksize(count_area->dl) == 0) - device->bp_block = count_area->dl; + block->bp_block = count_area->dl; } - if (device->bp_block == 0) { + if (block->bp_block == 0) { DEV_MESSAGE(KERN_WARNING, device, "%s", "Volume has incompatible disk layout"); return -EMEDIUMTYPE; } - device->s2b_shift = 0; /* bits to shift 512 to get a block */ - for (sb = 512; sb < device->bp_block; sb = sb << 1) - device->s2b_shift++; + block->s2b_shift = 0; /* bits to shift 512 to get a block */ + for (sb = 512; sb < block->bp_block; sb = sb << 1) + block->s2b_shift++; - blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block); - device->blocks = (private->rdc_data.no_cyl * + blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); + block->blocks = (private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl * blk_per_trk); DEV_MESSAGE(KERN_INFO, device, "(%dkB blks): %dkB at %dkB/trk %s", - (device->bp_block >> 10), + (block->bp_block >> 10), ((private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl * - blk_per_trk * (device->bp_block >> 9)) >> 1), - ((blk_per_trk * device->bp_block) >> 10), + blk_per_trk * (block->bp_block >> 9)) >> 1), + ((blk_per_trk * block->bp_block) >> 10), private->uses_cdl ? "compatible disk layout" : "linux disk layout"); return 0; } -static int -dasd_eckd_do_analysis(struct dasd_device *device) +static int dasd_eckd_do_analysis(struct dasd_block *block) { struct dasd_eckd_private *private; - private = (struct dasd_eckd_private *) device->private; + private = (struct dasd_eckd_private *) block->base->private; if (private->init_cqr_status < 0) - return dasd_eckd_start_analysis(device); + return dasd_eckd_start_analysis(block); else - return dasd_eckd_end_analysis(device); + return dasd_eckd_end_analysis(block); } +static int dasd_eckd_ready_to_online(struct dasd_device *device) +{ + return dasd_alias_add_device(device); +}; + +static int dasd_eckd_online_to_ready(struct dasd_device *device) +{ + return dasd_alias_remove_device(device); +}; + static int -dasd_eckd_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) +dasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) { struct dasd_eckd_private *private; - private = (struct dasd_eckd_private *) device->private; - if (dasd_check_blocksize(device->bp_block) == 0) { + private = (struct dasd_eckd_private *) block->base->private; + if (dasd_check_blocksize(block->bp_block) == 0) { geo->sectors = recs_per_track(&private->rdc_data, - 0, device->bp_block); + 0, block->bp_block); } geo->cylinders = private->rdc_data.no_cyl; geo->heads = private->rdc_data.trk_per_cyl; @@ -1037,7 +1280,7 @@ dasd_eckd_format_device(struct dasd_device * device, locate_record(ccw++, (struct LO_eckd_data *) data, fdata->start_unit, 0, rpt + 1, DASD_ECKD_CCW_WRITE_RECORD_ZERO, device, - device->bp_block); + device->block->bp_block); data += sizeof(struct LO_eckd_data); break; case 0x04: /* Invalidate track. */ @@ -1110,43 +1353,28 @@ dasd_eckd_format_device(struct dasd_device * device, ccw++; } } - fcp->device = device; - fcp->retries = 2; /* set retry counter to enable ERP */ + fcp->startdev = device; + fcp->memdev = device; + clear_bit(DASD_CQR_FLAGS_USE_ERP, &fcp->flags); + fcp->retries = 5; /* set retry counter to enable default ERP */ fcp->buildclk = get_clock(); fcp->status = DASD_CQR_FILLED; return fcp; } -static dasd_era_t -dasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb) +static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) { - struct dasd_device *device = (struct dasd_device *) cqr->device; - struct ccw_device *cdev = device->cdev; - - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - - switch (cdev->id.cu_type) { - case 0x3990: - case 0x2105: - case 0x2107: - case 0x1750: - return dasd_3990_erp_examine(cqr, irb); - case 0x9343: - return dasd_9343_erp_examine(cqr, irb); - case 0x3880: - default: - DEV_MESSAGE(KERN_WARNING, device, "%s", - "default (unknown CU type) - RECOVERABLE return"); - return dasd_era_recover; + cqr->status = DASD_CQR_FILLED; + if (cqr->block && (cqr->startdev != cqr->block->base)) { + dasd_eckd_reset_ccw_to_base_io(cqr); + cqr->startdev = cqr->block->base; } -} +}; static dasd_erp_fn_t dasd_eckd_erp_action(struct dasd_ccw_req * cqr) { - struct dasd_device *device = (struct dasd_device *) cqr->device; + struct dasd_device *device = (struct dasd_device *) cqr->startdev; struct ccw_device *cdev = device->cdev; switch (cdev->id.cu_type) { @@ -1168,8 +1396,37 @@ dasd_eckd_erp_postaction(struct dasd_ccw_req * cqr) return dasd_default_erp_postaction; } -static struct dasd_ccw_req * -dasd_eckd_build_cp(struct dasd_device * device, struct request *req) + +static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, + struct irb *irb) +{ + char mask; + + /* first of all check for state change pending interrupt */ + mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; + if ((irb->scsw.dstat & mask) == mask) { + dasd_generic_handle_state_change(device); + return; + } + + /* summary unit check */ + if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && irb->ecw[7] == 0x0D) { + dasd_alias_handle_summary_unit_check(device, irb); + return; + } + + /* just report other unsolicited interrupts */ + DEV_MESSAGE(KERN_DEBUG, device, "%s", + "unsolicited interrupt received"); + device->discipline->dump_sense(device, NULL, irb); + dasd_schedule_device_bh(device); + + return; +}; + +static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, + struct dasd_block *block, + struct request *req) { struct dasd_eckd_private *private; unsigned long *idaws; @@ -1185,8 +1442,11 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) sector_t first_trk, last_trk; unsigned int first_offs, last_offs; unsigned char cmd, rcmd; + int use_prefix; + struct dasd_device *basedev; - private = (struct dasd_eckd_private *) device->private; + basedev = block->base; + private = (struct dasd_eckd_private *) basedev->private; if (rq_data_dir(req) == READ) cmd = DASD_ECKD_CCW_READ_MT; else if (rq_data_dir(req) == WRITE) @@ -1194,13 +1454,13 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) else return ERR_PTR(-EINVAL); /* Calculate number of blocks/records per track. */ - blksize = device->bp_block; + blksize = block->bp_block; blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); /* Calculate record id of first and last block. */ - first_rec = first_trk = req->sector >> device->s2b_shift; + first_rec = first_trk = req->sector >> block->s2b_shift; first_offs = sector_div(first_trk, blk_per_trk); last_rec = last_trk = - (req->sector + req->nr_sectors - 1) >> device->s2b_shift; + (req->sector + req->nr_sectors - 1) >> block->s2b_shift; last_offs = sector_div(last_trk, blk_per_trk); /* Check struct bio and count the number of blocks for the request. */ count = 0; @@ -1209,20 +1469,33 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) if (bv->bv_len & (blksize - 1)) /* Eckd can only do full blocks. */ return ERR_PTR(-EINVAL); - count += bv->bv_len >> (device->s2b_shift + 9); + count += bv->bv_len >> (block->s2b_shift + 9); #if defined(CONFIG_64BIT) if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) - cidaw += bv->bv_len >> (device->s2b_shift + 9); + cidaw += bv->bv_len >> (block->s2b_shift + 9); #endif } /* Paranoia. */ if (count != last_rec - first_rec + 1) return ERR_PTR(-EINVAL); - /* 1x define extent + 1x locate record + number of blocks */ - cplength = 2 + count; - /* 1x define extent + 1x locate record + cidaws*sizeof(long) */ - datasize = sizeof(struct DE_eckd_data) + sizeof(struct LO_eckd_data) + - cidaw * sizeof(unsigned long); + + /* use the prefix command if available */ + use_prefix = private->features.feature[8] & 0x01; + if (use_prefix) { + /* 1x prefix + number of blocks */ + cplength = 2 + count; + /* 1x prefix + cidaws*sizeof(long) */ + datasize = sizeof(struct PFX_eckd_data) + + sizeof(struct LO_eckd_data) + + cidaw * sizeof(unsigned long); + } else { + /* 1x define extent + 1x locate record + number of blocks */ + cplength = 2 + count; + /* 1x define extent + 1x locate record + cidaws*sizeof(long) */ + datasize = sizeof(struct DE_eckd_data) + + sizeof(struct LO_eckd_data) + + cidaw * sizeof(unsigned long); + } /* Find out the number of additional locate record ccws for cdl. */ if (private->uses_cdl && first_rec < 2*blk_per_trk) { if (last_rec >= 2*blk_per_trk) @@ -1232,26 +1505,42 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) } /* Allocate the ccw request. */ cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - cplength, datasize, device); + cplength, datasize, startdev); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; - /* First ccw is define extent. */ - if (define_extent(ccw++, cqr->data, first_trk, - last_trk, cmd, device) == -EAGAIN) { - /* Clock not in sync and XRC is enabled. Try again later. */ - dasd_sfree_request(cqr, device); - return ERR_PTR(-EAGAIN); + /* First ccw is define extent or prefix. */ + if (use_prefix) { + if (prefix(ccw++, cqr->data, first_trk, + last_trk, cmd, basedev, startdev) == -EAGAIN) { + /* Clock not in sync and XRC is enabled. + * Try again later. + */ + dasd_sfree_request(cqr, startdev); + return ERR_PTR(-EAGAIN); + } + idaws = (unsigned long *) (cqr->data + + sizeof(struct PFX_eckd_data)); + } else { + if (define_extent(ccw++, cqr->data, first_trk, + last_trk, cmd, startdev) == -EAGAIN) { + /* Clock not in sync and XRC is enabled. + * Try again later. + */ + dasd_sfree_request(cqr, startdev); + return ERR_PTR(-EAGAIN); + } + idaws = (unsigned long *) (cqr->data + + sizeof(struct DE_eckd_data)); } /* Build locate_record+read/write/ccws. */ - idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data)); LO_data = (struct LO_eckd_data *) (idaws + cidaw); recid = first_rec; if (private->uses_cdl == 0 || recid > 2*blk_per_trk) { /* Only standard blocks so there is just one locate record. */ ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, LO_data++, first_trk, first_offs + 1, - last_rec - recid + 1, cmd, device, blksize); + last_rec - recid + 1, cmd, basedev, blksize); } rq_for_each_segment(bv, req, iter) { dst = page_address(bv->bv_page) + bv->bv_offset; @@ -1281,7 +1570,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, LO_data++, trkid, recoffs + 1, - 1, rcmd, device, count); + 1, rcmd, basedev, count); } /* Locate record for standard blocks ? */ if (private->uses_cdl && recid == 2*blk_per_trk) { @@ -1289,7 +1578,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) locate_record(ccw++, LO_data++, trkid, recoffs + 1, last_rec - recid + 1, - cmd, device, count); + cmd, basedev, count); } /* Read/write ccw. */ ccw[-1].flags |= CCW_FLAG_CC; @@ -1310,7 +1599,9 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) } if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); - cqr->device = device; + cqr->startdev = startdev; + cqr->memdev = startdev; + cqr->block = block; cqr->expires = 5 * 60 * HZ; /* 5 minutes */ cqr->lpm = private->path_data.ppm; cqr->retries = 256; @@ -1333,10 +1624,10 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) if (!dasd_page_cache) goto out; - private = (struct dasd_eckd_private *) cqr->device->private; - blksize = cqr->device->bp_block; + private = (struct dasd_eckd_private *) cqr->block->base->private; + blksize = cqr->block->bp_block; blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); - recid = req->sector >> cqr->device->s2b_shift; + recid = req->sector >> cqr->block->s2b_shift; ccw = cqr->cpaddr; /* Skip over define extent & locate record. */ ccw++; @@ -1367,10 +1658,71 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) } out: status = cqr->status == DASD_CQR_DONE; - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return status; } +/* + * Modify ccw chain in cqr so it can be started on a base device. + * + * Note that this is not enough to restart the cqr! + * Either reset cqr->startdev as well (summary unit check handling) + * or restart via separate cqr (as in ERP handling). + */ +void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *cqr) +{ + struct ccw1 *ccw; + struct PFX_eckd_data *pfxdata; + + ccw = cqr->cpaddr; + pfxdata = cqr->data; + + if (ccw->cmd_code == DASD_ECKD_CCW_PFX) { + pfxdata->validity.verify_base = 0; + pfxdata->validity.hyper_pav = 0; + } +} + +#define DASD_ECKD_CHANQ_MAX_SIZE 4 + +static struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base, + struct dasd_block *block, + struct request *req) +{ + struct dasd_eckd_private *private; + struct dasd_device *startdev; + unsigned long flags; + struct dasd_ccw_req *cqr; + + startdev = dasd_alias_get_start_dev(base); + if (!startdev) + startdev = base; + private = (struct dasd_eckd_private *) startdev->private; + if (private->count >= DASD_ECKD_CHANQ_MAX_SIZE) + return ERR_PTR(-EBUSY); + + spin_lock_irqsave(get_ccwdev_lock(startdev->cdev), flags); + private->count++; + cqr = dasd_eckd_build_cp(startdev, block, req); + if (IS_ERR(cqr)) + private->count--; + spin_unlock_irqrestore(get_ccwdev_lock(startdev->cdev), flags); + return cqr; +} + +static int dasd_eckd_free_alias_cp(struct dasd_ccw_req *cqr, + struct request *req) +{ + struct dasd_eckd_private *private; + unsigned long flags; + + spin_lock_irqsave(get_ccwdev_lock(cqr->memdev->cdev), flags); + private = (struct dasd_eckd_private *) cqr->memdev->private; + private->count--; + spin_unlock_irqrestore(get_ccwdev_lock(cqr->memdev->cdev), flags); + return dasd_eckd_free_cp(cqr, req); +} + static int dasd_eckd_fill_info(struct dasd_device * device, struct dasd_information2_t * info) @@ -1384,9 +1736,9 @@ dasd_eckd_fill_info(struct dasd_device * device, info->characteristics_size = sizeof(struct dasd_eckd_characteristics); memcpy(info->characteristics, &private->rdc_data, sizeof(struct dasd_eckd_characteristics)); - info->confdata_size = sizeof (struct dasd_eckd_confdata); + info->confdata_size = sizeof(struct dasd_eckd_confdata); memcpy(info->configuration_data, &private->conf_data, - sizeof (struct dasd_eckd_confdata)); + sizeof(struct dasd_eckd_confdata)); return 0; } @@ -1419,7 +1771,8 @@ dasd_eckd_release(struct dasd_device *device) cqr->cpaddr->flags |= CCW_FLAG_SLI; cqr->cpaddr->count = 32; cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ @@ -1429,7 +1782,7 @@ dasd_eckd_release(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1459,7 +1812,8 @@ dasd_eckd_reserve(struct dasd_device *device) cqr->cpaddr->flags |= CCW_FLAG_SLI; cqr->cpaddr->count = 32; cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ @@ -1469,7 +1823,7 @@ dasd_eckd_reserve(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1498,7 +1852,8 @@ dasd_eckd_steal_lock(struct dasd_device *device) cqr->cpaddr->flags |= CCW_FLAG_SLI; cqr->cpaddr->count = 32; cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ @@ -1508,7 +1863,7 @@ dasd_eckd_steal_lock(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1526,52 +1881,52 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) cqr = dasd_smalloc_request(dasd_eckd_discipline.name, 1 /* PSF */ + 1 /* RSSD */ , - (sizeof (struct dasd_psf_prssd_data) + - sizeof (struct dasd_rssd_perf_stats_t)), + (sizeof(struct dasd_psf_prssd_data) + + sizeof(struct dasd_rssd_perf_stats_t)), device); if (IS_ERR(cqr)) { DEV_MESSAGE(KERN_WARNING, device, "%s", "Could not allocate initialization request"); return PTR_ERR(cqr); } - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; cqr->retries = 0; cqr->expires = 10 * HZ; /* Prepare for Read Subsystem Data */ prssdp = (struct dasd_psf_prssd_data *) cqr->data; - memset(prssdp, 0, sizeof (struct dasd_psf_prssd_data)); + memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); prssdp->order = PSF_ORDER_PRSSD; - prssdp->suborder = 0x01; /* Perfomance Statistics */ + prssdp->suborder = 0x01; /* Performance Statistics */ prssdp->varies[1] = 0x01; /* Perf Statistics for the Subsystem */ ccw = cqr->cpaddr; ccw->cmd_code = DASD_ECKD_CCW_PSF; - ccw->count = sizeof (struct dasd_psf_prssd_data); + ccw->count = sizeof(struct dasd_psf_prssd_data); ccw->flags |= CCW_FLAG_CC; ccw->cda = (__u32)(addr_t) prssdp; /* Read Subsystem Data - Performance Statistics */ stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); - memset(stats, 0, sizeof (struct dasd_rssd_perf_stats_t)); + memset(stats, 0, sizeof(struct dasd_rssd_perf_stats_t)); ccw++; ccw->cmd_code = DASD_ECKD_CCW_RSSD; - ccw->count = sizeof (struct dasd_rssd_perf_stats_t); + ccw->count = sizeof(struct dasd_rssd_perf_stats_t); ccw->cda = (__u32)(addr_t) stats; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on(cqr); if (rc == 0) { - /* Prepare for Read Subsystem Data */ prssdp = (struct dasd_psf_prssd_data *) cqr->data; stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); if (copy_to_user(argp, stats, sizeof(struct dasd_rssd_perf_stats_t))) rc = -EFAULT; } - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1594,7 +1949,7 @@ dasd_eckd_get_attrib(struct dasd_device *device, void __user *argp) rc = 0; if (copy_to_user(argp, (long *) &attrib, - sizeof (struct attrib_data_t))) + sizeof(struct attrib_data_t))) rc = -EFAULT; return rc; @@ -1627,8 +1982,10 @@ dasd_eckd_set_attrib(struct dasd_device *device, void __user *argp) } static int -dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp) +dasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp) { + struct dasd_device *device = block->base; + switch (cmd) { case BIODASDGATTR: return dasd_eckd_get_attrib(device, argp); @@ -1685,9 +2042,8 @@ dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page) * Print sense data and related channel program. * Parts are printed because printk buffer is only 1024 bytes. */ -static void -dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, - struct irb *irb) +static void dasd_eckd_dump_sense(struct dasd_device *device, + struct dasd_ccw_req *req, struct irb *irb) { char *page; struct ccw1 *first, *last, *fail, *from, *to; @@ -1743,37 +2099,40 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, } printk("%s", page); - /* dump the Channel Program (max 140 Bytes per line) */ - /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */ - first = req->cpaddr; - for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); - to = min(first + 6, last); - len = sprintf(page, KERN_ERR PRINTK_HEADER - " Related CP in req: %p\n", req); - dasd_eckd_dump_ccw_range(first, to, page + len); - printk("%s", page); + if (req) { + /* req == NULL for unsolicited interrupts */ + /* dump the Channel Program (max 140 Bytes per line) */ + /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */ + first = req->cpaddr; + for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); + to = min(first + 6, last); + len = sprintf(page, KERN_ERR PRINTK_HEADER + " Related CP in req: %p\n", req); + dasd_eckd_dump_ccw_range(first, to, page + len); + printk("%s", page); - /* print failing CCW area (maximum 4) */ - /* scsw->cda is either valid or zero */ - len = 0; - from = ++to; - fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */ - if (from < fail - 2) { - from = fail - 2; /* there is a gap - print header */ - len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n"); - } - to = min(fail + 1, last); - len += dasd_eckd_dump_ccw_range(from, to, page + len); - - /* print last CCWs (maximum 2) */ - from = max(from, ++to); - if (from < last - 1) { - from = last - 1; /* there is a gap - print header */ - len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); + /* print failing CCW area (maximum 4) */ + /* scsw->cda is either valid or zero */ + len = 0; + from = ++to; + fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */ + if (from < fail - 2) { + from = fail - 2; /* there is a gap - print header */ + len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n"); + } + to = min(fail + 1, last); + len += dasd_eckd_dump_ccw_range(from, to, page + len); + + /* print last CCWs (maximum 2) */ + from = max(from, ++to); + if (from < last - 1) { + from = last - 1; /* there is a gap - print header */ + len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); + } + len += dasd_eckd_dump_ccw_range(from, last, page + len); + if (len > 0) + printk("%s", page); } - len += dasd_eckd_dump_ccw_range(from, last, page + len); - if (len > 0) - printk("%s", page); free_page((unsigned long) page); } @@ -1796,16 +2155,20 @@ static struct dasd_discipline dasd_eckd_discipline = { .ebcname = "ECKD", .max_blocks = 240, .check_device = dasd_eckd_check_characteristics, + .uncheck_device = dasd_eckd_uncheck_device, .do_analysis = dasd_eckd_do_analysis, + .ready_to_online = dasd_eckd_ready_to_online, + .online_to_ready = dasd_eckd_online_to_ready, .fill_geometry = dasd_eckd_fill_geometry, .start_IO = dasd_start_IO, .term_IO = dasd_term_IO, + .handle_terminated_request = dasd_eckd_handle_terminated_request, .format_device = dasd_eckd_format_device, - .examine_error = dasd_eckd_examine_error, .erp_action = dasd_eckd_erp_action, .erp_postaction = dasd_eckd_erp_postaction, - .build_cp = dasd_eckd_build_cp, - .free_cp = dasd_eckd_free_cp, + .handle_unsolicited_interrupt = dasd_eckd_handle_unsolicited_interrupt, + .build_cp = dasd_eckd_build_alias_cp, + .free_cp = dasd_eckd_free_alias_cp, .dump_sense = dasd_eckd_dump_sense, .fill_info = dasd_eckd_fill_info, .ioctl = dasd_eckd_ioctl, diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index 712ff165013..fc2509c939b 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -39,6 +39,8 @@ #define DASD_ECKD_CCW_READ_CKD_MT 0x9e #define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d #define DASD_ECKD_CCW_RESERVE 0xB4 +#define DASD_ECKD_CCW_PFX 0xE7 +#define DASD_ECKD_CCW_RSCK 0xF9 /* * Perform Subsystem Function / Sub-Orders @@ -137,6 +139,25 @@ struct LO_eckd_data { __u16 length; } __attribute__ ((packed)); +/* Prefix data for format 0x00 and 0x01 */ +struct PFX_eckd_data { + unsigned char format; + struct { + unsigned char define_extend:1; + unsigned char time_stamp:1; + unsigned char verify_base:1; + unsigned char hyper_pav:1; + unsigned char reserved:4; + } __attribute__ ((packed)) validity; + __u8 base_address; + __u8 aux; + __u8 base_lss; + __u8 reserved[7]; + struct DE_eckd_data define_extend; + struct LO_eckd_data locate_record; + __u8 LO_extended_data[4]; +} __attribute__ ((packed)); + struct dasd_eckd_characteristics { __u16 cu_type; struct { @@ -254,7 +275,9 @@ struct dasd_eckd_confdata { } __attribute__ ((packed)) ned; struct { unsigned char flags; /* byte 0 */ - unsigned char res2[7]; /* byte 1- 7 */ + unsigned char res1; /* byte 1 */ + __u16 format; /* byte 2-3 */ + unsigned char res2[4]; /* byte 4-7 */ unsigned char sua_flags; /* byte 8 */ __u8 base_unit_addr; /* byte 9 */ unsigned char res3[22]; /* byte 10-31 */ @@ -343,6 +366,11 @@ struct dasd_eckd_path { __u8 npm; }; +struct dasd_rssd_features { + char feature[256]; +} __attribute__((packed)); + + /* * Perform Subsystem Function - Prepare for Read Subsystem Data */ @@ -365,4 +393,99 @@ struct dasd_psf_ssc_data { unsigned char reserved[59]; } __attribute__((packed)); + +/* + * some structures and definitions for alias handling + */ +struct dasd_unit_address_configuration { + struct { + char ua_type; + char base_ua; + } unit[256]; +} __attribute__((packed)); + + +#define MAX_DEVICES_PER_LCU 256 + +/* flags on the LCU */ +#define NEED_UAC_UPDATE 0x01 +#define UPDATE_PENDING 0x02 + +enum pavtype {NO_PAV, BASE_PAV, HYPER_PAV}; + + +struct alias_root { + struct list_head serverlist; + spinlock_t lock; +}; + +struct alias_server { + struct list_head server; + struct dasd_uid uid; + struct list_head lculist; +}; + +struct summary_unit_check_work_data { + char reason; + struct dasd_device *device; + struct work_struct worker; +}; + +struct read_uac_work_data { + struct dasd_device *device; + struct delayed_work dwork; +}; + +struct alias_lcu { + struct list_head lcu; + struct dasd_uid uid; + enum pavtype pav; + char flags; + spinlock_t lock; + struct list_head grouplist; + struct list_head active_devices; + struct list_head inactive_devices; + struct dasd_unit_address_configuration *uac; + struct summary_unit_check_work_data suc_data; + struct read_uac_work_data ruac_data; + struct dasd_ccw_req *rsu_cqr; +}; + +struct alias_pav_group { + struct list_head group; + struct dasd_uid uid; + struct alias_lcu *lcu; + struct list_head baselist; + struct list_head aliaslist; + struct dasd_device *next; +}; + + +struct dasd_eckd_private { + struct dasd_eckd_characteristics rdc_data; + struct dasd_eckd_confdata conf_data; + struct dasd_eckd_path path_data; + struct eckd_count count_area[5]; + int init_cqr_status; + int uses_cdl; + struct attrib_data_t attrib; /* e.g. cache operations */ + struct dasd_rssd_features features; + + /* alias managemnet */ + struct dasd_uid uid; + struct alias_pav_group *pavgroup; + struct alias_lcu *lcu; + int count; +}; + + + +int dasd_alias_make_device_known_to_lcu(struct dasd_device *); +void dasd_alias_disconnect_device_from_lcu(struct dasd_device *); +int dasd_alias_add_device(struct dasd_device *); +int dasd_alias_remove_device(struct dasd_device *); +struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *); +void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *); +void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *); + #endif /* DASD_ECKD_H */ diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index 0c081a664ee..6e53ab606e9 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -336,7 +336,7 @@ static void dasd_eer_write_snss_trigger(struct dasd_device *device, unsigned long flags; struct eerbuffer *eerb; - snss_rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; + snss_rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; if (snss_rc) data_size = 0; else @@ -404,10 +404,11 @@ void dasd_eer_snss(struct dasd_device *device) set_bit(DASD_FLAG_EER_SNSS, &device->flags); return; } + /* cdev is already locked, can't use dasd_add_request_head */ clear_bit(DASD_FLAG_EER_SNSS, &device->flags); cqr->status = DASD_CQR_QUEUED; - list_add(&cqr->list, &device->ccw_queue); - dasd_schedule_bh(device); + list_add(&cqr->devlist, &device->ccw_queue); + dasd_schedule_device_bh(device); } /* @@ -415,7 +416,7 @@ void dasd_eer_snss(struct dasd_device *device) */ static void dasd_eer_snss_cb(struct dasd_ccw_req *cqr, void *data) { - struct dasd_device *device = cqr->device; + struct dasd_device *device = cqr->startdev; unsigned long flags; dasd_eer_write(device, cqr, DASD_EER_STATECHANGE); @@ -458,7 +459,7 @@ int dasd_eer_enable(struct dasd_device *device) if (!cqr) return -ENOMEM; - cqr->device = device; + cqr->startdev = device; cqr->retries = 255; cqr->expires = 10 * HZ; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index caa5d91420f..8f10000851a 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -46,6 +46,8 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize, if (cqr == NULL) return ERR_PTR(-ENOMEM); memset(cqr, 0, sizeof(struct dasd_ccw_req)); + INIT_LIST_HEAD(&cqr->devlist); + INIT_LIST_HEAD(&cqr->blocklist); data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L); cqr->cpaddr = NULL; if (cplength > 0) { @@ -66,7 +68,7 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize, } void -dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device) +dasd_free_erp_request(struct dasd_ccw_req *cqr, struct dasd_device * device) { unsigned long flags; @@ -81,11 +83,11 @@ dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device) * dasd_default_erp_action just retries the current cqr */ struct dasd_ccw_req * -dasd_default_erp_action(struct dasd_ccw_req * cqr) +dasd_default_erp_action(struct dasd_ccw_req *cqr) { struct dasd_device *device; - device = cqr->device; + device = cqr->startdev; /* just retry - there is nothing to save ... I got no sense data.... */ if (cqr->retries > 0) { @@ -93,12 +95,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr) "default ERP called (%i retries left)", cqr->retries); cqr->lpm = LPM_ANYPATH; - cqr->status = DASD_CQR_QUEUED; + cqr->status = DASD_CQR_FILLED; } else { DEV_MESSAGE (KERN_WARNING, device, "%s", "default ERP called (NO retry left)"); cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock (); + cqr->stopclk = get_clock(); } return cqr; } /* end dasd_default_erp_action */ @@ -117,15 +119,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr) * RETURN VALUES * cqr pointer to the original CQR */ -struct dasd_ccw_req * -dasd_default_erp_postaction(struct dasd_ccw_req * cqr) +struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr) { - struct dasd_device *device; int success; BUG_ON(cqr->refers == NULL || cqr->function == NULL); - device = cqr->device; success = cqr->status == DASD_CQR_DONE; /* free all ERPs - but NOT the original cqr */ @@ -133,10 +132,10 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr) struct dasd_ccw_req *refers; refers = cqr->refers; - /* remove the request from the device queue */ - list_del(&cqr->list); + /* remove the request from the block queue */ + list_del(&cqr->blocklist); /* free the finished erp request */ - dasd_free_erp_request(cqr, device); + dasd_free_erp_request(cqr, cqr->memdev); cqr = refers; } @@ -157,7 +156,7 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb) { struct dasd_device *device; - device = cqr->device; + device = cqr->startdev; /* dump sense data */ if (device->discipline && device->discipline->dump_sense) device->discipline->dump_sense(device, cqr, irb); diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 1d95822e0b8..d13ea05089a 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -117,6 +117,7 @@ locate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw, static int dasd_fba_check_characteristics(struct dasd_device *device) { + struct dasd_block *block; struct dasd_fba_private *private; struct ccw_device *cdev = device->cdev; void *rdc_data; @@ -133,6 +134,16 @@ dasd_fba_check_characteristics(struct dasd_device *device) } device->private = (void *) private; } + block = dasd_alloc_block(); + if (IS_ERR(block)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "could not allocate dasd block structure"); + kfree(device->private); + return PTR_ERR(block); + } + device->block = block; + block->base = device; + /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); rc = dasd_generic_read_dev_chars(device, "FBA ", &rdc_data, 32); @@ -155,60 +166,37 @@ dasd_fba_check_characteristics(struct dasd_device *device) return 0; } -static int -dasd_fba_do_analysis(struct dasd_device *device) +static int dasd_fba_do_analysis(struct dasd_block *block) { struct dasd_fba_private *private; int sb, rc; - private = (struct dasd_fba_private *) device->private; + private = (struct dasd_fba_private *) block->base->private; rc = dasd_check_blocksize(private->rdc_data.blk_size); if (rc) { - DEV_MESSAGE(KERN_INFO, device, "unknown blocksize %d", + DEV_MESSAGE(KERN_INFO, block->base, "unknown blocksize %d", private->rdc_data.blk_size); return rc; } - device->blocks = private->rdc_data.blk_bdsa; - device->bp_block = private->rdc_data.blk_size; - device->s2b_shift = 0; /* bits to shift 512 to get a block */ + block->blocks = private->rdc_data.blk_bdsa; + block->bp_block = private->rdc_data.blk_size; + block->s2b_shift = 0; /* bits to shift 512 to get a block */ for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1) - device->s2b_shift++; + block->s2b_shift++; return 0; } -static int -dasd_fba_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) +static int dasd_fba_fill_geometry(struct dasd_block *block, + struct hd_geometry *geo) { - if (dasd_check_blocksize(device->bp_block) != 0) + if (dasd_check_blocksize(block->bp_block) != 0) return -EINVAL; - geo->cylinders = (device->blocks << device->s2b_shift) >> 10; + geo->cylinders = (block->blocks << block->s2b_shift) >> 10; geo->heads = 16; - geo->sectors = 128 >> device->s2b_shift; + geo->sectors = 128 >> block->s2b_shift; return 0; } -static dasd_era_t -dasd_fba_examine_error(struct dasd_ccw_req * cqr, struct irb * irb) -{ - struct dasd_device *device; - struct ccw_device *cdev; - - device = (struct dasd_device *) cqr->device; - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - - cdev = device->cdev; - switch (cdev->id.dev_type) { - case 0x3370: - return dasd_3370_erp_examine(cqr, irb); - case 0x9336: - return dasd_9336_erp_examine(cqr, irb); - default: - return dasd_era_recover; - } -} - static dasd_erp_fn_t dasd_fba_erp_action(struct dasd_ccw_req * cqr) { @@ -221,13 +209,34 @@ dasd_fba_erp_postaction(struct dasd_ccw_req * cqr) if (cqr->function == dasd_default_erp_action) return dasd_default_erp_postaction; - DEV_MESSAGE(KERN_WARNING, cqr->device, "unknown ERP action %p", + DEV_MESSAGE(KERN_WARNING, cqr->startdev, "unknown ERP action %p", cqr->function); return NULL; } -static struct dasd_ccw_req * -dasd_fba_build_cp(struct dasd_device * device, struct request *req) +static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device, + struct irb *irb) +{ + char mask; + + /* first of all check for state change pending interrupt */ + mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; + if ((irb->scsw.dstat & mask) == mask) { + dasd_generic_handle_state_change(device); + return; + } + + /* check for unsolicited interrupts */ + DEV_MESSAGE(KERN_DEBUG, device, "%s", + "unsolicited interrupt received"); + device->discipline->dump_sense(device, NULL, irb); + dasd_schedule_device_bh(device); + return; +}; + +static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, + struct dasd_block *block, + struct request *req) { struct dasd_fba_private *private; unsigned long *idaws; @@ -242,17 +251,17 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) unsigned int blksize, off; unsigned char cmd; - private = (struct dasd_fba_private *) device->private; + private = (struct dasd_fba_private *) block->base->private; if (rq_data_dir(req) == READ) { cmd = DASD_FBA_CCW_READ; } else if (rq_data_dir(req) == WRITE) { cmd = DASD_FBA_CCW_WRITE; } else return ERR_PTR(-EINVAL); - blksize = device->bp_block; + blksize = block->bp_block; /* Calculate record id of first and last block. */ - first_rec = req->sector >> device->s2b_shift; - last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift; + first_rec = req->sector >> block->s2b_shift; + last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift; /* Check struct bio and count the number of blocks for the request. */ count = 0; cidaw = 0; @@ -260,7 +269,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) if (bv->bv_len & (blksize - 1)) /* Fba can only do full blocks. */ return ERR_PTR(-EINVAL); - count += bv->bv_len >> (device->s2b_shift + 9); + count += bv->bv_len >> (block->s2b_shift + 9); #if defined(CONFIG_64BIT) if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) cidaw += bv->bv_len / blksize; @@ -284,13 +293,13 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) } /* Allocate the ccw request. */ cqr = dasd_smalloc_request(dasd_fba_discipline.name, - cplength, datasize, device); + cplength, datasize, memdev); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; /* First ccw is define extent. */ define_extent(ccw++, cqr->data, rq_data_dir(req), - device->bp_block, req->sector, req->nr_sectors); + block->bp_block, req->sector, req->nr_sectors); /* Build locate_record + read/write ccws. */ idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data)); LO_data = (struct LO_fba_data *) (idaws + cidaw); @@ -326,7 +335,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) ccw[-1].flags |= CCW_FLAG_CC; } ccw->cmd_code = cmd; - ccw->count = device->bp_block; + ccw->count = block->bp_block; if (idal_is_needed(dst, blksize)) { ccw->cda = (__u32)(addr_t) idaws; ccw->flags = CCW_FLAG_IDA; @@ -342,7 +351,9 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) } if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); - cqr->device = device; + cqr->startdev = memdev; + cqr->memdev = memdev; + cqr->block = block; cqr->expires = 5 * 60 * HZ; /* 5 minutes */ cqr->retries = 32; cqr->buildclk = get_clock(); @@ -363,8 +374,8 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) if (!dasd_page_cache) goto out; - private = (struct dasd_fba_private *) cqr->device->private; - blksize = cqr->device->bp_block; + private = (struct dasd_fba_private *) cqr->block->base->private; + blksize = cqr->block->bp_block; ccw = cqr->cpaddr; /* Skip over define extent & locate record. */ ccw++; @@ -394,10 +405,15 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) } out: status = cqr->status == DASD_CQR_DONE; - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return status; } +static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr) +{ + cqr->status = DASD_CQR_FILLED; +}; + static int dasd_fba_fill_info(struct dasd_device * device, struct dasd_information2_t * info) @@ -546,9 +562,10 @@ static struct dasd_discipline dasd_fba_discipline = { .fill_geometry = dasd_fba_fill_geometry, .start_IO = dasd_start_IO, .term_IO = dasd_term_IO, - .examine_error = dasd_fba_examine_error, + .handle_terminated_request = dasd_fba_handle_terminated_request, .erp_action = dasd_fba_erp_action, .erp_postaction = dasd_fba_erp_postaction, + .handle_unsolicited_interrupt = dasd_fba_handle_unsolicited_interrupt, .build_cp = dasd_fba_build_cp, .free_cp = dasd_fba_free_cp, .dump_sense = dasd_fba_dump_sense, diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c index 47ba4462708..aee6565aaf9 100644 --- a/drivers/s390/block/dasd_genhd.c +++ b/drivers/s390/block/dasd_genhd.c @@ -25,14 +25,15 @@ /* * Allocate and register gendisk structure for device. */ -int -dasd_gendisk_alloc(struct dasd_device *device) +int dasd_gendisk_alloc(struct dasd_block *block) { struct gendisk *gdp; + struct dasd_device *base; int len; /* Make sure the minor for this device exists. */ - if (device->devindex >= DASD_PER_MAJOR) + base = block->base; + if (base->devindex >= DASD_PER_MAJOR) return -EBUSY; gdp = alloc_disk(1 << DASD_PARTN_BITS); @@ -41,9 +42,9 @@ dasd_gendisk_alloc(struct dasd_device *device) /* Initialize gendisk structure. */ gdp->major = DASD_MAJOR; - gdp->first_minor = device->devindex << DASD_PARTN_BITS; + gdp->first_minor = base->devindex << DASD_PARTN_BITS; gdp->fops = &dasd_device_operations; - gdp->driverfs_dev = &device->cdev->dev; + gdp->driverfs_dev = &base->cdev->dev; /* * Set device name. @@ -53,53 +54,51 @@ dasd_gendisk_alloc(struct dasd_device *device) * dasdaaaa - dasdzzzz : 456976 devices, added up = 475252 */ len = sprintf(gdp->disk_name, "dasd"); - if (device->devindex > 25) { - if (device->devindex > 701) { - if (device->devindex > 18277) + if (base->devindex > 25) { + if (base->devindex > 701) { + if (base->devindex > 18277) len += sprintf(gdp->disk_name + len, "%c", - 'a'+(((device->devindex-18278) + 'a'+(((base->devindex-18278) /17576)%26)); len += sprintf(gdp->disk_name + len, "%c", - 'a'+(((device->devindex-702)/676)%26)); + 'a'+(((base->devindex-702)/676)%26)); } len += sprintf(gdp->disk_name + len, "%c", - 'a'+(((device->devindex-26)/26)%26)); + 'a'+(((base->devindex-26)/26)%26)); } - len += sprintf(gdp->disk_name + len, "%c", 'a'+(device->devindex%26)); + len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26)); - if (device->features & DASD_FEATURE_READONLY) + if (block->base->features & DASD_FEATURE_READONLY) set_disk_ro(gdp, 1); - gdp->private_data = device; - gdp->queue = device->request_queue; - device->gdp = gdp; - set_capacity(device->gdp, 0); - add_disk(device->gdp); + gdp->private_data = block; + gdp->queue = block->request_queue; + block->gdp = gdp; + set_capacity(block->gdp, 0); + add_disk(block->gdp); return 0; } /* * Unregister and free gendisk structure for device. */ -void -dasd_gendisk_free(struct dasd_device *device) +void dasd_gendisk_free(struct dasd_block *block) { - if (device->gdp) { - del_gendisk(device->gdp); - device->gdp->queue = NULL; - put_disk(device->gdp); - device->gdp = NULL; + if (block->gdp) { + del_gendisk(block->gdp); + block->gdp->queue = NULL; + put_disk(block->gdp); + block->gdp = NULL; } } /* * Trigger a partition detection. */ -int -dasd_scan_partitions(struct dasd_device * device) +int dasd_scan_partitions(struct dasd_block *block) { struct block_device *bdev; - bdev = bdget_disk(device->gdp, 0); + bdev = bdget_disk(block->gdp, 0); if (!bdev || blkdev_get(bdev, FMODE_READ, 1) < 0) return -ENODEV; /* @@ -117,7 +116,7 @@ dasd_scan_partitions(struct dasd_device * device) * is why the assignment to device->bdev is done AFTER * the BLKRRPART ioctl. */ - device->bdev = bdev; + block->bdev = bdev; return 0; } @@ -125,8 +124,7 @@ dasd_scan_partitions(struct dasd_device * device) * Remove all inodes in the system for a device, delete the * partitions and make device unusable by setting its size to zero. */ -void -dasd_destroy_partitions(struct dasd_device * device) +void dasd_destroy_partitions(struct dasd_block *block) { /* The two structs have 168/176 byte on 31/64 bit. */ struct blkpg_partition bpart; @@ -137,8 +135,8 @@ dasd_destroy_partitions(struct dasd_device * device) * Get the bdev pointer from the device structure and clear * device->bdev to lower the offline open_count limit again. */ - bdev = device->bdev; - device->bdev = NULL; + bdev = block->bdev; + block->bdev = NULL; /* * See fs/partition/check.c:delete_partition @@ -149,17 +147,16 @@ dasd_destroy_partitions(struct dasd_device * device) memset(&barg, 0, sizeof(struct blkpg_ioctl_arg)); barg.data = (void __force __user *) &bpart; barg.op = BLKPG_DEL_PARTITION; - for (bpart.pno = device->gdp->minors - 1; bpart.pno > 0; bpart.pno--) + for (bpart.pno = block->gdp->minors - 1; bpart.pno > 0; bpart.pno--) ioctl_by_bdev(bdev, BLKPG, (unsigned long) &barg); - invalidate_partition(device->gdp, 0); + invalidate_partition(block->gdp, 0); /* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */ blkdev_put(bdev); - set_capacity(device->gdp, 0); + set_capacity(block->gdp, 0); } -int -dasd_gendisk_init(void) +int dasd_gendisk_init(void) { int rc; @@ -174,8 +171,7 @@ dasd_gendisk_init(void) return 0; } -void -dasd_gendisk_exit(void) +void dasd_gendisk_exit(void) { unregister_blkdev(DASD_MAJOR, "dasd"); } diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index d427daeef51..44b2984dfbe 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -64,13 +64,7 @@ * SECTION: Type definitions */ struct dasd_device; - -typedef enum { - dasd_era_fatal = -1, /* no chance to recover */ - dasd_era_none = 0, /* don't recover, everything alright */ - dasd_era_msg = 1, /* don't recover, just report... */ - dasd_era_recover = 2 /* recovery action recommended */ -} dasd_era_t; +struct dasd_block; /* BIT DEFINITIONS FOR SENSE DATA */ #define DASD_SENSE_BIT_0 0x80 @@ -151,19 +145,22 @@ do { \ struct dasd_ccw_req { unsigned int magic; /* Eye catcher */ - struct list_head list; /* list_head for request queueing. */ + struct list_head devlist; /* for dasd_device request queue */ + struct list_head blocklist; /* for dasd_block request queue */ /* Where to execute what... */ - struct dasd_device *device; /* device the request is for */ + struct dasd_block *block; /* the originating block device */ + struct dasd_device *memdev; /* the device used to allocate this */ + struct dasd_device *startdev; /* device the request is started on */ struct ccw1 *cpaddr; /* address of channel program */ - char status; /* status of this request */ + char status; /* status of this request */ short retries; /* A retry counter */ unsigned long flags; /* flags of this request */ /* ... and how */ unsigned long starttime; /* jiffies time of request start */ int expires; /* expiration period in jiffies */ - char lpm; /* logical path mask */ + char lpm; /* logical path mask */ void *data; /* pointer to data area */ /* these are important for recovering erroneous requests */ @@ -178,20 +175,27 @@ struct dasd_ccw_req { unsigned long long endclk; /* TOD-clock of request termination */ /* Callback that is called after reaching final status. */ - void (*callback)(struct dasd_ccw_req *, void *data); - void *callback_data; + void (*callback)(struct dasd_ccw_req *, void *data); + void *callback_data; }; /* * dasd_ccw_req -> status can be: */ -#define DASD_CQR_FILLED 0x00 /* request is ready to be processed */ -#define DASD_CQR_QUEUED 0x01 /* request is queued to be processed */ -#define DASD_CQR_IN_IO 0x02 /* request is currently in IO */ -#define DASD_CQR_DONE 0x03 /* request is completed successfully */ -#define DASD_CQR_ERROR 0x04 /* request is completed with error */ -#define DASD_CQR_FAILED 0x05 /* request is finally failed */ -#define DASD_CQR_CLEAR 0x06 /* request is clear pending */ +#define DASD_CQR_FILLED 0x00 /* request is ready to be processed */ +#define DASD_CQR_DONE 0x01 /* request is completed successfully */ +#define DASD_CQR_NEED_ERP 0x02 /* request needs recovery action */ +#define DASD_CQR_IN_ERP 0x03 /* request is in recovery */ +#define DASD_CQR_FAILED 0x04 /* request is finally failed */ +#define DASD_CQR_TERMINATED 0x05 /* request was stopped by driver */ + +#define DASD_CQR_QUEUED 0x80 /* request is queued to be processed */ +#define DASD_CQR_IN_IO 0x81 /* request is currently in IO */ +#define DASD_CQR_ERROR 0x82 /* request is completed with error */ +#define DASD_CQR_CLEAR_PENDING 0x83 /* request is clear pending */ +#define DASD_CQR_CLEARED 0x84 /* request was cleared */ +#define DASD_CQR_SUCCESS 0x85 /* request was successfull */ + /* per dasd_ccw_req flags */ #define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */ @@ -214,52 +218,71 @@ struct dasd_discipline { struct list_head list; /* used for list of disciplines */ - /* - * Device recognition functions. check_device is used to verify - * the sense data and the information returned by read device - * characteristics. It returns 0 if the discipline can be used - * for the device in question. - * do_analysis is used in the step from device state "basic" to - * state "accept". It returns 0 if the device can be made ready, - * it returns -EMEDIUMTYPE if the device can't be made ready or - * -EAGAIN if do_analysis started a ccw that needs to complete - * before the analysis may be repeated. - */ - int (*check_device)(struct dasd_device *); - int (*do_analysis) (struct dasd_device *); - - /* - * Device operation functions. build_cp creates a ccw chain for - * a block device request, start_io starts the request and - * term_IO cancels it (e.g. in case of a timeout). format_device - * returns a ccw chain to be used to format the device. - */ + /* + * Device recognition functions. check_device is used to verify + * the sense data and the information returned by read device + * characteristics. It returns 0 if the discipline can be used + * for the device in question. uncheck_device is called during + * device shutdown to deregister a device from its discipline. + */ + int (*check_device) (struct dasd_device *); + void (*uncheck_device) (struct dasd_device *); + + /* + * do_analysis is used in the step from device state "basic" to + * state "accept". It returns 0 if the device can be made ready, + * it returns -EMEDIUMTYPE if the device can't be made ready or + * -EAGAIN if do_analysis started a ccw that needs to complete + * before the analysis may be repeated. + */ + int (*do_analysis) (struct dasd_block *); + + /* + * Last things to do when a device is set online, and first things + * when it is set offline. + */ + int (*ready_to_online) (struct dasd_device *); + int (*online_to_ready) (struct dasd_device *); + + /* + * Device operation functions. build_cp creates a ccw chain for + * a block device request, start_io starts the request and + * term_IO cancels it (e.g. in case of a timeout). format_device + * returns a ccw chain to be used to format the device. + * handle_terminated_request allows to examine a cqr and prepare + * it for retry. + */ struct dasd_ccw_req *(*build_cp) (struct dasd_device *, + struct dasd_block *, struct request *); int (*start_IO) (struct dasd_ccw_req *); int (*term_IO) (struct dasd_ccw_req *); + void (*handle_terminated_request) (struct dasd_ccw_req *); struct dasd_ccw_req *(*format_device) (struct dasd_device *, struct format_data_t *); int (*free_cp) (struct dasd_ccw_req *, struct request *); - /* - * Error recovery functions. examine_error() returns a value that - * indicates what to do for an error condition. If examine_error() + + /* + * Error recovery functions. examine_error() returns a value that + * indicates what to do for an error condition. If examine_error() * returns 'dasd_era_recover' erp_action() is called to create a - * special error recovery ccw. erp_postaction() is called after - * an error recovery ccw has finished its execution. dump_sense - * is called for every error condition to print the sense data - * to the console. - */ - dasd_era_t(*examine_error) (struct dasd_ccw_req *, struct irb *); + * special error recovery ccw. erp_postaction() is called after + * an error recovery ccw has finished its execution. dump_sense + * is called for every error condition to print the sense data + * to the console. + */ dasd_erp_fn_t(*erp_action) (struct dasd_ccw_req *); dasd_erp_fn_t(*erp_postaction) (struct dasd_ccw_req *); void (*dump_sense) (struct dasd_device *, struct dasd_ccw_req *, struct irb *); + void (*handle_unsolicited_interrupt) (struct dasd_device *, + struct irb *); + /* i/o control functions. */ - int (*fill_geometry) (struct dasd_device *, struct hd_geometry *); + int (*fill_geometry) (struct dasd_block *, struct hd_geometry *); int (*fill_info) (struct dasd_device *, struct dasd_information2_t *); - int (*ioctl) (struct dasd_device *, unsigned int, void __user *); + int (*ioctl) (struct dasd_block *, unsigned int, void __user *); }; extern struct dasd_discipline *dasd_diag_discipline_pointer; @@ -267,12 +290,18 @@ extern struct dasd_discipline *dasd_diag_discipline_pointer; /* * Unique identifier for dasd device. */ +#define UA_NOT_CONFIGURED 0x00 +#define UA_BASE_DEVICE 0x01 +#define UA_BASE_PAV_ALIAS 0x02 +#define UA_HYPER_PAV_ALIAS 0x03 + struct dasd_uid { - __u8 alias; + __u8 type; char vendor[4]; char serial[15]; __u16 ssid; - __u8 unit_addr; + __u8 real_unit_addr; + __u8 base_unit_addr; }; /* @@ -293,14 +322,9 @@ struct dasd_uid { struct dasd_device { /* Block device stuff. */ - struct gendisk *gdp; - struct request_queue *request_queue; - spinlock_t request_queue_lock; - struct block_device *bdev; + struct dasd_block *block; + unsigned int devindex; - unsigned long blocks; /* size of volume in blocks */ - unsigned int bp_block; /* bytes per block */ - unsigned int s2b_shift; /* log2 (bp_block/512) */ unsigned long flags; /* per device flags */ unsigned short features; /* copy of devmap-features (read-only!) */ @@ -316,9 +340,8 @@ struct dasd_device { int state, target; int stopped; /* device (ccw_device_start) was stopped */ - /* Open and reference count. */ + /* reference count. */ atomic_t ref_count; - atomic_t open_count; /* ccw queue and memory for static ccw/erp buffers. */ struct list_head ccw_queue; @@ -337,20 +360,45 @@ struct dasd_device { struct ccw_device *cdev; + /* hook for alias management */ + struct list_head alias_list; +}; + +struct dasd_block { + /* Block device stuff. */ + struct gendisk *gdp; + struct request_queue *request_queue; + spinlock_t request_queue_lock; + struct block_device *bdev; + atomic_t open_count; + + unsigned long blocks; /* size of volume in blocks */ + unsigned int bp_block; /* bytes per block */ + unsigned int s2b_shift; /* log2 (bp_block/512) */ + + struct dasd_device *base; + struct list_head ccw_queue; + spinlock_t queue_lock; + + atomic_t tasklet_scheduled; + struct tasklet_struct tasklet; + struct timer_list timer; + #ifdef CONFIG_DASD_PROFILE struct dasd_profile_info_t profile; #endif }; + + /* reasons why device (ccw_device_start) was stopped */ #define DASD_STOPPED_NOT_ACC 1 /* not accessible */ #define DASD_STOPPED_QUIESCE 2 /* Quiesced */ #define DASD_STOPPED_PENDING 4 /* long busy */ #define DASD_STOPPED_DC_WAIT 8 /* disconnected, wait */ -#define DASD_STOPPED_DC_EIO 16 /* disconnected, return -EIO */ +#define DASD_STOPPED_SU 16 /* summary unit check handling */ /* per device flags */ -#define DASD_FLAG_DSC_ERROR 2 /* return -EIO when disconnected */ #define DASD_FLAG_OFFLINE 3 /* device is in offline processing */ #define DASD_FLAG_EER_SNSS 4 /* A SNSS is required */ #define DASD_FLAG_EER_IN_USE 5 /* A SNSS request is running */ @@ -489,6 +537,9 @@ dasd_kmalloc_set_cda(struct ccw1 *ccw, void *cda, struct dasd_device *device) struct dasd_device *dasd_alloc_device(void); void dasd_free_device(struct dasd_device *); +struct dasd_block *dasd_alloc_block(void); +void dasd_free_block(struct dasd_block *); + void dasd_enable_device(struct dasd_device *); void dasd_set_target_state(struct dasd_device *, int); void dasd_kick_device(struct dasd_device *); @@ -497,18 +548,23 @@ void dasd_add_request_head(struct dasd_ccw_req *); void dasd_add_request_tail(struct dasd_ccw_req *); int dasd_start_IO(struct dasd_ccw_req *); int dasd_term_IO(struct dasd_ccw_req *); -void dasd_schedule_bh(struct dasd_device *); +void dasd_schedule_device_bh(struct dasd_device *); +void dasd_schedule_block_bh(struct dasd_block *); int dasd_sleep_on(struct dasd_ccw_req *); int dasd_sleep_on_immediatly(struct dasd_ccw_req *); int dasd_sleep_on_interruptible(struct dasd_ccw_req *); -void dasd_set_timer(struct dasd_device *, int); -void dasd_clear_timer(struct dasd_device *); +void dasd_device_set_timer(struct dasd_device *, int); +void dasd_device_clear_timer(struct dasd_device *); +void dasd_block_set_timer(struct dasd_block *, int); +void dasd_block_clear_timer(struct dasd_block *); int dasd_cancel_req(struct dasd_ccw_req *); +int dasd_flush_device_queue(struct dasd_device *); int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *); void dasd_generic_remove (struct ccw_device *cdev); int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *); int dasd_generic_set_offline (struct ccw_device *cdev); int dasd_generic_notify(struct ccw_device *, int); +void dasd_generic_handle_state_change(struct dasd_device *); int dasd_generic_read_dev_chars(struct dasd_device *, char *, void **, int); @@ -542,10 +598,10 @@ int dasd_busid_known(char *); /* externals in dasd_gendisk.c */ int dasd_gendisk_init(void); void dasd_gendisk_exit(void); -int dasd_gendisk_alloc(struct dasd_device *); -void dasd_gendisk_free(struct dasd_device *); -int dasd_scan_partitions(struct dasd_device *); -void dasd_destroy_partitions(struct dasd_device *); +int dasd_gendisk_alloc(struct dasd_block *); +void dasd_gendisk_free(struct dasd_block *); +int dasd_scan_partitions(struct dasd_block *); +void dasd_destroy_partitions(struct dasd_block *); /* externals in dasd_ioctl.c */ int dasd_ioctl(struct inode *, struct file *, unsigned int, unsigned long); @@ -563,20 +619,9 @@ struct dasd_ccw_req *dasd_alloc_erp_request(char *, int, int, void dasd_free_erp_request(struct dasd_ccw_req *, struct dasd_device *); void dasd_log_sense(struct dasd_ccw_req *, struct irb *); -/* externals in dasd_3370_erp.c */ -dasd_era_t dasd_3370_erp_examine(struct dasd_ccw_req *, struct irb *); - /* externals in dasd_3990_erp.c */ -dasd_era_t dasd_3990_erp_examine(struct dasd_ccw_req *, struct irb *); struct dasd_ccw_req *dasd_3990_erp_action(struct dasd_ccw_req *); -/* externals in dasd_9336_erp.c */ -dasd_era_t dasd_9336_erp_examine(struct dasd_ccw_req *, struct irb *); - -/* externals in dasd_9336_erp.c */ -dasd_era_t dasd_9343_erp_examine(struct dasd_ccw_req *, struct irb *); -struct dasd_ccw_req *dasd_9343_erp_action(struct dasd_ccw_req *); - /* externals in dasd_eer.c */ #ifdef CONFIG_DASD_EER int dasd_eer_init(void); diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 672eb0a3dd0..91a64630cb0 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -38,15 +38,15 @@ dasd_ioctl_api_version(void __user *argp) static int dasd_ioctl_enable(struct block_device *bdev) { - struct dasd_device *device = bdev->bd_disk->private_data; + struct dasd_block *block = bdev->bd_disk->private_data; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - dasd_enable_device(device); + dasd_enable_device(block->base); /* Formatting the dasd device can change the capacity. */ mutex_lock(&bdev->bd_mutex); - i_size_write(bdev->bd_inode, (loff_t)get_capacity(device->gdp) << 9); + i_size_write(bdev->bd_inode, (loff_t)get_capacity(block->gdp) << 9); mutex_unlock(&bdev->bd_mutex); return 0; } @@ -58,7 +58,7 @@ dasd_ioctl_enable(struct block_device *bdev) static int dasd_ioctl_disable(struct block_device *bdev) { - struct dasd_device *device = bdev->bd_disk->private_data; + struct dasd_block *block = bdev->bd_disk->private_data; if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -71,7 +71,7 @@ dasd_ioctl_disable(struct block_device *bdev) * using the BIODASDFMT ioctl. Therefore the correct state for the * device is DASD_STATE_BASIC that allows to do basic i/o. */ - dasd_set_target_state(device, DASD_STATE_BASIC); + dasd_set_target_state(block->base, DASD_STATE_BASIC); /* * Set i_size to zero, since read, write, etc. check against this * value. @@ -85,19 +85,19 @@ dasd_ioctl_disable(struct block_device *bdev) /* * Quiesce device. */ -static int -dasd_ioctl_quiesce(struct dasd_device *device) +static int dasd_ioctl_quiesce(struct dasd_block *block) { unsigned long flags; + struct dasd_device *base; + base = block->base; if (!capable (CAP_SYS_ADMIN)) return -EACCES; - DEV_MESSAGE (KERN_DEBUG, device, "%s", - "Quiesce IO on device"); - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped |= DASD_STOPPED_QUIESCE; - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + DEV_MESSAGE(KERN_DEBUG, base, "%s", "Quiesce IO on device"); + spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); + base->stopped |= DASD_STOPPED_QUIESCE; + spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); return 0; } @@ -105,22 +105,21 @@ dasd_ioctl_quiesce(struct dasd_device *device) /* * Quiesce device. */ -static int -dasd_ioctl_resume(struct dasd_device *device) +static int dasd_ioctl_resume(struct dasd_block *block) { unsigned long flags; + struct dasd_device *base; + base = block->base; if (!capable (CAP_SYS_ADMIN)) return -EACCES; - DEV_MESSAGE (KERN_DEBUG, device, "%s", - "resume IO on device"); - - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped &= ~DASD_STOPPED_QUIESCE; - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + DEV_MESSAGE(KERN_DEBUG, base, "%s", "resume IO on device"); + spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); + base->stopped &= ~DASD_STOPPED_QUIESCE; + spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); - dasd_schedule_bh (device); + dasd_schedule_block_bh(block); return 0; } @@ -130,22 +129,23 @@ dasd_ioctl_resume(struct dasd_device *device) * commands to format a single unit of the device. In terms of the ECKD * devices this means CCWs are generated to format a single track. */ -static int -dasd_format(struct dasd_device * device, struct format_data_t * fdata) +static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) { struct dasd_ccw_req *cqr; + struct dasd_device *base; int rc; - if (device->discipline->format_device == NULL) + base = block->base; + if (base->discipline->format_device == NULL) return -EPERM; - if (device->state != DASD_STATE_BASIC) { - DEV_MESSAGE(KERN_WARNING, device, "%s", + if (base->state != DASD_STATE_BASIC) { + DEV_MESSAGE(KERN_WARNING, base, "%s", "dasd_format: device is not disabled! "); return -EBUSY; } - DBF_DEV_EVENT(DBF_NOTICE, device, + DBF_DEV_EVENT(DBF_NOTICE, base, "formatting units %d to %d (%d B blocks) flags %d", fdata->start_unit, fdata->stop_unit, fdata->blksize, fdata->intensity); @@ -156,20 +156,20 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata) * enabling the device later. */ if (fdata->start_unit == 0) { - struct block_device *bdev = bdget_disk(device->gdp, 0); + struct block_device *bdev = bdget_disk(block->gdp, 0); bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize); bdput(bdev); } while (fdata->start_unit <= fdata->stop_unit) { - cqr = device->discipline->format_device(device, fdata); + cqr = base->discipline->format_device(base, fdata); if (IS_ERR(cqr)) return PTR_ERR(cqr); rc = dasd_sleep_on_interruptible(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); if (rc) { if (rc != -ERESTARTSYS) - DEV_MESSAGE(KERN_ERR, device, + DEV_MESSAGE(KERN_ERR, base, " Formatting of unit %d failed " "with rc = %d", fdata->start_unit, rc); @@ -186,7 +186,7 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata) static int dasd_ioctl_format(struct block_device *bdev, void __user *argp) { - struct dasd_device *device = bdev->bd_disk->private_data; + struct dasd_block *block = bdev->bd_disk->private_data; struct format_data_t fdata; if (!capable(CAP_SYS_ADMIN)) @@ -194,51 +194,47 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp) if (!argp) return -EINVAL; - if (device->features & DASD_FEATURE_READONLY) + if (block->base->features & DASD_FEATURE_READONLY) return -EROFS; if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) return -EFAULT; if (bdev != bdev->bd_contains) { - DEV_MESSAGE(KERN_WARNING, device, "%s", + DEV_MESSAGE(KERN_WARNING, block->base, "%s", "Cannot low-level format a partition"); return -EINVAL; } - return dasd_format(device, &fdata); + return dasd_format(block, &fdata); } #ifdef CONFIG_DASD_PROFILE /* * Reset device profile information */ -static int -dasd_ioctl_reset_profile(struct dasd_device *device) +static int dasd_ioctl_reset_profile(struct dasd_block *block) { - memset(&device->profile, 0, sizeof (struct dasd_profile_info_t)); + memset(&block->profile, 0, sizeof(struct dasd_profile_info_t)); return 0; } /* * Return device profile information */ -static int -dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp) +static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) { if (dasd_profile_level == DASD_PROFILE_OFF) return -EIO; - if (copy_to_user(argp, &device->profile, - sizeof (struct dasd_profile_info_t))) + if (copy_to_user(argp, &block->profile, + sizeof(struct dasd_profile_info_t))) return -EFAULT; return 0; } #else -static int -dasd_ioctl_reset_profile(struct dasd_device *device) +static int dasd_ioctl_reset_profile(struct dasd_block *block) { return -ENOSYS; } -static int -dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp) +static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) { return -ENOSYS; } @@ -247,87 +243,88 @@ dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp) /* * Return dasd information. Used for BIODASDINFO and BIODASDINFO2. */ -static int -dasd_ioctl_information(struct dasd_device *device, - unsigned int cmd, void __user *argp) +static int dasd_ioctl_information(struct dasd_block *block, + unsigned int cmd, void __user *argp) { struct dasd_information2_t *dasd_info; unsigned long flags; int rc; + struct dasd_device *base; struct ccw_device *cdev; struct ccw_dev_id dev_id; - if (!device->discipline->fill_info) + base = block->base; + if (!base->discipline->fill_info) return -EINVAL; dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL); if (dasd_info == NULL) return -ENOMEM; - rc = device->discipline->fill_info(device, dasd_info); + rc = base->discipline->fill_info(base, dasd_info); if (rc) { kfree(dasd_info); return rc; } - cdev = device->cdev; + cdev = base->cdev; ccw_device_get_id(cdev, &dev_id); dasd_info->devno = dev_id.devno; - dasd_info->schid = _ccw_device_get_subchannel_number(device->cdev); + dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev); dasd_info->cu_type = cdev->id.cu_type; dasd_info->cu_model = cdev->id.cu_model; dasd_info->dev_type = cdev->id.dev_type; dasd_info->dev_model = cdev->id.dev_model; - dasd_info->status = device->state; + dasd_info->status = base->state; /* * The open_count is increased for every opener, that includes * the blkdev_get in dasd_scan_partitions. * This must be hidden from user-space. */ - dasd_info->open_count = atomic_read(&device->open_count); - if (!device->bdev) + dasd_info->open_count = atomic_read(&block->open_count); + if (!block->bdev) dasd_info->open_count++; /* * check if device is really formatted * LDL / CDL was returned by 'fill_info' */ - if ((device->state < DASD_STATE_READY) || - (dasd_check_blocksize(device->bp_block))) + if ((base->state < DASD_STATE_READY) || + (dasd_check_blocksize(block->bp_block))) dasd_info->format = DASD_FORMAT_NONE; dasd_info->features |= - ((device->features & DASD_FEATURE_READONLY) != 0); + ((base->features & DASD_FEATURE_READONLY) != 0); - if (device->discipline) - memcpy(dasd_info->type, device->discipline->name, 4); + if (base->discipline) + memcpy(dasd_info->type, base->discipline->name, 4); else memcpy(dasd_info->type, "none", 4); - if (device->request_queue->request_fn) { + if (block->request_queue->request_fn) { struct list_head *l; #ifdef DASD_EXTENDED_PROFILING { struct list_head *l; - spin_lock_irqsave(&device->lock, flags); - list_for_each(l, &device->request_queue->queue_head) + spin_lock_irqsave(&block->lock, flags); + list_for_each(l, &block->request_queue->queue_head) dasd_info->req_queue_len++; - spin_unlock_irqrestore(&device->lock, flags); + spin_unlock_irqrestore(&block->lock, flags); } #endif /* DASD_EXTENDED_PROFILING */ - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - list_for_each(l, &device->ccw_queue) + spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); + list_for_each(l, &base->ccw_queue) dasd_info->chanq_len++; - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), + spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); } rc = 0; if (copy_to_user(argp, dasd_info, ((cmd == (unsigned int) BIODASDINFO2) ? - sizeof (struct dasd_information2_t) : - sizeof (struct dasd_information_t)))) + sizeof(struct dasd_information2_t) : + sizeof(struct dasd_information_t)))) rc = -EFAULT; kfree(dasd_info); return rc; @@ -339,7 +336,7 @@ dasd_ioctl_information(struct dasd_device *device, static int dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) { - struct dasd_device *device = bdev->bd_disk->private_data; + struct dasd_block *block = bdev->bd_disk->private_data; int intval; if (!capable(CAP_SYS_ADMIN)) @@ -351,11 +348,10 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) return -EFAULT; set_disk_ro(bdev->bd_disk, intval); - return dasd_set_feature(device->cdev, DASD_FEATURE_READONLY, intval); + return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval); } -static int -dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd, +static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd, unsigned long arg) { struct cmbdata __user *argp = (void __user *) arg; @@ -363,7 +359,7 @@ dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd, struct cmbdata data; int ret; - ret = cmf_readall(device->cdev, &data); + ret = cmf_readall(block->base->cdev, &data); if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp)))) return -EFAULT; return ret; @@ -374,10 +370,10 @@ dasd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct block_device *bdev = inode->i_bdev; - struct dasd_device *device = bdev->bd_disk->private_data; + struct dasd_block *block = bdev->bd_disk->private_data; void __user *argp = (void __user *)arg; - if (!device) + if (!block) return -ENODEV; if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) { @@ -391,33 +387,33 @@ dasd_ioctl(struct inode *inode, struct file *file, case BIODASDENABLE: return dasd_ioctl_enable(bdev); case BIODASDQUIESCE: - return dasd_ioctl_quiesce(device); + return dasd_ioctl_quiesce(block); case BIODASDRESUME: - return dasd_ioctl_resume(device); + return dasd_ioctl_resume(block); case BIODASDFMT: return dasd_ioctl_format(bdev, argp); case BIODASDINFO: - return dasd_ioctl_information(device, cmd, argp); + return dasd_ioctl_information(block, cmd, argp); case BIODASDINFO2: - return dasd_ioctl_information(device, cmd, argp); + return dasd_ioctl_information(block, cmd, argp); case BIODASDPRRD: - return dasd_ioctl_read_profile(device, argp); + return dasd_ioctl_read_profile(block, argp); case BIODASDPRRST: - return dasd_ioctl_reset_profile(device); + return dasd_ioctl_reset_profile(block); case BLKROSET: return dasd_ioctl_set_ro(bdev, argp); case DASDAPIVER: return dasd_ioctl_api_version(argp); case BIODASDCMFENABLE: - return enable_cmf(device->cdev); + return enable_cmf(block->base->cdev); case BIODASDCMFDISABLE: - return disable_cmf(device->cdev); + return disable_cmf(block->base->cdev); case BIODASDREADALLCMB: - return dasd_ioctl_readall_cmb(device, cmd, arg); + return dasd_ioctl_readall_cmb(block, cmd, arg); default: /* if the discipline has an ioctl method try it. */ - if (device->discipline->ioctl) { - int rval = device->discipline->ioctl(device, cmd, argp); + if (block->base->discipline->ioctl) { + int rval = block->base->discipline->ioctl(block, cmd, argp); if (rval != -ENOIOCTLCMD) return rval; } diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index ac7e8ef504c..28a86f07004 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -54,11 +54,16 @@ static int dasd_devices_show(struct seq_file *m, void *v) { struct dasd_device *device; + struct dasd_block *block; char *substr; device = dasd_device_from_devindex((unsigned long) v - 1); if (IS_ERR(device)) return 0; + if (device->block) + block = device->block; + else + return 0; /* Print device number. */ seq_printf(m, "%s", device->cdev->dev.bus_id); /* Print discipline string. */ @@ -67,14 +72,14 @@ dasd_devices_show(struct seq_file *m, void *v) else seq_printf(m, "(none)"); /* Print kdev. */ - if (device->gdp) + if (block->gdp) seq_printf(m, " at (%3d:%6d)", - device->gdp->major, device->gdp->first_minor); + block->gdp->major, block->gdp->first_minor); else seq_printf(m, " at (???:??????)"); /* Print device name. */ - if (device->gdp) - seq_printf(m, " is %-8s", device->gdp->disk_name); + if (block->gdp) + seq_printf(m, " is %-8s", block->gdp->disk_name); else seq_printf(m, " is ????????"); /* Print devices features. */ @@ -100,14 +105,14 @@ dasd_devices_show(struct seq_file *m, void *v) case DASD_STATE_READY: case DASD_STATE_ONLINE: seq_printf(m, "active "); - if (dasd_check_blocksize(device->bp_block)) + if (dasd_check_blocksize(block->bp_block)) seq_printf(m, "n/f "); else seq_printf(m, "at blocksize: %d, %ld blocks, %ld MB", - device->bp_block, device->blocks, - ((device->bp_block >> 9) * - device->blocks) >> 11); + block->bp_block, block->blocks, + ((block->bp_block >> 9) * + block->blocks) >> 11); break; default: seq_printf(m, "no stat"); @@ -137,7 +142,7 @@ static void dasd_devices_stop(struct seq_file *m, void *v) { } -static struct seq_operations dasd_devices_seq_ops = { +static const struct seq_operations dasd_devices_seq_ops = { .start = dasd_devices_start, .next = dasd_devices_next, .stop = dasd_devices_stop, diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 15a5789b773..7779bfce1c3 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -82,7 +82,7 @@ struct dcssblk_dev_info { struct request_queue *dcssblk_queue; }; -static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices); +static LIST_HEAD(dcssblk_devices); static struct rw_semaphore dcssblk_devices_sem; /* diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 130de19916f..7e73e39a174 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,7 +3,7 @@ # obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ - sclp_info.o sclp_config.o sclp_chp.o + sclp_cmd.o sclp_config.o sclp_cpi_sys.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index 20442fbf934..a86c0534cd4 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -295,7 +295,7 @@ module_init(mon_init); module_exit(mon_exit); module_param_named(max_bufs, mon_max_bufs, int, 0644); -MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers" +MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers " "that can be active at one time"); MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>"); diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 8d1c64a24de..0d98f1ff2ed 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -66,7 +66,7 @@ struct raw3270 { static DEFINE_MUTEX(raw3270_mutex); /* List of 3270 devices. */ -static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices); +static LIST_HEAD(raw3270_devices); /* * Flag to indicate if the driver has been registered. Some operations @@ -1210,7 +1210,7 @@ struct raw3270_notifier { void (*notifier)(int, int); }; -static struct list_head raw3270_notifier = LIST_HEAD_INIT(raw3270_notifier); +static LIST_HEAD(raw3270_notifier); int raw3270_register_notifier(void (*notifier)(int, int)) { diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index c7318a12585..aa8186d18ae 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -56,8 +56,6 @@ typedef unsigned int sclp_cmdw_t; #define SCLP_CMDW_READ_EVENT_DATA 0x00770005 #define SCLP_CMDW_WRITE_EVENT_DATA 0x00760005 #define SCLP_CMDW_WRITE_EVENT_MASK 0x00780005 -#define SCLP_CMDW_READ_SCP_INFO 0x00020001 -#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 #define GDS_ID_MDSMU 0x1310 #define GDS_ID_MDSROUTEINFO 0x1311 @@ -83,6 +81,8 @@ extern u64 sclp_facilities; #define SCLP_HAS_CHP_INFO (sclp_facilities & 0x8000000000000000ULL) #define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL) +#define SCLP_HAS_CPU_INFO (sclp_facilities & 0x0800000000000000ULL) +#define SCLP_HAS_CPU_RECONFIG (sclp_facilities & 0x0400000000000000ULL) struct gds_subvector { u8 length; diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c deleted file mode 100644 index c68f5e7e63a..00000000000 --- a/drivers/s390/char/sclp_chp.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * drivers/s390/char/sclp_chp.c - * - * Copyright IBM Corp. 2007 - * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> - */ - -#include <linux/types.h> -#include <linux/gfp.h> -#include <linux/errno.h> -#include <linux/completion.h> -#include <asm/sclp.h> -#include <asm/chpid.h> - -#include "sclp.h" - -#define TAG "sclp_chp: " - -#define SCLP_CMDW_CONFIGURE_CHANNEL_PATH 0x000f0001 -#define SCLP_CMDW_DECONFIGURE_CHANNEL_PATH 0x000e0001 -#define SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION 0x00030001 - -static inline sclp_cmdw_t get_configure_cmdw(struct chp_id chpid) -{ - return SCLP_CMDW_CONFIGURE_CHANNEL_PATH | chpid.id << 8; -} - -static inline sclp_cmdw_t get_deconfigure_cmdw(struct chp_id chpid) -{ - return SCLP_CMDW_DECONFIGURE_CHANNEL_PATH | chpid.id << 8; -} - -static void chp_callback(struct sclp_req *req, void *data) -{ - struct completion *completion = data; - - complete(completion); -} - -struct chp_cfg_sccb { - struct sccb_header header; - u8 ccm; - u8 reserved[6]; - u8 cssid; -} __attribute__((packed)); - -struct chp_cfg_data { - struct chp_cfg_sccb sccb; - struct sclp_req req; - struct completion completion; -} __attribute__((packed)); - -static int do_configure(sclp_cmdw_t cmd) -{ - struct chp_cfg_data *data; - int rc; - - if (!SCLP_HAS_CHP_RECONFIG) - return -EOPNOTSUPP; - /* Prepare sccb. */ - data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!data) - return -ENOMEM; - data->sccb.header.length = sizeof(struct chp_cfg_sccb); - data->req.command = cmd; - data->req.sccb = &(data->sccb); - data->req.status = SCLP_REQ_FILLED; - data->req.callback = chp_callback; - data->req.callback_data = &(data->completion); - init_completion(&data->completion); - - /* Perform sclp request. */ - rc = sclp_add_request(&(data->req)); - if (rc) - goto out; - wait_for_completion(&data->completion); - - /* Check response .*/ - if (data->req.status != SCLP_REQ_DONE) { - printk(KERN_WARNING TAG "configure channel-path request failed " - "(status=0x%02x)\n", data->req.status); - rc = -EIO; - goto out; - } - switch (data->sccb.header.response_code) { - case 0x0020: - case 0x0120: - case 0x0440: - case 0x0450: - break; - default: - printk(KERN_WARNING TAG "configure channel-path failed " - "(cmd=0x%08x, response=0x%04x)\n", cmd, - data->sccb.header.response_code); - rc = -EIO; - break; - } -out: - free_page((unsigned long) data); - - return rc; -} - -/** - * sclp_chp_configure - perform configure channel-path sclp command - * @chpid: channel-path ID - * - * Perform configure channel-path command sclp command for specified chpid. - * Return 0 after command successfully finished, non-zero otherwise. - */ -int sclp_chp_configure(struct chp_id chpid) -{ - return do_configure(get_configure_cmdw(chpid)); -} - -/** - * sclp_chp_deconfigure - perform deconfigure channel-path sclp command - * @chpid: channel-path ID - * - * Perform deconfigure channel-path command sclp command for specified chpid - * and wait for completion. On success return 0. Return non-zero otherwise. - */ -int sclp_chp_deconfigure(struct chp_id chpid) -{ - return do_configure(get_deconfigure_cmdw(chpid)); -} - -struct chp_info_sccb { - struct sccb_header header; - u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; - u8 standby[SCLP_CHP_INFO_MASK_SIZE]; - u8 configured[SCLP_CHP_INFO_MASK_SIZE]; - u8 ccm; - u8 reserved[6]; - u8 cssid; -} __attribute__((packed)); - -struct chp_info_data { - struct chp_info_sccb sccb; - struct sclp_req req; - struct completion completion; -} __attribute__((packed)); - -/** - * sclp_chp_read_info - perform read channel-path information sclp command - * @info: resulting channel-path information data - * - * Perform read channel-path information sclp command and wait for completion. - * On success, store channel-path information in @info and return 0. Return - * non-zero otherwise. - */ -int sclp_chp_read_info(struct sclp_chp_info *info) -{ - struct chp_info_data *data; - int rc; - - if (!SCLP_HAS_CHP_INFO) - return -EOPNOTSUPP; - /* Prepare sccb. */ - data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!data) - return -ENOMEM; - data->sccb.header.length = sizeof(struct chp_info_sccb); - data->req.command = SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION; - data->req.sccb = &(data->sccb); - data->req.status = SCLP_REQ_FILLED; - data->req.callback = chp_callback; - data->req.callback_data = &(data->completion); - init_completion(&data->completion); - - /* Perform sclp request. */ - rc = sclp_add_request(&(data->req)); - if (rc) - goto out; - wait_for_completion(&data->completion); - - /* Check response .*/ - if (data->req.status != SCLP_REQ_DONE) { - printk(KERN_WARNING TAG "read channel-path info request failed " - "(status=0x%02x)\n", data->req.status); - rc = -EIO; - goto out; - } - if (data->sccb.header.response_code != 0x0010) { - printk(KERN_WARNING TAG "read channel-path info failed " - "(response=0x%04x)\n", data->sccb.header.response_code); - rc = -EIO; - goto out; - } - memcpy(info->recognized, data->sccb.recognized, - SCLP_CHP_INFO_MASK_SIZE); - memcpy(info->standby, data->sccb.standby, - SCLP_CHP_INFO_MASK_SIZE); - memcpy(info->configured, data->sccb.configured, - SCLP_CHP_INFO_MASK_SIZE); -out: - free_page((unsigned long) data); - - return rc; -} diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c new file mode 100644 index 00000000000..b5c23396f8f --- /dev/null +++ b/drivers/s390/char/sclp_cmd.c @@ -0,0 +1,398 @@ +/* + * drivers/s390/char/sclp_cmd.c + * + * Copyright IBM Corp. 2007 + * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, + * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#include <linux/completion.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <asm/chpid.h> +#include <asm/sclp.h> +#include "sclp.h" + +#define TAG "sclp_cmd: " + +#define SCLP_CMDW_READ_SCP_INFO 0x00020001 +#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 + +struct read_info_sccb { + struct sccb_header header; /* 0-7 */ + u16 rnmax; /* 8-9 */ + u8 rnsize; /* 10 */ + u8 _reserved0[24 - 11]; /* 11-15 */ + u8 loadparm[8]; /* 24-31 */ + u8 _reserved1[48 - 32]; /* 32-47 */ + u64 facilities; /* 48-55 */ + u8 _reserved2[84 - 56]; /* 56-83 */ + u8 fac84; /* 84 */ + u8 _reserved3[91 - 85]; /* 85-90 */ + u8 flags; /* 91 */ + u8 _reserved4[100 - 92]; /* 92-99 */ + u32 rnsize2; /* 100-103 */ + u64 rnmax2; /* 104-111 */ + u8 _reserved5[4096 - 112]; /* 112-4095 */ +} __attribute__((packed, aligned(PAGE_SIZE))); + +static struct read_info_sccb __initdata early_read_info_sccb; +static int __initdata early_read_info_sccb_valid; + +u64 sclp_facilities; +static u8 sclp_fac84; + +static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb) +{ + int rc; + + __ctl_set_bit(0, 9); + rc = sclp_service_call(cmd, sccb); + if (rc) + goto out; + __load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT | + PSW_MASK_WAIT | PSW_DEFAULT_KEY); + local_irq_disable(); +out: + /* Contents of the sccb might have changed. */ + barrier(); + __ctl_clear_bit(0, 9); + return rc; +} + +void __init sclp_read_info_early(void) +{ + int rc; + int i; + struct read_info_sccb *sccb; + sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, + SCLP_CMDW_READ_SCP_INFO}; + + sccb = &early_read_info_sccb; + for (i = 0; i < ARRAY_SIZE(commands); i++) { + do { + memset(sccb, 0, sizeof(*sccb)); + sccb->header.length = sizeof(*sccb); + sccb->header.control_mask[2] = 0x80; + rc = sclp_cmd_sync_early(commands[i], sccb); + } while (rc == -EBUSY); + + if (rc) + break; + if (sccb->header.response_code == 0x10) { + early_read_info_sccb_valid = 1; + break; + } + if (sccb->header.response_code != 0x1f0) + break; + } +} + +void __init sclp_facilities_detect(void) +{ + if (!early_read_info_sccb_valid) + return; + sclp_facilities = early_read_info_sccb.facilities; + sclp_fac84 = early_read_info_sccb.fac84; +} + +unsigned long long __init sclp_memory_detect(void) +{ + unsigned long long memsize; + struct read_info_sccb *sccb; + + if (!early_read_info_sccb_valid) + return 0; + sccb = &early_read_info_sccb; + if (sccb->rnsize) + memsize = sccb->rnsize << 20; + else + memsize = sccb->rnsize2 << 20; + if (sccb->rnmax) + memsize *= sccb->rnmax; + else + memsize *= sccb->rnmax2; + return memsize; +} + +/* + * This function will be called after sclp_memory_detect(), which gets called + * early from early.c code. Therefore the sccb should have valid contents. + */ +void __init sclp_get_ipl_info(struct sclp_ipl_info *info) +{ + struct read_info_sccb *sccb; + + if (!early_read_info_sccb_valid) + return; + sccb = &early_read_info_sccb; + info->is_valid = 1; + if (sccb->flags & 0x2) + info->has_dump = 1; + memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN); +} + +static void sclp_sync_callback(struct sclp_req *req, void *data) +{ + struct completion *completion = data; + + complete(completion); +} + +static int do_sync_request(sclp_cmdw_t cmd, void *sccb) +{ + struct completion completion; + struct sclp_req *request; + int rc; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + request->command = cmd; + request->sccb = sccb; + request->status = SCLP_REQ_FILLED; + request->callback = sclp_sync_callback; + request->callback_data = &completion; + init_completion(&completion); + + /* Perform sclp request. */ + rc = sclp_add_request(request); + if (rc) + goto out; + wait_for_completion(&completion); + + /* Check response. */ + if (request->status != SCLP_REQ_DONE) { + printk(KERN_WARNING TAG "sync request failed " + "(cmd=0x%08x, status=0x%02x)\n", cmd, request->status); + rc = -EIO; + } +out: + kfree(request); + return rc; +} + +/* + * CPU configuration related functions. + */ + +#define SCLP_CMDW_READ_CPU_INFO 0x00010001 +#define SCLP_CMDW_CONFIGURE_CPU 0x00110001 +#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001 + +struct read_cpu_info_sccb { + struct sccb_header header; + u16 nr_configured; + u16 offset_configured; + u16 nr_standby; + u16 offset_standby; + u8 reserved[4096 - 16]; +} __attribute__((packed, aligned(PAGE_SIZE))); + +static void sclp_fill_cpu_info(struct sclp_cpu_info *info, + struct read_cpu_info_sccb *sccb) +{ + char *page = (char *) sccb; + + memset(info, 0, sizeof(*info)); + info->configured = sccb->nr_configured; + info->standby = sccb->nr_standby; + info->combined = sccb->nr_configured + sccb->nr_standby; + info->has_cpu_type = sclp_fac84 & 0x1; + memcpy(&info->cpu, page + sccb->offset_configured, + info->combined * sizeof(struct sclp_cpu_entry)); +} + +int sclp_get_cpu_info(struct sclp_cpu_info *info) +{ + int rc; + struct read_cpu_info_sccb *sccb; + + if (!SCLP_HAS_CPU_INFO) + return -EOPNOTSUPP; + sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + sccb->header.length = sizeof(*sccb); + rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb); + if (rc) + goto out; + if (sccb->header.response_code != 0x0010) { + printk(KERN_WARNING TAG "readcpuinfo failed " + "(response=0x%04x)\n", sccb->header.response_code); + rc = -EIO; + goto out; + } + sclp_fill_cpu_info(info, sccb); +out: + free_page((unsigned long) sccb); + return rc; +} + +struct cpu_configure_sccb { + struct sccb_header header; +} __attribute__((packed, aligned(8))); + +static int do_cpu_configure(sclp_cmdw_t cmd) +{ + struct cpu_configure_sccb *sccb; + int rc; + + if (!SCLP_HAS_CPU_RECONFIG) + return -EOPNOTSUPP; + /* + * This is not going to cross a page boundary since we force + * kmalloc to have a minimum alignment of 8 bytes on s390. + */ + sccb = kzalloc(sizeof(*sccb), GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + sccb->header.length = sizeof(*sccb); + rc = do_sync_request(cmd, sccb); + if (rc) + goto out; + switch (sccb->header.response_code) { + case 0x0020: + case 0x0120: + break; + default: + printk(KERN_WARNING TAG "configure cpu failed (cmd=0x%08x, " + "response=0x%04x)\n", cmd, sccb->header.response_code); + rc = -EIO; + break; + } +out: + kfree(sccb); + return rc; +} + +int sclp_cpu_configure(u8 cpu) +{ + return do_cpu_configure(SCLP_CMDW_CONFIGURE_CPU | cpu << 8); +} + +int sclp_cpu_deconfigure(u8 cpu) +{ + return do_cpu_configure(SCLP_CMDW_DECONFIGURE_CPU | cpu << 8); +} + +/* + * Channel path configuration related functions. + */ + +#define SCLP_CMDW_CONFIGURE_CHPATH 0x000f0001 +#define SCLP_CMDW_DECONFIGURE_CHPATH 0x000e0001 +#define SCLP_CMDW_READ_CHPATH_INFORMATION 0x00030001 + +struct chp_cfg_sccb { + struct sccb_header header; + u8 ccm; + u8 reserved[6]; + u8 cssid; +} __attribute__((packed)); + +static int do_chp_configure(sclp_cmdw_t cmd) +{ + struct chp_cfg_sccb *sccb; + int rc; + + if (!SCLP_HAS_CHP_RECONFIG) + return -EOPNOTSUPP; + /* Prepare sccb. */ + sccb = (struct chp_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + sccb->header.length = sizeof(*sccb); + rc = do_sync_request(cmd, sccb); + if (rc) + goto out; + switch (sccb->header.response_code) { + case 0x0020: + case 0x0120: + case 0x0440: + case 0x0450: + break; + default: + printk(KERN_WARNING TAG "configure channel-path failed " + "(cmd=0x%08x, response=0x%04x)\n", cmd, + sccb->header.response_code); + rc = -EIO; + break; + } +out: + free_page((unsigned long) sccb); + return rc; +} + +/** + * sclp_chp_configure - perform configure channel-path sclp command + * @chpid: channel-path ID + * + * Perform configure channel-path command sclp command for specified chpid. + * Return 0 after command successfully finished, non-zero otherwise. + */ +int sclp_chp_configure(struct chp_id chpid) +{ + return do_chp_configure(SCLP_CMDW_CONFIGURE_CHPATH | chpid.id << 8); +} + +/** + * sclp_chp_deconfigure - perform deconfigure channel-path sclp command + * @chpid: channel-path ID + * + * Perform deconfigure channel-path command sclp command for specified chpid + * and wait for completion. On success return 0. Return non-zero otherwise. + */ +int sclp_chp_deconfigure(struct chp_id chpid) +{ + return do_chp_configure(SCLP_CMDW_DECONFIGURE_CHPATH | chpid.id << 8); +} + +struct chp_info_sccb { + struct sccb_header header; + u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; + u8 standby[SCLP_CHP_INFO_MASK_SIZE]; + u8 configured[SCLP_CHP_INFO_MASK_SIZE]; + u8 ccm; + u8 reserved[6]; + u8 cssid; +} __attribute__((packed)); + +/** + * sclp_chp_read_info - perform read channel-path information sclp command + * @info: resulting channel-path information data + * + * Perform read channel-path information sclp command and wait for completion. + * On success, store channel-path information in @info and return 0. Return + * non-zero otherwise. + */ +int sclp_chp_read_info(struct sclp_chp_info *info) +{ + struct chp_info_sccb *sccb; + int rc; + + if (!SCLP_HAS_CHP_INFO) + return -EOPNOTSUPP; + /* Prepare sccb. */ + sccb = (struct chp_info_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + sccb->header.length = sizeof(*sccb); + rc = do_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb); + if (rc) + goto out; + if (sccb->header.response_code != 0x0010) { + printk(KERN_WARNING TAG "read channel-path info failed " + "(response=0x%04x)\n", sccb->header.response_code); + rc = -EIO; + goto out; + } + memcpy(info->recognized, sccb->recognized, SCLP_CHP_INFO_MASK_SIZE); + memcpy(info->standby, sccb->standby, SCLP_CHP_INFO_MASK_SIZE); + memcpy(info->configured, sccb->configured, SCLP_CHP_INFO_MASK_SIZE); +out: + free_page((unsigned long) sccb); + return rc; +} diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c index 82a13d9fdfe..5716487b8c9 100644 --- a/drivers/s390/char/sclp_cpi.c +++ b/drivers/s390/char/sclp_cpi.c @@ -1,255 +1,41 @@ /* - * Author: Martin Peschke <mpeschke@de.ibm.com> - * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation + * drivers/s390/char/sclp_cpi.c + * SCLP control programm identification * - * SCLP Control-Program Identification. + * Copyright IBM Corp. 2001, 2007 + * Author(s): Martin Peschke <mpeschke@de.ibm.com> + * Michael Ernst <mernst@de.ibm.com> */ -#include <linux/version.h> #include <linux/kmod.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <asm/ebcdic.h> -#include <asm/semaphore.h> - -#include "sclp.h" -#include "sclp_rw.h" - -#define CPI_LENGTH_SYSTEM_TYPE 8 -#define CPI_LENGTH_SYSTEM_NAME 8 -#define CPI_LENGTH_SYSPLEX_NAME 8 - -struct cpi_evbuf { - struct evbuf_header header; - u8 id_format; - u8 reserved0; - u8 system_type[CPI_LENGTH_SYSTEM_TYPE]; - u64 reserved1; - u8 system_name[CPI_LENGTH_SYSTEM_NAME]; - u64 reserved2; - u64 system_level; - u64 reserved3; - u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME]; - u8 reserved4[16]; -} __attribute__((packed)); - -struct cpi_sccb { - struct sccb_header header; - struct cpi_evbuf cpi_evbuf; -} __attribute__((packed)); - -/* Event type structure for write message and write priority message */ -static struct sclp_register sclp_cpi_event = -{ - .send_mask = EVTYP_CTLPROGIDENT_MASK -}; +#include <linux/version.h> +#include "sclp_cpi_sys.h" MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Identify this operating system instance " + "to the System z hardware"); +MODULE_AUTHOR("Martin Peschke <mpeschke@de.ibm.com>, " + "Michael Ernst <mernst@de.ibm.com>"); -MODULE_AUTHOR( - "Martin Peschke, IBM Deutschland Entwicklung GmbH " - "<mpeschke@de.ibm.com>"); - -MODULE_DESCRIPTION( - "identify this operating system instance to the S/390 " - "or zSeries hardware"); +static char *system_name = ""; +static char *sysplex_name = ""; -static char *system_name = NULL; module_param(system_name, charp, 0); MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters"); - -static char *sysplex_name = NULL; -#ifdef ALLOW_SYSPLEX_NAME module_param(sysplex_name, charp, 0); MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters"); -#endif - -/* use default value for this field (as well as for system level) */ -static char *system_type = "LINUX"; -static int -cpi_check_parms(void) +static int __init cpi_module_init(void) { - /* reject if no system type specified */ - if (!system_type) { - printk("cpi: bug: no system type specified\n"); - return -EINVAL; - } - - /* reject if system type larger than 8 characters */ - if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) { - printk("cpi: bug: system type has length of %li characters - " - "only %i characters supported\n", - strlen(system_type), CPI_LENGTH_SYSTEM_TYPE); - return -EINVAL; - } - - /* reject if no system name specified */ - if (!system_name) { - printk("cpi: no system name specified\n"); - return -EINVAL; - } - - /* reject if system name larger than 8 characters */ - if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) { - printk("cpi: system name has length of %li characters - " - "only %i characters supported\n", - strlen(system_name), CPI_LENGTH_SYSTEM_NAME); - return -EINVAL; - } - - /* reject if specified sysplex name larger than 8 characters */ - if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) { - printk("cpi: sysplex name has length of %li characters" - " - only %i characters supported\n", - strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME); - return -EINVAL; - } - return 0; + return sclp_cpi_set_data(system_name, sysplex_name, "LINUX", + LINUX_VERSION_CODE); } -static void -cpi_callback(struct sclp_req *req, void *data) -{ - struct semaphore *sem; - - sem = (struct semaphore *) data; - up(sem); -} - -static struct sclp_req * -cpi_prepare_req(void) -{ - struct sclp_req *req; - struct cpi_sccb *sccb; - struct cpi_evbuf *evb; - - req = kmalloc(sizeof(struct sclp_req), GFP_KERNEL); - if (req == NULL) - return ERR_PTR(-ENOMEM); - sccb = (struct cpi_sccb *) __get_free_page(GFP_KERNEL | GFP_DMA); - if (sccb == NULL) { - kfree(req); - return ERR_PTR(-ENOMEM); - } - memset(sccb, 0, sizeof(struct cpi_sccb)); - - /* setup SCCB for Control-Program Identification */ - sccb->header.length = sizeof(struct cpi_sccb); - sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); - sccb->cpi_evbuf.header.type = 0x0B; - evb = &sccb->cpi_evbuf; - - /* set system type */ - memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE); - memcpy(evb->system_type, system_type, strlen(system_type)); - sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); - EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); - - /* set system name */ - memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME); - memcpy(evb->system_name, system_name, strlen(system_name)); - sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME); - EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME); - - /* set system level */ - evb->system_level = LINUX_VERSION_CODE; - - /* set sysplex name */ - if (sysplex_name) { - memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME); - memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name)); - sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); - EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); - } - - /* prepare request data structure presented to SCLP driver */ - req->command = SCLP_CMDW_WRITE_EVENT_DATA; - req->sccb = sccb; - req->status = SCLP_REQ_FILLED; - req->callback = cpi_callback; - return req; -} - -static void -cpi_free_req(struct sclp_req *req) -{ - free_page((unsigned long) req->sccb); - kfree(req); -} - -static int __init -cpi_module_init(void) -{ - struct semaphore sem; - struct sclp_req *req; - int rc; - - rc = cpi_check_parms(); - if (rc) - return rc; - - rc = sclp_register(&sclp_cpi_event); - if (rc) { - /* could not register sclp event. Die. */ - printk(KERN_WARNING "cpi: could not register to hardware " - "console.\n"); - return -EINVAL; - } - if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) { - printk(KERN_WARNING "cpi: no control program identification " - "support\n"); - sclp_unregister(&sclp_cpi_event); - return -EOPNOTSUPP; - } - - req = cpi_prepare_req(); - if (IS_ERR(req)) { - printk(KERN_WARNING "cpi: couldn't allocate request\n"); - sclp_unregister(&sclp_cpi_event); - return PTR_ERR(req); - } - - /* Prepare semaphore */ - sema_init(&sem, 0); - req->callback_data = &sem; - /* Add request to sclp queue */ - rc = sclp_add_request(req); - if (rc) { - printk(KERN_WARNING "cpi: could not start request\n"); - cpi_free_req(req); - sclp_unregister(&sclp_cpi_event); - return rc; - } - /* make "insmod" sleep until callback arrives */ - down(&sem); - - rc = ((struct cpi_sccb *) req->sccb)->header.response_code; - if (rc != 0x0020) { - printk(KERN_WARNING "cpi: failed with response code 0x%x\n", - rc); - rc = -ECOMM; - } else - rc = 0; - - cpi_free_req(req); - sclp_unregister(&sclp_cpi_event); - - return rc; -} - - static void __exit cpi_module_exit(void) { } - -/* declare driver module init/cleanup functions */ module_init(cpi_module_init); module_exit(cpi_module_exit); - diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c new file mode 100644 index 00000000000..41617032afd --- /dev/null +++ b/drivers/s390/char/sclp_cpi_sys.c @@ -0,0 +1,400 @@ +/* + * drivers/s390/char/sclp_cpi_sys.c + * SCLP control program identification sysfs interface + * + * Copyright IBM Corp. 2001, 2007 + * Author(s): Martin Peschke <mpeschke@de.ibm.com> + * Michael Ernst <mernst@de.ibm.com> + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/stat.h> +#include <linux/device.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/kmod.h> +#include <linux/timer.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/completion.h> +#include <asm/ebcdic.h> +#include <asm/sclp.h> +#include "sclp.h" +#include "sclp_rw.h" +#include "sclp_cpi_sys.h" + +#define CPI_LENGTH_NAME 8 +#define CPI_LENGTH_LEVEL 16 + +struct cpi_evbuf { + struct evbuf_header header; + u8 id_format; + u8 reserved0; + u8 system_type[CPI_LENGTH_NAME]; + u64 reserved1; + u8 system_name[CPI_LENGTH_NAME]; + u64 reserved2; + u64 system_level; + u64 reserved3; + u8 sysplex_name[CPI_LENGTH_NAME]; + u8 reserved4[16]; +} __attribute__((packed)); + +struct cpi_sccb { + struct sccb_header header; + struct cpi_evbuf cpi_evbuf; +} __attribute__((packed)); + +static struct sclp_register sclp_cpi_event = { + .send_mask = EVTYP_CTLPROGIDENT_MASK, +}; + +static char system_name[CPI_LENGTH_NAME + 1]; +static char sysplex_name[CPI_LENGTH_NAME + 1]; +static char system_type[CPI_LENGTH_NAME + 1]; +static u64 system_level; + +static void set_data(char *field, char *data) +{ + memset(field, ' ', CPI_LENGTH_NAME); + memcpy(field, data, strlen(data)); + sclp_ascebc_str(field, CPI_LENGTH_NAME); +} + +static void cpi_callback(struct sclp_req *req, void *data) +{ + struct completion *completion = data; + + complete(completion); +} + +static struct sclp_req *cpi_prepare_req(void) +{ + struct sclp_req *req; + struct cpi_sccb *sccb; + struct cpi_evbuf *evb; + + req = kzalloc(sizeof(struct sclp_req), GFP_KERNEL); + if (!req) + return ERR_PTR(-ENOMEM); + sccb = (struct cpi_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) { + kfree(req); + return ERR_PTR(-ENOMEM); + } + + /* setup SCCB for Control-Program Identification */ + sccb->header.length = sizeof(struct cpi_sccb); + sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); + sccb->cpi_evbuf.header.type = 0x0b; + evb = &sccb->cpi_evbuf; + + /* set system type */ + set_data(evb->system_type, system_type); + + /* set system name */ + set_data(evb->system_name, system_name); + + /* set sytem level */ + evb->system_level = system_level; + + /* set sysplex name */ + set_data(evb->sysplex_name, sysplex_name); + + /* prepare request data structure presented to SCLP driver */ + req->command = SCLP_CMDW_WRITE_EVENT_DATA; + req->sccb = sccb; + req->status = SCLP_REQ_FILLED; + req->callback = cpi_callback; + return req; +} + +static void cpi_free_req(struct sclp_req *req) +{ + free_page((unsigned long) req->sccb); + kfree(req); +} + +static int cpi_req(void) +{ + struct completion completion; + struct sclp_req *req; + int rc; + int response; + + rc = sclp_register(&sclp_cpi_event); + if (rc) { + printk(KERN_WARNING "cpi: could not register " + "to hardware console.\n"); + goto out; + } + if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) { + printk(KERN_WARNING "cpi: no control program " + "identification support\n"); + rc = -EOPNOTSUPP; + goto out_unregister; + } + + req = cpi_prepare_req(); + if (IS_ERR(req)) { + printk(KERN_WARNING "cpi: could not allocate request\n"); + rc = PTR_ERR(req); + goto out_unregister; + } + + init_completion(&completion); + req->callback_data = &completion; + + /* Add request to sclp queue */ + rc = sclp_add_request(req); + if (rc) { + printk(KERN_WARNING "cpi: could not start request\n"); + goto out_free_req; + } + + wait_for_completion(&completion); + + if (req->status != SCLP_REQ_DONE) { + printk(KERN_WARNING "cpi: request failed (status=0x%02x)\n", + req->status); + rc = -EIO; + goto out_free_req; + } + + response = ((struct cpi_sccb *) req->sccb)->header.response_code; + if (response != 0x0020) { + printk(KERN_WARNING "cpi: failed with " + "response code 0x%x\n", response); + rc = -EIO; + } + +out_free_req: + cpi_free_req(req); + +out_unregister: + sclp_unregister(&sclp_cpi_event); + +out: + return rc; +} + +static int check_string(const char *attr, const char *str) +{ + size_t len; + size_t i; + + len = strlen(str); + + if ((len > 0) && (str[len - 1] == '\n')) + len--; + + if (len > CPI_LENGTH_NAME) + return -EINVAL; + + for (i = 0; i < len ; i++) { + if (isalpha(str[i]) || isdigit(str[i]) || + strchr("$@# ", str[i])) + continue; + return -EINVAL; + } + + return 0; +} + +static void set_string(char *attr, const char *value) +{ + size_t len; + size_t i; + + len = strlen(value); + + if ((len > 0) && (value[len - 1] == '\n')) + len--; + + for (i = 0; i < CPI_LENGTH_NAME; i++) { + if (i < len) + attr[i] = toupper(value[i]); + else + attr[i] = ' '; + } +} + +static ssize_t system_name_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return snprintf(page, PAGE_SIZE, "%s\n", system_name); +} + +static ssize_t system_name_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t len) +{ + int rc; + + rc = check_string("system_name", buf); + if (rc) + return rc; + + set_string(system_name, buf); + + return len; +} + +static struct kobj_attribute system_name_attr = + __ATTR(system_name, 0644, system_name_show, system_name_store); + +static ssize_t sysplex_name_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return snprintf(page, PAGE_SIZE, "%s\n", sysplex_name); +} + +static ssize_t sysplex_name_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t len) +{ + int rc; + + rc = check_string("sysplex_name", buf); + if (rc) + return rc; + + set_string(sysplex_name, buf); + + return len; +} + +static struct kobj_attribute sysplex_name_attr = + __ATTR(sysplex_name, 0644, sysplex_name_show, sysplex_name_store); + +static ssize_t system_type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return snprintf(page, PAGE_SIZE, "%s\n", system_type); +} + +static ssize_t system_type_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t len) +{ + int rc; + + rc = check_string("system_type", buf); + if (rc) + return rc; + + set_string(system_type, buf); + + return len; +} + +static struct kobj_attribute system_type_attr = + __ATTR(system_type, 0644, system_type_show, system_type_store); + +static ssize_t system_level_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + unsigned long long level = system_level; + + return snprintf(page, PAGE_SIZE, "%#018llx\n", level); +} + +static ssize_t system_level_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t len) +{ + unsigned long long level; + char *endp; + + level = simple_strtoull(buf, &endp, 16); + + if (endp == buf) + return -EINVAL; + if (*endp == '\n') + endp++; + if (*endp) + return -EINVAL; + + system_level = level; + + return len; +} + +static struct kobj_attribute system_level_attr = + __ATTR(system_level, 0644, system_level_show, system_level_store); + +static ssize_t set_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + int rc; + + rc = cpi_req(); + if (rc) + return rc; + + return len; +} + +static struct kobj_attribute set_attr = __ATTR(set, 0200, NULL, set_store); + +static struct attribute *cpi_attrs[] = { + &system_name_attr.attr, + &sysplex_name_attr.attr, + &system_type_attr.attr, + &system_level_attr.attr, + &set_attr.attr, + NULL, +}; + +static struct attribute_group cpi_attr_group = { + .attrs = cpi_attrs, +}; + +static struct kset *cpi_kset; + +int sclp_cpi_set_data(const char *system, const char *sysplex, const char *type, + const u64 level) +{ + int rc; + + rc = check_string("system_name", system); + if (rc) + return rc; + rc = check_string("sysplex_name", sysplex); + if (rc) + return rc; + rc = check_string("system_type", type); + if (rc) + return rc; + + set_string(system_name, system); + set_string(sysplex_name, sysplex); + set_string(system_type, type); + system_level = level; + + return cpi_req(); +} +EXPORT_SYMBOL(sclp_cpi_set_data); + +static int __init cpi_init(void) +{ + int rc; + + cpi_kset = kset_create_and_add("cpi", NULL, firmware_kobj); + if (!cpi_kset) + return -ENOMEM; + + rc = sysfs_create_group(&cpi_kset->kobj, &cpi_attr_group); + if (rc) + kset_unregister(cpi_kset); + + return rc; +} + +__initcall(cpi_init); diff --git a/drivers/s390/char/sclp_cpi_sys.h b/drivers/s390/char/sclp_cpi_sys.h new file mode 100644 index 00000000000..deef3e6ff49 --- /dev/null +++ b/drivers/s390/char/sclp_cpi_sys.h @@ -0,0 +1,15 @@ +/* + * drivers/s390/char/sclp_cpi_sys.h + * SCLP control program identification sysfs interface + * + * Copyright IBM Corp. 2007 + * Author(s): Michael Ernst <mernst@de.ibm.com> + */ + +#ifndef __SCLP_CPI_SYS_H__ +#define __SCLP_CPI_SYS_H__ + +int sclp_cpi_set_data(const char *system, const char *sysplex, + const char *type, u64 level); + +#endif /* __SCLP_CPI_SYS_H__ */ diff --git a/drivers/s390/char/sclp_info.c b/drivers/s390/char/sclp_info.c deleted file mode 100644 index a1136e05275..00000000000 --- a/drivers/s390/char/sclp_info.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * drivers/s390/char/sclp_info.c - * - * Copyright IBM Corp. 2007 - * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> - */ - -#include <linux/init.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <asm/sclp.h> -#include "sclp.h" - -struct sclp_readinfo_sccb { - struct sccb_header header; /* 0-7 */ - u16 rnmax; /* 8-9 */ - u8 rnsize; /* 10 */ - u8 _reserved0[24 - 11]; /* 11-23 */ - u8 loadparm[8]; /* 24-31 */ - u8 _reserved1[48 - 32]; /* 32-47 */ - u64 facilities; /* 48-55 */ - u8 _reserved2[91 - 56]; /* 56-90 */ - u8 flags; /* 91 */ - u8 _reserved3[100 - 92]; /* 92-99 */ - u32 rnsize2; /* 100-103 */ - u64 rnmax2; /* 104-111 */ - u8 _reserved4[4096 - 112]; /* 112-4095 */ -} __attribute__((packed, aligned(4096))); - -static struct sclp_readinfo_sccb __initdata early_readinfo_sccb; -static int __initdata early_readinfo_sccb_valid; - -u64 sclp_facilities; - -void __init sclp_readinfo_early(void) -{ - int ret; - int i; - struct sclp_readinfo_sccb *sccb; - sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, - SCLP_CMDW_READ_SCP_INFO}; - - /* Enable service signal subclass mask. */ - __ctl_set_bit(0, 9); - sccb = &early_readinfo_sccb; - for (i = 0; i < ARRAY_SIZE(commands); i++) { - do { - memset(sccb, 0, sizeof(*sccb)); - sccb->header.length = sizeof(*sccb); - sccb->header.control_mask[2] = 0x80; - ret = sclp_service_call(commands[i], sccb); - } while (ret == -EBUSY); - - if (ret) - break; - __load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT | - PSW_MASK_WAIT | PSW_DEFAULT_KEY); - local_irq_disable(); - /* - * Contents of the sccb might have changed - * therefore a barrier is needed. - */ - barrier(); - if (sccb->header.response_code == 0x10) { - early_readinfo_sccb_valid = 1; - break; - } - if (sccb->header.response_code != 0x1f0) - break; - } - /* Disable service signal subclass mask again. */ - __ctl_clear_bit(0, 9); -} - -void __init sclp_facilities_detect(void) -{ - if (!early_readinfo_sccb_valid) - return; - sclp_facilities = early_readinfo_sccb.facilities; -} - -unsigned long long __init sclp_memory_detect(void) -{ - unsigned long long memsize; - struct sclp_readinfo_sccb *sccb; - - if (!early_readinfo_sccb_valid) - return 0; - sccb = &early_readinfo_sccb; - if (sccb->rnsize) - memsize = sccb->rnsize << 20; - else - memsize = sccb->rnsize2 << 20; - if (sccb->rnmax) - memsize *= sccb->rnmax; - else - memsize *= sccb->rnmax2; - return memsize; -} - -/* - * This function will be called after sclp_memory_detect(), which gets called - * early from early.c code. Therefore the sccb should have valid contents. - */ -void __init sclp_get_ipl_info(struct sclp_ipl_info *info) -{ - struct sclp_readinfo_sccb *sccb; - - if (!early_readinfo_sccb_valid) - return; - sccb = &early_readinfo_sccb; - info->is_valid = 1; - if (sccb->flags & 0x2) - info->has_dump = 1; - memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN); -} diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index d6b06ab8118..ad7195d3de0 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c @@ -76,7 +76,7 @@ sclp_make_buffer(void *page, unsigned short columns, unsigned short htab) } /* - * Return a pointer to the orignal page that has been used to create + * Return a pointer to the original page that has been used to create * the buffer. */ void * diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index da25f8e2415..8246ef3ab09 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -1495,7 +1495,7 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request, device->cdev->dev.bus_id); return tape_3590_erp_basic(device, request, irb, -EPERM); case 0x8013: - PRINT_WARN("(%s): Another host has priviliged access to the " + PRINT_WARN("(%s): Another host has privileged access to the " "tape device\n", device->cdev->dev.bus_id); PRINT_WARN("(%s): To solve the problem unload the current " "cartridge!\n", device->cdev->dev.bus_id); diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 2fae6338ee1..7ad8cf15764 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -37,7 +37,7 @@ static void tape_long_busy_timeout(unsigned long data); * we can assign the devices to minor numbers of the same major * The list is protected by the rwlock */ -static struct list_head tape_device_list = LIST_HEAD_INIT(tape_device_list); +static LIST_HEAD(tape_device_list); static DEFINE_RWLOCK(tape_device_lock); /* diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c index cea49f001f8..c9b96d51b28 100644 --- a/drivers/s390/char/tape_proc.c +++ b/drivers/s390/char/tape_proc.c @@ -97,7 +97,7 @@ static void tape_proc_stop(struct seq_file *m, void *v) { } -static struct seq_operations tape_proc_seq = { +static const struct seq_operations tape_proc_seq = { .start = tape_proc_start, .next = tape_proc_next, .stop = tape_proc_stop, diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index e0c4c508e12..d364e0bfae1 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -683,7 +683,7 @@ static int vmlogrdr_register_driver(void) /* Register with iucv driver */ ret = iucv_register(&vmlogrdr_iucv_handler, 1); if (ret) { - printk (KERN_ERR "vmlogrdr: failed to register with" + printk (KERN_ERR "vmlogrdr: failed to register with " "iucv driver\n"); goto out; } diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index d70a6e65bf1..7689b500a10 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -759,7 +759,7 @@ static loff_t ur_llseek(struct file *file, loff_t offset, int whence) return newpos; } -static struct file_operations ur_fops = { +static const struct file_operations ur_fops = { .owner = THIS_MODULE, .open = ur_open, .release = ur_release, diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 7073daf7798..f523501e6e6 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -470,7 +470,7 @@ static loff_t zcore_lseek(struct file *file, loff_t offset, int orig) return rc; } -static struct file_operations zcore_fops = { +static const struct file_operations zcore_fops = { .owner = THIS_MODULE, .llseek = zcore_lseek, .read = zcore_read, diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index 5287631fbfc..b7a07a86629 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c @@ -1,12 +1,12 @@ /* * drivers/s390/cio/airq.c - * S/390 common I/O routines -- support for adapter interruptions + * Support for adapter interruptions * - * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Ingo Adlung (adlung@de.ibm.com) - * Cornelia Huck (cornelia.huck@de.ibm.com) - * Arnd Bergmann (arndb@de.ibm.com) + * Copyright IBM Corp. 1999,2007 + * Author(s): Ingo Adlung <adlung@de.ibm.com> + * Cornelia Huck <cornelia.huck@de.ibm.com> + * Arnd Bergmann <arndb@de.ibm.com> + * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> */ #include <linux/init.h> @@ -14,72 +14,131 @@ #include <linux/slab.h> #include <linux/rcupdate.h> +#include <asm/airq.h> + +#include "cio.h" #include "cio_debug.h" -#include "airq.h" -static adapter_int_handler_t adapter_handler; +#define NR_AIRQS 32 +#define NR_AIRQS_PER_WORD sizeof(unsigned long) +#define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD) -/* - * register for adapter interrupts - * - * With HiperSockets the zSeries architecture provides for - * means of adapter interrups, pseudo I/O interrupts that are - * not tied to an I/O subchannel, but to an adapter. However, - * it doesn't disclose the info how to enable/disable them, but - * to recognize them only. Perhaps we should consider them - * being shared interrupts, and thus build a linked list - * of adapter handlers ... to be evaluated ... - */ -int -s390_register_adapter_interrupt (adapter_int_handler_t handler) -{ - int ret; - char dbf_txt[15]; +union indicator_t { + unsigned long word[NR_AIRQ_WORDS]; + unsigned char byte[NR_AIRQS]; +} __attribute__((packed)); - CIO_TRACE_EVENT (4, "rgaint"); +struct airq_t { + adapter_int_handler_t handler; + void *drv_data; +}; - if (handler == NULL) - ret = -EINVAL; - else - ret = (cmpxchg(&adapter_handler, NULL, handler) ? -EBUSY : 0); - if (!ret) - synchronize_sched(); /* Allow interrupts to complete. */ +static union indicator_t indicators; +static struct airq_t *airqs[NR_AIRQS]; - sprintf (dbf_txt, "ret:%d", ret); - CIO_TRACE_EVENT (4, dbf_txt); +static int register_airq(struct airq_t *airq) +{ + int i; - return ret; + for (i = 0; i < NR_AIRQS; i++) + if (!cmpxchg(&airqs[i], NULL, airq)) + return i; + return -ENOMEM; } -int -s390_unregister_adapter_interrupt (adapter_int_handler_t handler) +/** + * s390_register_adapter_interrupt() - register adapter interrupt handler + * @handler: adapter handler to be registered + * @drv_data: driver data passed with each call to the handler + * + * Returns: + * Pointer to the indicator to be used on success + * ERR_PTR() if registration failed + */ +void *s390_register_adapter_interrupt(adapter_int_handler_t handler, + void *drv_data) { + struct airq_t *airq; + char dbf_txt[16]; int ret; - char dbf_txt[15]; - CIO_TRACE_EVENT (4, "urgaint"); - - if (handler == NULL) - ret = -EINVAL; - else { - adapter_handler = NULL; - synchronize_sched(); /* Allow interrupts to complete. */ - ret = 0; + airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); + if (!airq) { + ret = -ENOMEM; + goto out; } - sprintf (dbf_txt, "ret:%d", ret); - CIO_TRACE_EVENT (4, dbf_txt); - - return ret; + airq->handler = handler; + airq->drv_data = drv_data; + ret = register_airq(airq); + if (ret < 0) + kfree(airq); +out: + snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret); + CIO_TRACE_EVENT(4, dbf_txt); + if (ret < 0) + return ERR_PTR(ret); + else + return &indicators.byte[ret]; } +EXPORT_SYMBOL(s390_register_adapter_interrupt); -void -do_adapter_IO (void) +/** + * s390_unregister_adapter_interrupt - unregister adapter interrupt handler + * @ind: indicator for which the handler is to be unregistered + */ +void s390_unregister_adapter_interrupt(void *ind) { - CIO_TRACE_EVENT (6, "doaio"); + struct airq_t *airq; + char dbf_txt[16]; + int i; - if (adapter_handler) - (*adapter_handler) (); + i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]); + snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); + CIO_TRACE_EVENT(4, dbf_txt); + indicators.byte[i] = 0; + airq = xchg(&airqs[i], NULL); + /* + * Allow interrupts to complete. This will ensure that the airq handle + * is no longer referenced by any interrupt handler. + */ + synchronize_sched(); + kfree(airq); } +EXPORT_SYMBOL(s390_unregister_adapter_interrupt); + +#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) -EXPORT_SYMBOL (s390_register_adapter_interrupt); -EXPORT_SYMBOL (s390_unregister_adapter_interrupt); +void do_adapter_IO(void) +{ + int w; + int i; + unsigned long word; + struct airq_t *airq; + + /* + * Access indicator array in word-sized chunks to minimize storage + * fetch operations. + */ + for (w = 0; w < NR_AIRQ_WORDS; w++) { + word = indicators.word[w]; + i = w * NR_AIRQS_PER_WORD; + /* + * Check bytes within word for active indicators. + */ + while (word) { + if (word & INDICATOR_MASK) { + airq = airqs[i]; + if (likely(airq)) + airq->handler(&indicators.byte[i], + airq->drv_data); + else + /* + * Reset ill-behaved indicator. + */ + indicators.byte[i] = 0; + } + word <<= 8; + i++; + } + } +} diff --git a/drivers/s390/cio/airq.h b/drivers/s390/cio/airq.h deleted file mode 100644 index 7d6be3fdcd6..00000000000 --- a/drivers/s390/cio/airq.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef S390_AINTERRUPT_H -#define S390_AINTERRUPT_H - -typedef int (*adapter_int_handler_t)(void); - -extern int s390_register_adapter_interrupt(adapter_int_handler_t handler); -extern int s390_unregister_adapter_interrupt(adapter_int_handler_t handler); -extern void do_adapter_IO (void); - -#endif diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index bd5f16f80bf..e8597ec9224 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -348,7 +348,7 @@ cio_ignore_write(struct file *file, const char __user *user_buf, return user_len; } -static struct seq_operations cio_ignore_proc_seq_ops = { +static const struct seq_operations cio_ignore_proc_seq_ops = { .start = cio_ignore_proc_seq_start, .stop = cio_ignore_proc_seq_stop, .next = cio_ignore_proc_seq_next, diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 5baa517c3b6..3964056a9a4 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -35,8 +35,8 @@ ccwgroup_bus_match (struct device * dev, struct device_driver * drv) struct ccwgroup_device *gdev; struct ccwgroup_driver *gdrv; - gdev = container_of(dev, struct ccwgroup_device, dev); - gdrv = container_of(drv, struct ccwgroup_driver, driver); + gdev = to_ccwgroupdev(dev); + gdrv = to_ccwgroupdrv(drv); if (gdev->creator_id == gdrv->driver_id) return 1; @@ -75,8 +75,10 @@ static void ccwgroup_ungroup_callback(struct device *dev) struct ccwgroup_device *gdev = to_ccwgroupdev(dev); mutex_lock(&gdev->reg_mutex); - __ccwgroup_remove_symlinks(gdev); - device_unregister(dev); + if (device_is_registered(&gdev->dev)) { + __ccwgroup_remove_symlinks(gdev); + device_unregister(dev); + } mutex_unlock(&gdev->reg_mutex); } @@ -111,7 +113,7 @@ ccwgroup_release (struct device *dev) gdev = to_ccwgroupdev(dev); for (i = 0; i < gdev->count; i++) { - gdev->cdev[i]->dev.driver_data = NULL; + dev_set_drvdata(&gdev->cdev[i]->dev, NULL); put_device(&gdev->cdev[i]->dev); } kfree(gdev); @@ -196,11 +198,11 @@ int ccwgroup_create(struct device *root, unsigned int creator_id, goto error; } /* Don't allow a device to belong to more than one group. */ - if (gdev->cdev[i]->dev.driver_data) { + if (dev_get_drvdata(&gdev->cdev[i]->dev)) { rc = -EINVAL; goto error; } - gdev->cdev[i]->dev.driver_data = gdev; + dev_set_drvdata(&gdev->cdev[i]->dev, gdev); } gdev->creator_id = creator_id; @@ -234,8 +236,8 @@ int ccwgroup_create(struct device *root, unsigned int creator_id, error: for (i = 0; i < argc; i++) if (gdev->cdev[i]) { - if (gdev->cdev[i]->dev.driver_data == gdev) - gdev->cdev[i]->dev.driver_data = NULL; + if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) + dev_set_drvdata(&gdev->cdev[i]->dev, NULL); put_device(&gdev->cdev[i]->dev); } mutex_unlock(&gdev->reg_mutex); @@ -408,6 +410,7 @@ int ccwgroup_driver_register(struct ccwgroup_driver *cdriver) /* register our new driver with the core */ cdriver->driver.bus = &ccwgroup_bus_type; cdriver->driver.name = cdriver->name; + cdriver->driver.owner = cdriver->owner; return driver_register(&cdriver->driver); } @@ -463,8 +466,8 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev) { struct ccwgroup_device *gdev; - if (cdev->dev.driver_data) { - gdev = (struct ccwgroup_device *)cdev->dev.driver_data; + gdev = dev_get_drvdata(&cdev->dev); + if (gdev) { if (get_device(&gdev->dev)) { mutex_lock(&gdev->reg_mutex); if (device_is_registered(&gdev->dev)) diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 597c0c76a2a..e7ba16a74ef 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -89,7 +89,8 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) /* Copy data */ ret = 0; memset(ssd, 0, sizeof(struct chsc_ssd_info)); - if ((ssd_area->st != 0) && (ssd_area->st != 2)) + if ((ssd_area->st != SUBCHANNEL_TYPE_IO) && + (ssd_area->st != SUBCHANNEL_TYPE_MSG)) goto out_free; ssd->path_mask = ssd_area->path_mask; ssd->fla_valid_mask = ssd_area->fla_valid_mask; @@ -132,20 +133,16 @@ static void terminate_internal_io(struct subchannel *sch) device_set_intretry(sch); /* Call handler. */ if (sch->driver && sch->driver->termination) - sch->driver->termination(&sch->dev); + sch->driver->termination(sch); } -static int -s390_subchannel_remove_chpid(struct device *dev, void *data) +static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) { int j; int mask; - struct subchannel *sch; - struct chp_id *chpid; + struct chp_id *chpid = data; struct schib schib; - sch = to_subchannel(dev); - chpid = data; for (j = 0; j < 8; j++) { mask = 0x80 >> j; if ((sch->schib.pmcw.pim & mask) && @@ -158,7 +155,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) spin_lock_irq(sch->lock); stsch(sch->schid, &schib); - if (!schib.pmcw.dnv) + if (!css_sch_is_valid(&schib)) goto out_unreg; memcpy(&sch->schib, &schib, sizeof(struct schib)); /* Check for single path devices. */ @@ -172,12 +169,12 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) terminate_internal_io(sch); /* Re-start path verification. */ if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); } } else { /* trigger path verification. */ if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); else if (sch->lpm == mask) goto out_unreg; } @@ -201,12 +198,10 @@ void chsc_chp_offline(struct chp_id chpid) if (chp_get_status(chpid) <= 0) return; - bus_for_each_dev(&css_bus_type, NULL, &chpid, - s390_subchannel_remove_chpid); + for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid); } -static int -s390_process_res_acc_new_sch(struct subchannel_id schid) +static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) { struct schib schib; /* @@ -252,18 +247,10 @@ static int get_res_chpid_mask(struct chsc_ssd_info *ssd, return 0; } -static int -__s390_process_res_acc(struct subchannel_id schid, void *data) +static int __s390_process_res_acc(struct subchannel *sch, void *data) { int chp_mask, old_lpm; - struct res_acc_data *res_data; - struct subchannel *sch; - - res_data = data; - sch = get_subchannel_by_schid(schid); - if (!sch) - /* Check if a subchannel is newly available. */ - return s390_process_res_acc_new_sch(schid); + struct res_acc_data *res_data = data; spin_lock_irq(sch->lock); chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); @@ -279,10 +266,10 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) if (!old_lpm && sch->lpm) device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); out: spin_unlock_irq(sch->lock); - put_device(&sch->dev); + return 0; } @@ -305,7 +292,8 @@ static void s390_process_res_acc (struct res_acc_data *res_data) * The more information we have (info), the less scanning * will we have to do. */ - for_each_subchannel(__s390_process_res_acc, res_data); + for_each_subchannel_staged(__s390_process_res_acc, + s390_process_res_acc_new_sch, res_data); } static int @@ -499,8 +487,7 @@ void chsc_process_crw(void) } while (sei_area->flags & 0x80); } -static int -__chp_add_new_sch(struct subchannel_id schid) +static int __chp_add_new_sch(struct subchannel_id schid, void *data) { struct schib schib; @@ -514,45 +501,37 @@ __chp_add_new_sch(struct subchannel_id schid) } -static int -__chp_add(struct subchannel_id schid, void *data) +static int __chp_add(struct subchannel *sch, void *data) { int i, mask; - struct chp_id *chpid; - struct subchannel *sch; - - chpid = data; - sch = get_subchannel_by_schid(schid); - if (!sch) - /* Check if the subchannel is now available. */ - return __chp_add_new_sch(schid); + struct chp_id *chpid = data; + spin_lock_irq(sch->lock); for (i=0; i<8; i++) { mask = 0x80 >> i; if ((sch->schib.pmcw.pim & mask) && - (sch->schib.pmcw.chpid[i] == chpid->id)) { - if (stsch(sch->schid, &sch->schib) != 0) { - /* Endgame. */ - spin_unlock_irq(sch->lock); - return -ENXIO; - } + (sch->schib.pmcw.chpid[i] == chpid->id)) break; - } } if (i==8) { spin_unlock_irq(sch->lock); return 0; } + if (stsch(sch->schid, &sch->schib)) { + spin_unlock_irq(sch->lock); + css_schedule_eval(sch->schid); + return 0; + } sch->lpm = ((sch->schib.pmcw.pim & sch->schib.pmcw.pam & sch->schib.pmcw.pom) | mask) & sch->opm; if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); spin_unlock_irq(sch->lock); - put_device(&sch->dev); + return 0; } @@ -564,7 +543,8 @@ void chsc_chp_online(struct chp_id chpid) CIO_TRACE_EVENT(2, dbf_txt); if (chp_get_status(chpid) != 0) - for_each_subchannel(__chp_add, &chpid); + for_each_subchannel_staged(__chp_add, __chp_add_new_sch, + &chpid); } static void __s390_subchannel_vary_chpid(struct subchannel *sch, @@ -589,7 +569,7 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, if (!old_lpm) device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); break; } sch->opm &= ~mask; @@ -603,37 +583,29 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, terminate_internal_io(sch); /* Re-start path verification. */ if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); } } else if (!sch->lpm) { if (device_trigger_verify(sch) != 0) css_schedule_eval(sch->schid); } else if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); break; } spin_unlock_irqrestore(sch->lock, flags); } -static int s390_subchannel_vary_chpid_off(struct device *dev, void *data) +static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data) { - struct subchannel *sch; - struct chp_id *chpid; - - sch = to_subchannel(dev); - chpid = data; + struct chp_id *chpid = data; __s390_subchannel_vary_chpid(sch, *chpid, 0); return 0; } -static int s390_subchannel_vary_chpid_on(struct device *dev, void *data) +static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data) { - struct subchannel *sch; - struct chp_id *chpid; - - sch = to_subchannel(dev); - chpid = data; + struct chp_id *chpid = data; __s390_subchannel_vary_chpid(sch, *chpid, 1); return 0; @@ -643,13 +615,7 @@ static int __s390_vary_chpid_on(struct subchannel_id schid, void *data) { struct schib schib; - struct subchannel *sch; - sch = get_subchannel_by_schid(schid); - if (sch) { - put_device(&sch->dev); - return 0; - } if (stsch_err(schid, &schib)) /* We're through */ return -ENXIO; @@ -669,12 +635,13 @@ int chsc_chp_vary(struct chp_id chpid, int on) * Redo PathVerification on the devices the chpid connects to */ - bus_for_each_dev(&css_bus_type, NULL, &chpid, on ? - s390_subchannel_vary_chpid_on : - s390_subchannel_vary_chpid_off); if (on) - /* Scan for new devices on varied on path. */ - for_each_subchannel(__s390_vary_chpid_on, NULL); + for_each_subchannel_staged(s390_subchannel_vary_chpid_on, + __s390_vary_chpid_on, &chpid); + else + for_each_subchannel_staged(s390_subchannel_vary_chpid_off, + NULL, &chpid); + return 0; } @@ -1075,7 +1042,7 @@ chsc_determine_css_characteristics(void) scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!scsc_area) { - CIO_MSG_EVENT(0, "Was not able to determine available" + CIO_MSG_EVENT(0, "Was not able to determine available " "CHSCs due to no memory.\n"); return -ENOMEM; } diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 46905345159..60590a12d52 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -23,11 +23,12 @@ #include <asm/reset.h> #include <asm/ipl.h> #include <asm/chpid.h> -#include "airq.h" +#include <asm/airq.h> #include "cio.h" #include "css.h" #include "chsc.h" #include "ioasm.h" +#include "io_sch.h" #include "blacklist.h" #include "cio_debug.h" #include "chp.h" @@ -56,39 +57,37 @@ __setup ("cio_msg=", cio_setup); /* * Function: cio_debug_init - * Initializes three debug logs (under /proc/s390dbf) for common I/O: - * - cio_msg logs the messages which are printk'ed when CONFIG_DEBUG_IO is on + * Initializes three debug logs for common I/O: + * - cio_msg logs generic cio messages * - cio_trace logs the calling of different functions - * - cio_crw logs the messages which are printk'ed when CONFIG_DEBUG_CRW is on - * debug levels depend on CONFIG_DEBUG_IO resp. CONFIG_DEBUG_CRW + * - cio_crw logs machine check related cio messages */ -static int __init -cio_debug_init (void) +static int __init cio_debug_init(void) { - cio_debug_msg_id = debug_register ("cio_msg", 16, 4, 16*sizeof (long)); + cio_debug_msg_id = debug_register("cio_msg", 16, 1, 16 * sizeof(long)); if (!cio_debug_msg_id) goto out_unregister; - debug_register_view (cio_debug_msg_id, &debug_sprintf_view); - debug_set_level (cio_debug_msg_id, 2); - cio_debug_trace_id = debug_register ("cio_trace", 16, 4, 16); + debug_register_view(cio_debug_msg_id, &debug_sprintf_view); + debug_set_level(cio_debug_msg_id, 2); + cio_debug_trace_id = debug_register("cio_trace", 16, 1, 16); if (!cio_debug_trace_id) goto out_unregister; - debug_register_view (cio_debug_trace_id, &debug_hex_ascii_view); - debug_set_level (cio_debug_trace_id, 2); - cio_debug_crw_id = debug_register ("cio_crw", 4, 4, 16*sizeof (long)); + debug_register_view(cio_debug_trace_id, &debug_hex_ascii_view); + debug_set_level(cio_debug_trace_id, 2); + cio_debug_crw_id = debug_register("cio_crw", 16, 1, 16 * sizeof(long)); if (!cio_debug_crw_id) goto out_unregister; - debug_register_view (cio_debug_crw_id, &debug_sprintf_view); - debug_set_level (cio_debug_crw_id, 2); + debug_register_view(cio_debug_crw_id, &debug_sprintf_view); + debug_set_level(cio_debug_crw_id, 4); return 0; out_unregister: if (cio_debug_msg_id) - debug_unregister (cio_debug_msg_id); + debug_unregister(cio_debug_msg_id); if (cio_debug_trace_id) - debug_unregister (cio_debug_trace_id); + debug_unregister(cio_debug_trace_id); if (cio_debug_crw_id) - debug_unregister (cio_debug_crw_id); + debug_unregister(cio_debug_crw_id); printk(KERN_WARNING"cio: could not initialize debugging\n"); return -1; } @@ -147,7 +146,7 @@ cio_tpi(void) spin_lock(sch->lock); memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw)); if (sch->driver && sch->driver->irq) - sch->driver->irq(&sch->dev); + sch->driver->irq(sch); spin_unlock(sch->lock); irq_exit (); _local_bh_enable(); @@ -184,33 +183,35 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */ { char dbf_txt[15]; int ccode; + struct orb *orb; - CIO_TRACE_EVENT (4, "stIO"); - CIO_TRACE_EVENT (4, sch->dev.bus_id); + CIO_TRACE_EVENT(4, "stIO"); + CIO_TRACE_EVENT(4, sch->dev.bus_id); + orb = &to_io_private(sch)->orb; /* sch is always under 2G. */ - sch->orb.intparm = (__u32)(unsigned long)sch; - sch->orb.fmt = 1; + orb->intparm = (u32)(addr_t)sch; + orb->fmt = 1; - sch->orb.pfch = sch->options.prefetch == 0; - sch->orb.spnd = sch->options.suspend; - sch->orb.ssic = sch->options.suspend && sch->options.inter; - sch->orb.lpm = (lpm != 0) ? lpm : sch->lpm; + orb->pfch = sch->options.prefetch == 0; + orb->spnd = sch->options.suspend; + orb->ssic = sch->options.suspend && sch->options.inter; + orb->lpm = (lpm != 0) ? lpm : sch->lpm; #ifdef CONFIG_64BIT /* * for 64 bit we always support 64 bit IDAWs with 4k page size only */ - sch->orb.c64 = 1; - sch->orb.i2k = 0; + orb->c64 = 1; + orb->i2k = 0; #endif - sch->orb.key = key >> 4; + orb->key = key >> 4; /* issue "Start Subchannel" */ - sch->orb.cpa = (__u32) __pa (cpa); - ccode = ssch (sch->schid, &sch->orb); + orb->cpa = (__u32) __pa(cpa); + ccode = ssch(sch->schid, orb); /* process condition code */ - sprintf (dbf_txt, "ccode:%d", ccode); - CIO_TRACE_EVENT (4, dbf_txt); + sprintf(dbf_txt, "ccode:%d", ccode); + CIO_TRACE_EVENT(4, dbf_txt); switch (ccode) { case 0: @@ -405,8 +406,8 @@ cio_modify (struct subchannel *sch) /* * Enable subchannel. */ -int -cio_enable_subchannel (struct subchannel *sch, unsigned int isc) +int cio_enable_subchannel(struct subchannel *sch, unsigned int isc, + u32 intparm) { char dbf_txt[15]; int ccode; @@ -425,7 +426,7 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc) for (retry = 5, ret = 0; retry > 0; retry--) { sch->schib.pmcw.ena = 1; sch->schib.pmcw.isc = isc; - sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; + sch->schib.pmcw.intparm = intparm; ret = cio_modify(sch); if (ret == -ENODEV) break; @@ -567,7 +568,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) */ if (sch->st != 0) { CIO_DEBUG(KERN_INFO, 0, - "cio: Subchannel 0.%x.%04x reports " + "Subchannel 0.%x.%04x reports " "non-I/O subchannel type %04X\n", sch->schid.ssid, sch->schid.sch_no, sch->st); /* We stop here for non-io subchannels. */ @@ -576,11 +577,11 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) } /* Initialization for io subchannels. */ - if (!sch->schib.pmcw.dnv) { - /* io subchannel but device number is invalid. */ + if (!css_sch_is_valid(&sch->schib)) { err = -ENODEV; goto out; } + /* Devno is valid. */ if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) { /* @@ -600,7 +601,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) sch->lpm = sch->schib.pmcw.pam & sch->opm; CIO_DEBUG(KERN_INFO, 0, - "cio: Detected device %04x on subchannel 0.%x.%04X" + "Detected device %04x on subchannel 0.%x.%04X" " - PIM = %02X, PAM = %02X, POM = %02X\n", sch->schib.pmcw.dev, sch->schid.ssid, sch->schid.sch_no, sch->schib.pmcw.pim, @@ -680,7 +681,7 @@ do_IRQ (struct pt_regs *regs) sizeof (irb->scsw)); /* Call interrupt handler if there is one. */ if (sch->driver && sch->driver->irq) - sch->driver->irq(&sch->dev); + sch->driver->irq(sch); } if (sch) spin_unlock(sch->lock); @@ -698,8 +699,14 @@ do_IRQ (struct pt_regs *regs) #ifdef CONFIG_CCW_CONSOLE static struct subchannel console_subchannel; +static struct io_subchannel_private console_priv; static int console_subchannel_in_use; +void *cio_get_console_priv(void) +{ + return &console_priv; +} + /* * busy wait for the next interrupt on the console */ @@ -738,9 +745,9 @@ cio_test_for_console(struct subchannel_id schid, void *data) { if (stsch_err(schid, &console_subchannel.schib) != 0) return -ENXIO; - if (console_subchannel.schib.pmcw.dnv && - console_subchannel.schib.pmcw.dev == - console_devno) { + if ((console_subchannel.schib.pmcw.st == SUBCHANNEL_TYPE_IO) && + console_subchannel.schib.pmcw.dnv && + (console_subchannel.schib.pmcw.dev == console_devno)) { console_irq = schid.sch_no; return 1; /* found */ } @@ -758,6 +765,7 @@ cio_get_console_sch_no(void) /* VM provided us with the irq number of the console. */ schid.sch_no = console_irq; if (stsch(schid, &console_subchannel.schib) != 0 || + (console_subchannel.schib.pmcw.st != SUBCHANNEL_TYPE_IO) || !console_subchannel.schib.pmcw.dnv) return -1; console_devno = console_subchannel.schib.pmcw.dev; @@ -804,7 +812,7 @@ cio_probe_console(void) ctl_set_bit(6, 24); console_subchannel.schib.pmcw.isc = 7; console_subchannel.schib.pmcw.intparm = - (__u32)(unsigned long)&console_subchannel; + (u32)(addr_t)&console_subchannel; ret = cio_modify(&console_subchannel); if (ret) { console_subchannel_in_use = 0; @@ -1022,7 +1030,7 @@ static int __reipl_subchannel_match(struct subchannel_id schid, void *data) if (stsch_reset(schid, &schib)) return -ENXIO; - if (schib.pmcw.dnv && + if ((schib.pmcw.st == SUBCHANNEL_TYPE_IO) && schib.pmcw.dnv && (schib.pmcw.dev == match_id->devid.devno) && (schid.ssid == match_id->devid.ssid)) { match_id->schid = schid; @@ -1068,6 +1076,8 @@ int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo) return -ENODEV; if (stsch(schid, &schib)) return -ENODEV; + if (schib.pmcw.st != SUBCHANNEL_TYPE_IO) + return -ENODEV; if (!schib.pmcw.dnv) return -ENODEV; iplinfo->devno = schib.pmcw.dev; diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 7446c39951a..52afa4c784d 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -11,32 +11,32 @@ * path management control word */ struct pmcw { - __u32 intparm; /* interruption parameter */ - __u32 qf : 1; /* qdio facility */ - __u32 res0 : 1; /* reserved zeros */ - __u32 isc : 3; /* interruption sublass */ - __u32 res5 : 3; /* reserved zeros */ - __u32 ena : 1; /* enabled */ - __u32 lm : 2; /* limit mode */ - __u32 mme : 2; /* measurement-mode enable */ - __u32 mp : 1; /* multipath mode */ - __u32 tf : 1; /* timing facility */ - __u32 dnv : 1; /* device number valid */ - __u32 dev : 16; /* device number */ - __u8 lpm; /* logical path mask */ - __u8 pnom; /* path not operational mask */ - __u8 lpum; /* last path used mask */ - __u8 pim; /* path installed mask */ - __u16 mbi; /* measurement-block index */ - __u8 pom; /* path operational mask */ - __u8 pam; /* path available mask */ - __u8 chpid[8]; /* CHPID 0-7 (if available) */ - __u32 unused1 : 8; /* reserved zeros */ - __u32 st : 3; /* subchannel type */ - __u32 unused2 : 18; /* reserved zeros */ - __u32 mbfc : 1; /* measurement block format control */ - __u32 xmwme : 1; /* extended measurement word mode enable */ - __u32 csense : 1; /* concurrent sense; can be enabled ...*/ + u32 intparm; /* interruption parameter */ + u32 qf : 1; /* qdio facility */ + u32 res0 : 1; /* reserved zeros */ + u32 isc : 3; /* interruption sublass */ + u32 res5 : 3; /* reserved zeros */ + u32 ena : 1; /* enabled */ + u32 lm : 2; /* limit mode */ + u32 mme : 2; /* measurement-mode enable */ + u32 mp : 1; /* multipath mode */ + u32 tf : 1; /* timing facility */ + u32 dnv : 1; /* device number valid */ + u32 dev : 16; /* device number */ + u8 lpm; /* logical path mask */ + u8 pnom; /* path not operational mask */ + u8 lpum; /* last path used mask */ + u8 pim; /* path installed mask */ + u16 mbi; /* measurement-block index */ + u8 pom; /* path operational mask */ + u8 pam; /* path available mask */ + u8 chpid[8]; /* CHPID 0-7 (if available) */ + u32 unused1 : 8; /* reserved zeros */ + u32 st : 3; /* subchannel type */ + u32 unused2 : 18; /* reserved zeros */ + u32 mbfc : 1; /* measurement block format control */ + u32 xmwme : 1; /* extended measurement word mode enable */ + u32 csense : 1; /* concurrent sense; can be enabled ...*/ /* ... per MSCH, however, if facility */ /* ... is not installed, this results */ /* ... in an operand exception. */ @@ -52,31 +52,6 @@ struct schib { __u8 mda[4]; /* model dependent area */ } __attribute__ ((packed,aligned(4))); -/* - * operation request block - */ -struct orb { - __u32 intparm; /* interruption parameter */ - __u32 key : 4; /* flags, like key, suspend control, etc. */ - __u32 spnd : 1; /* suspend control */ - __u32 res1 : 1; /* reserved */ - __u32 mod : 1; /* modification control */ - __u32 sync : 1; /* synchronize control */ - __u32 fmt : 1; /* format control */ - __u32 pfch : 1; /* prefetch control */ - __u32 isic : 1; /* initial-status-interruption control */ - __u32 alcc : 1; /* address-limit-checking control */ - __u32 ssic : 1; /* suppress-suspended-interr. control */ - __u32 res2 : 1; /* reserved */ - __u32 c64 : 1; /* IDAW/QDIO 64 bit control */ - __u32 i2k : 1; /* IDAW 2/4kB block size control */ - __u32 lpm : 8; /* logical path mask */ - __u32 ils : 1; /* incorrect length */ - __u32 zero : 6; /* reserved zeros */ - __u32 orbx : 1; /* ORB extension control */ - __u32 cpa; /* channel program address */ -} __attribute__ ((packed,aligned(4))); - /* subchannel data structure used by I/O subroutines */ struct subchannel { struct subchannel_id schid; @@ -85,7 +60,7 @@ struct subchannel { enum { SUBCHANNEL_TYPE_IO = 0, SUBCHANNEL_TYPE_CHSC = 1, - SUBCHANNEL_TYPE_MESSAGE = 2, + SUBCHANNEL_TYPE_MSG = 2, SUBCHANNEL_TYPE_ADM = 3, } st; /* subchannel type */ @@ -99,11 +74,10 @@ struct subchannel { __u8 lpm; /* logical path mask */ __u8 opm; /* operational path mask */ struct schib schib; /* subchannel information block */ - struct orb orb; /* operation request block */ - struct ccw1 sense_ccw; /* static ccw for sense command */ struct chsc_ssd_info ssd_info; /* subchannel description */ struct device dev; /* entry in device tree */ struct css_driver *driver; + void *private; /* private per subchannel type data */ } __attribute__ ((aligned(8))); #define IO_INTERRUPT_TYPE 0 /* I/O interrupt type */ @@ -111,7 +85,7 @@ struct subchannel { #define to_subchannel(n) container_of(n, struct subchannel, dev) extern int cio_validate_subchannel (struct subchannel *, struct subchannel_id); -extern int cio_enable_subchannel (struct subchannel *, unsigned int); +extern int cio_enable_subchannel(struct subchannel *, unsigned int, u32); extern int cio_disable_subchannel (struct subchannel *); extern int cio_cancel (struct subchannel *); extern int cio_clear (struct subchannel *); @@ -125,6 +99,7 @@ extern int cio_get_options (struct subchannel *); extern int cio_modify (struct subchannel *); int cio_create_sch_lock(struct subchannel *); +void do_adapter_IO(void); /* Use with care. */ #ifdef CONFIG_CCW_CONSOLE @@ -133,10 +108,12 @@ extern void cio_release_console(void); extern int cio_is_console(struct subchannel_id); extern struct subchannel *cio_get_console_subchannel(void); extern spinlock_t * cio_get_console_lock(void); +extern void *cio_get_console_priv(void); #else #define cio_is_console(schid) 0 #define cio_get_console_subchannel() NULL -#define cio_get_console_lock() NULL; +#define cio_get_console_lock() NULL +#define cio_get_console_priv() NULL #endif extern int cio_show_msg; diff --git a/drivers/s390/cio/cio_debug.h b/drivers/s390/cio/cio_debug.h index c9bf8989930..d7429ef6c66 100644 --- a/drivers/s390/cio/cio_debug.h +++ b/drivers/s390/cio/cio_debug.h @@ -8,20 +8,19 @@ extern debug_info_t *cio_debug_msg_id; extern debug_info_t *cio_debug_trace_id; extern debug_info_t *cio_debug_crw_id; -#define CIO_TRACE_EVENT(imp, txt) do { \ +#define CIO_TRACE_EVENT(imp, txt) do { \ debug_text_event(cio_debug_trace_id, imp, txt); \ } while (0) -#define CIO_MSG_EVENT(imp, args...) do { \ - debug_sprintf_event(cio_debug_msg_id, imp , ##args); \ +#define CIO_MSG_EVENT(imp, args...) do { \ + debug_sprintf_event(cio_debug_msg_id, imp , ##args); \ } while (0) -#define CIO_CRW_EVENT(imp, args...) do { \ - debug_sprintf_event(cio_debug_crw_id, imp , ##args); \ +#define CIO_CRW_EVENT(imp, args...) do { \ + debug_sprintf_event(cio_debug_crw_id, imp , ##args); \ } while (0) -static inline void -CIO_HEX_EVENT(int level, void *data, int length) +static inline void CIO_HEX_EVENT(int level, void *data, int length) { if (unlikely(!cio_debug_trace_id)) return; @@ -32,9 +31,10 @@ CIO_HEX_EVENT(int level, void *data, int length) } } -#define CIO_DEBUG(printk_level,event_level,msg...) ({ \ - if (cio_show_msg) printk(printk_level msg); \ - CIO_MSG_EVENT (event_level, msg); \ -}) +#define CIO_DEBUG(printk_level, event_level, msg...) do { \ + if (cio_show_msg) \ + printk(printk_level "cio: " msg); \ + CIO_MSG_EVENT(event_level, msg); \ + } while (0) #endif diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index c3df2cd009a..3b45bbe6cce 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -51,6 +51,62 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data) return ret; } +struct cb_data { + void *data; + struct idset *set; + int (*fn_known_sch)(struct subchannel *, void *); + int (*fn_unknown_sch)(struct subchannel_id, void *); +}; + +static int call_fn_known_sch(struct device *dev, void *data) +{ + struct subchannel *sch = to_subchannel(dev); + struct cb_data *cb = data; + int rc = 0; + + idset_sch_del(cb->set, sch->schid); + if (cb->fn_known_sch) + rc = cb->fn_known_sch(sch, cb->data); + return rc; +} + +static int call_fn_unknown_sch(struct subchannel_id schid, void *data) +{ + struct cb_data *cb = data; + int rc = 0; + + if (idset_sch_contains(cb->set, schid)) + rc = cb->fn_unknown_sch(schid, cb->data); + return rc; +} + +int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), + int (*fn_unknown)(struct subchannel_id, + void *), void *data) +{ + struct cb_data cb; + int rc; + + cb.set = idset_sch_new(); + if (!cb.set) + return -ENOMEM; + idset_fill(cb.set); + cb.data = data; + cb.fn_known_sch = fn_known; + cb.fn_unknown_sch = fn_unknown; + /* Process registered subchannels. */ + rc = bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch); + if (rc) + goto out; + /* Process unregistered subchannels. */ + if (fn_unknown) + rc = for_each_subchannel(call_fn_unknown_sch, &cb); +out: + idset_free(cb.set); + + return rc; +} + static struct subchannel * css_alloc_subchannel(struct subchannel_id schid) { @@ -77,7 +133,7 @@ css_alloc_subchannel(struct subchannel_id schid) * This is fine even on 64bit since the subchannel is always located * under 2G. */ - sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; + sch->schib.pmcw.intparm = (u32)(addr_t)sch; ret = cio_modify(sch); if (ret) { kfree(sch->lock); @@ -237,11 +293,25 @@ get_subchannel_by_schid(struct subchannel_id schid) return dev ? to_subchannel(dev) : NULL; } +/** + * css_sch_is_valid() - check if a subchannel is valid + * @schib: subchannel information block for the subchannel + */ +int css_sch_is_valid(struct schib *schib) +{ + if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv) + return 0; + return 1; +} +EXPORT_SYMBOL_GPL(css_sch_is_valid); + static int css_get_subchannel_status(struct subchannel *sch) { struct schib schib; - if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) + if (stsch(sch->schid, &schib)) + return CIO_GONE; + if (!css_sch_is_valid(&schib)) return CIO_GONE; if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) return CIO_REVALIDATE; @@ -293,7 +363,7 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) action = UNREGISTER; if (sch->driver && sch->driver->notify) { spin_unlock_irqrestore(sch->lock, flags); - ret = sch->driver->notify(&sch->dev, event); + ret = sch->driver->notify(sch, event); spin_lock_irqsave(sch->lock, flags); if (ret) action = NONE; @@ -349,7 +419,7 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) /* Will be done on the slow path. */ return -EAGAIN; } - if (stsch_err(schid, &schib) || !schib.pmcw.dnv) { + if (stsch_err(schid, &schib) || !css_sch_is_valid(&schib)) { /* Unusable - ignore. */ return 0; } @@ -388,20 +458,56 @@ static int __init slow_subchannel_init(void) return 0; } -static void css_slow_path_func(struct work_struct *unused) +static int slow_eval_known_fn(struct subchannel *sch, void *data) { - struct subchannel_id schid; + int eval; + int rc; - CIO_TRACE_EVENT(4, "slowpath"); spin_lock_irq(&slow_subchannel_lock); - init_subchannel_id(&schid); - while (idset_sch_get_first(slow_subchannel_set, &schid)) { - idset_sch_del(slow_subchannel_set, schid); - spin_unlock_irq(&slow_subchannel_lock); - css_evaluate_subchannel(schid, 1); - spin_lock_irq(&slow_subchannel_lock); + eval = idset_sch_contains(slow_subchannel_set, sch->schid); + idset_sch_del(slow_subchannel_set, sch->schid); + spin_unlock_irq(&slow_subchannel_lock); + if (eval) { + rc = css_evaluate_known_subchannel(sch, 1); + if (rc == -EAGAIN) + css_schedule_eval(sch->schid); } + return 0; +} + +static int slow_eval_unknown_fn(struct subchannel_id schid, void *data) +{ + int eval; + int rc = 0; + + spin_lock_irq(&slow_subchannel_lock); + eval = idset_sch_contains(slow_subchannel_set, schid); + idset_sch_del(slow_subchannel_set, schid); spin_unlock_irq(&slow_subchannel_lock); + if (eval) { + rc = css_evaluate_new_subchannel(schid, 1); + switch (rc) { + case -EAGAIN: + css_schedule_eval(schid); + rc = 0; + break; + case -ENXIO: + case -ENOMEM: + case -EIO: + /* These should abort looping */ + break; + default: + rc = 0; + } + } + return rc; +} + +static void css_slow_path_func(struct work_struct *unused) +{ + CIO_TRACE_EVENT(4, "slowpath"); + for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn, + NULL); } static DECLARE_WORK(slow_path_work, css_slow_path_func); @@ -430,7 +536,6 @@ void css_schedule_eval_all(void) /* Reprobe subchannel if unregistered. */ static int reprobe_subchannel(struct subchannel_id schid, void *data) { - struct subchannel *sch; int ret; CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n", @@ -438,13 +543,6 @@ static int reprobe_subchannel(struct subchannel_id schid, void *data) if (need_reprobe) return -EAGAIN; - sch = get_subchannel_by_schid(schid); - if (sch) { - /* Already known. */ - put_device(&sch->dev); - return 0; - } - ret = css_probe_device(schid); switch (ret) { case 0: @@ -472,7 +570,7 @@ static void reprobe_all(struct work_struct *unused) /* Make sure initial subchannel scan is done. */ wait_event(ccw_device_init_wq, atomic_read(&ccw_device_init_count) == 0); - ret = for_each_subchannel(reprobe_subchannel, NULL); + ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL); CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret, need_reprobe); @@ -787,8 +885,8 @@ int sch_is_pseudo_sch(struct subchannel *sch) static int css_bus_match (struct device *dev, struct device_driver *drv) { - struct subchannel *sch = container_of (dev, struct subchannel, dev); - struct css_driver *driver = container_of (drv, struct css_driver, drv); + struct subchannel *sch = to_subchannel(dev); + struct css_driver *driver = to_cssdriver(drv); if (sch->st == driver->subchannel_type) return 1; @@ -796,32 +894,36 @@ css_bus_match (struct device *dev, struct device_driver *drv) return 0; } -static int -css_probe (struct device *dev) +static int css_probe(struct device *dev) { struct subchannel *sch; + int ret; sch = to_subchannel(dev); - sch->driver = container_of (dev->driver, struct css_driver, drv); - return (sch->driver->probe ? sch->driver->probe(sch) : 0); + sch->driver = to_cssdriver(dev->driver); + ret = sch->driver->probe ? sch->driver->probe(sch) : 0; + if (ret) + sch->driver = NULL; + return ret; } -static int -css_remove (struct device *dev) +static int css_remove(struct device *dev) { struct subchannel *sch; + int ret; sch = to_subchannel(dev); - return (sch->driver->remove ? sch->driver->remove(sch) : 0); + ret = sch->driver->remove ? sch->driver->remove(sch) : 0; + sch->driver = NULL; + return ret; } -static void -css_shutdown (struct device *dev) +static void css_shutdown(struct device *dev) { struct subchannel *sch; sch = to_subchannel(dev); - if (sch->driver->shutdown) + if (sch->driver && sch->driver->shutdown) sch->driver->shutdown(sch); } @@ -833,6 +935,34 @@ struct bus_type css_bus_type = { .shutdown = css_shutdown, }; +/** + * css_driver_register - register a css driver + * @cdrv: css driver to register + * + * This is mainly a wrapper around driver_register that sets name + * and bus_type in the embedded struct device_driver correctly. + */ +int css_driver_register(struct css_driver *cdrv) +{ + cdrv->drv.name = cdrv->name; + cdrv->drv.bus = &css_bus_type; + cdrv->drv.owner = cdrv->owner; + return driver_register(&cdrv->drv); +} +EXPORT_SYMBOL_GPL(css_driver_register); + +/** + * css_driver_unregister - unregister a css driver + * @cdrv: css driver to unregister + * + * This is a wrapper around driver_unregister. + */ +void css_driver_unregister(struct css_driver *cdrv) +{ + driver_unregister(&cdrv->drv); +} +EXPORT_SYMBOL_GPL(css_driver_unregister); + subsys_initcall(init_channel_subsystem); MODULE_LICENSE("GPL"); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 81215ef3243..b7055452355 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -58,64 +58,6 @@ struct pgid { __u32 tod_high; /* high word TOD clock */ } __attribute__ ((packed)); -#define MAX_CIWS 8 - -/* - * sense-id response buffer layout - */ -struct senseid { - /* common part */ - __u8 reserved; /* always 0x'FF' */ - __u16 cu_type; /* control unit type */ - __u8 cu_model; /* control unit model */ - __u16 dev_type; /* device type */ - __u8 dev_model; /* device model */ - __u8 unused; /* padding byte */ - /* extended part */ - struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */ -} __attribute__ ((packed,aligned(4))); - -struct ccw_device_private { - struct ccw_device *cdev; - struct subchannel *sch; - int state; /* device state */ - atomic_t onoff; - unsigned long registered; - struct ccw_dev_id dev_id; /* device id */ - struct subchannel_id schid; /* subchannel number */ - __u8 imask; /* lpm mask for SNID/SID/SPGID */ - int iretry; /* retry counter SNID/SID/SPGID */ - struct { - unsigned int fast:1; /* post with "channel end" */ - unsigned int repall:1; /* report every interrupt status */ - unsigned int pgroup:1; /* do path grouping */ - unsigned int force:1; /* allow forced online */ - } __attribute__ ((packed)) options; - struct { - unsigned int pgid_single:1; /* use single path for Set PGID */ - unsigned int esid:1; /* Ext. SenseID supported by HW */ - unsigned int dosense:1; /* delayed SENSE required */ - unsigned int doverify:1; /* delayed path verification */ - unsigned int donotify:1; /* call notify function */ - unsigned int recog_done:1; /* dev. recog. complete */ - unsigned int fake_irb:1; /* deliver faked irb */ - unsigned int intretry:1; /* retry internal operation */ - } __attribute__((packed)) flags; - unsigned long intparm; /* user interruption parameter */ - struct qdio_irq *qdio_data; - struct irb irb; /* device status */ - struct senseid senseid; /* SenseID info */ - struct pgid pgid[8]; /* path group IDs per chpid*/ - struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */ - struct work_struct kick_work; - wait_queue_head_t wait_q; - struct timer_list timer; - void *cmb; /* measurement information */ - struct list_head cmb_list; /* list of measured devices */ - u64 cmb_start_time; /* clock value of cmb reset */ - void *cmb_wait; /* deferred cmb enable/disable */ -}; - /* * A css driver handles all subchannels of one type. * Currently, we only care about I/O subchannels (type 0), these @@ -123,25 +65,35 @@ struct ccw_device_private { */ struct subchannel; struct css_driver { + struct module *owner; unsigned int subchannel_type; struct device_driver drv; - void (*irq)(struct device *); - int (*notify)(struct device *, int); - void (*verify)(struct device *); - void (*termination)(struct device *); + void (*irq)(struct subchannel *); + int (*notify)(struct subchannel *, int); + void (*verify)(struct subchannel *); + void (*termination)(struct subchannel *); int (*probe)(struct subchannel *); int (*remove)(struct subchannel *); void (*shutdown)(struct subchannel *); + const char *name; }; +#define to_cssdriver(n) container_of(n, struct css_driver, drv) + /* * all css_drivers have the css_bus_type */ extern struct bus_type css_bus_type; +extern int css_driver_register(struct css_driver *); +extern void css_driver_unregister(struct css_driver *); + extern void css_sch_device_unregister(struct subchannel *); extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); extern int css_init_done; +int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), + int (*fn_unknown)(struct subchannel_id, + void *), void *data); extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); extern void css_process_crw(int, int); extern void css_reiterate_subchannels(void); @@ -188,6 +140,8 @@ void css_schedule_eval(struct subchannel_id schid); void css_schedule_eval_all(void); int sch_is_pseudo_sch(struct subchannel *); +struct schib; +int css_sch_is_valid(struct schib *); extern struct workqueue_struct *slow_path_wq; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 74f6b539974..d35dc3f25d0 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -17,6 +17,7 @@ #include <linux/list.h> #include <linux/device.h> #include <linux/workqueue.h> +#include <linux/timer.h> #include <asm/ccwdev.h> #include <asm/cio.h> @@ -28,6 +29,12 @@ #include "css.h" #include "device.h" #include "ioasm.h" +#include "io_sch.h" + +static struct timer_list recovery_timer; +static spinlock_t recovery_lock; +static int recovery_phase; +static const unsigned long recovery_delay[] = { 3, 30, 300 }; /******************* bus type handling ***********************/ @@ -115,19 +122,18 @@ static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env) struct bus_type ccw_bus_type; -static int io_subchannel_probe (struct subchannel *); -static int io_subchannel_remove (struct subchannel *); -static int io_subchannel_notify(struct device *, int); -static void io_subchannel_verify(struct device *); -static void io_subchannel_ioterm(struct device *); +static void io_subchannel_irq(struct subchannel *); +static int io_subchannel_probe(struct subchannel *); +static int io_subchannel_remove(struct subchannel *); +static int io_subchannel_notify(struct subchannel *, int); +static void io_subchannel_verify(struct subchannel *); +static void io_subchannel_ioterm(struct subchannel *); static void io_subchannel_shutdown(struct subchannel *); static struct css_driver io_subchannel_driver = { + .owner = THIS_MODULE, .subchannel_type = SUBCHANNEL_TYPE_IO, - .drv = { - .name = "io_subchannel", - .bus = &css_bus_type, - }, + .name = "io_subchannel", .irq = io_subchannel_irq, .notify = io_subchannel_notify, .verify = io_subchannel_verify, @@ -142,6 +148,8 @@ struct workqueue_struct *ccw_device_notify_work; wait_queue_head_t ccw_device_init_wq; atomic_t ccw_device_init_count; +static void recovery_func(unsigned long data); + static int __init init_ccw_bus_type (void) { @@ -149,6 +157,7 @@ init_ccw_bus_type (void) init_waitqueue_head(&ccw_device_init_wq); atomic_set(&ccw_device_init_count, 0); + setup_timer(&recovery_timer, recovery_func, 0); ccw_device_work = create_singlethread_workqueue("cio"); if (!ccw_device_work) @@ -166,7 +175,8 @@ init_ccw_bus_type (void) if ((ret = bus_register (&ccw_bus_type))) goto out_err; - if ((ret = driver_register(&io_subchannel_driver.drv))) + ret = css_driver_register(&io_subchannel_driver); + if (ret) goto out_err; wait_event(ccw_device_init_wq, @@ -186,7 +196,7 @@ out_err: static void __exit cleanup_ccw_bus_type (void) { - driver_unregister(&io_subchannel_driver.drv); + css_driver_unregister(&io_subchannel_driver); bus_unregister(&ccw_bus_type); destroy_workqueue(ccw_device_notify_work); destroy_workqueue(ccw_device_work); @@ -773,7 +783,7 @@ static void sch_attach_device(struct subchannel *sch, { css_update_ssd_info(sch); spin_lock_irq(sch->lock); - sch->dev.driver_data = cdev; + sch_set_cdev(sch, cdev); cdev->private->schid = sch->schid; cdev->ccwlock = sch->lock; device_trigger_reprobe(sch); @@ -795,7 +805,7 @@ static void sch_attach_disconnected_device(struct subchannel *sch, put_device(&other_sch->dev); return; } - other_sch->dev.driver_data = NULL; + sch_set_cdev(other_sch, NULL); /* No need to keep a subchannel without ccw device around. */ css_sch_device_unregister(other_sch); put_device(&other_sch->dev); @@ -831,12 +841,12 @@ static void sch_create_and_recog_new_device(struct subchannel *sch) return; } spin_lock_irq(sch->lock); - sch->dev.driver_data = cdev; + sch_set_cdev(sch, cdev); spin_unlock_irq(sch->lock); /* Start recognition for the new ccw device. */ if (io_subchannel_recog(cdev, sch)) { spin_lock_irq(sch->lock); - sch->dev.driver_data = NULL; + sch_set_cdev(sch, NULL); spin_unlock_irq(sch->lock); if (cdev->dev.release) cdev->dev.release(&cdev->dev); @@ -940,7 +950,7 @@ io_subchannel_register(struct work_struct *work) cdev->private->dev_id.devno, ret); put_device(&cdev->dev); spin_lock_irqsave(sch->lock, flags); - sch->dev.driver_data = NULL; + sch_set_cdev(sch, NULL); spin_unlock_irqrestore(sch->lock, flags); kfree (cdev->private); kfree (cdev); @@ -1022,7 +1032,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) int rc; struct ccw_device_private *priv; - sch->dev.driver_data = cdev; + sch_set_cdev(sch, cdev); sch->driver = &io_subchannel_driver; cdev->ccwlock = sch->lock; @@ -1082,7 +1092,7 @@ static void ccw_device_move_to_sch(struct work_struct *work) } if (former_parent) { spin_lock_irq(former_parent->lock); - former_parent->dev.driver_data = NULL; + sch_set_cdev(former_parent, NULL); spin_unlock_irq(former_parent->lock); css_sch_device_unregister(former_parent); /* Reset intparm to zeroes. */ @@ -1096,6 +1106,18 @@ out: put_device(&cdev->dev); } +static void io_subchannel_irq(struct subchannel *sch) +{ + struct ccw_device *cdev; + + cdev = sch_get_cdev(sch); + + CIO_TRACE_EVENT(3, "IRQ"); + CIO_TRACE_EVENT(3, sch->dev.bus_id); + if (cdev) + dev_fsm_event(cdev, DEV_EVENT_INTERRUPT); +} + static int io_subchannel_probe (struct subchannel *sch) { @@ -1104,13 +1126,13 @@ io_subchannel_probe (struct subchannel *sch) unsigned long flags; struct ccw_dev_id dev_id; - if (sch->dev.driver_data) { + cdev = sch_get_cdev(sch); + if (cdev) { /* * This subchannel already has an associated ccw_device. * Register it and exit. This happens for all early * device, e.g. the console. */ - cdev = sch->dev.driver_data; cdev->dev.groups = ccwdev_attr_groups; device_initialize(&cdev->dev); ccw_device_register(cdev); @@ -1132,6 +1154,11 @@ io_subchannel_probe (struct subchannel *sch) */ dev_id.devno = sch->schib.pmcw.dev; dev_id.ssid = sch->schid.ssid; + /* Allocate I/O subchannel private data. */ + sch->private = kzalloc(sizeof(struct io_subchannel_private), + GFP_KERNEL | GFP_DMA); + if (!sch->private) + return -ENOMEM; cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); if (!cdev) cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), @@ -1149,16 +1176,18 @@ io_subchannel_probe (struct subchannel *sch) return 0; } cdev = io_subchannel_create_ccwdev(sch); - if (IS_ERR(cdev)) + if (IS_ERR(cdev)) { + kfree(sch->private); return PTR_ERR(cdev); - + } rc = io_subchannel_recog(cdev, sch); if (rc) { spin_lock_irqsave(sch->lock, flags); - sch->dev.driver_data = NULL; + sch_set_cdev(sch, NULL); spin_unlock_irqrestore(sch->lock, flags); if (cdev->dev.release) cdev->dev.release(&cdev->dev); + kfree(sch->private); } return rc; @@ -1170,25 +1199,25 @@ io_subchannel_remove (struct subchannel *sch) struct ccw_device *cdev; unsigned long flags; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return 0; - cdev = sch->dev.driver_data; /* Set ccw device to not operational and drop reference. */ spin_lock_irqsave(cdev->ccwlock, flags); - sch->dev.driver_data = NULL; + sch_set_cdev(sch, NULL); cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); ccw_device_unregister(cdev); put_device(&cdev->dev); + kfree(sch->private); return 0; } -static int -io_subchannel_notify(struct device *dev, int event) +static int io_subchannel_notify(struct subchannel *sch, int event) { struct ccw_device *cdev; - cdev = dev->driver_data; + cdev = sch_get_cdev(sch); if (!cdev) return 0; if (!cdev->drv) @@ -1198,22 +1227,20 @@ io_subchannel_notify(struct device *dev, int event) return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0; } -static void -io_subchannel_verify(struct device *dev) +static void io_subchannel_verify(struct subchannel *sch) { struct ccw_device *cdev; - cdev = dev->driver_data; + cdev = sch_get_cdev(sch); if (cdev) dev_fsm_event(cdev, DEV_EVENT_VERIFY); } -static void -io_subchannel_ioterm(struct device *dev) +static void io_subchannel_ioterm(struct subchannel *sch) { struct ccw_device *cdev; - cdev = dev->driver_data; + cdev = sch_get_cdev(sch); if (!cdev) return; /* Internal I/O will be retried by the interrupt handler. */ @@ -1231,7 +1258,7 @@ io_subchannel_shutdown(struct subchannel *sch) struct ccw_device *cdev; int ret; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); if (cio_is_console(sch->schid)) return; @@ -1271,6 +1298,9 @@ ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch) { int rc; + /* Attach subchannel private data. */ + sch->private = cio_get_console_priv(); + memset(sch->private, 0, sizeof(struct io_subchannel_private)); /* Initialize the ccw_device structure. */ cdev->dev.parent= &sch->dev; rc = io_subchannel_recog(cdev, sch); @@ -1456,6 +1486,7 @@ int ccw_driver_register(struct ccw_driver *cdriver) drv->bus = &ccw_bus_type; drv->name = cdriver->name; + drv->owner = cdriver->owner; return driver_register(drv); } @@ -1481,6 +1512,60 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev) return sch->schid; } +static int recovery_check(struct device *dev, void *data) +{ + struct ccw_device *cdev = to_ccwdev(dev); + int *redo = data; + + spin_lock_irq(cdev->ccwlock); + switch (cdev->private->state) { + case DEV_STATE_DISCONNECTED: + CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); + dev_fsm_event(cdev, DEV_EVENT_VERIFY); + *redo = 1; + break; + case DEV_STATE_DISCONNECTED_SENSE_ID: + *redo = 1; + break; + } + spin_unlock_irq(cdev->ccwlock); + + return 0; +} + +static void recovery_func(unsigned long data) +{ + int redo = 0; + + bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check); + if (redo) { + spin_lock_irq(&recovery_lock); + if (!timer_pending(&recovery_timer)) { + if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1) + recovery_phase++; + mod_timer(&recovery_timer, jiffies + + recovery_delay[recovery_phase] * HZ); + } + spin_unlock_irq(&recovery_lock); + } else + CIO_MSG_EVENT(2, "recovery: end\n"); +} + +void ccw_device_schedule_recovery(void) +{ + unsigned long flags; + + CIO_MSG_EVENT(2, "recovery: schedule\n"); + spin_lock_irqsave(&recovery_lock, flags); + if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) { + recovery_phase = 0; + mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ); + } + spin_unlock_irqrestore(&recovery_lock, flags); +} + MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ccw_device_set_online); EXPORT_SYMBOL(ccw_device_set_offline); diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 0d408960043..d40a2ffaa00 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -5,6 +5,8 @@ #include <asm/atomic.h> #include <linux/wait.h> +#include "io_sch.h" + /* * states of the device statemachine */ @@ -74,7 +76,6 @@ extern struct workqueue_struct *ccw_device_notify_work; extern wait_queue_head_t ccw_device_init_wq; extern atomic_t ccw_device_init_count; -void io_subchannel_irq (struct device *pdev); void io_subchannel_recog_done(struct ccw_device *cdev); int ccw_device_cancel_halt_clear(struct ccw_device *); @@ -87,6 +88,8 @@ int ccw_device_recognition(struct ccw_device *); int ccw_device_online(struct ccw_device *); int ccw_device_offline(struct ccw_device *); +void ccw_device_schedule_recovery(void); + /* Function prototypes for device status and basic sense stuff. */ void ccw_device_accumulate_irb(struct ccw_device *, struct irb *); void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index bfad421cda6..4b92c84fb43 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -25,14 +25,16 @@ #include "ioasm.h" #include "chp.h" +static int timeout_log_enabled; + int device_is_online(struct subchannel *sch) { struct ccw_device *cdev; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return 0; - cdev = sch->dev.driver_data; return (cdev->private->state == DEV_STATE_ONLINE); } @@ -41,9 +43,9 @@ device_is_disconnected(struct subchannel *sch) { struct ccw_device *cdev; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return 0; - cdev = sch->dev.driver_data; return (cdev->private->state == DEV_STATE_DISCONNECTED || cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID); } @@ -53,19 +55,21 @@ device_set_disconnected(struct subchannel *sch) { struct ccw_device *cdev; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return; - cdev = sch->dev.driver_data; ccw_device_set_timeout(cdev, 0); cdev->private->flags.fake_irb = 0; cdev->private->state = DEV_STATE_DISCONNECTED; + if (cdev->online) + ccw_device_schedule_recovery(); } void device_set_intretry(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); if (!cdev) return; cdev->private->flags.intretry = 1; @@ -75,13 +79,62 @@ int device_trigger_verify(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); if (!cdev || !cdev->online) return -EINVAL; dev_fsm_event(cdev, DEV_EVENT_VERIFY); return 0; } +static int __init ccw_timeout_log_setup(char *unused) +{ + timeout_log_enabled = 1; + return 1; +} + +__setup("ccw_timeout_log", ccw_timeout_log_setup); + +static void ccw_timeout_log(struct ccw_device *cdev) +{ + struct schib schib; + struct subchannel *sch; + struct io_subchannel_private *private; + int cc; + + sch = to_subchannel(cdev->dev.parent); + private = to_io_private(sch); + cc = stsch(sch->schid, &schib); + + printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, " + "device information:\n", get_clock()); + printk(KERN_WARNING "cio: orb:\n"); + print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, + &private->orb, sizeof(private->orb), 0); + printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id); + printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id); + printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, " + "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm); + + if ((void *)(addr_t)private->orb.cpa == &private->sense_ccw || + (void *)(addr_t)private->orb.cpa == cdev->private->iccws) + printk(KERN_WARNING "cio: last channel program (intern):\n"); + else + printk(KERN_WARNING "cio: last channel program:\n"); + + print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, + (void *)(addr_t)private->orb.cpa, + sizeof(struct ccw1), 0); + printk(KERN_WARNING "cio: ccw device state: %d\n", + cdev->private->state); + printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc); + printk(KERN_WARNING "cio: schib:\n"); + print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, + &schib, sizeof(schib), 0); + printk(KERN_WARNING "cio: ccw device flags:\n"); + print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, + &cdev->private->flags, sizeof(cdev->private->flags), 0); +} + /* * Timeout function. It just triggers a DEV_EVENT_TIMEOUT. */ @@ -92,6 +145,8 @@ ccw_device_timeout(unsigned long data) cdev = (struct ccw_device *) data; spin_lock_irq(cdev->ccwlock); + if (timeout_log_enabled) + ccw_timeout_log(cdev); dev_fsm_event(cdev, DEV_EVENT_TIMEOUT); spin_unlock_irq(cdev->ccwlock); } @@ -122,9 +177,9 @@ device_kill_pending_timer(struct subchannel *sch) { struct ccw_device *cdev; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return; - cdev = sch->dev.driver_data; ccw_device_set_timeout(cdev, 0); } @@ -268,7 +323,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) switch (state) { case DEV_STATE_NOT_OPER: CIO_DEBUG(KERN_WARNING, 2, - "cio: SenseID : unknown device %04x on subchannel " + "SenseID : unknown device %04x on subchannel " "0.%x.%04x\n", cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); break; @@ -294,7 +349,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) } /* Issue device info message. */ CIO_DEBUG(KERN_INFO, 2, - "cio: SenseID : device 0.%x.%04x reports: " + "SenseID : device 0.%x.%04x reports: " "CU Type/Mod = %04X/%02X, Dev Type/Mod = " "%04X/%02X\n", cdev->private->dev_id.ssid, @@ -304,7 +359,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) break; case DEV_STATE_BOXED: CIO_DEBUG(KERN_WARNING, 2, - "cio: SenseID : boxed device %04x on subchannel " + "SenseID : boxed device %04x on subchannel " "0.%x.%04x\n", cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); break; @@ -349,7 +404,7 @@ ccw_device_oper_notify(struct work_struct *work) sch = to_subchannel(cdev->dev.parent); if (sch->driver && sch->driver->notify) { spin_unlock_irqrestore(cdev->ccwlock, flags); - ret = sch->driver->notify(&sch->dev, CIO_OPER); + ret = sch->driver->notify(sch, CIO_OPER); spin_lock_irqsave(cdev->ccwlock, flags); } else ret = 0; @@ -389,7 +444,7 @@ ccw_device_done(struct ccw_device *cdev, int state) if (state == DEV_STATE_BOXED) CIO_DEBUG(KERN_WARNING, 2, - "cio: Boxed device %04x on subchannel %04x\n", + "Boxed device %04x on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (cdev->private->flags.donotify) { @@ -500,7 +555,8 @@ ccw_device_recognition(struct ccw_device *cdev) (cdev->private->state != DEV_STATE_BOXED)) return -EINVAL; sch = to_subchannel(cdev->dev.parent); - ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc); + ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc, + (u32)(addr_t)sch); if (ret != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return ret; @@ -587,9 +643,10 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) default: /* Reset oper notify indication after verify error. */ cdev->private->flags.donotify = 0; - if (cdev->online) + if (cdev->online) { + ccw_device_set_timeout(cdev, 0); dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - else + } else ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } @@ -610,7 +667,8 @@ ccw_device_online(struct ccw_device *cdev) sch = to_subchannel(cdev->dev.parent); if (css_init_done && !get_device(&cdev->dev)) return -ENODEV; - ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc); + ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc, + (u32)(addr_t)sch); if (ret != 0) { /* Couldn't enable the subchannel for i/o. Sick device. */ if (ret == -ENODEV) @@ -937,7 +995,7 @@ void device_kill_io(struct subchannel *sch) int ret; struct ccw_device *cdev; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, 3*HZ); @@ -990,7 +1048,8 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) struct subchannel *sch; sch = to_subchannel(cdev->dev.parent); - if (cio_enable_subchannel(sch, sch->schib.pmcw.isc) != 0) + if (cio_enable_subchannel(sch, sch->schib.pmcw.isc, + (u32)(addr_t)sch) != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return; @@ -1006,9 +1065,9 @@ device_trigger_reprobe(struct subchannel *sch) { struct ccw_device *cdev; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return; - cdev = sch->dev.driver_data; if (cdev->private->state != DEV_STATE_DISCONNECTED) return; @@ -1028,7 +1087,7 @@ device_trigger_reprobe(struct subchannel *sch) sch->schib.pmcw.ena = 0; if ((sch->lpm & (sch->lpm - 1)) != 0) sch->schib.pmcw.mp = 1; - sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; + sch->schib.pmcw.intparm = (u32)(addr_t)sch; /* We should also udate ssd info, but this has to wait. */ /* Check if this is another device which appeared on the same sch. */ if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { @@ -1223,21 +1282,4 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { }, }; -/* - * io_subchannel_irq is called for "real" interrupts or for status - * pending conditions on msch. - */ -void -io_subchannel_irq (struct device *pdev) -{ - struct ccw_device *cdev; - - cdev = to_subchannel(pdev)->dev.driver_data; - - CIO_TRACE_EVENT (3, "IRQ"); - CIO_TRACE_EVENT (3, pdev->bus_id); - if (cdev) - dev_fsm_event(cdev, DEV_EVENT_INTERRUPT); -} - EXPORT_SYMBOL_GPL(ccw_device_set_timeout); diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c index 156f3f9786b..918b8b89cf9 100644 --- a/drivers/s390/cio/device_id.c +++ b/drivers/s390/cio/device_id.c @@ -24,6 +24,7 @@ #include "css.h" #include "device.h" #include "ioasm.h" +#include "io_sch.h" /* * Input : @@ -219,11 +220,13 @@ ccw_device_check_sense_id(struct ccw_device *cdev) return -EAGAIN; } if (irb->scsw.cc == 3) { - if ((sch->orb.lpm & - sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0) + u8 lpm; + + lpm = to_io_private(sch)->orb.lpm; + if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0) CIO_MSG_EVENT(2, "SenseID : path %02X for device %04x " "on subchannel 0.%x.%04x is " - "'not operational'\n", sch->orb.lpm, + "'not operational'\n", lpm, cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); return -EACCES; diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 7fd2dadc329..49b58eb0fab 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -501,7 +501,7 @@ ccw_device_stlck(struct ccw_device *cdev) return -ENOMEM; } spin_lock_irqsave(sch->lock, flags); - ret = cio_enable_subchannel(sch, 3); + ret = cio_enable_subchannel(sch, 3, (u32)(addr_t)sch); if (ret) goto out_unlock; /* diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index cb1879a9681..c52449a1f9f 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -22,6 +22,7 @@ #include "css.h" #include "device.h" #include "ioasm.h" +#include "io_sch.h" /* * Helper function called from interrupt context to decide whether an @@ -155,10 +156,13 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev) return -EAGAIN; } if (irb->scsw.cc == 3) { + u8 lpm; + + lpm = to_io_private(sch)->orb.lpm; CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x," " lpm %02X, became 'not operational'\n", cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, sch->orb.lpm); + sch->schid.sch_no, lpm); return -EACCES; } i = 8 - ffs(cdev->private->imask); diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c index aa96e675259..ebe0848cfe3 100644 --- a/drivers/s390/cio/device_status.c +++ b/drivers/s390/cio/device_status.c @@ -20,6 +20,7 @@ #include "css.h" #include "device.h" #include "ioasm.h" +#include "io_sch.h" /* * Check for any kind of channel or interface control check but don't @@ -310,6 +311,7 @@ int ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb) { struct subchannel *sch; + struct ccw1 *sense_ccw; sch = to_subchannel(cdev->dev.parent); @@ -326,15 +328,16 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb) /* * We have ending status but no sense information. Do a basic sense. */ - sch->sense_ccw.cmd_code = CCW_CMD_BASIC_SENSE; - sch->sense_ccw.cda = (__u32) __pa(cdev->private->irb.ecw); - sch->sense_ccw.count = SENSE_MAX_COUNT; - sch->sense_ccw.flags = CCW_FLAG_SLI; + sense_ccw = &to_io_private(sch)->sense_ccw; + sense_ccw->cmd_code = CCW_CMD_BASIC_SENSE; + sense_ccw->cda = (__u32) __pa(cdev->private->irb.ecw); + sense_ccw->count = SENSE_MAX_COUNT; + sense_ccw->flags = CCW_FLAG_SLI; /* Reset internal retry indication. */ cdev->private->flags.intretry = 0; - return cio_start (sch, &sch->sense_ccw, 0xff); + return cio_start(sch, sense_ccw, 0xff); } /* diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h new file mode 100644 index 00000000000..8c613160bfc --- /dev/null +++ b/drivers/s390/cio/io_sch.h @@ -0,0 +1,163 @@ +#ifndef S390_IO_SCH_H +#define S390_IO_SCH_H + +#include "schid.h" + +/* + * operation request block + */ +struct orb { + u32 intparm; /* interruption parameter */ + u32 key : 4; /* flags, like key, suspend control, etc. */ + u32 spnd : 1; /* suspend control */ + u32 res1 : 1; /* reserved */ + u32 mod : 1; /* modification control */ + u32 sync : 1; /* synchronize control */ + u32 fmt : 1; /* format control */ + u32 pfch : 1; /* prefetch control */ + u32 isic : 1; /* initial-status-interruption control */ + u32 alcc : 1; /* address-limit-checking control */ + u32 ssic : 1; /* suppress-suspended-interr. control */ + u32 res2 : 1; /* reserved */ + u32 c64 : 1; /* IDAW/QDIO 64 bit control */ + u32 i2k : 1; /* IDAW 2/4kB block size control */ + u32 lpm : 8; /* logical path mask */ + u32 ils : 1; /* incorrect length */ + u32 zero : 6; /* reserved zeros */ + u32 orbx : 1; /* ORB extension control */ + u32 cpa; /* channel program address */ +} __attribute__ ((packed, aligned(4))); + +struct io_subchannel_private { + struct orb orb; /* operation request block */ + struct ccw1 sense_ccw; /* static ccw for sense command */ +} __attribute__ ((aligned(8))); + +#define to_io_private(n) ((struct io_subchannel_private *)n->private) +#define sch_get_cdev(n) (dev_get_drvdata(&n->dev)) +#define sch_set_cdev(n, c) (dev_set_drvdata(&n->dev, c)) + +#define MAX_CIWS 8 + +/* + * sense-id response buffer layout + */ +struct senseid { + /* common part */ + u8 reserved; /* always 0x'FF' */ + u16 cu_type; /* control unit type */ + u8 cu_model; /* control unit model */ + u16 dev_type; /* device type */ + u8 dev_model; /* device model */ + u8 unused; /* padding byte */ + /* extended part */ + struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */ +} __attribute__ ((packed, aligned(4))); + +struct ccw_device_private { + struct ccw_device *cdev; + struct subchannel *sch; + int state; /* device state */ + atomic_t onoff; + unsigned long registered; + struct ccw_dev_id dev_id; /* device id */ + struct subchannel_id schid; /* subchannel number */ + u8 imask; /* lpm mask for SNID/SID/SPGID */ + int iretry; /* retry counter SNID/SID/SPGID */ + struct { + unsigned int fast:1; /* post with "channel end" */ + unsigned int repall:1; /* report every interrupt status */ + unsigned int pgroup:1; /* do path grouping */ + unsigned int force:1; /* allow forced online */ + } __attribute__ ((packed)) options; + struct { + unsigned int pgid_single:1; /* use single path for Set PGID */ + unsigned int esid:1; /* Ext. SenseID supported by HW */ + unsigned int dosense:1; /* delayed SENSE required */ + unsigned int doverify:1; /* delayed path verification */ + unsigned int donotify:1; /* call notify function */ + unsigned int recog_done:1; /* dev. recog. complete */ + unsigned int fake_irb:1; /* deliver faked irb */ + unsigned int intretry:1; /* retry internal operation */ + } __attribute__((packed)) flags; + unsigned long intparm; /* user interruption parameter */ + struct qdio_irq *qdio_data; + struct irb irb; /* device status */ + struct senseid senseid; /* SenseID info */ + struct pgid pgid[8]; /* path group IDs per chpid*/ + struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */ + struct work_struct kick_work; + wait_queue_head_t wait_q; + struct timer_list timer; + void *cmb; /* measurement information */ + struct list_head cmb_list; /* list of measured devices */ + u64 cmb_start_time; /* clock value of cmb reset */ + void *cmb_wait; /* deferred cmb enable/disable */ +}; + +static inline int ssch(struct subchannel_id schid, volatile struct orb *addr) +{ + register struct subchannel_id reg1 asm("1") = schid; + int ccode; + + asm volatile( + " ssch 0(%2)\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); + return ccode; +} + +static inline int rsch(struct subchannel_id schid) +{ + register struct subchannel_id reg1 asm("1") = schid; + int ccode; + + asm volatile( + " rsch\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); + return ccode; +} + +static inline int csch(struct subchannel_id schid) +{ + register struct subchannel_id reg1 asm("1") = schid; + int ccode; + + asm volatile( + " csch\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); + return ccode; +} + +static inline int hsch(struct subchannel_id schid) +{ + register struct subchannel_id reg1 asm("1") = schid; + int ccode; + + asm volatile( + " hsch\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); + return ccode; +} + +static inline int xsch(struct subchannel_id schid) +{ + register struct subchannel_id reg1 asm("1") = schid; + int ccode; + + asm volatile( + " .insn rre,0xb2760000,%1,0\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); + return ccode; +} + +#endif diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h index 7153dd95908..652ea3625f9 100644 --- a/drivers/s390/cio/ioasm.h +++ b/drivers/s390/cio/ioasm.h @@ -109,72 +109,6 @@ static inline int tpi( volatile struct tpi_info *addr) return ccode; } -static inline int ssch(struct subchannel_id schid, - volatile struct orb *addr) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " ssch 0(%2)\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); - return ccode; -} - -static inline int rsch(struct subchannel_id schid) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " rsch\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : "d" (reg1) : "cc"); - return ccode; -} - -static inline int csch(struct subchannel_id schid) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " csch\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : "d" (reg1) : "cc"); - return ccode; -} - -static inline int hsch(struct subchannel_id schid) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " hsch\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : "d" (reg1) : "cc"); - return ccode; -} - -static inline int xsch(struct subchannel_id schid) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " .insn rre,0xb2760000,%1,0\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : "d" (reg1) : "cc"); - return ccode; -} - static inline int chsc(void *chsc_area) { typedef struct { char _[4096]; } addr_type; diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 40a3208c7cf..e2a781b6b21 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -48,11 +48,11 @@ #include <asm/debug.h> #include <asm/s390_rdev.h> #include <asm/qdio.h> +#include <asm/airq.h> #include "cio.h" #include "css.h" #include "device.h" -#include "airq.h" #include "qdio.h" #include "ioasm.h" #include "chsc.h" @@ -96,7 +96,7 @@ static debug_info_t *qdio_dbf_slsb_in; static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change during a while loop */ static DEFINE_SPINLOCK(ttiq_list_lock); -static int register_thinint_result; +static void *tiqdio_ind; static void tiqdio_tl(unsigned long); static DECLARE_TASKLET(tiqdio_tasklet,tiqdio_tl,0); @@ -399,7 +399,7 @@ qdio_get_indicator(void) { int i; - for (i=1;i<INDICATORS_PER_CACHELINE;i++) + for (i = 0; i < INDICATORS_PER_CACHELINE; i++) if (!indicator_used[i]) { indicator_used[i]=1; return indicators+i; @@ -1408,8 +1408,7 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set) if (q->hydra_gives_outbound_pcis) { if (!q->siga_sync_done_on_thinints) { SYNC_MEMORY_ALL; - } else if ((!q->siga_sync_done_on_outb_tis)&& - (q->hydra_gives_outbound_pcis)) { + } else if (!q->siga_sync_done_on_outb_tis) { SYNC_MEMORY_ALL_OUTB; } } else { @@ -1911,8 +1910,7 @@ qdio_fill_thresholds(struct qdio_irq *irq_ptr, } } -static int -tiqdio_thinint_handler(void) +static void tiqdio_thinint_handler(void *ind, void *drv_data) { QDIO_DBF_TEXT4(0,trace,"thin_int"); @@ -1925,7 +1923,6 @@ tiqdio_thinint_handler(void) tiqdio_clear_global_summary(); tiqdio_inbound_checks(); - return 0; } static void @@ -2445,7 +2442,7 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero) real_addr_dev_st_chg_ind=0; } else { real_addr_local_summary_bit= - virt_to_phys((volatile void *)indicators); + virt_to_phys((volatile void *)tiqdio_ind); real_addr_dev_st_chg_ind= virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind); } @@ -3740,23 +3737,25 @@ static void tiqdio_register_thinints(void) { char dbf_text[20]; - register_thinint_result= - s390_register_adapter_interrupt(&tiqdio_thinint_handler); - if (register_thinint_result) { - sprintf(dbf_text,"regthn%x",(register_thinint_result&0xff)); + + tiqdio_ind = + s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL); + if (IS_ERR(tiqdio_ind)) { + sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_ind)); QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_PRINT_ERR("failed to register adapter handler " \ - "(rc=%i).\nAdapter interrupts might " \ + "(rc=%li).\nAdapter interrupts might " \ "not work. Continuing.\n", - register_thinint_result); + PTR_ERR(tiqdio_ind)); + tiqdio_ind = NULL; } } static void tiqdio_unregister_thinints(void) { - if (!register_thinint_result) - s390_unregister_adapter_interrupt(&tiqdio_thinint_handler); + if (tiqdio_ind) + s390_unregister_adapter_interrupt(tiqdio_ind); } static int @@ -3768,8 +3767,8 @@ qdio_get_qdio_memory(void) for (i=1;i<INDICATORS_PER_CACHELINE;i++) indicator_used[i]=0; indicators = kzalloc(sizeof(__u32)*(INDICATORS_PER_CACHELINE), - GFP_KERNEL); - if (!indicators) + GFP_KERNEL); + if (!indicators) return -ENOMEM; return 0; } @@ -3780,7 +3779,6 @@ qdio_release_qdio_memory(void) kfree(indicators); } - static void qdio_unregister_dbf_views(void) { diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 6d7aad18f6f..37870e4e938 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -57,7 +57,7 @@ of the queue to 0 */ #define QDIO_ESTABLISH_TIMEOUT (1*HZ) -#define QDIO_ACTIVATE_TIMEOUT ((5*HZ)>>10) +#define QDIO_ACTIVATE_TIMEOUT (5*HZ) #define QDIO_CLEANUP_CLEAR_TIMEOUT (20*HZ) #define QDIO_CLEANUP_HALT_TIMEOUT (10*HZ) #define QDIO_FORCE_CHECK_TIMEOUT (10*HZ) diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 3561982749e..c3076217871 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -2416,7 +2416,7 @@ init_ccw_bk(struct net_device *dev) privptr->p_buff_pages_perwrite); #endif if (p_buff==NULL) { - printk(KERN_INFO "%s:%s __get_free_pages" + printk(KERN_INFO "%s:%s __get_free_pages " "for writes buf failed : get is for %d pages\n", dev->name, __FUNCTION__, diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 0fd663b23d7..7bfe8d707a3 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1115,7 +1115,7 @@ list_modified: rc = lcs_send_setipm(card, ipm); spin_lock_irqsave(&card->ipm_lock, flags); if (rc) { - PRINT_INFO("Adding multicast address failed." + PRINT_INFO("Adding multicast address failed. " "Table possibly full!\n"); /* store ipm in failed list -> will be added * to ipm_list again, so a retry will be done diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index d6e93f15440..f3d893cfe61 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -198,8 +198,7 @@ struct iucv_connection { /** * Linked list of all connection structs. */ -static struct list_head iucv_connection_list = - LIST_HEAD_INIT(iucv_connection_list); +static LIST_HEAD(iucv_connection_list); static DEFINE_RWLOCK(iucv_connection_rwlock); /** diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c index f1ff165a5e0..46ecd03a597 100644 --- a/drivers/s390/net/qeth_proc.c +++ b/drivers/s390/net/qeth_proc.c @@ -146,7 +146,7 @@ qeth_procfile_seq_show(struct seq_file *s, void *it) return 0; } -static struct seq_operations qeth_procfile_seq_ops = { +static const struct seq_operations qeth_procfile_seq_ops = { .start = qeth_procfile_seq_start, .stop = qeth_procfile_seq_stop, .next = qeth_procfile_seq_next, @@ -264,7 +264,7 @@ qeth_perf_procfile_seq_show(struct seq_file *s, void *it) return 0; } -static struct seq_operations qeth_perf_procfile_seq_ops = { +static const struct seq_operations qeth_perf_procfile_seq_ops = { .start = qeth_procfile_seq_start, .stop = qeth_procfile_seq_stop, .next = qeth_procfile_seq_next, diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index 47bb47b4858..8735a415a11 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c @@ -42,7 +42,7 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver"); static struct iucv_path *smsg_path; static DEFINE_SPINLOCK(smsg_list_lock); -static struct list_head smsg_list = LIST_HEAD_INIT(smsg_list); +static LIST_HEAD(smsg_list); static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]); static void smsg_message_pending(struct iucv_path *, struct iucv_message *); diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 4f86c0e1296..2dc8110ebf7 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -1286,7 +1286,7 @@ zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action) * note: no lock in subsequent strategy routines * (this allows these routine to call schedule, e.g. * kmalloc with such flags or qdio_initialize & friends) - * Note: in case of timeout, the seperate strategies will fail + * Note: in case of timeout, the separate strategies will fail * anyhow. No need for a special action. Even worse, a nameserver * failure would not wake up waiting ports without the call. */ diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index fe57941ab55..e45f85f7c7e 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -502,7 +502,7 @@ zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *fsf_req) fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_SQ_NO_RECOM: - ZFCP_LOG_NORMAL("bug: No recommendation could be given for a" + ZFCP_LOG_NORMAL("bug: No recommendation could be given for a " "problem on the adapter %s " "Stopping all operations on this adapter. ", zfcp_get_busid_by_adapter(fsf_req->adapter)); @@ -813,7 +813,7 @@ zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req) read_unlock_irqrestore(&zfcp_data.config_lock, flags); if (!port || (port->d_id != (status_buffer->d_id & ZFCP_DID_MASK))) { - ZFCP_LOG_NORMAL("bug: Reopen port indication received for" + ZFCP_LOG_NORMAL("bug: Reopen port indication received for " "nonexisting port with d_id 0x%06x on " "adapter %s. Ignored.\n", status_buffer->d_id & ZFCP_DID_MASK, @@ -2281,7 +2281,7 @@ zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action) &lock_flags, &fsf_req); if (retval) { ZFCP_LOG_INFO("error: Out of resources. Could not create an " - "exchange port data request for" + "exchange port data request for " "the adapter %s.\n", zfcp_get_busid_by_adapter(adapter)); write_unlock_irqrestore(&adapter->request_queue.queue_lock, @@ -2340,7 +2340,7 @@ zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter, 0, NULL, &lock_flags, &fsf_req); if (retval) { ZFCP_LOG_INFO("error: Out of resources. Could not create an " - "exchange port data request for" + "exchange port data request for " "the adapter %s.\n", zfcp_get_busid_by_adapter(adapter)); write_unlock_irqrestore(&adapter->request_queue.queue_lock, @@ -4725,7 +4725,7 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags, /* allocate new FSF request */ fsf_req = zfcp_fsf_req_alloc(pool, req_flags); if (unlikely(NULL == fsf_req)) { - ZFCP_LOG_DEBUG("error: Could not put an FSF request into" + ZFCP_LOG_DEBUG("error: Could not put an FSF request into " "the outbound (send) queue.\n"); ret = -ENOMEM; goto failed_fsf_req; diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 51d92b196ee..22fdc17e0d0 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -529,7 +529,7 @@ zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *fsf_req) /** - * zfcp_qdio_sbale_fill - set address and lenght in current SBALE + * zfcp_qdio_sbale_fill - set address and length in current SBALE * on request_queue */ static void diff --git a/drivers/serial/21285.c b/drivers/serial/21285.c index facb6785561..6a48dfa1efe 100644 --- a/drivers/serial/21285.c +++ b/drivers/serial/21285.c @@ -277,6 +277,8 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_iflag & INPCK) port->read_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY; + tty_encode_baud_rate(tty, baud, baud); + /* * Which character status flags should we ignore? */ diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index 6f475b60986..ac2a3ef28d5 100644 --- a/drivers/serial/bfin_5xx.c +++ b/drivers/serial/bfin_5xx.c @@ -442,7 +442,8 @@ static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP, INTR_ON_BUF, DIMENSION_LINEAR, - DATA_SIZE_8)); + DATA_SIZE_8, + DMA_SYNC_RESTART)); set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail)); set_dma_x_count(uart->tx_dma_channel, uart->tx_count); set_dma_x_modify(uart->tx_dma_channel, 1); @@ -689,7 +690,8 @@ static int bfin_serial_startup(struct uart_port *port) set_dma_config(uart->rx_dma_channel, set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO, INTR_ON_ROW, DIMENSION_2D, - DATA_SIZE_8)); + DATA_SIZE_8, + DMA_SYNC_RESTART)); set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT); set_dma_x_modify(uart->rx_dma_channel, 1); set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f81d08d6538..77a3759d6fc 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -308,7 +308,7 @@ config USB_S3C2410_DEBUG config USB_GADGET_AT91 boolean "AT91 USB Device Port" - depends on ARCH_AT91 && !ARCH_AT91SAM9RL + depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 select USB_GADGET_SELECTED help Many Atmel AT91 processors (such as the AT91RM2000) have a diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 5cfa3d1c441..74e1f4be10b 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -47,7 +47,7 @@ #endif #ifdef CONFIG_TPS65010 -#include <asm/arch/tps65010.h> +#include <linux/i2c/tps65010.h> #else #define LOW 0 diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index ca2a6abbc11..6c52c66b659 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -112,9 +112,9 @@ static int isp1301_detach(struct i2c_client *client); static int isp1301_command(struct i2c_client *client, unsigned int cmd, void *arg); -static unsigned short normal_i2c[] = +static const unsigned short normal_i2c[] = { ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END }; -static unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END }; +static const unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END }; static struct i2c_client_address_data addr_data = { .normal_i2c = normal_i2c, @@ -123,7 +123,6 @@ static struct i2c_client_address_data addr_data = { }; struct i2c_driver isp1301_driver = { - .id = I2C_DRIVERID_I2CDEV, /* Fake Id */ .class = I2C_CLASS_HWMON, .attach_adapter = isp1301_probe, .detach_client = isp1301_detach, diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 5b3dbcfcda4..758435f8a6f 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -889,7 +889,7 @@ config FB_S1D13XXX config FB_ATMEL tristate "AT91/AT32 LCD Controller support" - depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || AVR32) + depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9 || AVR32) select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 7c30cc8df71..f8e71114750 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -30,7 +30,7 @@ #define ATMEL_LCDC_CVAL_DEFAULT 0xc8 #define ATMEL_LCDC_DMA_BURST_LEN 8 -#if defined(CONFIG_ARCH_AT91SAM9263) +#if defined(CONFIG_ARCH_AT91SAM9263) || defined(CONFIG_ARCH_AT91CAP9) #define ATMEL_LCDC_FIFO_SIZE 2048 #else #define ATMEL_LCDC_FIFO_SIZE 512 diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c index 74d11c31898..c8e7427a0bc 100644 --- a/drivers/video/bf54x-lq043fb.c +++ b/drivers/video/bf54x-lq043fb.c @@ -224,7 +224,8 @@ static int config_dma(struct bfin_bf54xfb_info *fbi) set_dma_config(CH_EPPI0, set_bfin_dma_config(DIR_READ, DMA_FLOW_AUTO, INTR_DISABLE, DIMENSION_2D, - DATA_SIZE_32)); + DATA_SIZE_32, + DMA_NOSYNC_KEEP_DMA_BUF)); set_dma_x_count(CH_EPPI0, (LCD_X_RES * LCD_BPP) / DMA_BUS_SIZE); set_dma_x_modify(CH_EPPI0, DMA_BUS_SIZE / 8); set_dma_y_count(CH_EPPI0, LCD_Y_RES); @@ -263,8 +264,7 @@ static int request_ports(struct bfin_bf54xfb_info *fbi) } } - gpio_direction_output(disp); - gpio_set_value(disp, 1); + gpio_direction_output(disp, 1); return 0; } diff --git a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c index 49cd53e46c0..0cd58f84fb4 100644 --- a/drivers/video/matrox/matroxfb_maven.c +++ b/drivers/video/matrox/matroxfb_maven.c @@ -1232,7 +1232,7 @@ static int maven_shutdown_client(struct i2c_client* clnt) { return 0; } -static unsigned short normal_i2c[] = { MAVEN_I2CID, I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { MAVEN_I2CID, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; static struct i2c_driver maven_driver; diff --git a/drivers/video/omap/lcd_h3.c b/drivers/video/omap/lcd_h3.c index c604d935c18..31e978349a8 100644 --- a/drivers/video/omap/lcd_h3.c +++ b/drivers/video/omap/lcd_h3.c @@ -21,9 +21,9 @@ #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/i2c/tps65010.h> #include <asm/arch/gpio.h> -#include <asm/arch/tps65010.h> #include <asm/arch/omapfb.h> #define MODULE_NAME "omapfb-lcd_h3" diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index d93eb626b2f..0fd5820d5c6 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -29,7 +29,7 @@ * However, the chip cannot be detected without doing an i2c write, * so use the force module parameter. */ -static unsigned short normal_i2c[] = {I2C_CLIENT_END}; +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; /** * Insmod parameters |